Java 流(Stream)、文件(File)和IO

一、Java 流(Stream)、文件(File)和IO概念和关系

Java 中的流(Stream)、文件(File)和 IO 之间是紧密关联但又各有侧重的概念,它们共同构成了 Java 输入输出系统的核心。可以用"工具-对象-操作"的关系来理解:

  1. IO 是总称

    IO(Input/Output,输入/输出)是所有数据传输操作的统称,涵盖了程序与外部设备(文件、网络、键盘等)之间的数据交换。

    流(Stream)和文件(File)都是实现 IO 操作的具体方式或操作对象。

  2. File 是操作的实体
    java.io.File 类代表文件系统中的一个文件或目录,是 IO 操作的具体对象

    但它本身不能直接读写数据,只能描述文件的属性(如路径、大小、是否存在等),并提供创建、删除等文件管理功能。

  3. Stream 是操作的工具

    流(Stream)是连接程序与外部数据源(包括 File)的数据传输通道 ,是实现 IO 操作的核心工具。

    当需要读写 File 中的内容时,必须通过流来实现:

    • 读文件:用输入流(如 FileInputStream)从 File 中读取数据到程序
    • 写文件:用输出流(如 FileOutputStream)从程序写入数据到 File
  4. 三者协作关系

    一个典型的文件 IO 操作流程是:
    创建 File 对象(指定要操作的文件)通过流连接 File(如 new FileInputStream(file))通过流进行 IO 操作(读/写数据)

二、读写文件

2.1 操作文件时涉及的字符流和字节流的概念介绍:

字节流

  • 基本概念:字节流是以字节(8位二进制数据)作为基本处理单位的流,它能对各种类型的文件进行读写操作,因为计算机中所有的数据在底层都是以字节形式存储和传输的。字节流可以直接操作二进制数据,无论是文本文件还是非文本文件(如图像、音频、视频等)都能处理。

  • 字节输入流(InputStream)

    用于从文件等数据源读取字节数据到程序中。它是字节流输入操作的抽象基类,提供了如 read() 等基础方法用于读取字节数据。

  • 字节输出流(OutputStream)

    负责将程序中的字节数据写入到文件等目标数据源中。它是字节流输出操作的抽象基类,提供了 write() 等方法用于写入字节数据。

字符流

  • 基本概念:字符流以字符(一般是16位的Unicode编码字符)为基本处理单位,更侧重于处理文本文件,它在读写文本时会按照字符编码规范进行转换,能更好地保证文本内容的正确读写,还能方便地按行等文本特性进行操作。

  • 字符输入流(Reader)

    用于从文件等数据源读取字符数据到程序里,是字符流输入操作的抽象基类,提供了 read() 等方法来获取字符数据。

  • 字符输出流(Writer)

    主要用于将程序中的字符数据写入到文件等目标数据源中,是字符流输出操作的抽象基类,具备 write() 等方法来输出字符数据。

字节流、字符流常用关系图

2.2 字节流常用方法

字节流常用于处理二进制数据,例如文件、图像、时频。

类名 类型 描述
InputStream 抽象类(输入流) 所有字节输入流的超类,处理字节的输入操作
OutputStream 抽象类(输出流) 所有字节输出流的超类,处理字节的输出操作
FileInputStream 输入流 从文件中读取字节数据
FileOutputStream 输出流 将字节数据写入文件
BufferedInputStream 输入流 为字节输入流提供缓冲功能,提高读取效率
BufferedOutputStream 输出流 为字节输出流提供缓冲功能,提高写入效率
ByteArrayInputStream 输入流 将内存中的字节数组作为输入源
ByteArrayOutputStream 输出流 将数据写入到内存中的字节数组
DataInputStream 输入流 允许从输入流中读取Java原生数据类型(如int、float、boolean等)
DataOutputStream 输出流 允许向输出流中写入Java原生数据类型
ObjectInputStream 输入流 从输入流中读取序列化对象
ObjectOutputStream 输出流 将对象序列化并写入输出流中
PipedInputStream 输入流 用于在管道中读取字节数据,通常与PipedOutputStream配合使用
PipedOutputStream 输出流 用于在管道中写入字节数据,通常与PipedInputStream配合使用
FilterInputStream 输入流 字节输入流的包装类,用于对其他输入流进行过滤处理
FilterOutputStream 输出流 字节输出流的包装类,用于对其他输出流进行过滤处理
SequenceInputStream 输入流 将多个输入流串联为一个输入流进行处理

以下是基于表格中各类字节流的读写文件示例,涵盖不同场景的文件操作:

1. FileInputStream/FileOutputStream(基础文件读写)

FileInputStream 和 FileOutputStream 是 Java IO 中用于直接操作文件的字节流,专门负责从文件读取字节数据和向文件写入字节数据,是处理文件字节流的基础类。

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) {
        // 写入文件
        try {
            FileOutputStream fileOutputStream =new FileOutputStream("file.txt");
            String data="Hello byteStream!";
            fileOutputStream.write(data.getBytes());
            fileOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            FileInputStream fileInputStream =new FileInputStream("file.txt");
            int c;
            while((c=fileInputStream.read())!=-1){
                System.out.print((char)c);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

2. BufferedInputStream/BufferedOutputStream(缓冲流高效读写)

BufferedInputStream 和 BufferedOutputStream 是 Java IO 中提供缓冲功能的过滤流,它们通过在内存中维护缓冲区来减少物理 I/O 操作次数,从而显著提升读写效率。

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) {
        // 写入文件
        try {
            BufferedOutputStream bufferedOutputStream =new BufferedOutputStream(new FileOutputStream("file.txt"));
            String data="Hello byteStream!";
            bufferedOutputStream.write(data.getBytes());
            bufferedOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            BufferedInputStream bufferedInputStream =new BufferedInputStream(new FileInputStream("file.txt"));
            int c;
            while((c=bufferedInputStream.read())!=-1){
                System.out.print((char)c);
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

3. DataInputStream/DataOutputStream(读写基本数据类型)

DataInputStream 和 DataOutputStream 是 Java IO 中用于读写基本数据类型的过滤流,它们可以直接操作 Java 原生数据类型(如 int、double、boolean 等),无需手动处理字节转换,简化了基本类型数据的读写操作。

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) {
        // 写入文件
        try {
            DataOutputStream bufferedOutputStream =new DataOutputStream(new FileOutputStream("file.bat"));
            bufferedOutputStream.writeInt(100);       // 写入整数
            bufferedOutputStream.writeDouble(3.14);   // 写入双精度浮点数
            bufferedOutputStream.writeUTF("Hello byteStream!");
            bufferedOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            DataInputStream bufferedInputStream =new DataInputStream(new FileInputStream("file.bat"));
            System.out.println(bufferedInputStream.readInt());
            System.out.println(bufferedInputStream.readDouble());
            System.out.println(bufferedInputStream.readUTF());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

4. ObjectInputStream/ObjectOutputStream(对象序列化)

ObjectInputStream 和 ObjectOutputStream 是 Java 中用于对象序列化与反序列化的字节流,主要功能是将内存中的 Java 对象转换为字节序列(序列化)并写入流中,或从流中读取字节序列并恢复为 Java 对象(反序列化)。

java 复制代码
import java.io.*;

// 需实现Serializable接口才能被序列化
class User implements Serializable {
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
}

public class Main {
    public static void main(String[] args) {
        // 序列化对象写入文件
        try {
            ObjectOutputStream objectOutputStream =new ObjectOutputStream(new FileOutputStream("file.obj"));
            User user=new User("Alice", 18);
            objectOutputStream.writeObject(user);
            objectOutputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("file.obj"));
            User user=(User) objectInputStream.readObject();
            System.out.println(user);
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }
}

5.ByteArrayOutputStream(内存字节数组读写)

它可以将数据写入内存中的字节数组缓冲区。创建后可以用来收集和操作字节数据,常用于临时存储或处理二进制数据。

java 复制代码
public class Main {
    public static void main(String[] args) {
        ByteArrayOutputStream byteArrayOutputStream =new ByteArrayOutputStream();
        byte [] bytes = new byte[1024];

        try {


            bytes[0]=(byte) 'H';
            bytes[1]=(byte) 'e';
            bytes[2]=(byte) 'l';
            bytes[3]=(byte) 'l';
            bytes[4]=(byte) 'o';
            byteArrayOutputStream.write(bytes,0,5);
            byteArrayOutputStream.close();

        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        byte [] bytes1 = new byte[1024];
        int c;
        try{
            while ((c=byteArrayInputStream.read(bytes1))!=-1){
                System.out.println(new String(bytes1, 0, c));
            }
        }catch (IOException e){
            throw new RuntimeException(e);
        }


    }
}

6. SequenceInputStream(合并多个输入流)

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) {
        try {
            SequenceInputStream sequenceInputStream =new SequenceInputStream(new FileInputStream("file.txt"),new FileInputStream("test.txt"));
            int c;
            while((c=sequenceInputStream.read())!=-1){
                System.out.print((char)c);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

}
}

7. FilterInputStream/FilterOutStream

FilterInputStream 和 FilterOutputStream 是 Java IO 中的过滤流基类,属于装饰器模式(Decorator Pattern)的典型实现,用于对现有流进行功能扩展或增强。

java 复制代码
import java.io.FilterInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;

// 自定义过滤输入流:转换为大写
class UpperCaseInputStream extends FilterInputStream {
    /**
     * Creates a <code>FilterInputStream</code>
     * by assigning the  argument <code>in</code>
     * to the field <code>this.in</code> so as
     * to remember it for later use.
     *
     * @param in the underlying input stream, or <code>null</code> if
     *           this instance is to be created without an underlying stream.
     */
    protected UpperCaseInputStream(InputStream in) {
        super(in);
    }
    @Override
    public int read(byte [] b,int off,int len ) throws IOException {
         int result = super.read(b, off, len);//读取的字节数
         if(result!=-1){
             for(int i=off;i<off+result;i++){
                 b[i]= (byte) Character.toUpperCase((char)b[i]);
             }
         }
         return result;

    }
}

public class Main {
    public static void main(String[] args) {
        try {
            UpperCaseInputStream
                    upperCaseInputStream =new UpperCaseInputStream(new FileInputStream("file.txt"));
            int c;
            byte [] bytes=new byte[1024];
            while((c=upperCaseInputStream.read(bytes))!=-1){
                System.out.print(new String(bytes,0,c));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

8.pipedOutputStream/pipedInputStream

PipedOutputStream 和 PipedInputStream 是 Java 中用于线程间通信的管道流,二者必须配合使用,形成 "生产者 - 消费者" 模式的数据传输通道。

java 复制代码
public class Main {
    public static void main(String[] args) {
        PipedOutputStream pipedOutputStream =new PipedOutputStream();
        PipedInputStream pipedInputStream;
        try {
            pipedInputStream =new PipedInputStream(pipedOutputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    pipedOutputStream.write("Hello byteStream!".getBytes());
                    pipedOutputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();

        new Thread(new Runnable(){
            @Override


            public void run() {
                try {
                    int c;
                    while((c=pipedInputStream.read())!=-1){
                        System.out.print((char)c);
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }

}

2.3 字符流常用方法

字符流常用于处理文本数据。

类名 类型 描述
Reader 抽象类(输入流) 所有字符输入流的超类,处理字符的输入操作
Writer 抽象类(输出流) 所有字符输出流的超类,处理字符的输出操作
FileReader 输入流 从文件中读取字符数据
FileWriter 输出流 将字符数据写入文件
BufferedReader 输入流 为字符输入流提供缓冲功能,支持按行读取,提高读取效率
BufferedWriter 输出流 为字符输出流提供缓冲功能,支持按行写入,提高写入效率
CharArrayReader 输入流 将字符数组作为输入源
CharArrayWriter 输出流 将数据写入到字符数组
StringReader 输入流 将字符串作为输入源
StringWriter 输出流 将数据写入到字符串缓冲区
PrintWriter 输出流 便捷的字符输出流,支持自动刷新和格式化输出
PipedReader 输入流 用于在管道中读取字符数据,通常与 PipedWriter 配合使用
PipedWriter 输出流 用于在管道中写入字符数据,通常与 PipedReader 配合使用
LineNumberReader 输入流 带行号的缓冲字符输入流,允许跟踪读取的行号
PushbackReader 输入流 允许在读取字符后将字符推回流中,以便再次读取

以下是字符流各类的读写文件示例,每个示例均包含写入和读取操作:

1. FileReader/FileWriter(文件字符读写)

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) {
        // 写入文本到文件
        try (FileWriter writer = new FileWriter("test.txt")) {
            writer.write("Hello, FileReader/FileWriter!\n");
            writer.write("这是一行行中文文本");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 从文件读取文本
        try (FileReader reader = new FileReader("test.txt")) {
            char[] buffer = new char[1024];
            int len;
            while ((len = reader.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2. BufferedReader/BufferedWriter(缓冲字符流)

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 按行写入文本(指定UTF-8编码)
        try (BufferedWriter writer = new BufferedWriter(
                new OutputStreamWriter(new FileOutputStream("buffered.txt"), StandardCharsets.UTF_8))) {
            writer.write("第一行内容");
            writer.newLine(); // 跨平台换行
            writer.write("第二行内容");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 按行读取文本
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(new FileInputStream("buffered.txt"), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) { // 按行读取
                System.out.println("读取到:" + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3. CharArrayReader/CharArrayWriter(字符数组流)

CharArrayReader 和 CharArrayWriter 是 Java 中以字符数组为操作对象的字符流,所有操作均在内存中基于字符数组完成,无需依赖外部存储。

java 复制代码
import java.io.*;

public class CharArrayReadWriteExample {
    public static void main(String[] args) {
        // CharArrayWriter先写入字符数组(内存中)
        CharArrayWriter caw = new CharArrayWriter();
        try {
            caw.write("先写入内存字符数组,再转存到文件");
            char[] charData = caw.toCharArray(); // 获取字符数组

            // 写入文件
            try (FileWriter fw = new FileWriter("charArray.txt")) {
                fw.write(charData);
            }

            // CharArrayReader从字符数组读取
            CharArrayReader car = new CharArrayReader(charData);
            char[] buffer = new char[1024];
            int len;
            System.out.println("CharArrayReader读取内容:");
            while ((len = car.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }
            car.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            caw.close();
        }
    }
}

4. StringReader/StringWriter(字符串流)

StringReader 和 StringWriter 是 Java 中操作内存字符串的字符流,无需依赖外部文件,所有操作均在内存中完成。

java 复制代码
import java.io.*;

public class StringReadWriteExample {
    public static void main(String[] args) {
        // StringWriter写入字符串缓冲区
        StringWriter sw = new StringWriter();
        try {
            sw.write("字符串流操作示例\n");
            sw.write("数据保存在StringBuffer中");
            String data = sw.toString(); // 获取字符串

            // 写入文件
            try (FileWriter fw = new FileWriter("string.txt")) {
                fw.write(data);
            }

            // StringReader从字符串读取
            StringReader sr = new StringReader(data);
            char[] buffer = new char[1024];
            int len;
            System.out.println("StringReader读取内容:");
            while ((len = sr.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }
            sr.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            sw.close();
        }
    }
}

5. PrintWriter(格式化字符输出流)

PrintWriter 是 Java 中功能强大的字符输出流,提供了便捷的文本写入、格式化输出和自动刷新等功能,广泛用于输出文本数据到文件、控制台或其他输出流。

java 复制代码
import java.io.*;

public class PrintWriterExample {
    public static void main(String[] args) {
        // PrintWriter写入(支持格式化和自动刷新)
        try (PrintWriter pw = new PrintWriter(new FileWriter("print.txt"), true)) { // 第二个参数开启自动刷新
            pw.println("PrintWriter便捷输出");
            pw.printf("格式化输出:%d + %d = %d%n", 2, 3, 5); // 类似printf格式
            pw.println("自动刷新生效");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 读取验证
        try (BufferedReader br = new BufferedReader(new FileReader("print.txt"))) {
            System.out.println("PrintWriter写入内容:");
            String line;
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

6. PipedReader/PipedWriter(管道字符流,线程间通信)

java 复制代码
import java.io.*;

public class PipedReadWriteExample {
    public static void main(String[] args) throws IOException {
        // 创建管道流对(必须绑定)
        PipedWriter pw = new PipedWriter();
        PipedReader pr = new PipedReader(pw);

        // 写入线程
        Thread writerThread = new Thread(() -> {
            try {
                pw.write("管道流线程间通信数据");
                pw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        // 读取线程(并写入文件)
        Thread readerThread = new Thread(() -> {
            try (FileWriter fw = new FileWriter("piped.txt")) {
                char[] buffer = new char[1024];
                int len;
                while ((len = pr.read(buffer)) != -1) {
                    fw.write(buffer, 0, len); // 写入文件
                    System.out.println("管道读取内容:" + new String(buffer, 0, len));
                }
                pr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        writerThread.start();
        readerThread.start();
    }
}

7. LineNumberReader(带行号的字符输入流)

LineNumberReader 是 Java 中带有行号跟踪功能的缓冲字符输入流,继承自 BufferedReader,在提供高效缓冲读取的同时,能自动跟踪当前读取的行号,方便定位文本中的特定行。

java 复制代码
import java.io.*;

public class LineNumberReaderExample {
    public static void main(String[] args) {
        // 先写入多行数据
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("linenumber.txt"))) {
            bw.write("第一行内容");
            bw.newLine();
            bw.write("第二行内容");
            bw.newLine();
            bw.write("第三行内容");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // LineNumberReader读取并跟踪行号
        try (LineNumberReader lnr = new LineNumberReader(new FileReader("linenumber.txt"))) {
            lnr.setLineNumber(1); // 设置起始行号(默认从0开始)
            String line;
            System.out.println("带行号的内容:");
            while ((line = lnr.readLine()) != null) {
                System.out.printf("第%d行:%s%n", lnr.getLineNumber(), line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

8. PushbackReader(可回退字符的输入流)

PushbackReader 是 Java 中的一个字符输入流,继承自 FilterReader,它的特殊之处在于允许将已读取的字符 "推回"(回退)到流中。

java 复制代码
import java.io.*;

public class Main {
    public static void main(String[] args) {

        try {
            PushbackReader reader =new PushbackReader(new FileReader("file.txt"));
            int c;
            while((c=reader.read())!=-1){
                if(c=='o'){
                    reader.unread(c);
                    break;
                }
                System.out.println(":"+(char)c
                );
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}