【笔记】Java的I/O流

前言

Java通过I/O流实现文件读写学习笔记

继承结构

graph TD
  Object --> InputStream
  InputStream --> FileInputStream

  Object --> OutputStream
  OutputStream --> FileOutputStream

  Object --> Reader
  Reader --> InputStreamReader
  InputStreamReader --> FileReader

  Object --> Writer
  Writer --> OutputStreamWriter
  OutputStreamWriter --> FileWriter

  InputStream --> BufferedInputStream
  OutputStream --> BufferedOutputStream
  Reader --> BufferedReader
  Writer --> BufferedWriter

  AutoCloseable --> Closeable
  Closeable --> InputStream

  Closeable --> OutputStream
  Flushable --> OutputStream

  Readable --> Reader
  Closeable --> Reader

  Appendable --> Writer
  Closeable --> Writer
  Flushable --> Writer


  style Object fill:#f0f8ff,stroke:#696969
  style InputStream fill:#f0f8ff,stroke:#696969
  style FileInputStream fill:#f0f8ff,stroke:#696969

  style OutputStream fill:#f0f8ff,stroke:#696969
  style FileOutputStream fill:#f0f8ff,stroke:#696969

  style Reader fill:#f0f8ff,stroke:#696969
  style InputStreamReader fill:#f0f8ff,stroke:#696969
  style FileReader fill:#f0f8ff,stroke:#696969

  style Writer fill:#f0f8ff,stroke:#696969
  style OutputStreamWriter fill:#f0f8ff,stroke:#696969
  style FileWriter fill:#f0f8ff,stroke:#696969
  
  style BufferedInputStream fill:#f0f8ff,stroke:#696969
  style BufferedOutputStream fill:#f0f8ff,stroke:#696969
  style BufferedReader fill:#f0f8ff,stroke:#696969
  style BufferedWriter fill:#f0f8ff,stroke:#696969

  style AutoCloseable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1

  style Flushable fill:#f0f8ff,stroke:#4169e1

  style Readable fill:#f0f8ff,stroke:#4169e1

  style Appendable fill:#f0f8ff,stroke:#4169e1

文件字节流

继承结构

graph TD
  Object --> InputStream
  InputStream --> FileInputStream

  Object --> OutputStream
  OutputStream --> FileOutputStream

  AutoCloseable --> Closeable
  Closeable --> InputStream

  Closeable --> OutputStream
  Flushable --> OutputStream


  style Object fill:#f0f8ff,stroke:#696969
  style InputStream fill:#f0f8ff,stroke:#696969
  style FileInputStream fill:#f0f8ff,stroke:#696969

  style OutputStream fill:#f0f8ff,stroke:#696969
  style FileOutputStream fill:#f0f8ff,stroke:#696969

  style AutoCloseable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1

输入流

创建对象

  • 文件不存在会抛出异常

<file>:文件路径

1
2
3
4
5
FileInputStream fileInputStream = new FileInputStream("<file>");

...

fileInputStream.close();
  • 处理异常
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("<file>");

...

} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
  • 通过Java7的try-with-resources(自动资源管理)来自动释放资源

只要实现了AutoCloseable接口的类,就可以通过try-with-resources来创建对象

1
2
3
4
5
try (FileInputStream fileInputStream = new FileInputStream("<file>")) {
...
} catch (IOException e) {
e.printStackTrace();
}

读取字节

读取一个字节数据
  • 返回读取的字节数据,如果返回-1表示文件读取完毕
1
2
int b = fileInputStream.read();
char c = (char) b;
读取字节数组
  • 返回读取的有效长度
1
2
3
4
5
byte[] buffer = new byte[1024];
int length = fileInputStream.read(buffer);
for (int i = 0; i < length; i++) {
char c = (char) buffer[i];
}
读取指定长度的字节数据
  • 返回读取的有效长度

0:从buffer的哪个索引存储数据
1024:读取的长度是多少

1
2
3
4
5
byte[] buffer = new byte[1024];
int length = fileInputStream.read(buffer, 0, 1024);
for (int i = 0; i < length; i++) {
char c = (char) buffer[i];
}

跳过指定长度的字节数据

1
fileInputStream.skip(1024);

输出流

创建对象

  • 文件不存在会自动创建空文件

false:是否是追加写入,缺省值为false表示从文件头覆盖写入,为true表示从文件尾追加写入

1
2
3
4
5
try (FileInputStream fileInputStream = new FileInputStream("<file>"), false) {
...
} catch (IOException e) {
e.printStackTrace();
}

写入字节

写入一个字节数据
1
2
byte b = '';
fileInputStream.write(b);
写入字节数组
1
2
byte[] buffer = "".getBytes();
fileInputStream.write(buffer);
写入指定长度的字节数据

0:从buffer的哪个索引开始写入数据
buffer.length:写入的长度是多少

1
2
byte[] buffer = "".getBytes();
fileInputStream.write(buffer, 0, buffer.length);
指定字符集
1
2
byte[] buffer = "".getBytes("UTF-8");
fileInputStream.write(buffer);

强制刷新缓存

  • Java内部有缓存机制,每次指定写入数据并不会立即写入到文件,而是缓存满了才会执行文件的写入
  • 通过flush()方法可以强制刷新缓存,立即将缓存的所有数据写入到文件
1
fileInputStream.flush();

同时创建输入流和输出流对象并捕获异常

1
2
3
4
5
try (FileInputStream fileInputStream = new FileInputStream("<file>"); FileOutputStream fileOutputStream = new FileOutputStream("<file>")) {
...
} catch (IOException e) {
e.printStackTrace();
}

文件字符流

继承结构

graph TD
  Object --> Reader
  Reader --> InputStreamReader
  InputStreamReader --> FileReader

  Object --> Writer
  Writer --> OutputStreamWriter
  OutputStreamWriter --> FileWriter

  Readable --> Reader
  Closeable --> Reader

  Appendable --> Writer
  Closeable --> Writer
  Flushable --> Writer


  style Object fill:#f0f8ff,stroke:#696969
  style Reader fill:#f0f8ff,stroke:#696969
  style InputStreamReader fill:#f0f8ff,stroke:#696969
  style FileReader fill:#f0f8ff,stroke:#696969

  style Writer fill:#f0f8ff,stroke:#696969
  style OutputStreamWriter fill:#f0f8ff,stroke:#696969
  style FileWriter fill:#f0f8ff,stroke:#696969
  
  style Readable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Appendable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1

输入流

创建对象

  • 文件不存在会抛出异常
1
2
3
4
5
try (FileReader fileReader = new FileReader("<file>")) {
...
} catch (IOException e) {
e.printStackTrace();
}

读取字符

读取一个字符数据
  • 返回读取的字节数据,如果返回-1表示文件读取完毕
1
2
int b = fileReader.read();
char c = (char) b;
读取字符数组
1
2
3
4
5
char[] cs = new char[1024];
int length = fileReader.read(cs);
for (int i = 0; i < length; i++) {
char c = cs[i];
}
转换为字符串
1
2
3
char[] cs = new char[1024];
fileReader.read(cs);
String s = new String(cs);

跳过指定字符的数据

1
fileReader.skip(1);

输出流

创建对象

  • 文件不存在会自动创建

false:是否是追加写入,缺省值为false表示从文件头覆盖写入,为true表示从文件尾追加写入

1
2
3
try (FileWriter fileWriter = new FileWriter("<file>", false)) {
...
}

写入字符

写入一个字符
1
2
char c = '';
fileWriter.write(c);
写入字符数组
1
2
char[] cs = "".toCharArray();
fileWriter.write(cs);
写入字符串
1
2
String s = "";
fileWriter.write(s);

追加写入字符

  • append()方法返回FileWriter对象,可以链式调用
1
2
3
4
String c = '';
String cs = "".toCharArray();
String s = "";
fileWriter.append(c).append(cs).append(s);

强制刷新缓存

1
fileWriter.flush();

获取字符编码

1
String encoding = fileWriter.getEncoding();

带缓冲区的字节流

继承结构

graph TD
  Object --> InputStream
  InputStream --> BufferedInputStream

  Object --> OutputStream
  OutputStream --> BufferedOutputStream

  AutoCloseable --> Closeable
  Closeable --> InputStream

  Closeable --> OutputStream
  Flushable --> OutputStream


  style Object fill:#f0f8ff,stroke:#696969
  style InputStream fill:#f0f8ff,stroke:#696969
  style BufferedInputStream fill:#f0f8ff,stroke:#696969

  style OutputStream fill:#f0f8ff,stroke:#696969
  style BufferedOutputStream fill:#f0f8ff,stroke:#696969

  style AutoCloseable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1

输入流

创建对象

1
2
3
4
5
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}
手动指定缓冲区大小
1
2
3
4
5
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("<file>"), 1024)) {
...
} catch (IOException e) {
e.printStackTrace();
}

读取字节

传送门

重新读取
标记

0:标记接下来读取几次之内可以回溯,这个数只有大于缓冲区大小才会生效,如果这个数小于等于缓冲区大小,这个数实际上都会与缓冲区大小保持一致

1
bufferedInputStream.mark(0);
回溯
  • 在回溯之前必须先标记
  • 如果读取次数超过标记次数之后进行回溯会报错
1
bufferedInputStream.reset();

输出流

创建对象

1
2
3
4
5
try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}
手动指定缓冲区大小
1
2
3
4
5
try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("<file>"), 1024)) {
...
} catch (IOException e) {
e.printStackTrace();
}

写入字节

传送门

带缓冲区的字符流

继承结构

graph TD
  Object --> Reader
  Reader --> BufferedReader

  Object --> Writer
  Writer --> BufferedWriter

  Readable --> Reader
  Closeable --> Reader

  Appendable --> Writer
  Closeable --> Writer
  Flushable --> Writer


  style Object fill:#f0f8ff,stroke:#696969
  style Reader fill:#f0f8ff,stroke:#696969
  style BufferedReader fill:#f0f8ff,stroke:#696969

  style Writer fill:#f0f8ff,stroke:#696969
  style BufferedWriter fill:#f0f8ff,stroke:#696969
  
  style Readable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Appendable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1

输入流

创建对象

1
2
3
4
5
try (BufferedReader bufferedReader = new BufferedReader(new FileReader("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}
手动指定缓冲区大小
1
2
3
4
5
try (BufferedReader bufferedReader = new BufferedReader(new FileReader("<file>"), 1024)) {
...
} catch (IOException e) {
e.printStackTrace();
}

读取字符

传送门

读取一行字符数据
  • 返回读取的字节数据,如果返回-1表示文件读取完毕
1
String line = bufferedReader.readLine();

判断是否支持标记

1
boolean ok = bufferedReader.markSupported();

输出流

创建对象

1
2
3
4
5
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}
手动指定缓冲区大小
1
2
3
4
5
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("<file>"), 1024)) {
...
} catch (IOException e) {
e.printStackTrace();
}

写入字符

传送门

写入换行符
1
bufferedWriter.newLine();

转换流

文件字节流转换为文件字符流

继承结构

graph TD
  Object --> Reader
  Reader --> InputStreamReader

  Object --> Writer
  Writer --> OutputStreamWriter

  Readable --> Reader
  Closeable --> Reader

  Appendable --> Writer
  Closeable --> Writer
  Flushable --> Writer


  style Object fill:#f0f8ff,stroke:#696969
  style Reader fill:#f0f8ff,stroke:#696969
  style InputStreamReader fill:#f0f8ff,stroke:#696969

  style Writer fill:#f0f8ff,stroke:#696969
  style OutputStreamWriter fill:#f0f8ff,stroke:#696969

  style Readable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Appendable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1

输入流

创建对象
1
2
3
4
5
try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}

输出流

创建对象
1
2
3
4
5
try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}

打印流

继承结构

graph TD
  Object --> Reader
  Reader --> InputStreamReader

  Object --> Writer
  Writer --> OutputStreamWriter

  Readable --> Reader
  Closeable --> Reader

  Appendable --> Writer
  Closeable --> Writer
  Flushable --> Writer


  style Object fill:#f0f8ff,stroke:#696969
  style Reader fill:#f0f8ff,stroke:#696969
  style InputStreamReader fill:#f0f8ff,stroke:#696969

  style Writer fill:#f0f8ff,stroke:#696969
  style OutputStreamWriter fill:#f0f8ff,stroke:#696969

  style Readable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Appendable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1

输出流

创建对象

1
2
3
4
5
try (PrintStream printStream = new PrintStream(new FileOutputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}

打印字符串

1
2
String s = "";
printStream.print(s);

打印末尾换行的字符串

1
2
String s = "";
printStream.println(s);

打印包含格式说明符的字符串

传送门

1
2
String s = "%s";
printStream.printf(s, "");

数据流

继承结构

graph TD
  Object --> InputStream
  InputStream --> FilterInputStream
  FilterInputStream --> DataInputStream

  Object --> OutputStream
  OutputStream --> FilterOutputStream
  FilterOutputStream --> DataOutputStream

  AutoCloseable --> Closeable
  Closeable --> InputStream

  Closeable --> OutputStream
  Flushable --> OutputStream

  DataInput --> DataInputStream
  DataOutput --> DataOutputStream


  style Object fill:#f0f8ff,stroke:#696969
  style InputStream fill:#f0f8ff,stroke:#696969
  style FilterInputStream fill:#f0f8ff,stroke:#696969
  style DataInputStream fill:#f0f8ff,stroke:#696969

  style OutputStream fill:#f0f8ff,stroke:#696969
  style FilterOutputStream fill:#f0f8ff,stroke:#696969
  style DataOutputStream fill:#f0f8ff,stroke:#696969

  style AutoCloseable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1
  style DataInput fill:#f0f8ff,stroke:#4169e1
  style DataOutput fill:#f0f8ff,stroke:#4169e1

输入流

创建对象

1
2
3
4
5
try (DataInputStream dataInputStream = new DataInputStream(new FileInputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}

读取数据

  • 从文件中读取字节数组,并直接转换为基本数据类型
1
byte result = dataInputStream.readByte();
1
short result = dataInputStream.readShort();
1
int result = dataInputStream.readInt();
1
long result = dataInputStream.readLong();
1
double result = dataInputStream.readDouble();
1
float result = dataInputStream.readFloat();
1
boolean result = dataInputStream.readBoolean();
1
char result = dataInputStream.readChar();

输出流

创建对象

1
2
3
4
5
try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
}

写入数据

1
dataOutputStream.writeByte(0);
1
dataOutputStream.writeShort(0);
1
dataOutputStream.writeInt(0);
1
dataOutputStream.writeLong(0);
1
dataOutputStream.writeDouble(0);
1
dataOutputStream.writeFloat(0);
1
dataOutputStream.writeBoolean(false);
1
dataOutputStream.writeChar(' ');

对象流

继承结构

graph TD
  Object --> InputStream
  InputStream --> ObjectInputStream

  Object --> OutputStream
  OutputStream --> ObjectOutputStream

  AutoCloseable --> Closeable
  Closeable --> InputStream

  Closeable --> OutputStream
  Flushable --> OutputStream

  DataInput --> ObjectInput
  AutoCloseable --> ObjectInput

  ObjectInput --> ObjectInputStream
  ObjectStreamConstants --> ObjectInputStream

  DataOutput --> ObjectOutput
  AutoCloseable --> ObjectOutput

  ObjectOutput --> ObjectOutputStream
  ObjectStreamConstants --> ObjectOutputStream


  style Object fill:#f0f8ff,stroke:#696969
  style InputStream fill:#f0f8ff,stroke:#696969
  style ObjectInputStream fill:#f0f8ff,stroke:#696969

  style OutputStream fill:#f0f8ff,stroke:#696969
  style ObjectOutputStream fill:#f0f8ff,stroke:#696969

  style AutoCloseable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1
  style DataInput fill:#f0f8ff,stroke:#4169e1
  style DataOutput fill:#f0f8ff,stroke:#4169e1
  style ObjectInput fill:#f0f8ff,stroke:#4169e1
  style ObjectOutput fill:#f0f8ff,stroke:#4169e1
  style ObjectStreamConstants fill:#f0f8ff,stroke:#4169e1

定义对象

  • 对象必须实现Serializable接口才能通过ObjectInputStreamObjectOutputStream读写对象
  • 实现
1
2
3
class Cls implements Serializable {
...
}

忽略字段

  • 通过transient关键字忽略指定字段进行序列化和反序列化
1
2
3
class Cls implements Serializable {
transient int field;
}

指定版本

  • 在序列化和反序列化的过程中,可以通过定义serialVersionUID字段来指定版本,如果序列化和反序列化时版本不一致,会抛出异常
  • 即便是没有显式定义serialVersionUID字段,JVM在序列化时也会根据类的结构隐式的添加serialVersionUID字段,通过计算哈希值作为serialVersionUID字段的值
1
2
3
class Cls implements Serializable {
private static final long serialVersionUID = 1L;
}

输入流

创建对象

1
2
3
4
5
6
7
try (ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

读取数据

1
Cls cls = (Cls) objectInputStream.readObject();

输出流

创建对象

1
2
3
4
5
6
7
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("<file>"))) {
...
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

写入数据

1
2
3
Cls cls = new Cls();

objectOutputStream.writeObject(cls);

字节数组流

继承结构

graph TD
  Object --> InputStream
  InputStream --> ByteArrayInputStream

  Object --> OutputStream
  OutputStream --> ByteArrayOutputStream

  AutoCloseable --> Closeable
  Closeable --> InputStream

  Closeable --> OutputStream
  Flushable --> OutputStream


  style Object fill:#f0f8ff,stroke:#696969
  style InputStream fill:#f0f8ff,stroke:#696969
  style ByteArrayInputStream fill:#f0f8ff,stroke:#696969

  style OutputStream fill:#f0f8ff,stroke:#696969
  style ByteArrayOutputStream fill:#f0f8ff,stroke:#696969

  style AutoCloseable fill:#f0f8ff,stroke:#4169e1
  style Closeable fill:#f0f8ff,stroke:#4169e1
  style Flushable fill:#f0f8ff,stroke:#4169e1

输入流

创建对象

1
2
3
4
5
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream("".getBytes())) {
...
} catch (IOException e) {
e.printStackTrace();
}

读取数据

1
char c = (char) byteArrayInputStream.read();

输出流

创建对象

1
2
3
4
5
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
...
} catch (IOException e) {
e.printStackTrace();
}

写入数据

1
byteArrayOutputStream.write("".getBytes());

获取数据

1
byte[] bytes = byteArrayOutputStream.toByteArray();

完成