IO 流与文件操作

IO 流与文件操作

IO 流与文件操作

IO 就是 Input/Output,输入输出,用于读写文件、网络数据等。Java 中 IO 靠流来完成。

IO 流的分类

Java IO 流分为很多种,可以按不同维度分类:

  1. 按方向分:
  • 输入流:读数据,从磁盘/网络读到内存
  • 输出流:写数据,从内存写到磁盘/网络
  1. 按数据类型分:
  • 字节流:以字节为单位,InputStream、OutputStream,适合所有类型文件(文本、图片、视频都可以)
  • 字符流:以字符为单位,Reader、Writer,只适合纯文本文件(比如 txt, java 源码)
  1. 按功能分:
  • 节点流:直接操作数据源,比如 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 关键字:修饰的字段不会被序列化。

上一篇
下一篇