一、什么是Java IO流
- 概念
- 想象你有一个管道,一端连接着数据源(比如一个文件,就像一个装满水的水桶,数据就是桶里的水),另一端连接着你的程序。这个管道就是IO流。如果数据从数据源通过管道进入程序,这就是输入流(Input Stream),相当于从水桶往你的容器里抽水;如果数据从程序通过管道送到目标(比如把处理后的数据存到另一个文件),这就是输出流(Output Stream),就像把你容器里的水倒入另一个水桶。
- 用途
- 它的存在就是为了让Java程序能够和外界(如文件系统、网络等)进行数据交互。比如说,你想读取一个文本文件里的内容到程序里进行处理,或者把程序里生成的数据保存到一个文件中,都得靠IO流来完成这个数据的传输过程。
二、Java IO流的分类
(一)字节流
- 字节输入流(InputStream)
- 基本介绍
- 字节输入流就像是一个小助手,它每次从数据源(如文件)中一个字节一个字节地把数据拿过来给程序。它是所有字节输入流的抽象基类,意味着其他具体的字节输入流类(如
FileInputStream
)都是在它的基础上构建的。
- 示例
- 假设我们有一个名为
input.txt
的文件,里面存储了一些字节数据(可以是任何二进制数据,像图片、视频等在计算机里都是以字节形式存储的),我们要用FileInputStream
来读取这个文件。
import java.io.FileInputStream;
import java.io.IOException;
public class ByteInputStreamExample {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("input.txt");
int data;
while ((data = fis.read())!= -1) {
// 这里的data就是每次读取到的一个字节,它是一个0 - 255之间的整数
System.out.print(data + " ");
}
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 在这个例子中,
while
循环里的fis.read()
方法每次从文件里读取一个字节,当读到文件末尾时,它会返回 - 1,循环就结束了。
- 字节输出流(OutputStream)
- 基本介绍
- 字节输出流则是把程序里的数据一个字节一个字节地送到目标(如文件)里。它也是一个抽象基类,像
FileOutputStream
就是它的一个具体实现类。
- 示例
- 我们要把一些字节数据(这里简单地用整数表示字节的值)写入到一个名为
output.txt
的文件中。
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteOutputStreamExample {
public static void main(String[] args) {
try {
FileOutputStream fos = new FileOutputStream("output.txt");
byte[] data = {65, 66, 67}; // 这里代表字符'A'、'B'、'C'的ASCII码值
fos.write(data);
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 在这个例子中,我们创建了一个
FileOutputStream
对象,然后用write
方法把字节数组里的数据写入到文件中。
(二)字符流
- 字符输入流(Reader)
- 基本介绍
- 字符输入流和字节输入流有点类似,但它是专门用来处理字符数据的。因为在计算机里,字符有不同的编码方式(如UTF - 8、GBK等),字符流在读取字符时会根据指定的编码方式进行转换。它是字符输入流的抽象基类,
FileReader
是它的一个具体实现类。
- 示例
- 假如我们有一个文本文件
input.txt
,里面存储着一些文本内容,我们用FileReader
来读取它。
import java.io.FileReader;
import java.io.IOException;
public class CharacterInputStreamExample {
public static void main(String[] args) {
try {
FileReader fr = new FileReader("input.txt");
int data;
while ((data = fr.read())!= -1) {
// 这里的data是字符的Unicode编码值,我们可以把它转换为字符输出
System.out.print((char) data);
}
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 这里
while
循环里的fr.read()
方法每次读取一个字符的编码值,我们把它转换为字符后输出。
- 字符输出流(Writer)
- 基本介绍
- 字符输出流用于把程序中的字符数据按照指定的编码方式写入到目标(如文件)中。
FileWriter
是字符输出流的一个具体实现类。
- 示例
- 我们要把一些字符写入到一个名为
output.txt
的文件中。
import java.io.FileWriter;
import java.io.IOException;
public class CharacterOutputStreamExample {
public static void main(String[] args) {
try {
FileWriter fw = new FileWriter("output.txt");
fw.write("Hello, World!");
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 在这个例子中,
FileWriter
的write
方法把字符串"Hello, World!"写入到文件中。
三、处理流
- 缓冲流(BufferedInputStream和BufferedOutputStream、BufferedReader和BufferedWriter)
- 基本介绍
- 缓冲流就像是一个有缓存功能的小仓库。比如你要从文件里读取很多数据,如果没有缓冲流,每次都要直接从文件系统读取一个字节或者一个字符,这很慢。有了缓冲流,它会先把一部分数据读到自己的缓存区(就像先把货物搬到仓库里),然后程序从缓存区里拿数据就快多了。同样,在写入数据时,也是先把数据放到缓存区,等缓存区满了或者程序主动刷新缓存区时,才把数据真正写到目标里。
- 示例(以BufferedReader和BufferedWriter为例)
- 我们要读取一个文本文件
input.txt
,并把内容处理后写入到output.txt
中。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedStreamExample {
public static void main(String[] args) {
try {
BufferedReader br = new BufferedReader(new FileReader("input.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"));
String line;
while ((line = br.readLine())!= -1) {
// 这里可以对读取到的每行数据进行处理,比如把所有字母转换为大写
String processedLine = line.toUpperCase();
bw.write(processedLine);
bw.newLine();
}
br.close();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 在这个例子中,
BufferedReader
的readLine
方法可以一次读取一行文本,BufferedWriter
的newLine
方法用于写入一个换行符。
- 数据流(DataInputStream和DataOutputStream)
- 基本介绍
- 数据流可以让我们方便地读写基本数据类型(如
int
、double
、boolean
等)和字符串。它会把这些数据类型按照一定的格式转换为字节流进行传输。
- 示例(以DataOutputStream为例)
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class DataOutputStreamExample {
public static void main(String[] args) {
try {
FileOutputStream fos = new FileOutputStream("data.txt");
DataOutputStream dos = new DataOutputStream(fos);
dos.writeInt(10);
dos.writeDouble(3.14);
dos.writeBoolean(true);
dos.writeUTF("Hello");
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
- 在这个例子中,
DataOutputStream
的writeInt
、writeDouble
、writeBoolean
和writeUTF
方法分别用于写入int
类型、double
类型、boolean
类型的数据和UTF - 8
编码的字符串。
四、注意事项
- 资源关闭
- 不管是字节流还是字符流,在使用完后都要关闭。就像你用完水管后要关上水龙头一样,不关闭流可能会导致资源泄漏,比如文件一直被占用不能被其他程序访问等。在Java中,可以使用
try - with - resources
语句来自动关闭流,就像前面示例中的try
语句块里的流对象,在try
块结束时会自动调用close
方法关闭流。
- 编码问题
- 在使用字符流时,要特别注意编码问题。如果编码不一致,可能会导致读取和写入的数据出现乱码。例如,如果你用
UTF - 8
编码写入数据,但是用GBK
编码读取数据,就很可能出现乱码现象。所以要确保在整个数据传输过程中编码保持一致。