Java中的IO流小结

Java中的IO

文件的操作

  1. 有三种构造器, 可以"捆绑"到某个路径, 然后调用file.createNewFile() 可以创造文件
  2. 一些常用的文件方法, 包括但不限于获取名字, 路径, 父级目录, 大小, 是否存在, 删除等...
  3. 一些常用的目录方法, 这里其实目录也可以当作一种特殊的文件, file.mkdirs() 可以创建多级目录, file.mkdir()可以创建一级目录

IO流

Java程序中, 对于数据的输入/输出以"流"的方式进行, java.io包下提供了各种"流"类和接口, 用以获取不同种类的数据, 并通过方法输入或者输出数据

常用的类

一. 节点流

节点流是底层流, 直接跟数据源相连

一. FileInputStream/FileOutputStream
  1. FileInputStream 用来读取文件的内容, 常用的方法为int read(byte[] b), 如果没有, 返回-1, 否则返回实际读取的长度
@Test
public void readTest() {
    String path = "D:\\mypile\\acm\\hello.txt";
    FileInputStream fileInputStream = null;
    try {
        fileInputStream = new FileInputStream(path);
        byte[] bytes = new byte[256];

        int len;
        while ((len = fileInputStream.read(bytes)) != -1) {
            System.out.print(new String(bytes, 0, len));
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        try {
            fileInputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
  1. FileOutputStream 用来写入文件, 大概写法同上, 如果文件不存在就会创建文件, 有一个注意点就是我们构造器后面可以传入一个Boolean变量来将append设置为true或者false, 如果append 为 true, 就代表我们不会覆盖文件, 一直往后写, 如果是false就会不断覆盖
二. FileReader/FileWriter
  1. FileReader, FileWriter 是 字符的输入和输出流, 因为是按照字符读取, 所以传入的是char数组, 大概方法和字节流一样

注意:

  1. 注意汉字的编码形式, 比如在utf-8中, 一个汉字占三个字节, 所以一般利用字符流
  2. 对于写入文件的FileWriter, 写入后记得flush() 或者 close, 要不然写不进去

二. 包装流

处理流包装了节点流, 既可以消除不同节点流的实现差异, 也可以提供更方便的方法来完成输入输出. 处理流对节点流进行包装, 使用了修饰器设计模式, 不会直接与数据源相连, 操作更加便捷, 性能更加高

  1. Java提供了包装流/处理流: BufferedReader/BufferedWriter, 功能更加强大, 其类中有属性Reader/Writer, 所以封装了一个节点流, 可以放上述所说的子类
一.BufferedReader/BufferedWriter

BufferedReader/BufferedWriter 都是字符流, 关闭的时候只需要关闭外部流即可, 一般用于文本文件的读写, 如果音频图片等应该使用字节流

@Test
public void readTest() throws IOException {
    String path = "D:\\mypile\\acm\\hello.txt";
    BufferedReader bufferedReader = new BufferedReader(new FileReader(path));

    // 按行读取
    String line;
    while ((line = bufferedReader.readLine()) != null) {
        System.out.println(line);
    }
    // 只需关闭外部流, 内部流也会关闭
    bufferedReader.close();

    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(path, true));

    bufferedWriter.write("\n你好世界\n");

    bufferedWriter.close();
}
二.BufferedInputStream/BufferedOutputStream

处理流也有可以处理二进制文件的流, 也就是字节流: BufferedInputStream/BufferedOutputStream

@Test
public void readT2() throws IOException {
    String srcPath = "D:\\mypile\\acm\\hello.txt";
    String destPath = "D:\\mypile\\acm\\hello2.txt";

    BufferedInputStream bufferedInputStream;
    BufferedOutputStream bufferedOutputStream;

    bufferedInputStream = new BufferedInputStream(new FileInputStream(srcPath));
    bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destPath));
    
    byte[] buff = new byte[1024];
    int len = 0;
    while ((len = bufferedInputStream.read(buff)) != -1) {
        bufferedOutputStream.write(buff, 0, len);
    }
    
    if (bufferedInputStream != null) {
        bufferedInputStream.close();
    }
    if (bufferedOutputStream != null) {
        bufferedOutputStream.close();
    }
}

字节是根本, 所以字节流也可以处理文本文件

三. 对象处理流

有的时候我们需要保存具体的数据类型或者对象到文件中, 读出的时候也要保留数据类型或者对象的类型, 简而言之就是希望能够将基本数据类型或者对象进行序列化 或者 反序列化操作

  1. 序列化: 在保存数据时, 保存数据的值和数据类型
  2. 反序列化: 恢复数据时, 恢复数据的值和数据类型

需要让某个对象支持序列化机制, 则必须让其类是可序列化的, 为了让某个类可序列化, 该类就要实现如下两个结构之一

Serializable // 这是一个标记接口, 是一个空接口, 所以一般实现这个
Externalizable // 有方法需要实现

一. ObjectInputStream/ObjectOutputStream
  1. ObjectInputStream/ObjectOutputStream 是对象处理流, 可以将数据序列化存取
class Dog implements Serializable {

}

@Test
public void ObjectStreamTest() throws IOException, ClassNotFoundException {
    String filePath = "D:\\mypile\\acm\\serial.txt";

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));

    // 序列化数据到文件中
    oos.writeInt(100); // int -> Integer (实现了Serializable接口)
    oos.writeBoolean(true);
    oos.writeUTF("序列化练习");
    oos.writeObject(new Dog());
    oos.close();

    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));

    // 读取的顺序一定要和保存的顺序保持一致, 否则会出现异常

    System.out.println(ois.readInt());
    System.out.println(ois.readBoolean());
    System.out.println(ois.readUTF());
    Object o = ois.readObject();
    System.out.println(o.getClass());
    
    ois.close();

}

注意

  1. 读写顺序要一致
  2. 要求序列化或者反序列化的对象, 需要实现Serializable接口
  3. 序列化的类中建议添加SerialVersionUID, 为了提高版本的兼容性
  4. 序列化对象时, 默认将里面的所有的属性都序列化, 除了static或者transient修饰的成员
  5. 序列化对象时, 要求里面的属性的类型也需要实现序列化接口, 例如内部类
  6. 序列化具备可继承性, 也就是如果某个类已经实现序列化, 则他的子类也已经默认实现了序列化, 例如NumberInteger
  7. 注意序列化的类在读和写这两个位置必须是共有的

四. 标准输入输出流

  1. System.in

编译类型是InputStream, 但是运行类型是BufferedInputStream, 属于字节流, 表示的是标准输入, 键盘

  1. System.out

编译类型是PrintStream, 运行类型也是PrintStream, 表示标准输出, 显示器

五. 转换流

可以把字节流转换成字符流, 我们对字节流指定编码方式, 这样的话就可以解决乱码问题

1. InputStreamReader/OutputStreamWriter

可以将InputStream/OutputStream 这样的字节流包装成字符流Reader/Writer 同时指定编码方式

@Test
public void transformTest() throws IOException {
    // 使用 OutputStreamWriter 转换流
    // 将字节流 FileOutputStream 转换成 字符流 OutputStreamWriter
    String path = "D:\\mypile\\acm\\transform.txt";

    BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(
            new FileOutputStream(path), "utf-8"));

    bufferedWriter.write("你好, 世界");

    bufferedWriter.close();

    // 使用InputStreamReader 转换流
    // 将字节流FileInputStream 转成 字符流 InputStreamReader 指定编码utf-8
    InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(path), "utf-8");

    // 把InputStreamReader 传给 BufferedReader
    BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

    // 读取
    String line = bufferedReader.readLine();
    System.out.println(line);

    bufferedReader.close();
    
}

六. 打印流

打印流只有输出流, PrintStream属于字节流的子类, PrintWriter是字符流

使用方法

@Test
public void PrintTest() throws IOException {
    PrintStream out = new PrintStream(System.out);
    out.print("hello");
    // print底层其实调用的是writer, 所以可以直接用write
    out.write("hello".getBytes());

    // 可以修改打印流输出的位置
    String path = "D:\\mypile\\acm\\print.txt";
    System.setOut(new PrintStream(path));
    System.out.println("hello");
    out.close();

    PrintWriter printWriter = new PrintWriter(System.out);
    printWriter.print("hello");

    // 重定向
    PrintWriter printWriter1 = new PrintWriter(path);

    // 必须close才能写进去文件, close = flush + 关闭流
    printWriter.close();
    printWriter1.close();

}

七. Properties

Hashtable的一个子类, 轻松地读取以及写入配置文件

配置文件的格式:

键=值
...

使用方法

@Test
public void PropertiesTest() throws IOException {
    String path = "src\\root.properties";

    // 创建对象
    Properties properties = new Properties();

    // 加载指定的配置文件
    properties.load(new FileReader(path));

    // 把配置信息显示到控制台
    properties.list(System.out);

    // 获取指定的值, 根据key
    System.out.println(properties.getProperty("password"));
    System.out.println(properties.getProperty("root"));

    //创建新的配置
    properties.setProperty("charset", "gbk");
    properties.setProperty("user", "tom");

    properties.list(System.out);
    // 也可以先创建properties再创建文件, 可以使用store方法

    // 修改key对应的value
    properties.setProperty("user", "john");

    properties.list(System.out);
}

posted @   Xingon2356  阅读(2)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)
点击右上角即可分享
微信分享提示