「java技术分享」一篇彻底读懂java中的IO流

一、IO概念

• I/O 即输入Input/ 输出Output的缩写,其实就是计算机调度把各个存储中(包括内存和外部存储)的数据写入写出的过程;

• java中用“流(stream)”来抽象表示这么一个写入写出的功能,封装成一个“类”,都放在java.io这个包里面。

二、来理解“流”是什么?

通过“流”的形式允许java程序使用相同的方式来访问不同的输入/输出源。stream是从起源(source)到接收的(sink)的有序数据。我们这里把输入/输出源对比成“水桶”,那么流就是“管道”,这个“管道”的粗细、单向性等属性也就是区分了不同“流”的特性。

image

三、IO流的分类

可以从三个不同的维度进行分类:

• 1、按照流的方向(输出输入都是站在程序所在内存的角度划分的)

• 输入流:只能从中读取数据【主要由InputStream和Reader作为基类】

• 输出流:只能向其写入数据【主要由outputStream和Writer作为基类】

在下图中,从磁盘读取数据到内存是输入流,从client读取数据到server是输入流;同样,把内存数据写到磁盘是输出流,把server数据写到client是输出流

image

• 2、按照流的操作颗粒度划分

• 字节流:以字节为单元,可操作任何数据【主要由InputStream和outPutStream作为基类】

• 字符流:以字符为单元,只能操作纯字符数据,比较方便【主要由Reader和Writer作为基类】

• 3、按照流的角色划分

• 节点流:可以从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,也叫【低级流,主要由】

• 处理流:用于对一个已存在的流进行连接和封装,通过封装后的流来实现数据的读/写功能,也叫【高级流】

下图中可以看出来,处理流就是在基础的字节流上,进行了封装,增加了特定的功能,使得传输更适合特定的场景。

image

四、流的原理以及一共有多少IO流

• 1、流的原理解析

流其实我们可以想象成一个“水管”,源端和目的端就是两个“水桶”,数据是通过这个“水管”进行流动传输的,以InputStream和Reader为例,水管的每个“水滴”就是具体的数据,如果是字节流,那么一个“水滴”就是一个字节,如果是字符流,那么一个“水滴”就是一个字符。

当创建一个流对象的时候,如fis=new FileInputStream(“…\xx\xx.txt”),记录指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InputStream或者Reader里面取出一个或者多个“水滴”后,记录指针自定向后移动;除此之外,InputStream和Reader里面都提供了一些方法来控制记录指针的移动。

image

如果是处理流的话,就相当于在这个水管上面装了一些“控制阀门”,最终用户只要关心“阀门”具备的能力就行

image

• 2、java中所有流汇总

java.io中子类有40个“流”类,我们用以下表格来综合划分,当然这些流你不用都去花时间一个一个看过来,我们只要熟悉掌握几类常用的“流”就足够了,后续项目中如果用到陌生的“流”,知道原理的话可以快速地去检索学习下就ok了。

image

上图中我们可以看到,InputStream/Reader,OutputStream/Writer 是整个I/O体系的基类,他们本身不能用来创建实例,下面开始逐一对常见的I/O类进行实战

五、常见IO流的实战

1、访问操作文件(FileInputStream/FileReader ,FileOutputStream/FileWriter)

FileInputStream中包含以使用FileInputStream为例,类中包含的属性及方法,我们可以在线查看jdk的api文档http://tool.oschina.net/apidocs/apidoc?api=jdk-zh),下面只是截图了FileInputStream的构造方法:

image

1)使用FileInputStream,从文件读取数据

import java.io.*;
public class TestFileImportStream {

    public static void main(String[] args) {
        int b=0;
        FileInputStream in = null;
        try {
        in =new FileInputStream("C:\\Users\\41639\\Desktop\\java\\FileText\\src\\TestFileImportStream.java");
        }catch(FileNotFoundException e){
            System.out.println("file is not found");
            System.exit(-1);
        }
        try {
            long num=0;
            while ((b=in.read())!=-1) {
                System.out.println((char)b);
                num++;
            }
            in.close();
            System.out.println();
            System.out.println("共读取了"+num+"个字节");
        }catch(IOException e) {
            System.out.println("IO异常,读取失败");
            System.exit(-1);
        }
    }

}

2)使用FileOutputStream,往文件里写数据

<import java.io.*;
public class TextFileOutputStream {

    public static void main(String[] args) {
        int b=0;
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
        in =new FileInputStream("C:\\Users\\41639\\Desktop\\java\\FileText\\src\\TestFileImportStream.java");
        out=new FileOutputStream("C:\\Users\\41639\\Desktop\\java\\temp\\out.java");
        }catch(FileNotFoundException e){
            System.out.println("file is not found");
            System.exit(-1);
        }
        try {
            while ((b=in.read())!=-1) {
                out.write(b);
            }
            in.close();
            out.close();
        }catch(IOException e) {
            System.out.println("IO异常,读取失败");
            System.exit(-1);
        }
        System.out.println("文件复制完成");
    }

}

PS:大家也可以尝试使用FileReader、FileWriter,看看和FileInputStream、FileOutputStream有什么区别

2、缓存流的使用(BufferedInputStream/BufferedOutputStream,BufferedReader/BufferedWriter)

import java.io.*;
public class TestBufferStream {
    public static void main (String[] args) throws IOException{
        BufferedInputStream bis=null;
        BufferedOutputStream bos=null;
        try {
            FileInputStream fis = new FileInputStream("C:\\Users\\41639\\Desktop\\java\\FileText\\src\\TestFileImportStream.java");
            FileOutputStream fos = new FileOutputStream("C:\\Users\\41639\\Desktop\\java\\temp\\out2.java");
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            byte[] b = new byte[1024];
            int off=0;
            while ((off=bis.read(b))>0) {
                bos.write(b,0,off);
            }
            bis.close();
            bos.close();
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            bis.close();
            bos.close();
        }       
    }
}

从代码中可以看到,他们最基本的其实也是FileInputStream和FileOutputStream,在这个“流”的基础上,又加了缓存的功能流BufferedInputStream和BufferedOutputStream。相对比较好理解。

3、转换流的使用(InputStreamReader/OutputStreamWriter)

字面意思理解,转化流就是用来转化的,那么到底是什么转什么呢?我们可以通过以下的例子来熟悉。读取键盘输入的每一行内容,并写入到文本中,直到遇到over行结束输入

import java.io.*;
public class TransStreamTest {

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\Users\\41639\\Desktop\\java\\temp\\test1031.txt"));
        String line =null;
        while ((line=br.readLine())!=null) {
            if ("over".contentEquals(line)) {
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
    bw.close();
    br.close();
    }

}

PS:readLine()方法在进行读取一行时,只有遇到回车(\r)或者换行符(\n)才会返回读取结果,这就是“读取一行的意思”,有兴趣的同学查看readLine()源码

4、对象流的使用(FileInputStream/ObjectOutputStream)

前面讲了字节和字符流,包括封装在他们上面的处理流,那么我们想,在程序设计的过程中,我们都是用类和对象来描述定义,能不能直接把对象进行传输。答案当然是肯定的,对象流其实就是一种特殊的处理流水,也是在基础的字节流上去作封装。【可以应用于游戏存盘】

下面程序使用一个对象流,把对象直接写到文件中

import java.io.*;
public class ObjectStreamTest {
    public static void main(String[] args) throws Exception{
        try {
            Person P=new Person("Jeccica",26);
            FileOutputStream fos=new FileOutputStream("C:\\Users\\admin\\Desktop\\Java\\temp\\22.txt");
            ObjectOutputStream oos=new ObjectOutputStream(fos);
            oos.writeObject(P);
            oos.flush();
            oos.close();
        }catch(FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e) {
            e.printStackTrace();
        }           
        
        FileInputStream fis=new FileInputStream("C:\\Users\\admin\\Desktop\\Java\\temp\\22.txt");
        ObjectInputStream ois=new ObjectInputStream(fis);
        Person P2=(Person)ois.readObject();
        System.out.println(P2.name+"的年龄为"+P2.age);
    }

}

class Person implements Serializable{
    String name=null;
    int age=0;
    Person(String _name,int _age){
        name=_name;
        age=_age;
    }
}

5、字节数组流的使用(ByteArrayInputStream/ByteArrayOutputStream)【通常结合数据流DataInputStream/DataOutputStream】

我们分析了常见的节点流和常见的处理流等,经常而言我们都是针对文件的操作,然后带上缓冲的节点流进行处理,但有时候为了提升效率,我们发现频繁的读写文件并不是太好,那么于是出现了字节数组流,即存放在内存中,因此又称之为内存流;其中字节数组流也一种节点流;除了节点流外,我们也将学习另外一种处理流,即数据流。数据处理流是用于针对数据类型传输处理的,是一种处理流,即是在节点流之上的增强处理,一般用于序列化和反序列化的时候用到。

import java.io.*;
public class DataStream {
    public static void main(String[] args) {
        ByteArrayOutputStream baos=new ByteArrayOutputStream();//创建字节数组流,同时会在内存里面创建数组
        DataOutputStream dos=new DataOutputStream(baos);//对字节数组流外封装成数据处理流
        try {
            dos.writeDouble(Math.random());//利用数据流里面的写入方法,写一个Double类型的随机数据
            dos.writeBoolean(true);
            ByteArrayInputStream bias=new ByteArrayInputStream(baos.toByteArray());//toByteArray()方法是创建一个新分配的字节数组。数组的大小和当前输出流的大小。这里指的是baos这个字节数组
            System.out.println(bias.available());
            DataInputStream dis=new DataInputStream(bias);
            System.out.println(dis.readDouble());
            System.out.println(dis.readBoolean());      
            dos.close();
            dis.close();
        }catch (IOException e) {
            e.printStackTrace();
        }
    }
}

感谢你看到这里,我是程序员麦冬,一个java开发从业者,深耕行业六年了,每天都会分享java相关技术文章或行业资讯

欢迎大家关注和转发文章,后期还有福利赠送!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343