IO 流与文件操作
IO 流与文件操作
IO 就是 Input/Output,输入输出,用于读写文件、网络数据等。Java 中 IO 靠流来完成。
IO 流的分类
Java IO 流分为很多种,可以按不同维度分类:
- 按方向分:
- 输入流:读数据,从磁盘/网络读到内存
- 输出流:写数据,从内存写到磁盘/网络
- 按数据类型分:
- 字节流:以字节为单位,
InputStream、OutputStream,适合所有类型文件(文本、图片、视频都可以) - 字符流:以字符为单位,
Reader、Writer,只适合纯文本文件(比如 txt, java 源码)
- 按功能分:
- 节点流:直接操作数据源,比如
FileInputStream - 包装流(处理流):包装其他流,提供额外功能,比如
BufferedInputStream
整体分类图:
flowchart BT
字节流 --> InputStream --> FileInputStream & BufferedInputStream & ObjectInputStream
字节流 --> OutputStream --> FileOutputStream & BufferedOutputStream & ObjectOutputStream
字符流 --> Reader --> FileReader & BufferedReader
字符流 --> Writer --> FileWriter & BufferedWriter
File 类操作文件
java.io.File 类代表文件或目录,提供方法操作文件。
常用方法:
import java.io.File;
import java.io.IOException;
// 创建 File 对象
File file = new File("test.txt");
// 判断文件是否存在
boolean exists = file.exists();
// 是否是文件
boolean isFile = file.isFile();
// 是否是目录
boolean isDirectory = file.isDirectory();
// 获取绝对路径
String absolutePath = file.getAbsolutePath();
// 获取文件名
String name = file.getName();
// 获取文件大小(字节)
long length = file.length();
// 创建新文件
file.createNewFile();
// 创建目录
File dir = new File("mydir");
dir.mkdir(); // 创建单级目录
dir.mkdirs(); // 创建多级目录
// 删除文件或目录
file.delete();
// 列出目录下文件
File[] files = dir.listFiles();
for (File f : files) {
System.out.println(f.getName());
}
遍历目录所有文件(递归):
public static void listFiles(File dir) {
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
if (f.isFile()) {
System.out.println("文件:" + f.getAbsolutePath());
} else {
System.out.println("目录:" + f.getAbsolutePath());
listFiles(f); // 递归遍历子目录
}
}
}
}
字节流与字符流
字节流读写文件
读文件 FileInputStream:
import java.io.FileInputStream;
import java.io.IOException;
// 一次读一个字节
try (FileInputStream fis = new FileInputStream("test.txt")) {
int b;
while ((b = fis.read()) != -1) { // read() 返回 -1 表示读完
System.out.print((char) b);
}
} catch (IOException e) {
e.printStackTrace();
}
一次读多个字节:
try (FileInputStream fis = new FileInputStream("test.txt")) {
byte[] buffer = new byte[1024]; // 1KB 缓冲区
int len; // 实际读取的字节数
while ((len = fis.read(buffer)) != -1) {
// 处理读取到的数据,buffer[0..len-1] 是有效数据
System.out.println(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
写文件 FileOutputStream:
import java.io.FileOutputStream;
import java.io.IOException;
String content = "Hello World!";
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
// true 表示追加,不写就是覆盖
// try (FileOutputStream fos = new FileOutputStream("output.txt", true))
fos.write(content.getBytes()); // 字符串转字节数组写入
} catch (IOException e) {
e.printStackTrace();
}
字节流复制文件:
public static void copyFile(String src, String dest) throws IOException {
try (FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest)) {
byte[] buffer = new byte[1024 * 8]; // 8KB 缓冲区
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
}
}
整体使用
@Test
public void test7 () {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream("a.png");
fileOutputStream = new FileOutputStream("b.png");
byte[] buffer = new byte[1024];
int length = -1;
while ((length = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, length);
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
if (fileOutputStream !=null)
{
fileOutputStream.close();
}
if(fileInputStream != null){
fileInputStream.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
字符流读写文件
字符流适合纯文本,自动处理字符编码:
FileReader 读:
import java.io.FileReader;
import java.io.IOException;
try (FileReader fr = new FileReader("test.txt")) {
char[] buffer = new char[1024];
int len;
while ((len = fr.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
FileWriter 写:
try (FileWriter fw = new FileWriter("output.txt")) {
fw.write("你好,世界");
fw.write("\n"); // 换行
fw.flush(); // 刷新缓冲区
} catch (IOException e) {
e.printStackTrace();
}
缓冲流
缓冲流包装节点流,提供缓冲区,减少 IO 次数,提高性能,有 BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter。
BufferedReader 按行读:
try (FileReader fr = new FileReader("test.txt");
BufferedReader br = new BufferedReader(fr)) {
String line;
while ((line = br.readLine()) != null) { // readLine 返回 null 表示读完
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
缓冲流复制文件:
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src.jpg"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("dest.jpg"))) {
byte[] buffer = new byte[8192];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
缓冲流包装后性能比普通字节流高很多,推荐使用。
转换流
转换流用于字节流转字符流,可以指定编码:
InputStreamReader:字节输入流转字符输入流 OutputStreamWriter:字节输出流转字符输出流
示例:指定编码读取:
// 按 GBK 编码读取文件
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis, "GBK");
BufferedReader br = new BufferedReader(isr);
对象序列化
对象序列化就是把对象转成字节序列,写到文件,或者通过网络传输。反序列化就是从字节恢复对象。
要求:对象必须实现 Serializable 接口(标记接口,没有方法)。
序列化:
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
// 定义类实现 Serializable
public class Person implements Serializable {
private static final long serialVersionUID = 1L; // 版本号,建议加上
private String name;
private int age;
// 构造 getter setter...
}
// 序列化对象到文件
Person p = new Person("张三", 20);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.obj"))) {
oos.writeObject(p);
} catch (Exception e) {
e.printStackTrace();
}
反序列化:
import java.io.FileInputStream;
import java.io.ObjectInputStream;
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.obj"))) {
Person p = (Person) ois.readObject();
System.out.println(p.getName() + ", " + p.getAge());
} catch (Exception e) {
e.printStackTrace();
}
//读
@Test
public void test5() {
Student studnet = new Student(1,"1",1,"1");
FileOutputStream fileOutputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
fileOutputStream = new FileOutputStream("stu");
objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(studnet);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if(objectOutputStream != null){
try {
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//写
@Test
public void test6() {
FileInputStream fileInputStream = null;
ObjectInputStream objectInputStream = null;
try {
fileInputStream = new FileInputStream("stu");
objectInputStream = new ObjectInputStream(fileInputStream);
Student student = (Student) objectInputStream.readObject();
System.out.println(student);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if(objectInputStream != null){
try {
objectInputStream.close();
} catch (IOException e) {}
}
if(fileInputStream != null){
try {
fileInputStream.close();
} catch (IOException e) {}
}
}
}
}
注意:
serialVersionUID用来标识类版本,类修改后如果版本号不同,反序列化会失败,建议手动指定。
transient 关键字:修饰的字段不会被序列化。