InputStream 和 OutputStream 基础

大家好,我是指北君。

在 java 中将程序通常会和其他外部设备进行数据交互,比如写入磁盘,网络发送数据等等,今天我们来学学 java 中 基础的 IO 流。

IO 流

与其他外部设备进行数据交互,比如将数据从内存中保存到磁盘文件中或者从网络上下载数据并加载到内存中,这个过程都是一种单向且有顺序的数据传输,被称之为流。

IO 就是 Input 输入和 Output 输出。输入输出以内存为中心的流向划分的。传输数据到内存就是输入流,从内存中输出数据就是输出流。

InputStream

InputStream 是所有输入流的父类,是一个抽象类,读取的数据单位是字节(byte)。

主要的抽象方法是 read(),这个方法就是读取数据内容并返回 -1~255 的 int 值。read() 方法是一个阻塞的方法,只有将内容全部读取完成之后才能运行下一行代码。

1
public abstract int read() throws IOException;

以 FileInputStream 实现类作为示例:

1
2
3
4
5
6
7
8
9
public static void main(String[] args) throws Exception {
    // input.txt 内容为 hello, inputStream
    InputStream input = new FileInputStream("input.txt");
    int n = 0;
    while((n = input.read()) != -1){
        System.out.print((char) n);
    }
    input.close();
}

示例中的 read() 方法只能一个字节的一个字节读取数据,效率不高,当文件中存在多个字节为一个汉字的中文时,上面的示例将打印出乱码。

InputStream 支持将一次性读取多个字节到缓冲区,利用缓冲区提高效率。返回值的数据不再是读取的数据字节,而是读取的字节数。并且可以正常的打印出中文字符。

1
2
3
4
// 将读取的内容填充到 byte 数组
public int read(byte b[]) throws IOException
// 将读取的内容填充 byte 数组中 off 开始,len 长度的区域
public int read(byte b[], int off, int len)  throws IOException

将缓冲区大小设置为 1024 个字节示例:

1
2
3
4
5
6
7
8
9
10
11
 public static void main(String[] args) throws Exception {
    //input.txt 文件内容为 你好, inputStream
    InputStream input = new FileInputStream("input.txt");

    byte[] result = new byte[1024];

    while(input.read(result) != -1){
        System.out.print(new String(result, "utf-8"));
    }
    input.close();
}

OutputStream

OutputStream 是所有输出流的父类。和 InputStream 一样是一个抽象类。

主要的抽象方法是 write(),也是一个阻塞的方法,只有将内容全部写完成之后才能运行下一行代码。write() 方法和 read() 方法一样都是一个字节一个字节的操作的。

1
public abstract void write(int b) throws IOException;

以 FileInputStream 实现类作为示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main(String[] args) throws Exception {
    OutputStream out = new FileOutputStream("out.txt");
    try {
        out.write("h".getBytes());
        out.write("e".getBytes());
        out.write("l".getBytes());
        out.write("l".getBytes());
        out.write("0".getBytes());
    } finally {
        if(out != null) {
            out.close();
        }
    }
}

write() 按单个字节写入磁盘的效率比较低下,OutputStream 提供了 write(byte[]) 一次性大批量的将字节输出到磁盘。对于 IO 设备来说,一次性写入 1 个字节和写入 1000 个字节的时间都是差不多的。

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) throws Exception {
    OutputStream out = new FileOutputStream("out.txt");
    try {
        out.write("哈喽,outputStream".getBytes());
    } finally {
        if(out != null) {
            out.close();
        }
    }
}

关闭资源

不管是 InputStream 还是 OutputStream 在使用资源之后都需要调用 close()方法。在示例中如果在 close() 方法调用之前抛出异常则不会自动关闭资源。以下两种方式都可以关闭资源:

  1. try..finally 方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws Exception {
    InputStream input = new FileInputStream("input.txt");
    try {
        byte[] result = new byte[1024];

        while(input.read(result) != -1){
            System.out.print(new String(result, "utf-8"));
        }
    } finally {
        if(input != null) {
            input.close();
        }
    }

}
  1. try(resource)

实现了 Closeable 接口的 InputStream 和 OutputStream 使用 try(resource) 时,编译器会自动增加 finally。

1
2
3
4
5
6
public static void main(String[] args) throws Exception {
    try (OutputStream out = new FileOutputStream("out.txt")){
        out.write("编译器会添加 finally".getBytes());
    }

}

总结

今天就是简单地给大家介绍 Java 的 IO 流,为接下来学其他 IO 类打个基础。

Java Geek Tech wechat
欢迎订阅 Java 技术指北,这里分享关于 Java 的一切。