Java IO流
前置知识
- IO在计算机中指input/output,即输入/输出,用于处理设备之间的数据传输,如文件读写、网络间的通信等
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存中)
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
- Java中对于数据的输入/输出操作以“流”的形式进行
- Java序列化和反序列化
- Java序列化和反序列化需要对象类实现Serializable接口
- 对象序列化机制:把内存中Java对象转换成二进制流,从而允许把这种二进制流持久化地保存在磁盘上或者通过网络将这种二进制流传输到另一个网络节点
- 对象反序列化机制:将Java对象的二进制流恢复成原来的Java对象
Java IO 简介
java.io 包下提供了各种“流”类的接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据,而流的本质是一种有序的数据集合,有数据源和目的地。对于计算机来说,数据都是以二进制形式读出或写入的,如何将数据从数据源送到目的地的操作就是流操作过程
Java IO流分类
-
-
按照操作单元划分,可以划分为字节流和字符流
-
按照流的角色划分为节点流和处理流
-
节点流:可以从一个或向一个特定的地方(节点)读写数据。如FileReader
-
-
-
Java IO 流都是从如下 4 个抽象类基类中派生出来的
-
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流
-
字符流
Reader(字符输入流)
Reader
用于从源头(通常是文件,如文本文件)读取数据(字符信息)到内存中,是所有字符输入流的父类
Reader
常用方法
read()
:从输入流读取一个字符,如果已到达流的末尾,则为 -1read(char[] cbuf)
:从输入流中读取一些字符,并将它们存储到字符数组cbuf
中,等价于read(cbuf, 0, cbuf.length)
read(char[] cbuf, int off, int len)
:在read(char[] cbuf)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字节数)skip(long n)
:忽略输入流中的 n 个字符 ,返回实际忽略的字符数close()
:关闭输入流并释放相关的系统资源
InputStreamReader
是字节流转换为字符流的桥梁,其子类FileReader
是基于该基础上的封装,可以直接操作字符文件
// 字节流转换为字符流的桥梁
public class InputStreamReader extends Reader {
}
// 用于读取字符文件
public class FileReader extends InputStreamReader {
}
FileReader 使用实例
FileReader fileReader = null;
try {
//指明要操作文件
File file = new File("test.txt");
//提供具体的流
fileReader = new FileReader(file);
//数据字符读入
int data = 0;
while ((data = fileReader.read()) != -1) {
System.out.print((char) data);
}
/*
char[] alphabet = new char[2];
int length = 0;
while ((length = fileReader.read(alphabet,0,alphabet.length)) != -1){
System.out.print(new String(alphabet,0,length));
}
*/
} catch (IOException e) {
e.printStackTrace();
} finally {
if (Objects.nonNull(fileReader)) {
try {
//JVM垃圾回收机制只回收JVM堆内存里的对象空间,对其他物理连接,比如数据库连接、输入输出流、Socket连接无能为力,必须手动释放资源
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Writer(字符输出流)
Writer
用于将数据(字符信息)写入到目的地(通常是文件),是所有字节输出流的父类
Writer
常用方法
write(int c)
:写入单个字符write(char[] cbuf)
:写入字符数组cbuf
,等价于write(cbuf, 0, cbuf.length)
write(char[] cbuf, int off, int len)
:在write(char[] cbuf)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字节数)write(String str)
:写入字符串,等价于write(str, 0, str.length())
write(String str, int off, int len)
:在write(String str)
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字节数)append(CharSequence csq)
:将指定的字符序列附加到指定的Writer
对象并返回该Writer
对象append(char c)
:将指定的字符附加到指定的Writer
对象并返回该Writer
对象flush()
:刷新此输出流并强制写出所有缓冲的输出字符close()
:关闭输出流释放相关的系统资源
OutputStreamWriter
是字符流转换为字节流的桥梁,其子类FileWriter
是基于该基础上的封装,可以直接将字符写入到文件
// 字符流转换为字节流的桥梁
public class OutputStreamWriter extends Writer {
}
// 用于写入字符到文件
public class FileWriter extends OutputStreamWriter {
}
FileWriter 使用实例
FileWriter fileWriter = null;
try {
//指明要写出到的文件,如果文件不存在会自动创建
File file = new File("test.txt");
//提供具体的流,并说明是否在源文件上追加还是直接覆盖
fileWriter = new FileWriter(file,false);
//数据写出
fileWriter.write("hello world ");
fileWriter.write(97);
fileWriter.append((char) 99);
char[] alphabet = "123 ".toCharArray();
fileWriter.write(alphabet,0,alphabet.length);
fileWriter.write("one line",0,5);
} catch (IOException e) {
e.printStackTrace();
} finally {
//JVM垃圾回收机制只回收JVM堆内存里的对象空间,对其他物理连接,比如数据库连接、输入输出流、Socket连接无能为力,必须手动释放资源
if (Objects.nonNull(fileWriter)) {
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Reader和Writer共用实例--文件复制
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
File sourceFile = new File("source.txt");
File destinationFile = new File("destination.txt");
fileReader = new FileReader(sourceFile);
fileWriter = new FileWriter(destinationFile,false);
char[] alphabet = new char[10];
//记录每次读入alphabet数组中字符的个数
int length = 0;
while ((length = fileReader.read(alphabet, 0, alphabet.length)) != -1) {
//每次写出length个字符
fileWriter.write(alphabet,0,length);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//JVM垃圾回收机制只回收JVM堆内存里的对象空间,对其他物理连接,比如数据库连接、输入输出流、Socket连接无能为力,必须手动释放资源
if (Objects.nonNull(fileReader)) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (Objects.nonNull(fileWriter)) {
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节流
InputStream(字节输入流)
InputStream
用于从源头(通常是非文本文件,如图片、视频、音频、word文件等)读取数据(字节信息)到内存中,是所有字节输入流的父类,通常结合字节缓冲输入流使用
InputStream
常用方法 :
read()
:返回输入流中下一个字节的数据。返回的值介于0到255之间。如果未读取任何字节,则代码返回-1
,表示文件结束read(byte b[ ])
:从输入流中读取一些字节存储到数组b
中。如果数组b
的长度为零,则不读取。如果没有可用字节读取,返回-1
。如果有可用字节读取,则最多读取的字节数最多等于b.length
, 返回读取的字节数。这个方法等价于read(b, 0, b.length)
read(byte b[], int off, int len)
:在read(byte b[ ])
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字节数)skip(long n)
:忽略输入流中的n个字节 ,返回实际忽略的字节数available()
:返回输入流中可以读取的字节数close()
:关闭输入流释放相关的系统资源
DataInputStream
用于读取指定类型数据,不能单独使用,必须结合 FileInputStream
DataInputStream dataInputStream = null;
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("data.txt");
dataInputStream = new DataInputStream(inputStream);
System.out.println(dataInputStream.readChar());
System.out.println(dataInputStream.readDouble());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (Objects.nonNull(dataInputStream)) {
try {
dataInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (Objects.nonNull(inputStream)) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ObjectInputStream
用于从输入流中读取 Java 对象(反序列化)
- 不能反序列化static和transient修饰的成员变量
ObjectInputStream objectInputStream = null;
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("object.txt");
objectInputStream = new ObjectInputStream(inputStream);
Object o = objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (Objects.nonNull(objectInputStream)) {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (Objects.nonNull(inputStream)) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
OutputStream(字节输出流)
OutputStream
用于将数据(字节信息)写入到目的地(通常是文件),是所有字节输出流的父类,通常结合字节缓冲输出流使用
OutputStream
常用方法 :
write(int b)
:将特定字节写入输出流write(byte b[ ])
:将数组b
写入到输出流,等价于write(b, 0, b.length)
write(byte[] b, int off, int len)
:在write(byte b[ ])
方法的基础上增加了off
参数(偏移量)和len
参数(要读取的最大字节数)flush()
:刷新此输出流并强制写出所有缓冲的输出字节close()
:关闭输出流释放相关的系统资源
DataOutputStream
用于写入指定类型数据,不能单独使用,必须结合 FileOutputStream
try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("data.txt"))) {
dataOutputStream.writeDouble(20D);
dataOutputStream.writeBoolean(false);
} catch (IOException e) {
e.printStackTrace();
}
ObjectOutputStream
将对象写入到输出流,进行java对象的序列化
- 不能序列化static和transient修饰的成员变量
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("object.txt"))) {
objectOutputStream.writeObject(new Object());
}catch (IOException e){
e.printStackTrace();
}
OutputStream和InputStream共用实例--文件复制
try(
FileInputStream inputStream = new FileInputStream("source.png");
FileOutputStream outputStream = new FileOutputStream("destination.png",false)
){
byte[] data = new byte[1024];
int length = 0;
while ((length = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data,0,length);
}
}catch (IOException e){
e.printStackTrace();
}
缓冲流
缓冲流作用:IO 操作是很消耗性能的,缓冲流将数据加载至缓冲区,一次性读取/写入多个字节,从而避免频繁的 IO 操作,提高流的传输效率
BufferedInputStream(字节缓冲输入流)
BufferedInputStream
从源头(通常是文件)读取数据(字节信息)到内存的过程中不会一个字节一个字节的读取,而是会先将读取到的字节存放在缓存区(字节数组),并从内部缓冲区中单独读取字节
public class BufferedInputStream extends FilterInputStream {
//默认缓冲区大小
private static int DEFAULT_BUFFER_SIZE = 8192;
//缓冲区
protected volatile byte buf[];
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
}
BufferedOutputStream(字节缓冲输出流)
BufferedOutputStream
将数据(字节信息)写入到目的地(通常是文件)的过程中不会一个字节一个字节的写入,而是会先将要写入的字节存放在缓存区,并从内部缓冲区中单独写入字节
public class BufferedOutputStream extends FilterOutputStream {
//缓冲区
protected byte buf[];
public BufferedOutputStream(OutputStream out) {
//默认缓冲区大小为8192
this(out, 8192);
}
public BufferedOutputStream(OutputStream out, int size) {
super(out);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
}
BufferedReader(字符缓冲输入流)
BufferedReader
从源头(通常是文件)读取数据(字符信息)到内存的过程中不会一个字符一个字符的读取,而是会先将读取到的字符存放在缓存区(字符数组),并从内部缓冲区中单独读取字符
public class BufferedReader extends Reader {
private Reader in;
//缓冲区
private char cb[];
private int nChars, nextChar;
//默认缓冲区大小
private static int defaultCharBufferSize = 8192;
public BufferedReader(Reader in) {
this(in, defaultCharBufferSize);
}
public BufferedReader(Reader in, int sz) {
super(in);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.in = in;
cb = new char[sz];
nextChar = nChars = 0;
}
}
BufferedWriter(字符缓冲输出流)
BufferedWriter
将数据(字符信息)写入到目的地(通常是文件)的过程中不会一个字符一个字符的写入,而是会先将要写入的字符存放在缓存区,并从内部缓冲区中单独写入字符
public class BufferedWriter extends Writer {
private Writer out;
//缓冲区
private char cb[];
private int nChars, nextChar;
//默认缓冲区大小
private static int defaultCharBufferSize = 8192;
private String lineSeparator;
public BufferedWriter(Writer out) {
this(out, defaultCharBufferSize);
}
public BufferedWriter(Writer out, int sz) {
super(out);
if (sz <= 0)
throw new IllegalArgumentException("Buffer size <= 0");
this.out = out;
cb = new char[sz];
nChars = sz;
nextChar = 0;
lineSeparator = java.security.AccessController.doPrivileged(
new sun.security.action.GetPropertyAction("line.separator"));
}
}
使用缓冲流复制文件
try (
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("source.png"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("destination.png"))
) {
byte[] source = new byte[1024];
int length = 0;
while ((length = bis.read(source, 0, source.length)) != -1) {
bos.write(source,0,length);
}
}catch (IOException e){
e.printStackTrace();
}
转换流
简介
- 按照某种规则,将字符存储到计算机中,称为编码
- 将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码
Reader和Writer最重要的子类是InputStreamReader和OutputStreamWriter类
- InputStreamReader类包含了一个底层输入流,可以从中读取原始字节。它根据指定的编码方式,将这些字节转换为Unicode字符 --- 字节转字符
- OutputStreamWriter从运行的程序中接收Unicode字符,然后使用指定的编码方式将这些字符转换为字节,再将这些字节写入底层输出流中 --- 字符转字节
什么时候使用转换流
- 源头或者目的对应设备是字节流,但操作的却是文本数据时,可使用转换流作为桥梁
- 读取字节数据并写入文件中---字节转字符
-
try ( BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter writer = new BufferedWriter(new FileWriter("destination.txt")) ) { String line = null; while ((line = reader.readLine()) != null) { if ("break".equals(line)){ break; } writer.write(line); writer.newLine(); writer.flush(); } } catch (IOException e) { e.printStackTrace(); }
-
- 读取文本文件并输出到控制台---字符转字节
-
try ( BufferedReader reader = new BufferedReader(new FileReader("destination.txt")); BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out)) ) { String line = null; while ((line = reader.readLine()) != null) { writer.write(line); writer.newLine(); } }catch (IOException e){ e.printStackTrace(); }
-
- 读取字节数据并写入文件中---字节转字符
- 操作文本涉及到具体的编码式,必须使用转换流
-
指定编码读取文本文件
-
try ( BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("destination.txt"), StandardCharsets.UTF_8)); ) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } }catch (IOException e){ e.printStackTrace(); }
-
- 指定编码将数据写出到文本文件
-
try ( BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("destination.txt"), StandardCharsets.UTF_8)) ) { writer.write("采用指定的编码输出文本文件"); }catch (IOException e){ e.printStackTrace(); }
-
-
参考https://javaguide.cn/java/io/io-basis.html#io-%E6%B5%81%E7%AE%80%E4%BB%8B
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南