Java IO 是資料持久化與網路傳輸的基石。本文系統梳理 java.io 與 java.nio 核心架構,聚焦檔案管理、位元組/字元流體系、緩衝裝飾、隨機存取與壓縮操作,並揭示裝飾者模式在流設計中的精妙應用。
// 路徑表示(不保證檔案存在)
File f = new File("/data/logs/app.log");
File f2 = new File("/data", "logs/app.log"); // 父目錄 + 子路徑
File f3 = new File(uri); // 從 URI 建立
// 常用查詢
boolean exists = f.exists();
boolean isFile = f.isFile();
boolean isDir = f.isDirectory();
long len = f.length(); // 位元組長度
long lastMod = f.lastModified(); // 毫秒時間戳
String[] names = f.list(); // 列出檔名
File[] files = f.listFiles(); // 列出 File 物件(可傳 FilenameFilter/FileFilter)
// 建立/刪除
f.createNewFile(); // 建立空檔
f.mkdir(); f.mkdirs(); // 建立單層/多層目錄
f.delete(); // 刪除(目錄必須為空)
f.deleteOnExit(); // JVM 退出時刪除
// 路徑轉換
String abs = f.getAbsolutePath();
String canon = f.getCanonicalPath(); // 解析 . .. 符號連結
Path path = f.toPath(); // 互操作 NIO.2
| 維度 | 位元組流 | 字元流 |
|---|---|---|
| 基類 | InputStream / OutputStream | Reader / Writer |
| 處理單位 | 8-bit byte | 16-bit char (UTF-16) |
| 典型用途 | 圖片、影音、二進位協議、序列化 | 文字檔、JSON、XML、CSV |
| 轉換橋樑 | InputStreamReader (解碼) / OutputStreamWriter (編碼)核心參數: Charset (StandardCharsets.UTF_8) | |
FileInputStream, FileOutputStream, FileReader, FileWriterByteArrayInputStream, ByteArrayOutputStream(記憶體緩衝)PipedInputStream, PipedOutputStream(執行緒管道)BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter — 大幅提升 I/O 效能,必加。InputStreamReader, OutputStreamWriter(指定編碼)。DataInputStream, DataOutputStream(基本型別序列化)。ObjectInputStream, ObjectOutputStream(需實作 Serializable)。PrintStream (System.out), PrintWriter(不拋 IOException)。GZIPInputStream, ZipInputStream, JarInputStream。
// 讀取 UTF-8 文字檔,逐行處理(標準範本)
try (BufferedReader br = new BufferedReader(
new InputStreamReader(new FileInputStream("data.txt"), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
process(line);
}
}
// 寫入 UTF-8 文字檔,緩衝+自動 flush
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream("out.txt"), StandardCharsets.UTF_8))) {
bw.write("Hello");
bw.newLine(); // 跨平台換行
}
// 二進位複製:緩衝位元組流
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src.bin"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("dst.bin"))) {
byte[] buf = new byte[8192];
int len;
while ((len = bis.read(buf)) != -1) {
bos.write(buf, 0, len);
}
}
唯一同時實作 DataInput/DataOutput 且非流繼承體系的類,支援指標跳轉。
// "rw" 讀寫模式
try (RandomAccessFile raf = new RandomAccessFile("data.dat", "rw")) {
raf.seek(100); // 移動指標
raf.writeInt(0xCAFEBABE); // 寫入 int
raf.seek(100);
int val = raf.readInt(); // 讀回
// 檔案鎖(進程級,跨 JVM 可見)
FileLock lock = raf.getChannel().tryLock(0, 100, false); // 共享鎖
if (lock != null) { /* 安全操作 */ lock.release(); }
}
try (ZipOutputStream zos = new ZipOutputStream(
new BufferedOutputStream(new FileOutputStream("backup.zip")))) {
addToZip(zos, new File("src_dir"), "");
}
void addToZip(ZipOutputStream zos, File file, String parent) throws IOException {
String entryName = parent + file.getName();
if (file.isDirectory()) {
zos.putNextEntry(new ZipEntry(entryName + "/"));
zos.closeEntry();
for (File child : file.listFiles()) addToZip(zos, child, entryName + "/");
} else {
zos.putNextEntry(new ZipEntry(entryName));
try (FileInputStream fis = new FileInputStream(file)) {
byte[] buf = new byte[8192];
int len;
while ((len = fis.read(buf)) != -1) zos.write(buf, 0, len);
}
zos.closeEntry();
}
}
try (ZipInputStream zis = new ZipInputStream(new FileInputStream("backup.zip"))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
File out = new File("extract_dir", entry.getName());
if (entry.isDirectory()) { out.mkdirs(); continue; }
out.getParentFile().mkdirs();
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(out))) {
byte[] buf = new byte[8192];
int len;
while ((len = zis.read(buf)) != -1) bos.write(buf, 0, len);
}
zis.closeEntry();
}
}
更豐富、函數式、支援屬性、走訪、監聽:
Path path = Paths.get("/data/logs/app.log");
// 基本操作
Files.createDirectories(path.getParent());
Files.writeString(path, "content", StandardCharsets.UTF_8, StandardOpenOption.CREATE);
String content = Files.readString(path, StandardCharsets.UTF_8);
Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE);
Files.deleteIfExists(path);
// 走訪目錄樹
Files.walkFileTree(Paths.get("/data"), new SimpleFileVisitor() {
@Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith(".log")) process(file);
return FileVisitResult.CONTINUE;
}
});
// 監聽目錄變更
WatchService ws = FileSystems.getDefault().newWatchService();
Paths.get("/data").register(ws, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
while (true) {
WatchKey key = ws.take();
for (WatchEvent> ev : key.pollEvents()) { /* 處理事件 */ }
key.reset();
}
BufferedInputStream/OutputStream/Reader/Writer,預設 8KB,可調大小。byte[8192] / char[8192],避免單位元組/字元調用。FileChannel.transferTo/From 零拷貝(Kernel 空間直接傳輸)、MappedByteBuffer 記憶體映射。StandardCharsets.UTF_8,避免平台預設編碼差異導致亂碼。try-with-resources 自動按相反順序關閉外層流 → 內層流。掌握「節點流接資料源、處理流裝飾功能」的裝飾者模式本質,熟練組合緩衝、轉碼、壓縮、序列化流,再配合 NIO.2 的 Files/Path 現代 API,即可應對 99% 的 Java IO 場景。記住:無緩衝不 IO,無編碼不字元,無 try-with-resources 不關閉。