[Java基础] 输入输出流
往期回顾
[Java基础] 流程控制
[Java基础] 运算符
[Java基础] 基本数据类型
[Java基础] Java HashMap 的数据结构和底层原理
[Java基础] 面向对象编程
[Java基础] 异常处理机制
[Java基础] 集合框架
目录
分类
字节流
字符流
管道流
打印流
总结
实战
字节流实战
文件读写(FileInputStream 和 FileOutputStream)
基本数据类型读写(DataInputStream 和 DataOutputStream)
对象流(ObjectInputStream 和 ObjectOutputStream)
字符流实战(FileReader 和 FileWriter)
管道流实战(PipedInputStream 和 PipedOutputStream)
打印流实战(PrintStream 和 PrintWriter)
最佳实践
使用 try-with-resources 语句
使用缓冲流
处理异常
使用合适的流类型
使用字符集
分块读写
分类
Java中的输入输出流主要分为四大类:字节流、字符流、管道流和打印流。每种流都有其特定的应用场景,下面详细介绍一下这些流及其应用场景。
字节流
字节流用于处理二进制数据,如图片、音频、视频等。字节流的基类是InputStream
和OutputStream
。
- FileInputStream 和 FileOutputStream:用于文件的读写操作。
- 应用场景:读取或写入文件,如复制文件、读取配置文件等。
- BufferedInputStream 和 BufferedOutputStream:提供缓冲功能,提高读写性能。
- 应用场景:当需要频繁读写文件时,使用缓冲流可以显著提高性能。
- DataInputStream 和 DataOutputStream:支持读写基本数据类型。
- 应用场景:处理包含基本数据类型的数据文件,如二进制文件、配置文件等。
- ObjectInputStream 和 ObjectOutputStream:支持对象的序列化和反序列化。
- 应用场景:持久化对象状态,如保存和恢复对象的状态,网络传输对象等。
字符流
字符流用于处理文本数据,以字符为单位进行读写。字符流的基类是Reader
和Writer
。
- FileReader 和 FileWriter:用于文件的字符读写。
- 应用场景:读取或写入文本文件,如日志文件、配置文件等。
- BufferedReader 和 BufferedWriter:提供缓冲功能,可以一次读取一行或多行文本。
- 应用场景:处理大量文本数据,如日志分析、文本处理等。
- InputStreamReader 和 OutputStreamWriter:作为字节流和字符流之间的桥梁,使用指定的字符集进行转换。
- 应用场景:处理不同字符集的文本文件,如读取UTF-8编码的文件。
管道流
管道流用于线程间的通信,实现线程间的协作。
- PipedInputStream 和 PipedOutputStream:用于线程间的字节数据传输。
- 应用场景:多线程环境下,一个线程生成字节数据,另一个线程消费字节数据。
- PipedReader 和 PipedWriter:用于线程间的字符数据传输。
- 应用场景:多线程环境下,一个线程生成文本数据,另一个线程消费文本数据。
打印流
打印流提供格式化的输出,通常用于标准输出和错误输出。
- PrintStream 和 PrintWriter:支持格式化的输出。
- 应用场景:控制台输出、日志记录、调试信息等。
总结
- 字节流:适用于处理二进制数据,如文件复制、图像处理等。
- 字符流:适用于处理文本数据,如读写文本文件、日志处理等。
- 管道流:适用于多线程环境下的数据传输。
- 打印流:适用于格式化的输出,如控制台输出、日志记录等。
实战
以下是一些关于Java中字节流、字符流、管道流和打印流的实战代码示例。
字节流实战
文件读写(FileInputStream 和 FileOutputStream)
这个示例展示了如何使用字节流从文件中读取数据并将其写入到另一个文件中。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException; public class ByteStreamExample { public static void main(String[] args) { String inputFilePath = "source.txt"; String outputFilePath = "destination.txt"; //这里使用了try-with-resource简化资源管理,这是Java7引入的新特性try (FileInputStream fis = new FileInputStream(inputFilePath); FileOutputStream fos = new FileOutputStream(outputFilePath)) { int data; while ((data = fis.read()) != -1) { fos.write(data); } System.out.println("File copied successfully!"); } catch (IOException e) { e.printStackTrace(); } }
}
在这个例子中,source.txt
是源文件,destination.txt
是目标文件。程序会读取 source.txt
的内容并将其写入 destination.txt
。
基本数据类型读写(DataInputStream 和 DataOutputStream)
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class DataIOExample {public static void main(String[] args) {String filePath = "C:\\path\\to\\datafile.dat";// 写入基本数据类型try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(filePath))) {dos.writeInt(123);dos.writeDouble(3.14);dos.writeUTF("Hello, World!");} catch (IOException e) {e.printStackTrace();}// 读取基本数据类型try (DataInputStream dis = new DataInputStream(new FileInputStream(filePath))) {int intValue = dis.readInt();double doubleValue = dis.readDouble();String stringValue = dis.readUTF();System.out.println("整数: " + intValue);System.out.println("双精度浮点数: " + doubleValue);System.out.println("字符串: " + stringValue);} catch (IOException e) {e.printStackTrace();}}
}
对象流(ObjectInputStream 和 ObjectOutputStream)
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;public class ObjectSerializationExample {public static void main(String[] args) {String filePath = "C:\\path\\to\\objectfile.ser";// 序列化对象try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {Person person = new Person("Alice", 30);oos.writeObject(person);} catch (IOException e) {e.printStackTrace();}// 反序列化对象try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {Person person = (Person) ois.readObject();System.out.println("姓名: " + person.getName());System.out.println("年龄: " + person.getAge());} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}//进行序列化和反序列化的对象必须实现Serializable接口static class Person implements Serializable {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public int getAge() {return age;}}
}
字符流实战(FileReader 和 FileWriter)
这个示例展示了如何使用字符流从文件中读取文本数据并将其写入到另一个文件中。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; public class CharacterStreamExample { public static void main(String[] args) { String inputFilePath = "input.txt"; String outputFilePath = "output.txt"; try (BufferedReader br = new BufferedReader(new FileReader(inputFilePath)); BufferedWriter bw = new BufferedWriter(new FileWriter(outputFilePath))) { String line; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); // 添加新行 } System.out.println("File copied successfully!"); } catch (IOException e) { e.printStackTrace(); } }
}
在这个例子中,程序会逐行读取 input.txt
的内容,并将其写入 output.txt
,同时保留每行的换行符。
管道流实战(PipedInputStream 和 PipedOutputStream)
管道流允许在线程之间传输数据。以下示例展示了如何使用管道流在两个线程之间发送和接收数据。
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream; public class PipedStreamExample { public static void main(String[] args) { try (PipedInputStream pin = new PipedInputStream(); PipedOutputStream pout = new PipedOutputStream(pin)) { // 生产者线程 Thread producer = new Thread(() -> { try { String data = "Hello from Producer!"; pout.write(data.getBytes()); pout.close(); } catch (IOException e) { e.printStackTrace(); } }); // 消费者线程 Thread consumer = new Thread(() -> { try { int data; StringBuilder sb = new StringBuilder(); while ((data = pin.read()) != -1) { sb.append((char) data); } System.out.println("Received: " + sb.toString()); } catch (IOException e) { e.printStackTrace(); } }); producer.start(); consumer.start(); producer.join(); consumer.join(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }
}
在这个例子中,生产者线程将字符串数据写入 PipedOutputStream
,而消费者线程从 PipedInputStream
中读取数据。
打印流实战(PrintStream 和 PrintWriter)
打印流提供了一种方便的方法来格式化输出文本数据。以下示例展示了如何使用PrintStream
输出字符到控制台以及使用 PrintWriter
写字符串到内存。
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;public class PrintStreamExample {public static void main(String[] args) {// 使用 PrintStream 输出到控制台PrintStream ps = System.out;ps.println("Hello, World!");ps.printf("整数: %d, 双精度浮点数: %.2f\n", 123, 3.14);// 使用 PrintWriter 输出到字符串StringWriter sw = new StringWriter();PrintWriter pw = new PrintWriter(sw);pw.println("Hello, World!");pw.printf("整数: %d, 双精度浮点数: %.2f\n", 123, 3.14);pw.flush();System.out.println("输出到字符串: " + sw.toString());}
}
最佳实践
在Java中处理输入输出流时,遵循一些最佳实践可以提高代码的健壮性、可读性和性能。以下是一些常见的最佳实践:
使用 try-with-resources
语句
try-with-resources
语句可以确保资源在使用完毕后自动关闭,避免资源泄露。这是Java 7引入的一个重要特性。
使用缓冲流
缓冲流可以显著提高读写性能。常见的缓冲流有 BufferedInputStream
、BufferedOutputStream
、BufferedReader
和 BufferedWriter
。
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;public class BufferedInputStreamExample {public static void main(String[] args) {String filePath = "C:\\path\\to\\file.txt";try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath))) {int byteRead;while ((byteRead = bis.read()) != -1) {System.out.print((char) byteRead);}} catch (IOException e) {e.printStackTrace();}}
}
处理异常
合理地处理异常,确保程序在遇到错误时能够优雅地退出或继续运行。
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;public class ExceptionHandlingExample {public static void main(String[] args) {String filePath = "C:\\path\\to\\file.txt";try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}} catch (IOException e) {System.err.println("文件读取失败: " + e.getMessage());}}
}
使用合适的流类型
根据具体需求选择合适的流类型。例如,处理文本文件时使用字符流(Reader
和 Writer
),处理二进制文件时使用字节流(InputStream
和 OutputStream
)。
使用字符集
在处理文本文件时,显式指定字符集可以避免乱码问题。使用 InputStreamReader
和 OutputStreamWriter
时可以指定字符集。
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.IOException;public class CharsetExample {public static void main(String[] args) {String filePath = "C:\\path\\to\\file.txt";try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8"))) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}} catch (IOException e) {e.printStackTrace();}}
}
分块读写
对于大文件,分块读写可以减少内存消耗和提高性能。
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;public class ChunkedFileCopyExample {public static void main(String[] args) {String srcFilePath = "C:\\path\\to\\source\\file.txt";String destFilePath = "C:\\path\\to\\destination\\file_copy.txt";try (FileInputStream fis = new FileInputStream(srcFilePath);FileOutputStream fos = new FileOutputStream(destFilePath)) {byte[] buffer = new byte[1024];int length;while ((length = fis.read(buffer)) > 0) {fos.write(buffer, 0, length);}System.out.println("文件复制成功!");} catch (IOException e) {e.printStackTrace();}}
}