Java IO流

前置知识

  • IO在计算机中指input/output,即输入/输出,用于处理设备之间的数据传输,如文件读写、网络间的通信等
    • 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存中)
    • 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
  • Java中对于数据的输入/输出操作以“流”的形式进行
  • Java序列化和反序列化
    • Java序列化和反序列化需要对象类实现Serializable接口
    • 对象序列化机制:把内存中Java对象转换成二进制流,从而允许把这种二进制流持久化地保存在磁盘上或者通过网络将这种二进制流传输到另一个网络节点
    • 对象反序列化机制:将Java对象的二进制流恢复成原来的Java对象

Java IO 简介

java.io 包下提供了各种“流”类的接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据,而流的本质是一种有序的数据集合,有数据源和目的地。对于计算机来说,数据都是以二进制形式读出或写入的,如何将数据从数据源送到目的地的操作就是流操作过程


Java IO流分类

  • 按照流的流向划分,可以分为输入流和输出流

  • 按照操作单元划分,可以划分为字节流和字符流

  • 按照流的角色划分节点流和处理流

    • 节点流:可以从一个或向一个特定的地方(节点)读写数据。如FileReader

    • 处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如BufferedReader

      • 处理流的构造方法总是要带一个其他的流对象做参数

      • 一个流对象经过其他流的多次包装,称为流的链接

Java IO 流都是从如下 4 个抽象类基类中派生出来的

  • InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流

  • OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流


字符流

Reader(字符输入流)

Reader用于从源头(通常是文件,如文本文件)读取数据(字符信息)到内存中,是所有字符输入流的父类

Reader 常用方法 

  • read() :从输入流读取一个字符,如果已到达流的末尾,则为 -1
  • read(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

posted @   伊文小哥  阅读(201)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示