Java I/O 常用的实现类

FileInputStream与FileoutputStream

FileInputStream类可以将一个文件的内容作为字节流读取,我们看一下源码:

  • FileDescriptor 文件描述符
  • FileChannel Java Nio FileChannel 是一个连接文件的通道,通过这个文件通道可以从文件读数据,向文件里写数据,是Java nio的替代方法,可以使用Java IO API进行读取文件
  • native操作
    • initIDs
    • open0 打开
    • available 剩余可用字节数;如遇结尾返回0;
    • read0 读取字节
    • close0 关闭
  • 构造:
    • 以文件路径构建
    • 以文件对象构建
    • 以文件描述符构建

FileoutputStream是使用字节流方式将数据写入文件,实现与FileinputStream一样,一个是字节流方式读文件,一个是字节流方式写文件。

需要注意的是,FileInputStream和FileoutputStream是直接从文件中读取数据到应用程序内存中,或直接从程序中写到文件上,没有缓冲区的概念(注:FileoutputStream的flush方法没有实现内容)。

FilterInputStream、FilterOutputStream与装饰者模式

查看FilterInputStream与FilterOutputStream的源码,我们并没有看出有什么特殊的实现,只是内置了InputStream和OutputStream,并在对应的方法上调用了实例in和实例out的方法,这就是装饰者模式。主要是提供与真实类一样的功能,并且将请求转给真实的类,并且在特定情况下可以扩展功能,这种扩展不会影响原类的功能。使用装饰器模式,感觉像实现了复杂一点的继承关系。

BufferedInputStream与BufferedOutputStream

BufferedInputStream增加了缓冲区,不像FileInputStream每次一个字节一个字节的读取,而是一次读取一块数据到内存缓冲区中,再从缓冲区中读取字节流,减少了访问磁盘的次数,而在内存缓冲区读取相当于内存级别的操作,所以,读取效率更快。BufferedInputStream是FilterInputStream的子类,也就是说,BufferedInputStream可以指定任意一个InputStream对象为装饰器的实际对象,这里可以理解成,即可以读取网络I/O,又可以读取磁盘I/O。

然后读一下源码看一下具体实现:

  • 设置缓冲区,通过byte[] buf(缓存数组)、pos(读取位置)、count(缓冲区最后一个有效索引+1)来控制缓冲区的读取位置和长度
  • 支持mark、reset方法,通过markpos(标记位置),marklimit(标记位置变为无效之前可读取的最大字节数限制),实现重读数组中的标记字节
    • mark记录标记位置
    • reset回到mark的位置重新读取
  • fill方法—填充缓冲区
    • 没启用mark,正常填充缓冲区,读取buffer.length-pos长度数据,pos正常都会归0,向缓冲区的0位置开始填充整个缓冲区长度的数据
    • 启用mark,会保留markpos到pos中间的字节,然后继续填满缓冲区
    • 如果,markpos是buffer的0索引位置,则需要扩展缓冲区大小,来读取新的字节,因为标记字节就已经占满了缓冲区,扩充环翠区的大小为原缓冲区长度与marklimit之间的最小值,直到扩展到最大缓冲区大小MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8
    • 如果读取,超过marklimit,则将标记置为无效

BufferedOutputStream增加了缓冲区,可以每次向缓冲区中写字节流,然后再统一写到磁盘或者网络,减少IO的次数,提升效率。

BufferedOutputStream的flush方法,会调用一次flushBuffer方法,执行装饰器真实类的写方法,并且调用真实类的flush方法。(FileOutputStream方法的flush方法有什么作用?)

DataInputStream与DataOutputStream是一个与平台无关的输入输出流的类,提供基本数据类型的输入和输出操作。查看源码这两个类实现了DataInput和DataOutput接口,并且继承自FilterInputStream和FilterOutputStream。所以这两类仍然使用了装饰器模式,并且实现DataInput与DataOutput中的基本数据类型的输入和输出操作。

RandomAccessFile与常见的输入输出类不同,查看类图可以看出,它实现了DataInput和DateOutput接口,而底层的读、写等方法都是JNI方法,所以它与其他的输入输出类不同,而且它同时提供读写两种操作,因为操作比较灵活,所以命名为Random访问文件。

PipedInputStream与PipedOutputStream是建立Java两个线程之间的管道,两个线程通过建立起来的管道进行输入和输出字节流。

查看源码可以看出:

  • PipedInputStream与PipedOutputStream的数据读写,实际上是通过PipedInputStream的receive和read两个同步方法实现,使用了经典的notify和wait模型;
  • 输入输出中间有缓冲区byte[] buffer,使用int类型的in和out表示读写下标;
  • 使用readSide和writeSide代表读写两个线程;
public class PipStream {    PipedInputStream inputStream;    PipedOutputStream outputStream;    public void read() {        inputStream = new PipedInputStream(4096);        outputStream = new PipedOutputStream();        try {            outputStream.connect(inputStream);        } catch (IOException e) {            e.printStackTrace();        }        Thread thread1 = new Thread(() -> {            try {                int data = inputStream.read();                while (data != -1) {                    System.out.println(data);                    data = inputStream.read();                }            } catch (IOException e) {                e.printStackTrace();            }        });        thread1.start();        Thread thread2 = new Thread(() -> {            int i = 0;            while (true) {                if (i >= 100) {                    break;                }                try {                    outputStream.write(1);                } catch (IOException e) {                    e.printStackTrace();                }                i++;                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }        });        thread2.start();    }    public static void main(String[] args) {        new PipStream().read();    }}

ObjectInputStream与ObjectOutputStream实现的是对基本类型或对象的序列化与反序列化操作,也是装饰器模式,可以将对象或者基本类型的数据存入文件或者网络中,再通过文件和网络重新读取序列化之前的基本类型或对象数据。应用场景包括实现对象序列化为二进制数据,再反序列化从二进制数据变成对象。

public class ObjectStream {    private static String readPath = "C:\\Users\\Administrator\\Desktop\\person.txt";    public static void main(String[] args) {        Person person = new Person();        person.setPwd("1234");        person.setUid("4321");        try {            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(readPath));            objectOutputStream.writeObject(person);            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(readPath));            Object object = objectInputStream.readObject();            Person p = (Person) object;            System.out.println("pwd:" + p.getPwd() + ",uid" + p.getUid());        } catch (IOException | ClassNotFoundException e) {            e.printStackTrace();        }    }}

InputStreamReader与OutputStreamWriter是将基于字节流的InputStream和OutputStream转化成基于字符流的Reader和Writer,构造方法需要指定字符编码,以防止字节转成字符时候乱码,查看源码发现,底层都是通过StreamEncoder来实现的,包括读取策略,写入策略,字符编码,缓冲区等。

FileReader、FileWriter与FileInputStream、FileoutputStream类似,都是从一个文件中进行读取和写入,只不过FileReader和FileWriter是以字符形式,FileReader和FileWriter是InputStreamReader和OutputStreamWriter的子类,只是包装了一下FileInputStream、FileoutputStream类。

PipedReader、PipedWriter可以将管道中的数据以字符形式读取和写入,其工作原理与PipedInputStream和PipedOutputStream类似。

BufferedReader、BufferedWriter为Reader、Writer类提供缓冲区,实现原理与BufferedInputStream和BufferedOutputStream类似,但不相同,主要区别就是一个读取字节,一个读取字符(文本),BufferedReader还提供readLine方法,使读取文本更加方便,BufferedWriter也可以写入换行符(换行符是在构造方法中获取系统的换行标识符)。

FilterReader、FilterWriter是装饰器类,用来扩展Reader、Writer,目前来看没有什么实际使用场景。

其他特殊IO类

ByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter是字节数组和字符数组的输入输出对象,它们都是通过读取和写入字节或字符数组实现。(注:关闭对于这四个类来说,没有意义)

LineNumberReader是BufferedReader的子类,主要是增加了LineNumber的概念。

SequenceInputStream是Java提供的可以将两个或者多个InputStream合并成一个,按照顺序分别读取每个输入流中的数据。

PushbackInputStreamPushbackReader是IO API提供的具有回退机制的输入字节流,输入字符流。


  1. https://docs.oracle.com/javase/8/docs/api/java/io/FileInputStream.html
  2. https://blog.csdn.net/ai_bao_zi/article/details/81205286

原文发布于微信公众号 - BanzClub(banz-club)

原文发表时间:2019-05-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券