Java IO 流概述与基础应用
Java IO 流是处理数据输入输出的核心机制。 IO 流的分类(字节流/字符流、节点流/处理流)及超类结构。内容涵盖文件流读写、块读写优化、缓冲流应用、对象序列化与反序列化,以及字符转换流的使用场景和编码处理。通过代码示例演示了文件复制、文本读写及对象持久化的基本实践,帮助开发者掌握 Java IO 的核心用法。

Java IO 流是处理数据输入输出的核心机制。 IO 流的分类(字节流/字符流、节点流/处理流)及超类结构。内容涵盖文件流读写、块读写优化、缓冲流应用、对象序列化与反序列化,以及字符转换流的使用场景和编码处理。通过代码示例演示了文件复制、文本读写及对象持久化的基本实践,帮助开发者掌握 Java IO 的核心用法。

Java 中的 IO 流是用于处理数据输入和输出的核心机制。通过应用 IO 流可以使 Java 程序能够与外部世界(如磁盘文件、网络、硬件设备等)进行数据交互。IO 流的全称为输入/输出流(Input/Output Stream),它是 Java 编程语言中用于数据传输的一种抽象模型。流可以被想象为数据的连续流动,就像水通过管道一样,数据通过流从一个地方流向另一个地方。
IO 流在 Java 开发中几乎无处不在,任何涉及到数据读写的地方都会用到 IO 流。常见的应用场景包括文件读写、网络通信、数据持久化等。
IO 流的分类可以从多个维度进行理解:
在 Java 中,输入输出 (IO) 流的超类主要有两类,分别对应于字节流和字符流。
字节流 (Byte Streams)
字符流 (Character Streams)
这些超类提供了基本的读写操作,而它们的子类则实现了特定的读写功能,例如从文件读取、向网络套接字写入、数据的缓冲等。例如:
这些类构成了 Java IO 流的层次结构,允许开发者根据不同的需求选择合适的流类型来进行数据的读写操作。
文件流是用来连接我们的程序与文件之间的'管道',用来读写文件中的数据。
文件流是继承自 InputStream 和 OutputStream 的流对象,其分类为:
java.io.FileOutputStream 输出流对象用于向文件中写入数据,对象构建通常会借助如下两个构造方法:
FileOutputStream(String path)
创建文件输出流对指定的 path 路径表示的文件进行写操作,如果该文件不存在则将其创建出来
FileOutputStream(File file)
创建文件输出流对指定的 file 对象表示的文件进行写操作,如果该文件不存在则将其创建出来
案例
package io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FOSDemo01 {
public static void main(String[] args) throws IOException {
// 1. 构建一个文件输出流对象
// 1) 创建文件对象
File file = new File("jsd/2406/fos.dat");
// jsd/2406/是目录
// 2) 获取文件对象的目录结构
File parent = file.getParentFile();
System.out.println(parent);
// 3). 假如目录不存在则创建目录 mkdirs()
if (!parent.exists()) {
// parent.exists() 假如返回 true 表示存在 parent.mkdirs(); 创建多层目录
parent.mkdirs();
System.out.println("文件不存在,创建成功");
}
// 4) 创建文件输出流对象 (用于向文件写数据 - 二进制)
// 构建对象方法 1
// FileOutputStream fos1 = new FileOutputStream("jsd/2406/fos.dat");
// 构建对象方法 2
FileOutputStream fos2 = new FileOutputStream(file);
// 5) 向文件中写入数据
fos2.write(3); // 这里的 3 表示 10 进制数据,3 的二进制数据为 00000011
fos2.write(4);
fos2.write(5);
System.out.println("数据写入 OK");
// 6) 释放资源 (关闭流对象)
fos2.close();
}
}
java.io.FileInputStream 文件输入流对象用于从文件中读取数据,常用构造方法有:
FileInputStream(String path)
基于给定的路径对应的文件创建文件输入流
FileInputStream(File file)
基于给定的 File 对象所表示的文件创建文件输入流
案例
package io;
import java.io.FileInputStream;
import java.io.IOException;
public class FISDemo01 {
public static void main(String[] args) throws IOException {
// 1. 构建文件输入流对象
FileInputStream fis = new FileInputStream("jsd/2406/fos.dat");
// 2. 读取数据
int n = 0;
while ((n = fis.read()) != -1) {
System.out.println(n);
}
// 3. 关闭流对象 (水龙头有打开是不是也会有关闭,否则浪费资源)
fis.close(); // 流对象关闭后会释放内存
}
}
复制文件的原理就是使用文件输入流从原文件中陆续读取出每一个字节,然后再使用文件输出流将字节陆续写入到另一个文件中完成的。
案例 1
package io;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyDemo01 {
public static void main(String[] args) throws IOException {
// 一、实现思路
// 1. 读文件内容 (a.png)
// 2. 将读取的内容写到 b.png 中
// 二、代码实现
// 1. 构建文件输入流、输出流对象
FileInputStream fis = new FileInputStream("a.png"); // 这个文件需要存在
FileOutputStream fos = new FileOutputStream("b.png"); // 这个文件不存在会自动创建
// 2. 读写数据 (复制)
int n = -1;
while ((n = fis.read()) != -1) { // 读取数据,n 为读到的数据,-1 表示读取到文件尾
fos.write(n); // 将读取的数据写入到 b.png 文件中
}
// 3. 关闭流对象
fis.close();
fos.close();
}
}
int read(byte[] data)
一次性读取给定字节数组总长度的字节量并存入到该数组中。
返回值为实际读取到的字节数。如果返回值为 -1 表示本次没有读取到任何字节已经是流的末尾了
void write(byte[] data)
一次性将给定数组中所有字节写出 void write(byte[] data, int offset, int len)
一次性将 data 数组中从下标 offset 处开始的连续 len 个字节一次性写出
案例实现:文件复制
package io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyDemo02 {
public static void main(String[] args) throws IOException {
// 一、实现思路
// 1. 读文件内容 (a.png)
// 2. 将读取的内容写到 b.png 中
// 二、代码实现
// 1. 构建文件输入流、输出流对象
FileInputStream fis = new FileInputStream("a.png"); // 这个文件需要存在
FileOutputStream fos = new FileOutputStream("b.png"); // 这个文件不存在会自动创建
// 2. 读写数据 (复制)
// 2.1 定义数组用于临时存储读取到数据
byte[] data = new byte[1024]; // 这里数组的大小是自定义
// 2.2 循环读取数据
int length = -1;
while ((length = fis.read(data)) != -1) { // 这里数组大小是 1024,所以最多一次读取 1024 个字节,length 表示读取的字节数
fos.write(data, 0, length); // 读了多少写多少,0 表示起始位置,length 表示写入的字节数
}
// 3. 关闭流对象
fis.close();
fos.close();
}
}
对于 FileInputStream 和 FileOutputStream 提供的方法只能读写字节数据,对于文本数据如何操作呢?
案例 1:写文本数据
package io;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
public class WriteStringDemo01 {
public static void main(String[] args) throws IOException {
String str = "我爱 Java";
// 构建文件输出流对象
FileOutputStream fos = new FileOutputStream("./f1.txt");
// 写字符数据
byte[] data = str.getBytes(StandardCharsets.UTF_8);
fos.write(data);
str = "Hello Tedu";
fos.write(str.getBytes(StandardCharsets.UTF_8));
// 释放资源
fos.close();
}
}
案例 2:读文本数据
package io;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class ReadStringDemo01 {
public static void main(String[] args) throws IOException {
// 1. 构建文件输入流对象
// 方式 1:
// File file = new File("./f1.txt");
// long size = file.length();
// FileInputStream fis = new FileInputStream(file);
// 方式 2:
FileInputStream fis = new FileInputStream("./f1.txt");
// 2. 读取文件内容 (将数据读取到字节数组中)
// byte[] data = new byte[(int)size];
byte[] data = new byte[fis.available()]; // fis.available() 获取流中有效字节数
int len = fis.read(data);
// 3. 将字节数组内容转换为字符串并输出
String str = new String(data, 0, len);
System.out.println(str);
// 4. 关闭流
fis.close();
}
}
概述
处理流是 Java IO 流体系中的一类重要的流,它们是在已经存在的流(称为基础流或节点流)之上构建的,为这些流添加额外的功能,而不改变原流本身。处理流的主要目的是为了增强或改变流的行为,比如提供缓冲、转换数据类型、序列化等等。
应用特征
处理流的典型特征是它们自身并不直接连接到物理的输入/输出设备,而是'装饰'在其他流之上,利用这些流进行数据的读写,并在此基础上提供附加服务。处理流的实例通常会把读写操作委托给底层的流,同时可能对数据进行一些预处理或后处理。
处理流可以串联使用,即一个处理流的输出可以作为另一个处理流的输入,形成流的链式结构。例如,可以先使用 BufferedInputStream 对 FileInputStream 进行包装,然后再用其它处理流包装这个缓冲流。
Java 中的缓冲流是一种处理流,有 java.io.BufferedInputStream 和 BufferedOutputStream,其作用就是加快读写效率,通常缓冲流的应用最终要链接在低级流上。
package io;
import java.io.*;
public class FileCopyDemo03 {
public static void main(String[] args) throws IOException {
// 1. 构建输入输出流对象 (缓冲流增强文件流)
BufferedInputStream bis = new BufferedInputStream(
// 处理流 (这个流对象内置一个缓冲区 -byte[])
new FileInputStream("./a.png")
); // 节点流
BufferedOutputStream bos = new BufferedOutputStream(
// 处理流
new FileOutputStream("./c.png")
); // 节点流
// 2. 读写数据 (拷贝)
byte[] data = new byte[16];
int len;
while ((len = bis.read(data)) != -1) {
bos.write(data, 0, len);
}
bos.flush(); // 这里表示刷新缓冲区内容到文件
// 3. 关闭流对象 (关闭外层流时,内层流会自动关闭)
bis.close();
bos.close();
}
}
Java 中的对象流也是一种处理流,有 java.io.ObjectInputStream 和 java.io.ObjectOutputStream 两个类型,用于实现对象的读写。也就是对象的序列化(把对象转化为字节)和反序列化(把字节转换为对象)。
Serializable 接口
为了能够被序列化,一个对象所属的类必须实现 Serializable 接口。Serializable 是一个标记接口,不包含任何方法,它的存在仅仅是为了标识一个类是可序列化的。如果尝试序列化一个没有实现 Serializable 接口的类的对象,将会抛出 NotSerializableException 异常。
设计一个 Message 对象类型
package io;
import java.io.Serializable;
public class Message implements Serializable {
// Serializable 只起到一个标识性作用
// 添加序列化 id(序列化时类中没有写序列化 id,系统也会基于类中的属性和方法自动生成)
private static final long serialVersionUID = 8725255648785219540L;
/*消息 id*/
private Integer id;
/**消息标题,
* 假如这个属性不希望系列化,
* 可以使用 transient 关键字修饰*/
private transient String title;
/**消息内容*/
private String content;
public Message() {}
public Message(Integer id, String title, String content) {
this.id = id;
this.title = title;
this.content = content;
}
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
@Override
public String toString() {
return "Message{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
'}';
}
}
对象序列化案例实现
package io;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamDemo01 {
public static void main(String[] args) throws IOException {
Message m1 = new Message(1, "传奇老师病了", "做了个小手术");
// 1. 构建流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("message.txt"));
// 2. 写对象 (序列化)
oos.writeObject(m1);
System.out.println("序列化 OK");
// 3. 关闭流对象
oos.close();
}
}
对象反序列化案例实现
package io;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInputStreamDemo02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 1. 创建流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("message.txt"));
// 2. 读取对象 (反序列化 - 先读出来的是字节,然后将字节转换对象)
Message msg = (Message) ois.readObject();
System.out.println(msg);
// 3. 关闭资源
ois.close();
}
}
在 Java 中,字符流(Character Streams)是用于处理文本数据的一种流类型,其基类是 java.io.Reader 和 java.io.Writer 两个抽象类。
应用场景?
字符流广泛应用于读写文本文件,处理字符串,以及在网络通信中传输文本数据。由于文本文件通常包含人类可读的内容,字符流可以确保正确处理各种字符集和编码,从而避免乱码问题。
为什么使用字符流?
字符流超类
java.io.Writer 所有字符输入流的超类
常用方法
void write(int c): 写出一个字符,写出给定 int 值'低 16'位表示的字符。
void write(char[] chs): 将给定字符数组中所有字符写出。
void write(String str): 将给定的字符串写出
void write(char[] chs, int offset, int len): 将给定的字符数组从 offset 处开始连续的 len 个字符写出
java.io.Reader 所有字符输出流的超类
常用方法
int read(): 读取一个字符,返回的 int 值'低 16'位有效。当返回值为 -1 时表达流读取到了末尾。
int read(char[] chs): 从该流中读取一个字符数组的 length 个字符并存入该数组,返回值为实际读取到的字符量。当返回值为 -1 时表达流读取到了
在 Java 中,字符转换流是 Java I/O 体系中的一类流,它们实现了字节流和字符流之间的转换。这类流包括 InputStreamReader 和 OutputStreamWriter 两个核心类。
InputStreamReader 是一个读取字节并使用指定的字符集将其解码为字符的 Reader;OutputStreamWriter 则是一个接收字符并使用指定的字符集将其编码为字节的 Writer。
应用场景
为什么用它?
OutputStreamWriter 应用案例 (字符输出转化流应用)
两个构造方法:
OutputStreamWriter(OutputStream out, Charset cs)
基于给定的字节输出流以及字符编码创建 OSW
OutputStreamWriter(OutputStream out)
该构造方法会根据系统默认字符集创建 OSW
具体应用:通过字符转换流将一些字符串写入到指定文件
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
public class OutputStreamWriterDemo01 {
public static void main(String[] args) throws IOException {
// 1. 构建字符转换输出流对象
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("./osw.txt"), StandardCharsets.UTF_8);
// 2. 写数据 (字符串)
osw.write("你好"); // 这里不会自动换行
osw.write("子千老师");
System.out.println("数据写出完毕");
// 3. 关闭流对象
osw.close();
}
}
InputStreamReader 应用案例 (字符输入转化流应用)
构造方法:
InputStreamReader(InputStream in, Charset cs)
基于给定的字节输入流以及字符编码创建当前转换流
InputStreamReader(InputStream in)
该构造方法会根据系统默认字符集创建当前转换流
具体应用案例:基于字符转换流从文件读取字符数据
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
public class InputStreamReaderDemo01 {
public static void main(String[] args) throws IOException {
// 1. 创建字符输入转换流对象
InputStreamReader isr = new InputStreamReader(new FileInputStream("./osw.txt"), StandardCharsets.UTF_8);
// 2. 读数据
int data = 0;
while ((data = isr.read()) != -1) {
System.out.print((char) data);
}
// 3. 关闭流对象
isr.close();
}
}
在 Java 中,字符缓冲流是用于提高读写性能的一对流,它们包括 BufferedReader 和 BufferedWriter。这些流通过内部缓存机制减少了对底层 I/O 设备的访问频率,从而提高了读写操作的效率。
在字符输出缓冲流应用时,通常会结合 PrintWriter 一起使用。这个对象可以一次写一行,并且可以自带刷新机制。
案例 1:PrintWriter 不同构造方法应用
package io;
import java.io.*;
public class PrintWriterDemo01 {
public static void main(String[] args) throws FileNotFoundException {
// 1. 构建 PrintWriter 对象
// 方式 1:需要手动刷新缓冲区或者关闭流
PrintWriter pw = new PrintWriter("./pw.txt");
// 方式 2:自动刷新,构造方法中的 true 表示自动刷新
// PrintWriter pw = new PrintWriter(
// new FileOutputStream("./pw.txt"), true
// );
// 2. 写入数据
pw.println("hello");
pw.println("world");
pw.println("Java");
// 3. 关闭流对象
pw.close();
}
}
案例 2:自己指定缓冲流
package io;
import java.io.*;
public class PrintWriterDemo02 {
public static void main(String[] args) throws FileNotFoundException {
// 1. 构建打印流对象 (装饰模式)
PrintWriter printWriter = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("./pw.txt"))
), true
);
// 2. 写数据
printWriter.println("hello");
printWriter.println("Java");
// 3. 关闭流对象
printWriter.close();
}
}
案例 3:字符输入缓冲流应用
package io;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class BufferedReaderDemo01 {
public static void main(String[] args) throws IOException {
// 1. 构建 BufferedReader 对象
BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("./src/main/java/io/FISDemo01.java"),
StandardCharsets.UTF_8
)
);
// 2. 循环读取文件中数据,读一行,输出一行
String line = null;
while ((line = br.readLine()) != null) {
// readLine 用于读取一行
System.out.println(line);
}
// 3. 关闭流对象
br.close();
}
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online