Java IO框架以InputStream
、OutputStream
、Reader
、Writer
四个抽象类为根基,构建了完整的流式IO体系。这种设计体现了以下核心原则:
InputStream/Reader
专注读取,OutputStream/Writer
专注写入)通过Mermaid语法呈现类继承关系(实际应用中需替换为UML图):
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流InputStream |
字节输出流OutputStream |
字符流 | 字符输入流Reader |
字符输出流Writer |
InputStream
/OutputStream
InputStream
核心APIpublic abstract class InputStream {
// 基础读取方法
public abstract int read() throws IOException; // 读取单个字节(0-255),返回-1表示结束
// 批量读取(重点方法)
public int read(byte b[]) throws IOException { // 读取到字节数组,返回实际读取数
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException; // 带偏移量的读取
// 流控制
public long skip(long n) throws IOException; // 跳过指定字节
public int available() throws IOException; // 返回可读字节数估计值
public void close() throws IOException; // 释放资源
// 标记控制
public boolean markSupported(); // 是否支持标记
public void mark(int readlimit); // 设置标记点
public void reset() throws IOException; // 重置到标记位置
}
关键方法说明:
read()
方法是阻塞式的,当流中没有数据时会阻塞当前线程skip()
在文件流中效率高(底层使用seek),但在网络流中可能无效mark/reset
不是所有流都支持,需先检查markSupported()
OutputStream
核心APIpublic abstract class OutputStream {
// 基础写入方法
public abstract void write(int b) throws IOException; // 写入单个字节(低8位)
// 批量写入
public void write(byte b[]) throws IOException { // 写入整个字节数组
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException; // 带偏移量的写入
// 流控制
public void flush() throws IOException; // 强制刷出缓冲区数据
public void close() throws IOException; // 释放资源
}
关键注意事项:
flush()
对无缓冲的流(如FileOutputStream
)无效FileInputStream/FileOutputStream
// 文件复制示例
try (InputStream is = new FileInputStream("source.dat");
OutputStream os = new FileOutputStream("target.dat")) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
os.flush(); // 确保数据落盘
} catch (FileNotFoundException e) {
System.err.println("文件未找到: " + e.getMessage());
} catch (IOException e) {
System.err.println("IO错误: " + e.getMessage());
}
重要特性:
File/String/FileDescriptor
参数FileOutputStream
会覆盖已有文件new FileOutputStream(file, true)
启用追加模式FileDescriptor.sync()
强制数据写入物理设备BufferedInputStream/BufferedOutputStream
// BufferedInputStream部分源码解析
public class BufferedInputStream extends FilterInputStream {
protected volatile byte[] buf; // 缓冲区数组
protected int count; // 有效数据长度
protected int pos; // 当前读取位置
public synchronized int read() throws IOException {
if (pos >= count) {
fill(); // 缓冲区空时填充数据
if (pos >= count) return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
private void fill() throws IOException {
// 从底层流读取数据到缓冲区
// 处理标记重置逻辑
}
}
性能对比测试:
文件大小 | 无缓冲耗时 | 8KB缓冲耗时 | 64KB缓冲耗时 |
---|---|---|---|
10MB | 1200ms | 450ms | 380ms |
100MB | 12500ms | 4200ms | 3600ms |
1GB | 130000ms | 43000ms | 37000ms |
最佳实践:
DataInputStream/DataOutputStream
// 数据持久化示例
try (DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(
new FileOutputStream("data.bin")))) {
dos.writeUTF("张三"); // UTF-8字符串
dos.writeInt(28); // 4字节整数
dos.writeDouble(75.5); // 8字节双精度
dos.writeBoolean(true); // 1字节布尔
}
// 读取示例
try (DataInputStream dis = new DataInputStream(
new BufferedInputStream(
new FileInputStream("data.bin")))) {
String name = dis.readUTF();
int age = dis.readInt();
double weight = dis.readDouble();
boolean married = dis.readBoolean();
}
数据类型对照表:
方法 | 字节数 | 说明 |
---|---|---|
writeBoolean | 1 | 非零值表示true |
writeByte | 1 | 有符号字节 |
writeShort | 2 | 大端序 |
writeChar | 2 | Unicode字符 |
writeInt | 4 | 大端序32位整数 |
writeLong | 8 | 大端序64位整数 |
writeFloat | 4 | IEEE 754单精度 |
writeDouble | 8 | IEEE 754双精度 |
writeUTF | 变长 | 修改版UTF-8(长度前缀) |
// 可序列化对象
class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private transient String password; // 不被序列化
// 构造方法、getter/setter省略
}
// 序列化与反序列化
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("user.dat"))) {
oos.writeObject(new User("Alice", "secret"));
}
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("user.dat"))) {
User user = (User) ois.readObject();
}
安全注意事项:
serialVersionUID
控制版本兼容性transient
关键字readObject/writeObject
自定义序列化// 字节数组流应用
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeUTF("测试数据");
dos.flush();
byte[] data = baos.toByteArray(); // 获取内存数据
ByteArrayInputStream bais = new ByteArrayInputStream(data);
DataInputStream dis = new DataInputStream(bais);
String str = dis.readUTF();
使用场景:
场景 | 建议缓冲区大小 | 说明 |
---|---|---|
小文件(<1MB) | 默认8KB | 平衡内存与性能 |
大文件顺序读取 | 64KB-256KB | 减少系统调用次数 |
网络流 | 4KB-8KB | 匹配MTU避免分片 |
随机访问 | 禁用缓冲 | 缓冲反而增加内存开销 |
// 正确关闭流的方式(JDK7+)
try (InputStream is = new FileInputStream("src");
OutputStream os = new FileOutputStream("dest")) {
// 流操作...
} // 自动调用close()
// 传统正确方式(JDK6-)
InputStream is = null;
try {
is = new FileInputStream("src");
// 流操作...
} finally {
if (is != null) {
try { is.close(); } catch (IOException e) { /* 日志记录 */ }
}
}
常见错误:
Java字节流体系通过InputStream
和OutputStream
两个抽象基类,构建了覆盖文件、网络、内存等多种数据源的统一处理模型。其核心优势体现在:
在实际开发中应遵循:
随着NIO的普及,虽然部分场景被Channel/Buffer取代,但传统字节流在简单IO操作、遗留系统集成等方面仍保持重要地位。理解其核心API与实现原理,是构建健壮Java应用的基础。
// 对照UNIX文件描述符实现
public class FileDescriptor {
// 标准流常量
public static final FileDescriptor in = initSystemFD(0);
public static final FileDescriptor out = initSystemFD(1);
public static final FileDescriptor err = initSystemFD(2);
// 本地方法实现
private static native long set(int d);
private native void close0() throws IOException;
}
// 多资源自动关闭示例
try (FileInputStream fis = new FileInputStream("source.dat");
BufferedInputStream bis = new BufferedInputStream(fis);
DigestInputStream dis = new DigestInputStream(bis,
MessageDigest.getInstance("SHA-256"))) {
while(dis.read(buffer) != -1) {
// 流处理过程
}
// 自动调用close()链:dis -> bis -> fis
}
// Windows独占访问实现
public class Win32FileSystem extends FileSystem {
public native FileChannel open(String path, boolean writeable)
throws IOException;
// 使用CreateFile API参数对照
private static final int GENERIC_READ = 0x80000000;
private static final int FILE_SHARE_READ = 0x00000001;
private static final int OPEN_EXISTING = 3;
}
// 分段校验和计算
public class LargeFileProcessor {
static final int SEGMENT_SIZE = 1024 * 1024 * 100; // 100MB
void process(File file) throws IOException {
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buffer = new byte[SEGMENT_SIZE];
long position = 0;
int bytesRead;
while ((bytesRead = fis.read(buffer)) > 0) {
if (bytesRead < SEGMENT_SIZE) {
buffer = Arrays.copyOf(buffer, bytesRead);
}
Checksum checksum = computeChecksum(buffer);
saveChecksum(position, checksum);
position += bytesRead;
}
}
}
}
BufferedInputStream
内存管理// 自定义环形缓冲区实现
public class CircularBufferInputStream extends BufferedInputStream {
private int markPosition = -1;
private int readLimit;
@Override
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return buf[pos++] & 0xff;
}
private void fill() throws IOException {
// 缓冲区滑动算法
if (markPosition < 0) {
pos = 0;
count = 0;
} else if (pos >= buf.length) {
System.arraycopy(buf, markPosition, buf, 0, count - markPosition);
count -= markPosition;
pos -= markPosition;
markPosition = 0;
}
// 填充新数据
int n = in.read(buf, count, buf.length - count);
if (n > 0)
count += n;
}
}
缓冲区大小 | 读取1GB文件耗时 | 内存占用 | CPU使用率 |
---|---|---|---|
1KB | 25.3秒 | 2MB | 85% |
8KB | 12.1秒 | 10MB | 63% |
64KB | 8.7秒 | 70MB | 45% |
1MB | 6.9秒 | 1.1GB | 32% |
DataInputStream
协议解析// TCP数据包解析示例
public class PacketDecoder {
static final int HEADER_SIZE = 16;
public Packet decode(DataInputStream dis) throws IOException {
// 读取魔数校验
if (dis.readInt() != 0xCAFEBABE) {
throw new InvalidPacketException("Invalid magic number");
}
// 读取包头
int version = dis.readUnsignedShort();
int flags = dis.readUnsignedShort();
long timestamp = dis.readLong();
// 读取动态长度内容
int payloadLength = dis.readInt();
byte[] payload = new byte[payloadLength];
dis.readFully(payload);
// 校验和验证
int checksum = dis.readUnsignedShort();
verifyChecksum(payload, checksum);
return new Packet(version, flags, timestamp, payload);
}
}
public class SafeObjectInputStream extends ObjectInputStream {
private static final Set<String> ALLOWED_CLASSES =
Set.of("java.util.ArrayList", "java.lang.String");
public SafeObjectInputStream(InputStream in) throws IOException {
super(in);
}
@Override
protected Class<?> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException
{
if (!ALLOWED_CLASSES.contains(desc.getName())) {
throw new InvalidClassException(
"Unauthorized deserialization attempt", desc.getName());
}
return super.resolveClass(desc);
}
}
// 安全使用示例
try (ObjectInputStream ois = new SafeObjectInputStream(
new FileInputStream("data.obj"))) {
Object obj = ois.readObject();
}
OutputStream
全类族详解FileOutputStream
内核探秘实现原理:
public class ExclusiveFileWriter {
void writeWithLock(String path, String content) throws IOException {
// try-with-resources自动管理三个资源:文件流、通道、锁
try (FileOutputStream fos = new FileOutputStream(path);
FileChannel channel = fos.getChannel();
FileLock lock = channel.tryLock()) { // 非阻塞尝试获取锁
if (lock == null) {
// 文件已被其他进程/线程锁定
throw new IOException("File is locked by another process");
}
// 独占写入操作
fos.write(content.getBytes());
fos.flush(); // 强制数据从JVM缓冲区刷入OS内核缓冲区
Thread.sleep(10000); // 模拟长时间持有锁的操作
}
// 自动关闭顺序:lock -> channel -> fos
}
}
关键点说明:
锁粒度控制:
FileLock
支持两种锁定范围:
FileLock.lock()
):允许多个进程读取,禁止写入FileLock.tryLock()
):完全独占访问跨进程同步:文件锁在操作系统级别生效,可同步不同JVM进程甚至不同语言编写的程序
异常处理要点:
catch (OverlappingFileLockException e) {
// 当同一线程重复获取锁时抛出
}
最佳实践:
FileChannel.force(true)
确保元数据写入磁盘lock(long position, long size, boolean shared)
)锁监控工具示例:
# Linux查看文件锁状态
lsof -p <pid> # 查看进程打开的文件描述符
flock -n /tmp/lock.lck -c "echo Lock acquired" # 命令行测试锁
代码深度解读:
public class TransactionalFileWriter {
void atomicWrite(File target, byte[] data) throws IOException {
File temp = File.createTempFile("tmp", ".dat");
try {
// 阶段一:写入临时文件
try (FileOutputStream fos = new FileOutputStream(temp)) {
fos.write(data);
fos.flush();
fos.getFD().sync(); // 强制数据落盘,绕过OS缓存
}
// 阶段二:原子替换
Files.move(temp.toPath(), target.toPath(),
StandardCopyOption.ATOMIC_MOVE, // 要求文件系统支持原子操作
StandardCopyOption.REPLACE_EXISTING);
} finally {
// 清理策略:无论成功与否都删除临时文件
if (temp.exists() && !temp.delete()) {
System.err.println("警告:临时文件删除失败: " + temp);
}
}
}
}
技术要点:
文件系统要求:
ATOMIC_MOVE
需要底层文件系统支持(如EXT4、NTFS)同步策略对比:
方法 | 保证级别 | 性能影响 |
---|---|---|
fd.sync() | 数据+元数据持久化 | 高 |
channel.force(true) | 同sync | 高 |
普通flush | 仅保证进入OS内核缓冲区 | 低 |
异常场景处理:
代码实现细节:
public class MappedBufferWriter {
void writeLargeFile(File file, byte[] data) throws IOException {
try (FileOutputStream fos = new FileOutputStream(file);
FileChannel channel = fos.getChannel()) {
// 内存映射参数说明:
// MapMode.READ_WRITE - 允许读写操作
// position - 映射起始偏移量(必须对齐页大小,通常4KB)
// size - 映射区域大小(最大Integer.MAX_VALUE)
MappedByteBuffer buffer = channel.map(
FileChannel.MapMode.READ_WRITE, 0, data.length);
// 直接内存操作(绕过JVM堆)
buffer.put(data);
// 强制持久化(等效于fsync)
buffer.force();
}
}
}
性能优化矩阵:
数据规模 | 传统写入耗时 | 内存映射耗时 | 内存占用比 |
---|---|---|---|
1MB | 2ms | 1ms | 1:1.2 |
100MB | 150ms | 80ms | 1:1.5 |
1GB | 1800ms | 950ms | 1:2 |
注意事项:
(position % 4096 == 0)
提升性能代码逻辑分解:
public class ResilientStreamWriter {
private static final int MAX_RETRY = 3;
void writeWithRetry(OutputStream os, byte[] data) throws IOException {
int attempt = 0;
while (attempt <= MAX_RETRY) {
try {
os.write(data);
os.flush();
return;
} catch (IOException e) {
if (++attempt > MAX_RETRY) throw e;
// 指数退避策略
Thread.sleep(1000 * (1 << attempt));
// 流状态重置
resetStream(os);
}
}
}
private void resetStream(OutputStream os) {
if (os instanceof BufferedOutputStream) {
// 清空缓冲区可能存在的损坏数据
((BufferedOutputStream) os).flush();
}
// 其他流类型重置逻辑(如Socket流重连)
if (os instanceof SocketOutputStream) {
((SocketOutputStream) os).getChannel().close();
// 重建连接...
}
}
}
恢复策略对比:
策略类型 | 适用场景 | 实现复杂度 | 数据一致性保证 |
---|---|---|---|
简单重试 | 临时性网络抖动 | 低 | 低 |
事务回滚 | 数据库操作 | 高 | 高 |
检查点恢复 | 长时间批处理 | 中 | 中 |
本方案 | 通用IO故障 | 中 | 中高 |
设计模式应用:
代码原理图解:
public class ZeroCopyTransfer {
void transfer(File source, SocketChannel socket) throws IOException {
try (FileInputStream fis = new FileInputStream(source);
FileChannel fc = fis.getChannel()) {
long position = 0;
long remaining = fc.size();
while (remaining > 0) {
// transferTo底层使用sendfile系统调用
long transferred = fc.transferTo(position, remaining, socket);
remaining -= transferred;
position += transferred;
}
}
}
}
零拷贝数据流对比:
ZeroCopy
网络设备内核Socket缓冲区用户缓冲区内核缓冲区传统拷贝ZeroCopy网络设备内核Socket缓冲区用户缓冲区内核缓冲区传统拷贝1. read(file)2. copy_to_user3. copy_from_user4. send1. sendfile(file, socket)2. direct send
性能测试数据:
文件大小 | 传统方式(ms) | 零拷贝(ms) | 吞吐量提升 |
---|---|---|---|
100MB | 450 | 120 | 3.75x |
1GB | 4200 | 980 | 4.29x |
10GB | 41000 | 9500 | 4.32x |
代码事件流分析:
public class AsyncIOExample {
void asyncWrite(AsynchronousFileChannel channel, ByteBuffer buffer) {
channel.write(buffer, 0, null, new CompletionHandler<Integer,Void>() {
@Override
public void completed(Integer result, Void attachment) {
// 成功回调(I/O线程池执行)
System.out.println("写入字节数: " + result);
// 链式操作示例
if (buffer.hasRemaining()) {
channel.write(buffer, position + result, null, this);
}
}
@Override
public void failed(Throwable exc, Void attachment) {
// 异常处理(需考虑重试策略)
exc.printStackTrace();
// 资源清理
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
线程模型说明:
Default AsynchronousChannelGroup
├── Thread-1: 处理I/O就绪事件
├── Thread-2: 执行回调函数
└── Thread-N: ...
自定义线程池配置:
AsynchronousChannelGroup.withThreadPool(ExecutorService)
生产环境建议:
CompletionHandler
链式调用实现流水线操作调优参数说明表:
参数 | 作用域 | 推荐值 | 影响维度 |
---|---|---|---|
-XX:+UseLargePages |
堆外内存 | 视物理内存而定 | 提升内存映射性能30%+ |
-XX:MaxDirectMemorySize |
直接内存 | 物理内存的1/4 | 防止Native OOM |
-Djava.nio.file.FastCopy=true |
文件拷贝 | 默认开启 | 加速Files.copy 操作 |
-XX:+DisableExplicitGC |
全堆 | 生产环境必选 | 防止System.gc() 停顿 |
内存分配对比:
// 传统堆内存分配
ByteBuffer heapBuffer = ByteBuffer.allocate(1024);
// 直接内存分配(不受GC影响)
ByteBuffer directBuffer = ByteBuffer.allocateDirect(1024);
// 内存映射分配(操作系统托管)
MappedByteBuffer mappedBuffer = channel.map(READ_WRITE, 0, size);
各内存类型特点:
内存类型 | 分配成本 | 访问速度 | GC影响 | 适用场景 |
---|---|---|---|---|
堆内存 | 低 | 一般 | 有 | 小对象、高频创建 |
直接内存 | 高 | 快 | 无 | 网络IO、零拷贝 |
内存映射 | 很高 | 最快 | 无 | 大文件随机访问 |
Linux系统优化步骤:
磁盘调度策略:
# 查看当前调度器
cat /sys/block/sda/queue/scheduler
# 设置为deadline(适合SSD)
echo deadline > /sys/block/sda/queue/scheduler
文件系统参数:
# 调整日志提交间隔(默认5秒)
mount -o remount,commit=60 /data
# 禁用atime更新
mount -o remount,noatime /data
网络优化:
# 增加TCP缓冲区大小
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
Windows系统优化建议:
注册表修改:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management
- LargePageMinimum = 0xFFFFFFFF
使用性能监视器(perfmon)监控IO等待
分片算法实现要点:
private StorageNode selectNode(byte[] data, int replica) {
// 一致性哈希算法实现
TreeMap<Integer, StorageNode> ring = buildConsistentHashRing();
// 计算数据分片哈希
int hash = MurmurHash.hash32(data);
// 顺时针查找节点
Map.Entry<Integer, StorageNode> entry = ring.ceilingEntry(hash);
if (entry == null) {
entry = ring.firstEntry();
}
return entry.getValue();
}
数据一致性保障:
哈希链完整性验证:
public class HashChainValidator {
boolean validate(File file) throws IOException {
try (DigestInputStream dis = new DigestInputStream(
new FileInputStream(file),
MessageDigest.getInstance("SHA-256"))) {
byte[] buffer = new byte[8192];
while (dis.read(buffer) != -1) {
// 实时计算哈希
}
// 分离数据和哈希值
byte[] fileHash = extractTrailingHash(dis.getMessageDigest());
byte[] computedHash = dis.getMessageDigest().digest();
return Arrays.equals(fileHash, computedHash);
}
}
}
审计日志要求:
版本演进关键点:
InputStream.readAllBytes()
transferTo
支持任意通道类型算法选择建议:
密钥生命周期管理:
调试代理模式实现:
public class StreamTapper extends FilterOutputStream {
private final OutputStream debugStream;
public StreamTapper(OutputStream out, OutputStream debug) {
super(out);
this.debugStream = debug;
}
@Override
public void write(int b) throws IOException {
debugStream.write(b); // 不影响主流程的调试输出
super.write(b); // 正常业务写入
}
// 支持流量统计
public static class TrafficCounter extends FilterOutputStream {
private long bytesWritten;
public TrafficCounter(OutputStream out) {
super(out);
}
@Override
public void write(int b) throws IOException {
bytesWritten++;
super.write(b);
}
}
}
生产调试建议:
public abstract class Reader {
// 基础读取方法
public int read() throws IOException
public int read(char cbuf[]) throws IOException
public abstract int read(char cbuf[], int off, int len) throws IOException
// 流控制方法
public long skip(long n) throws IOException
public boolean ready() throws IOException
public abstract void close() throws IOException
// 标记控制
public boolean markSupported()
public void mark(int readAheadLimit) throws IOException
public void reset() throws IOException
}
关键方法详解:
read(char[] cbuf, int off, int len)
:
cbuf
:目标缓冲区off
:开始存储字符的偏移量len
:要读取的最大字符数mark(int readAheadLimit)
:
markSupported()
检查public class CharArrayReader extends Reader {
public void mark(int readAheadLimit) {
if (readAheadLimit > buf.length) {
throw new IllegalArgumentException();
}
markedPos = nextPos;
}
}
public class InputStreamReader extends Reader {
// 核心构造方法
public InputStreamReader(InputStream in)
public InputStreamReader(InputStream in, String charsetName)
public InputStreamReader(InputStream in, Charset cs)
public InputStreamReader(InputStream in, CharsetDecoder dec)
}
编码处理示例:
// 处理GBK编码文件
try (Reader reader = new InputStreamReader(
new FileInputStream("data.txt"), "GBK")) {
char[] buffer = new char[1024];
int read;
while ((read = reader.read(buffer)) != -1) {
process(new String(buffer, 0, read));
}
}
// 自动检测BOM标记
public Charset detectCharset(InputStream is) throws IOException {
is.mark(4);
byte[] bom = new byte[4];
int read = is.read(bom);
is.reset();
if (read >= 3 && bom[0] == (byte)0xEF && bom[1] == (byte)0xBB && bom[2] == (byte)0xBF) {
return StandardCharsets.UTF_8;
}
// 其他编码检测逻辑...
}
public class BufferedReader extends Reader {
// 构造方法
public BufferedReader(Reader in, int sz)
public BufferedReader(Reader in)
// 增强方法
public String readLine() throws IOException
public Stream<String> lines()
// 性能优化方法
public int read(char[] cbuf, int off, int len) throws IOException
public long skip(long n) throws IOException
}
readLine()工作原理:
用户解析逻辑底层ReaderBufferedReader
用户解析逻辑底层ReaderBufferedReaderread()
填充缓冲区查找换行符返回整行字符串
批量读取优化:
// 高效读取大文本文件
try (BufferedReader br = new BufferedReader(
new FileReader("bigfile.txt"), 65536)) { // 64KB缓冲
char[] buffer = new char[8192];
int charsRead;
while ((charsRead = br.read(buffer)) != -1) {
processBuffer(buffer, charsRead);
}
}
public abstract class Writer {
// 基础写入方法
public void write(int c) throws IOException
public void write(char cbuf[]) throws IOException
public abstract void write(char cbuf[], int off, int len) throws IOException
public void write(String str) throws IOException
public void write(String str, int off, int len) throws IOException
// 流控制
public abstract void flush() throws IOException
public abstract void close() throws IOException
// Java 8新增
public Writer append(CharSequence csq) throws IOException
public Writer append(CharSequence csq, int start, int end)
}
字符串写入优化:
// 高效写入字符串片段
public void writeFragments(Writer writer, String[] parts) throws IOException {
char[] buffer = new char[4096];
int index = 0;
for (String part : parts) {
for (char c : part.toCharArray()) {
if (index == buffer.length) {
writer.write(buffer, 0, index);
index = 0;
}
buffer[index++] = c;
}
}
if (index > 0) {
writer.write(buffer, 0, index);
}
}
public class PrintWriter extends Writer {
// 基础打印方法
public void print(boolean b)
public void print(char c)
public void print(int i)
public void print(long l)
public void print(float f)
public void print(double d)
public void print(char s[])
public void print(String s)
public void print(Object obj)
// 格式化输出
public PrintWriter printf(String format, Object... args)
public PrintWriter printf(Locale l, String format, Object... args)
// 错误控制
public boolean checkError()
}
复合格式化示例:
try (PrintWriter pw = new PrintWriter("report.txt")) {
String item = "Notebook";
double price = 15.99;
int quantity = 5;
pw.printf("%-20s %10.2f %6d %12.2f\n",
item,
price,
quantity,
price * quantity);
pw.println("-".repeat(50));
pw.printf(Locale.US, "Total: $%,.2f", total);
}
输出效果:
Notebook 15.99 5 79.95
--------------------------------------------------
Total: $79.95
编码异常处理示例:
try (InputStreamReader reader = new InputStreamReader(
new FileInputStream("data.txt"), "GBK")) {
// 读取操作...
} catch (UnmappableCharacterException e) {
System.err.println("无法映射的字符位置: " + e.getInputLength());
// 替换策略示例
String replaced = new String(e.getBytes(),
StandardCharsets.UTF_8).replace("\ufffd", "?");
} catch (MalformedInputException e) {
System.err.println("错误的字节序列: " + Arrays.toString(e.getBytes()));
}
// 典型装饰器继承体系
public class FilterInputStream extends InputStream {
protected volatile InputStream in; // 被装饰对象
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {
return in.read(); // 委托基础功能
}
}
public class BufferedInputStream extends FilterInputStream {
private static final int DEFAULT_BUFFER_SIZE = 8192;
protected volatile byte[] buf; // 缓冲区
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public synchronized int read() throws IOException {
if (pos >= count) {
fill(); // 装饰器增强方法
if (pos >= count) return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
private void fill() throws IOException {
// 缓冲区填充逻辑...
}
}
// 多层装饰组合
InputStream is = new ProgressMonitoringInputStream( // 自定义装饰器
new BufferedInputStream(
new Base64InputStream( // 编解码装饰
new FileInputStream("data.bin")), 16384));
// 自定义装饰器实现
class ProgressMonitoringInputStream extends FilterInputStream {
private long bytesRead = 0;
private final long totalSize;
public ProgressMonitoringInputStream(InputStream in, long total) {
super(in);
this.totalSize = total;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
int n = super.read(b, off, len);
if (n > 0) {
bytesRead += n;
System.out.printf("进度: %.2f%%\n",
(bytesRead * 100.0) / totalSize);
}
return n;
}
}
装饰层数 | 读取速度 (MB/s) | 内存占用 (MB) | CPU使用率 (%) |
---|---|---|---|
0(原始流) | 320 | 0.5 | 15 |
1层缓冲 | 450 | 8.2 | 12 |
3层装饰 | 420 | 8.5 | 18 |
5层装饰 | 390 | 9.1 | 25 |
优化建议:
public class InputStreamReader extends Reader {
private final StreamDecoder sd; // 实际解码器
public InputStreamReader(InputStream in, CharsetDecoder dec) {
super(in);
sd = StreamDecoder.forDecoder(in, dec, 8192);
}
public int read(char cbuf[], int off, int len) throws IOException {
return sd.read(cbuf, off, len); // 适配调用
}
}
// 字节到字符的转换过程
sequenceDiagram
字节流->>StreamDecoder: 提供原始字节
StreamDecoder->>字符流: 按编码规则转换
字符流-->>客户端: 返回Unicode字符
public class EncodingManager {
private static final Map<String, Charset> ENCODINGS =
Map.of("GBK", Charset.forName("GBK"),
"BIG5", Charset.forName("BIG5"));
public static Reader createReader(InputStream is, String encoding)
throws UnsupportedEncodingException {
if (!ENCODINGS.containsKey(encoding)) {
throw new UnsupportedEncodingException(encoding);
}
return new InputStreamReader(is, ENCODINGS.get(encoding).newDecoder());
}
// 自动检测编码
public static Reader createAutoDetectReader(InputStream is)
throws IOException {
byte[] bom = new byte[4];
is.mark(4);
int read = is.read(bom);
is.reset();
if (read >= 3 && bom[0] == (byte)0xEF && bom[1] == (byte)0xBB) {
return new InputStreamReader(is, "UTF-8");
}
// 其他编码检测逻辑...
return new InputStreamReader(is, "UTF-8");
}
}
read(char[])
方法 FilterInputStream
模板解析public abstract class FilterInputStream extends InputStream {
// 模板方法定义处理流程
public int read(byte b[], int off, int len) throws IOException {
if (b == null) throw new NullPointerException();
int c = read(); // 抽象方法(由子类实现)
if (c == -1) return -1;
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) break;
b[off + i] = (byte)c;
}
} catch (IOException ee) {
// 异常处理模板...
}
return i;
}
}
public abstract class AuditInputStream extends FilterInputStream {
private final AuditTracker tracker;
protected AuditInputStream(InputStream in, AuditTracker tracker) {
super(in);
this.tracker = tracker;
}
@Override
public int read() throws IOException {
int data = super.read();
if (data != -1) {
trackRead(data); // 钩子方法
}
return data;
}
protected abstract void trackRead(int data);
// 具体实现
public static class LoggingInputStream extends AuditInputStream {
public LoggingInputStream(InputStream in) {
super(in, new FileAuditTracker());
}
@Override
protected void trackRead(int data) {
// 写入审计日志
}
}
}
操作类型 | 基础IO (ns/op) | 模板扩展IO (ns/op) | 开销比例 |
---|---|---|---|
单字节读取 | 15 | 23 | +53% |
8KB缓冲区读取 | 1200 | 1250 | +4.2% |
大文件顺序读 | 1,200,000 | 1,205,000 | +0.4% |
优化建议:
public class SecureIOFacade {
public InputStream createSecureInputStream(File file, String key)
throws Exception {
return new BufferedInputStream( // 装饰缓冲
new CipherInputStream( // 装饰解密
new FileInputStream(file),
initCipher(key, Cipher.DECRYPT_MODE)),
8192);
}
public OutputStream createSecureOutputStream(File file, String key)
throws Exception {
return new BufferedOutputStream(
new CipherOutputStream( // 装饰加密
new FileOutputStream(file),
initCipher(key, Cipher.ENCRYPT_MODE)),
8192);
}
private Cipher initCipher(String key, int mode)
throws GeneralSecurityException {
SecretKeySpec spec = new SecretKeySpec(key.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(mode, spec, new GCMParameterSpec(128, new byte[12]));
return cipher;
}
}
设计模式应用:
Java IO框架历经二十余年的发展,始终保持着强大的生命力。这套以四个基类(InputStream/OutputStream/Reader/Writer
)**为核心的体系,通过**装饰者模式的灵活扩展和适配器模式的编码转换,构建了支持多种数据源、多样处理需求的完整解决方案。
对于Java开发者,建议通过以下路径深化IO体系理解:
BufferedInputStream
、InputStreamReader
等典型实现,理解装饰器模式的具体应用FileNotFoundException
、SocketTimeoutException
等常见异常,建立完善的错误处理机制IO系统作为程序与外部世界的桥梁,其设计质量直接影响系统可靠性和性能表现。深入理解Java IO框架,不仅能编写出更健壮的代码,更能培养出良好的系统设计思维。随着云原生时代的到来,这套经典体系仍将在文件存储、网络通信、数据处理等领域持续发挥核心作用。