Java基础之Java IO

286 阅读6分钟

在计算机术语中,I/O(input/output)是系统和外部进行交流通信的方式。输入(input)是系统收到的信号或者数据,输出是系统对外发出的信号或者数据。我们通常所说的执行I/O操作就是对于输入或者输出数据的处理。

Java IO是Java提供的一系列用于处理输入和输出数据的API。Java对于输入和输出进行了抽象,用流(Stream)来表示输入输出。流可以代表任何类型的输入源或者输出目标。比如硬盘上的文件,内存,其他设备等。 流由一系列的数据组成,你可以把流想象成水流,程序可以使用输入流来读取数据,使用输出流来写出数据。流支持不同类型的数据,如字节数据,基本类型数据,对象等

从流中读取数据

向流中写入数据

Java Stream中根据处理数据的最小单位不同,可以分为字节流和字符流两种。

  • 字节流

    数据流中最小的数据单元是字节

  • 字符流 数据流中最小的数据单元是字符, Java中的字符是Unicode编码,一个字符占用两个字节。

根据这两种不同的类型,Java提供了一些列的Java类

字节流

字节输入流

InputStream是所有字节输入流的基类,提供了读取字节流的基本方法,其子类通过继承InputStream实现不同形式的字节流。我们先看下InputStream中的关键方法

方法 方法介绍
public abstract int read() 从流中读取一个字节数据,抽象方法由子类负责实现真正的读取
public int read(byte b[]) 从流中读取数组长度的字节数据,内部调用下面的方法实现
public int read(byte b[], int off, int len) 从流中读取len个字节,写入到从off位置开始的数组中
public long skip(long n) 跳过流中的几个字节
public int available() 流中可读字节的数量
public void close() 关闭字节流,释放系统资源
public synchronized void mark(int readlimit) 标记当前位置,调用reset方法之后还可以从当前位置开始读取,调用前需要通过markSupported检查实现类四是否支持mark
public synchronized void reset() 重置读取位置为上次 mark 标记的位置
public boolean markSupported() 判断当前流是否支持标记流
ByteArrayInputStream

ByteArrayInputStream把一个数组包装成输入流,从字节数组读取数据。相比于直接使用原始数组,ByteArrayInputStream提供了read-only功能。因为流没有提供更改数组内容的方法。

FileInputStream

FileInputStream从文件系统中读取字节数据,是我们使用比较多的一个类,内部实际读取文件内容的方法为native方法实现,一般读取配置包装类BufferedInputStream使用,减少I/O消耗。

PipedInputStream

PipedInputStream主要用户线程间通信,PipedInputStream连接到PipedOutputStream,一个线程通过PipedOutputStream写数据,另外一个通过PipedInputStream读取数据。但是线程间通信也可以采取共享内存到方式。所以使用过程中应用场景不多。

FilterInputStream

我们都知道Java IO包使用了经典的装饰模式,通过装饰器增加原始输入输出流的功能。FilterInputStream是输入装饰器的基类,本身没有提供特殊的功能,只是简单的把调用转发到原始输入流中。具体装饰器通过实现类来扩展。

  • BufferedInputStream

    BufferedInputStream是我们使用比较多的类,提供了读取输入的缓存功能,通过一次读取和缓存多个字节的数据到内存中,减少系统调用带来的开销。但是要注意的是并不是任何情况下通过BufferedInputStream都能提高读取的性能,通过阅读源码可以发现,BufferedInputStream默认读取8K(8192)的缓存数据,如果你一次读取的字节超过了缓存大小的数据,反而增加了一次数据的内存拷贝,降低了程序的性能。所以需要根据自身程序特点决定是否使用。

  • PushbackInputStream

    回退流,我们的输入流在只能顺序的从前往后读取字节,PushbackInputStream提供了回退功能,把读取进来不需要的字节回退到输入流的缓存中。

  • DataInputStream

    DataInputStream提供了我们从输入流中读取Java基本数据类型数据的功能。根据数据类型字节长度不同,读取相应的字节,转换为我们需要的基本数据类型。比如int类型,读取流中的四个字节,通过移位的方式组合成我们需要的int类型数据。

ObjectInputStream

ObjectInputStream用于对象的反序列化,从流中读取数据,转换为我们需要的Java对象。对象反序列化的过程不包括transient和静态字段。可反序列化的对象必须实现Serializable接口。

字节输出流

OutputStream是所有字节输出流的基类,提供了写出数据的基本方法,其子类通过继承OutputStream实现不同形式的字节输出流。我们先看下OutputStream中的关键方法

方法 方法介绍
public abstract void write(int b) 向输出流中写入一个字节,取的是int的低八位字节
public void write(byte b[]) 向输出流中写入字节数组
public void write(byte b[], int off, int len 向输出流中写入len长度的字节数据,从b[]的off位置开始
public void flush() 强制刷新,将缓冲中的数据写入的输出流。这里只是保证写入到输出流,,比如文件,只是保证写入到操作系统缓存。
public void close() 关闭字节流,释放系统资源
ByteArrayOutputStream

ByteArrayOutputStream写入到内存中的字节数组缓存中,数组大小会随着写入数量的增加而自动增长。可以通过toByteArray或者toString获取写入的数据

FileOutputStream

FileOutputStream写入字节到文件中

PipedOutputStream

PipedOutputStream管道输出流需要和PipedOutputStream配合使用,用于线程间通信。

FilterOutputStream

FilterOutputStream和FilterInputStream类似,是输出字节流的装饰类的基类,本身没有提供特殊的功能,只是简单的把调用转发到原始输入流中。具体装饰器通过实现类来扩展。

  • BufferedOutputStream BufferedOutputStream也和BufferedInputStream类似,提供了输出字节流的缓冲,通过暂时缓存写入数据,在写入到数据达到一定的数量后,一起写入到外部存储,减少系统调用带来的开销。 BufferedOutputStream默认缓冲区同样是8K(8192)大小。
  • DataOutputStream DataOutputStream提供了我们把Java基本数据类型数据写入到输出字节流的功能。
ObjectOutputStream

ObjectOutputStream用于对象的序列化,把Java对象对象写入到输出流中。对象序列化的过程不包括transient和静态字段。可序列化的对象必须实现Serializable接口。

字符流

字符流和字节流类似,基本的类结构和方法和字节流也基本相同。不同过的是字符流读取的单位是字符,而字节流是单个字节。需要注意的是FileReader和FileWriter,不是直接继承的Reader和Writer抽象类,而是通过OutputStreamReader和OutputStreamWriter进行桥接,底层依然使用的FileInputStream和FileOutputStream。

原文地址: blog.devlab.cn/java-io/#mo…