【图解Java】这下可以真的弄懂Java IO了~

写在前面

本文是建立在网上各类关于IO博客、学习中的实体书以及自己的使用经验总结的。参考文献在文末给出,若有原创作者自己的内容不愿意在本文中发布,请私信告知。

Java IO(Stream)

1.什么是流?

抽象的来说:流是一种在Java中对文件操作的形式,是Java内存中的一组有序数据序列。

具体的来说:Java程序将数据从源(文件、内存、键盘、网络)读入到内存中,形成了流,然后将这些流还可以写到另外的目的地(文件、内存、控制台、网络)。

2.为什么称为流?

之所以称为流,是因为这个数据序列在不同时刻可能代表不同的数据来源。

3.为什么使用流?

流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图 。通过使用流,我们可以说明想要完成什么任务,而不是说明如何去实现它

一、IO流的分类和概念

在这里插入图片描述

备注:①按列是依据操作方向(输入输出)出来分类;②按行是依据操作对象(字符字节)来分类;③带红框的是节点流,意味外面的处理流

分类字节输入流字节输出流字符输入流字符输出流
抽象基类InputStreamOutputStreamReaderWriter
访问文件FileInputStreamFileOutputStreamFileReaderFileWriter
访问数组ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
访问管道PipedInputStreamPipedOutputStreamPipedReaderPipedWriter
访问字符串StringReaderStringWriter
缓冲流BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
转换流InputStreamReaderOutputStreamWriter
对象流ObjectInputStreamObjectOutputStream
抽象基类FilterInputStreamFilterOutputStreamFilterReaderFilterWriter
打印流PrintStreamPrintWriter
推回输入流PushbackInputStreamPushbackReader
特殊流DataInputStreamDataOutputStream

【带着问题去思考】

  1. 为什么转换流只存在转为字符型
  2. 为什么对象流只存在字节型
  3. 处理流和节点流的关系是什么
  4. IO可以处理哪些类型
1.1 流的分类

按输入输出方向划分

  • 输入流:数据外存→内存
  • 输出流:数据内存→外存

按处理单元划分(可以想成字节处理ASCII码很强,字符流处理文本)

  • 字节流:1Byte
  • 字符流:2Byte

按是否直接作用于外部数据可以划分

  • 节点流:可以从一个特定的设备读写数据的流
    • 特点:当使用节点流进行输入输出时,程序直接连接到实际数据,和实际的输入输出节点连接。没有节点流是无法读到数据的
  • 处理流:对一个已存在的类进行连接和封装,封装后的刘实现读写功能。
    • 特点:①程序不会与数据直接接触;②处理流可以重复使用,只要处理流不变,程序的输入输出就可以不变

其中字节流和字符流、输入流和输出流图解如下。
在这里插入图片描述

流的原理和解析

​ 对于InputStream和Reader而言,它们把输入设备抽象成为一个”水管“,这个水管的每个“水滴”依次排列:
​ 从下图可以看出,字节流和字符流的处理方式其实很相似,只是它们处理的输入/输出单位不同而已。输入流使用隐式的记录指针来表示当前正准备从哪个“水滴”开始读取,每当程序从InputStream或者Reader里面取出一个或者多个“水滴”后,记录指针自定向后移动;除此之外,InputStream和Reader里面都提供了一些方法来控制记录指针的移动。

在这里插入图片描述

  • 性能的提高:主要以增加的方式来提供输入和输出的效率
  • 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的内容,而不是输入/输出一个或者多个水滴。

在这里插入图片描述

处理流可以套接在任何已存在的流的基础之基础上,这就允许Java采用相同的代码,透明的方式来访问不同的输入和输出设备的数据流。

在这里插入图片描述

二、IO流的常见用法

这是输入输出流(节点流)的程序流程模型

在这里插入图片描述

2.1 这里是输入流的一些读取方法

前面说过InputStream和Reader都是抽象类,本身不能创建实例,但它们分别有一个用于读取文件的输入流:FileInputStream和FileReader,它们都是节点流——会直接和指定文件关联。下面程序示范使用FileInputStream和FileReader。

在这里插入图片描述

(1)在InputStream里面包含如下3个方法。

  • int read():从输入流中读取单个字节,返回所读取的字节数据(字节数据可直接转换为int类型)。
  • int read(byte[] b):从输入流中最多读取(b.length)个字节的数据,并将其直接存储在字节数组b中,返回实际读取的字节数。
  • int read(byte[] b,int off,int len): 从输入流中最多读取 len 个字节的数据,并将其存储在数组b中,放入数组b中时。off并不是从数组起点开始,而是从off位置开始,返回实际读取的字节数。

==结果:==上述三种方法的结果基本一致:读取到缓冲区的总**字节**数,如果没有更多的数据,因为已经到达流的末尾,那么 -1 。 如果在读取中线问题,则抛出IOException。(如果用如下代码读取字节型,可以进行;但是含有中文字符,会输出乱码)

public class InputStreamTest {
    public static void main(String[] args) throws IOException{
        //创建流
        FileInputStream fileInputStream = null;

        try {
            fileInputStream = new FileInputStream("src/配置填写说明.docx");
            //创建一个长度为1024的水管
            byte[] b = new byte[1024];
            //用于保存的实际字节数
            int hasReader = 0;
            //使用循环来取水的过程
            while((hasReader = fileInputStream.read(b))>0){
                System.out.println(new String(b,0,hasReader));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            fileInputStream.close();
        }
    }
}

(2)在Reader中包含如下3个方法。

  • int read():从输入流中读取单个字符,返回所读取的字符数据(字节数据可直接转换为int类型)。——但这个方法有一个限制,就是:字符读取,作为0到65535( 0x00-0xffff ,即2的16次方)范围内的整数,如果已经达到流的末尾,则为-1。
  • int read(char[] b):从输入流中最多读取b.length个字符的数据,并将其存储在字节数组b中,返回实际读取的字符数。
  • int read(char[] b,int off,int len): 从输入流中最多读取len个字符的数据,并将其存储在数组b中,放入数组b中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数。

==结果:==上述三种方法的结果基本一致:读取到缓冲区的总**字符**数,如果没有更多的数据,因为已经到达流的末尾,那么 -1 。 如果在读取中线问题,则抛出IOException。

public class ReaderTest {
    public static void main(String[] args) throws IOException {
        //创建一个流
        
        FileReader fileReader = null;
        String file = "src/配置填写说明.docx";
        //选择需要读取的对象
        try {
            fileReader = new FileReader(file);
            //创建一个长度为1024的字符水管
            char[] c = new char[204800];
            //用于保存的实际字符数
            int hasReader = 0;
            while((hasReader = fileReader.read(c))>0){
                System.out.println(new String(c,0,hasReader));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            //关闭流
            fileReader.close();
        }

    }
}

【总结】通过比较可以得到如下结论:

  1. 以上是两种面向不同的处理单元(InputStream是面向字节流的,Reader是面向字符流的)

  2. 二者的方法是十分相似的

  3. 二者都需要抛出IOException

  4. 单纯使用这InputStream方法读取数据会有乱码问题(即读取中文的时候有乱码问题,比如“水管不够长”)

    我们知道,一个中文字符是占据两个字节的,InputStream中的字节数组不够,在读取读取最后一个字节是,发现正好是中文字符的一半。由于数据还没有读完且数组还有一个字节的空位,所以read会继续往后读。读取中文字符的一半也就出现了乱码

2.2 提供的一些移动指针的方法

InputStream和Reader共用的:

  • void mark(int readAheadLimit):在记录指针当前位置记录一个标记(mark)。
  • boolean markSupported():判断此输入流是否支持mark()操作,即是否支持记录标记。
  • void reset():将此流的记录指针重新定位到上一次记录标记(mark)的位置。
  • long skip(long n):记录指针向前移动n个字节/字符。
2.3 这里是输出流的一些写出方法

在这里插入图片描述

OutputStream和Writer的用法也非常相似,两个流都提供了如下三个方法:

  • void write(int c):将指定的字节/字符输出到输出流中,其中c即可以代表字节,也可以代表字符。
  • void write(byte[]/char[] buf):将字节数组/字符数组中的数据输出到指定输出流中。
  • void write(byte[]/char[] buf, int off,int len ):将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中。

因为字符流直接以字符作为操作单位,所以Writer可以用字符串来代替字符数组,即以String对象作为参数。Writer里面还包含如下两个方法。

  • void write(String str); 将str字符串里包含的字符输出到指定输出流中。
  • void write (String str, int off, int len); 将str字符串里面从off位置开始,长度为len的字符输出到指定输出流中。

FileOutputStream/FileWriter是Io中的文件输出流,下面介绍这两个类的用法。

FileOutputStream的用法:

public class OutputStreamTest {
    public static void main(String[] args) throws IOException {
        //创建一个输入流和输出流
        FileInputStream fis = null;
        FileOutputStream fos = null;

        try {
            //找到水源
            //读取数据
            fis = new FileInputStream("src/main/resources/data/配置填写说明.txt");
            //写到一个新文件中
            fos = new FileOutputStream("src/main/resources/data/OutputStream.txt");
            //创建一根水管
            byte[] b = new byte[1024];
            int hasByte = 0;
            while((hasByte = fis.read(b)) > 0){
                fos.write(b,0,hasByte);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            fis.close();
            fos.close();
        }
    }
}

运行程序,可以看到在输出目录会出现OutputStream.txt文件,里面的内容与InputStream.txt内容没有差别,

注: 使用java的io流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,可能还可以将输出流缓冲区中的数据flush到物理节点中里(因为在执行close()方法之前,自动执行输出流的flush()方法)。java很多输出流默认都提供了缓存功能,其实我们没有必要刻意去记忆哪些流有缓存功能,哪些流没有,只有正常关闭所有的输出流即可保证程序正常。

【总结】而FileWrite与FileOutputStream用法是一样的,①这两者这是操作对象的单位不同而已。另外,②在“水管”不够长的情况下,若读取以字符为单位的文件或者其他内容时,FileOutputStream获取会出现乱码

2.4 缓冲流的使用

(1)缓冲流是什么?

缓冲流是处理流的一种。它依赖于节点流(也就是直接作用到输入输出对象的IO流),它令节点流具有1个缓冲区,显著减少与外部设备的IO次数, 而且提供一些额外的方法。这里为什么说设置缓冲区可以减少与外部设备IO次数并提高效率在操作系统中会说到。涉及到中断、总线和一些解决机制。

(2)为什么使用缓冲流

  1. 减少设备与IO交互的次数(提升性能),内存和外存的交互是非常消耗时间的

  2. 使用一些缓冲流的额外的方法,提高编程效率。

  3. 缓冲流有预读机制,比起使用缓冲数组的缓冲效果更加明显,如果处理一些大数据文件,或者网络传输, 使用缓冲流的效果会更加好!

(3)如何使用缓冲流

缓冲流和前面几个节点流类似,就是在节点流的基础上套一层缓冲处理流而已。然后用这个缓冲流去完成各类操作。

缓冲流的使用(BufferedInputStream/BufferedReader, BufferedOutputStream/BufferedWriter):
下面介绍字节缓存流的 用法(字符缓存流的用法和字节缓存流一致就不介绍了):

public class BufferStream {
    public static void main(String[] args) throws IOException {
        //创建输入流、输出流、缓冲输入流、缓冲输出流
        FileInputStream fis = null;
        FileOutputStream fos = null;
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;

        try {
            //找到水源
            fis = new FileInputStream("IO/src/main/resources/data/InputStream.txt");
            fos = new FileOutputStream("IO/src/main/resources/data/BufferedOutputStream.txt");
            //使用缓冲流,当时由于它是处理流,所以要建立在节点流之上
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            //建立水管
            byte[] b = new byte[1024];
            int hasRead = 0;

            while((hasRead =bis.read(b))>0){
                bos.write(b,0,hasRead);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            bis.close();
            bos.close();
        }
    }
}

可以看到使用字节缓存流读取和写入数据的方式和文件流(FileInputStream,FileOutputStream)并没有什么不同,只是把处理流套接到文件流上进行读写。缓存流的原理下节介绍。

上面代码中我们使用了缓存流和文件流,但是我们只关闭了缓存流。这个需要注意一下,当我们使用处理流套接到节点流上的使用的时候,只需要关闭最上层的处理就可以了。java会自动帮我们关闭下层的节点流。

2.5 转换流的使用

(1)什么是转换流?

转换流就是用来实现将字节流转化成字符流,字符流与字节流之间的桥梁。

(2)为什么要使用转换流?

存在将字节流转换成字符流的转换流,因为字符流操作文本更简单;不存在把字符流转换成字节流的转换流,因为没有必要。

(3)如何使用转换流?

与前面类似的,转换流也有两种,分别对应了输入和输出:InputStreamReader/OutputStreamWriter。当我们遇到不大方便的输入、输出类型的时候,比如在读取的时候,要将字节输入流转换为字符输入流,就需要采用转换流InputStreamReader将其包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行内容。其中,OutputStreamWriter用于将字节输出流转换为字符输出流。如下代码所示:

public class InputStreamReaderTest {
    public static void main(String[] args) throws IOException {
        //创建流
        InputStreamReader isr = null;
        OutputStreamWriter osw = null;
        BufferedReader br = null;
        //BufferedWriter bw = new BufferedWriter(new FileWriter("IO/src/main/resources/data/BufferedWriter.txt"));
        //上面是写出到本地,下面是写出到控制台
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));
        try {
            //从键盘读入数据并写出到本地,此处已经把节点流转为处理流了
            //isr = new InputStreamReader(new FileInputStream(System.in));
            //上面是从键盘读入数据,下面是从文件中读入数据
            isr = new InputStreamReader(new FileInputStream("IO/src/main/resources/data/InputStream.txt"));
            //将转换流嵌套在缓冲流内,保证IO效率
            br = new BufferedReader(isr);
            String line = null;
            while ((line = (br.readLine())) != null) {
                if("\r".equals(line)){
                    break;
                }
                bw.write(line);
            }
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }finally {
            br.close();
            bw.close();
        }
    }
}

上面程序将System.in包装成BufferedReader,BufferedReader流具有缓存功能,在代码的注释也已经提到了,它可以一次读取一行文本(这也是一种十分好用的方法,有些IO类是没有这种方法的)——在设计时,以换行符为标志,如果它没有读到换行符,则程序堵塞,等到读到换行符为止。运行上面程序可以发现这个特征,当我们在控制台执行输入时,只有按下回车键,程序才会打印出刚刚输入的内容。同样的,从文件中读入数据也是这样一种状态,从文件中读入数据,同样的用BufferedReader包装,在读取完文件中每行数据时,就在控制台输出。

2.6 对象流的使用

(1)什么是对象流?

对象流就是将Java对象序列化(持久化),并保存到本地磁盘文件,或者将磁盘文件反序列化成Java对象。

在这里插入图片描述

(2)为什么使用对象流?

用于写入对象的信息和读取对象的信息,使得对象持久化。

【注意】为什么要做这些操作呢?请看实例下面的解释

  1. 如果想将一个对象写入到磁盘中,那么对象所属的类必须要进行序列化,实现Serializable 接口,Serializable接口没有任何方法,是一个标记接口
  2. 如果对象所属的类的成员变量发生改变,你在读取原来的对象是就会报错,如果想要解决报错,保证serialVersionUID是唯一
  3. 如果你不想将某些信息存入到磁盘 就可以同过transient关键字修饰成员变量
  4. 如果一个类中引用了另外的一个类,那么另外的这个类也要实现Serializable接口。

(3)如何使用对象流?

(ObjectInputStream/ObjectOutputStream)

序列化:将一个对象写入到本地文件中

反序列化:将一个本地文件中的对象读取出来

在这里插入图片描述

//创建要写入磁盘的类,这个类需要实现接口 Serializable(可系列化的)
class Student implements Serializable{
    // 在这里保证了serialVersionUID 的唯一性,防止属性变量的临时改变,从而造成写入id与读取id不同
    private static final long serialVersionUID = 1L;
    int id ; //额外需要添加一个属性
    private String name;
    private int age;
    private String gender;

    public Student(){
    }

    public  Student(String name,int age,String gender){
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}
public class ObjectIOStream {
    public static void main(String[] args) throws ClassNotFoundException {
        createObj();
        readObj();
    }

    public static void createObj() {
        try {
            //创建写入磁盘的位置,并用ObjectOutputStream流处理
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
                    "IO/src/main/resources/data/ObjectIOStream.txt"));
            //创建对象
            Student ffideak = new Student("ffideal", 22, "男");
            //向目标路径文件写入对象
            oos.writeObject(ffideak);
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void readObj() throws ClassNotFoundException {
        try {
            //找到读取对象的位置
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
                    "IO/src/main/resources/data/ObjectIOStream.txt"));
            Student ffideal = (Student) ois.readObject();
            System.out.println("name:"+ffideal.getName()+",age:"+ffideal.getAge()+",gendar:"+ffideal.getGender());
            ois.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【解释】

1.为什么要使用Serializable接口?

说到对象序列化,必须要讲到Serializable接口。Java对象所属的类必须实现Serializable接口。实现这个接口不必重写任何方法,其只作为可序列化的标识。

在这里建议:每一个实现Serializable接口的对象类,需要显示添加一个常数serialVersionUID,表明该类的版本。如果不添加该常数,对象序列化时会根据对象类的信息计算出默认的serialVersionUID的值。

serialVersionUID就像我们的身份证一样,每次序列化时,java编译器会根据serialVersionUID确认java对象类。这个时候,serialVersionUID的值就会出现不同(前一次写入不一样的serialVersionUID值),那么在反序列化时,如果serialVersionUID不一致,会抛出InvalidClassException异常。

使用对象流的一些注意事项
1.读取顺序和写入顺序一定要一致,不然会读取出错。
2.在对象属性前面加transient关键字,则该对象的属性不会被序列化。

三、何为NIO,和传统Io有何区别?

我们使用InputStream从输入流中读取数据时,如果没有读取到有效的数据,程序将在此处阻塞该线程的执行。其实传统的输入里和输出流都是阻塞式的进行输入和输出。 不仅如此,传统的输入流、输出流都是通过字节的移动来处理的(即使我们不直接处理字节流,但底层实现还是依赖于字节处理),也就是说,面向流的输入和输出一次只能处理一个字节,因此面向流的输入和输出系统效率通常不高。
    从JDk1.4开始,java提供了一系列改进的输入和输出处理的新功能,这些功能被统称为新IO(NIO)。新增了许多用于处理输入和输出的类,这些类都被放在java.nio包及其子包下,并且对原io的很多类都以NIO为基础进行了改写。新增了满足NIO的功能。
    NIO采用了内存映射对象的方式来处理输入和输出,NIO将文件或者文件的一块区域映射到内存中,这样就可以像访问内存一样来访问文件了。通过这种方式来进行输入/输出比传统的输入和输出要快的多。

JDk1.4使用NIO改写了传统IO后,传统IO的读写速度和NIO差不了太多。

四、在开发中正确使用Io流

了解了java io的整体类结构和每个类的一下特性后,我们可以在开发的过程中根据需要灵活的使用不同的Io流进行开发。下面是我整理2点原则:

  • 如果是操作二进制文件那我们就使用字节流,如果操作的是文本文件那我们就使用字符流。
  • 尽可能的多使用处理流,这会使我们的代码更加灵活,复用性更好。

五、总结(图解)

5.1 节点流

1) 输入字节流InputStream

  • **ByteArrayInputStream、StringBufferInputStream、FileInputStream:**是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
  • PipedInputStream: 是从与其它线程共用的管道中读取数据。PipedInputStream的一个实例要和PipedOutputStream的一个实例共同使用,共同完成管道的读取写入操作。主要用于线程操作。
  • DataInputStream: 将基础数据类型读取出来
  • ObjectInputStream 和所有 FilterInputStream 的子类都是装饰流(装饰器模式的主角)。

2)输出字节流OutputStream:

  • ByteArrayOutputStreamFileOutputStream: 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
  • PipedOutputStream: 是向与其它线程共用的管道中写入数据。
  • DataOutputStream: 将基础数据类型写入到文件中
  • ObjectOutputStream: 和所有 FilterOutputStream 的子类都是装饰流。

节点流的输入和输出类结构图

在这里插入图片描述

5.2 字符流

3)字符输入流Reader::

  • FileReader、CharReader、StringReader 是三种基本的介质流,它们分在本地文件、Char 数组、String中读取数据。
  • PipedReader:是从与其它线程共用的管道中读取数据
  • BufferedReader :加缓冲功能,避免频繁读写硬盘
  • InputStreamReader: 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。

4)字符输出流Writer:

  • StringWriter:向String 中写入数据。
  • CharArrayWriter:实现一个可用作字符输入流的字符缓冲区
  • PipedWriter:是向与其它线程共用的管道中写入数据
  • BufferedWriter : 增加缓冲功能,避免频繁读写硬盘。
  • PrintWriterPrintStream 将对象的格式表示打印到文本输出流。 极其类似,功能和使用也非常相似
  • OutputStreamWriter: 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似,后面会有它们的对应图。

字符流的输入和输出类结构图
在这里插入图片描述

六、补充一些面试题

1. 什么是IO流?
它是一种数据的流从源头流到目的地。比如文件拷贝,输入流和输出流都包括了。输入流从文件中读取数据存储到进程(process)中,输出流从进程中读取数据然后写入到目标文件。

2. 字节流和字符流的区别。
字节流在JDK1.0中就被引进了,用于操作包含ASCII字符的文件。JAVA也支持其他的字符如Unicode,为了读取包含Unicode字符的文件,JAVA语言设计者在JDK1.1中引入了字符流。ASCII作为Unicode的子集,对于英语字符的文件,可以可以使用字节流也可以使用字符流。

3.Java中流类的超类主要由那些?

  - java.io.InputStream
  - java.io.OutputStream
  - java.io.Reader
  - java.io.Writer

4. FileInputStream和FileOutputStream是什么?

这是在拷贝文件操作的时候,经常用到的两个类。在处理小文件的时候,它们性能表现还不错,在大文件的时候,最好使用BufferedInputStream (或 BufferedReader) 和 BufferedOutputStream (或 BufferedWriter)

5. 字节流和字符流,你更喜欢使用拿一个?
个人来说,更喜欢使用字符流,因为他们更新一些。许多在字符流中存在的特性,字节流中不存在。比如使用BufferedReader而不是BufferedInputStreams或DataInputStream,使用newLine()方法来读取下一行,但是在字节流中我们需要做额外的操作。

6.System.out.println()是什么?
println是PrintStream的一个方法。out是一个静态PrintStream类型的成员变量,System是一个java.lang包中的类,用于和底层的操作系统进行交互。

7.什么是Filter流?
Filter Stream是一种IO流主要作用是用来对存在的流增加一些额外的功能,像给目标文件增加源文件中不存在的行数,或者增加拷贝的性能。

8. 有哪些可用的Filter流?
在java.io包中主要由4个可用的filter Stream。两个字节filter stream,两个字符filter stream. 分别是FilterInputStream, FilterOutputStream, FilterReader and FilterWriter.这些类是抽象类,不能被实例化的。

有些Filter流的子类:
- LineNumberInputStream 给目标文件增加行号
- DataInputStream 有些特殊的方法如readInt(), readDouble()readLine() 等可以读取一个 int, double和一个string一次性的,
- BufferedInputStream 增加性能
- PushbackInputStream 推送要求的字节到系统中

9.SequenceInputStream的作用?
这个类的作用是将多个输入流合并成一个输入流,通过SequenceInputStream类包装后形成新的一个总的输入流。在拷贝多个文件到一个目标文件的时候是非常有用的。可用使用很少的代码实现

10.说说PrintStream和PrintWriter
他们两个的功能相同,但是属于不同的分类。字节流和字符流。他们都有println()方法。

11. 在文件拷贝的时候,那一种流可用提升更多的性能?
在字节流的时候,使用BufferedInputStream和BufferedOutputStream。
在字符流的时候,使用BufferedReader 和 BufferedWriter

12 .说说管道流(Piped Stream)
有四种管道流, PipedInputStream, PipedOutputStream, PipedReader 和 PipedWriter.在多个线程或进程中传递数据的时候管道流非常有用。

13.说说File类
它不属于 IO流,也不是用于文件操作的,它主要用于知道一个文件的属性,读写权限,大小等信息。注意:Java7中文件IO发生了很大的变化,专门引入了很多新的类来取代原来的基于java.io.File的文件IO操作方式。

14. 说说RandomAccessFile?

它在java.io包中是一个特殊的类,既不是输入流也不是输出流,它两者都可以做到。他是Object的直接子类。通常来说,一个流只有一个功能,要么读,要么写。但是RandomAccessFile既可以读文件,也可以写文件。 DataInputStream 和 DataOutStream有的方法,在RandomAccessFile中都存在。

参考资料(同致谢!)

java IO体系的学习总结

Java IO,硬骨头也能变软

Java IO常见面试题

Java 缓冲流简介及简单用法

java 对象流的简单使用

jdk api_1.8中文参考

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_之桐_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值