首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java I/O 進階:對象序列化、轉換流與隨機訪問文件 RandomAccessFile 詳解

     編輯:Java 綜合教學

Java I/O 核心補充:三大「其他流」實戰指南

除了常用的字節流、字符流、緩沖流,Java 提供了三類特殊流解決特定場景:對象序列化流 (持久化對象)、轉換流 (字節↔字符編碼橋梁)、隨機訪問流 (文件指針定位)。本文逐一拆解原理與代碼實戰。

一、 ObjectInputStream / ObjectOutputStream — 對象序列化與反序列化

1. 核心概念

  • 序列化:將對象狀態轉換為字節序列 (特定編碼格式) 寫入輸出流。
  • 反序列化:從輸入流讀取字節序列重建對象。
  • 前提:類必須實現 `java.io.Serializable` 標記接口 (無方法)。

2. 代碼實戰

// 實體類
class Student implements Serializable {
    private String name; private int age;
    // 構造、get/set、toString...
    // 關鍵:建議顯式聲明 serialVersionUID
    private static final long serialVersionUID = 1L;
}

// 序列化
Student stu = new Student("zhangsan", 30);
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/test.txt"))) {
    oos.writeObject(stu);
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/test.txt"))) {
    Student stu2 = (Student) ois.readObject();
    System.out.println(stu2); // Student [name=zhangsan, age=30]
}

3. 版本兼容性陷阱與解決

修改類結構 (如新增字段) 後反序列化舊數據會拋 `InvalidClassException` (serialVersionUID 不匹配)。

  • 解決:在類中顯式聲明 `private static final long serialVersionUID = 1L;` (固定值)。
  • Eclipse 提供快速修復:類名左側小燈 → Add default serial version ID 或 Add generated serial version ID。
  • 新增字段會自動賦默認值 (null/0),舊數據反序列化成功;刪除字段數據被忽略。

二、 InputStreamReader / OutputStreamWriter — 轉換流 (編碼橋梁)

1. 核心作用

  • 將「字節流」包裝為「字符流」,指定編碼集 (UTF-8, GBK 等)。
  • 解決:字節流構造方法可指定編碼,但 `FileReader`/`FileWriter` 只能用默認編碼;非默認編碼文件讀寫必須用轉換流。

2. 常見編碼表速查

編碼特點
ASCII7位,128字符,英數符號
ISO-8859-18位,西歐語言
GB2312中文標準,6763漢字
GBKGB2312超集,含生僻字、繁體
Unicode (UTF-16)固定2字節,Java 內部編碼
UTF-8可變長(1-4字節),兼容 ASCII,網頁首選

3. 代碼實戰:指定 UTF-8 讀寫

// 寫入 (指定 UTF-8)
try (BufferedWriter bw = new BufferedWriter(
        new OutputStreamWriter(new FileOutputStream("d:/code.txt"), "UTF-8"))) {
    bw.write("你好");
}

// 讀取 (指定 UTF-8)
try (BufferedReader br = new BufferedReader(
        new InputStreamReader(new FileInputStream("d:/code.txt"), "UTF-8"))) {
    String line;
    while ((line = br.readLine()) != null) System.out.println(line);
}

三、 RandomAccessFile — 隨機訪問文件 (可讀可寫、指針定位)

1. 核心特性

  • 行為類似「大型字節數組」,內部維護文件指針 (游標)。
  • 構造模式:"r" (只讀)、"rw" (讀寫)。
  • 關鍵方法:`seek(long pos)` 移動指針,`getFilePointer()` 獲取當前位置,`readInt()`/`writeInt()`、`readChar()`/`writeChars()` 等基本類型讀寫。
  • 寫入超過文件長度會自動擴展文件。

2. 實戰案例:定長記錄存取 (Person: 名字15字符30字節 + 年齡4字節 = 34字節/條)

class Person {
    private String name; // 固定15字符
    private int age;
    public static int SIZE = 34; // 每條記錄固定字節數
    // 構造時用 StringBuilder 固定長度,不足補 '\u0000'
    public Person(String name, int age) {
        StringBuilder sb = new StringBuilder(name != null ? name : "");
        sb.setLength(15); this.name = sb.toString(); this.age = age;
    }
    // 讀取輔助:讀15字符並替換 '\u0000' 為空格
    static String readName(RandomAccessFile raf) throws IOException {
        char[] cs = new char[15];
        for (int i=0; i<15; i++) cs[i] = raf.readChar();
        return new String(cs).replace('\u0000', ' ');
    }
}

// 寫入多條記錄
Person[] persons = {new Person("zhangsan",10), new Person("lisi",24), ...};
try (RandomAccessFile raf = new RandomAccessFile("d:/test2.txt", "rw")) {
    for (Person p : persons) {
        raf.writeChars(p.getName()); // 30字節
        raf.writeInt(p.getAge());    // 4字節
    }
}

// 隨機讀取第 N 條 (從0開始)
int n = 2; // 讀取第3條
raf.seek(n * Person.SIZE); // 移動指針
Person p = new Person();
p.setName(readName(raf));
p.setAge(raf.readInt());
System.out.println(p); // 姓名: lisi  年齡: 24

四、 總結對比

流類型核心場景關鍵點
ObjectInput/OutputStream對象持久化、網絡傳輸、深拷貝實現 Serializable、聲明 serialVersionUID、處理版本演進
InputStreamReader/OutputStreamWriter指定編碼讀寫文本文件、網絡通信編解碼字節↔字符橋梁、必須指定 charset、配合 BufferedReader/Writer 提效
RandomAccessFile大文件隨機讀寫、定長記錄存取、斷點續傳文件指針 seek()、rw 模式、非線程安全、非 NIO (Channel 更高性能)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved