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

Java IO 深度解析:從文件操作到進階流處理實戰

     編輯:Java 綜合教學

Java IO 深度解析:從文件操作到進階流處理實戰

初學 Java 時,IO 體系往往是讓人最頭疼的部分。網上的資料要麼只有結構圖缺乏實戰,要麼停留在舊版本,未涵盖 Java 7+ 的新特性。本文將從文件操作入手,層層遞進至字節流、字符流、對象序列化、特殊功能流等核心知識點,並附帶完整可運行的代碼示例,幫你建立清晰的 IO 知識體系。

一、一切的起點:File 類與文件操作

流的本質是對文件的處理。Java 通過 java.io.File 類提供了豐富的文件和目錄操作方法,如創建、刪除、重命名、獲取屬性等。以下示例展示常用操作:

package com.hxw.io;
import java.io.*;

public class FileExample {
    public static void main(String[] args) {
        createFile();
    }

    public static void createFile() {
        File f = new File("E:/電腦桌面/jar/files/create.txt");
        try {
            // 僅當文件不存在時創建
            f.createNewFile();
            System.out.println("該分區大小:" + f.getTotalSpace() / (1024*1024*1024) + "G");
            // 創建多級目錄
            f.mkdirs();
            // f.delete(); // 刪除文件/目錄
            System.out.println("文件名:" + f.getName());
            System.out.println("父目錄路徑:" + f.getParent());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

關鍵點: File 對象只代表路徑,不佔用實際磁盤空間;真正的讀寫需要流來完成。

二、字節流體系:處理二進制數據的基石

1. InputStream 與 FileInputStream:讀取字節

字節流以字節為單位,適合處理圖片、視頻等二進制文件。FileInputStream 是最基礎的文件輸入流。下面的例子統計文件字節長度,但逐字節讀取效率極低:

public class FileCount {
    public static void main(String[] args) {
        int count = 0;
        InputStream streamReader = null;
        try {
            streamReader = new FileInputStream(new File("D:/David/Java/java 高級進階/files/tiger.jpg"));
            while (streamReader.read() != -1) {
                count++;
            }
            System.out.println("---長度是:" + count + " 字節");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try { if (streamReader != null) streamReader.close(); } catch (IOException e) { e.printStackTrace(); }
        }
    }
}

痛點: 6萬多字節意味著 6 萬次系統調用。解決方案是使用緩衝區:read(byte[] b) 一次讀取塊數據,或直接包裝 BufferedInputStream。

2. mark/reset 機制:流的「後悔藥」

  • markSupported():判斷是否支持標記
  • mark(readlimit):在當前位置打標記,允許回退的最大字節數
  • reset():回到標記位置

FileInputStream 不支持,BufferedInputStream 支持。這在解析器、編譯器等需要回溯的場景很有用。

3. FileOutputStream 與緩衝區實戰:文件複製

輸出流同理,默認無緩衝。使用 byte 數組作為緩衝區可大幅提升性能:

public class FileCopy {
    public static void main(String[] args) {
        byte[] buffer = new byte[512];
        int numberRead = 0;
        FileInputStream input = null;
        FileOutputStream out = null;
        try {
            input = new FileInputStream("D:/David/Java/java 高級進階/files/tiger.jpg");
            out = new FileOutputStream("D:/David/Java/java 高級進階/files/tiger2.jpg");
            while ((numberRead = input.read(buffer)) != -1) {
                out.write(buffer, 0, numberRead); // 關鍵:只寫實際讀取的字節數
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 關閉流...
        }
    }
}

4. 對象序列化:ObjectInputStream / ObjectOutputStream

實現對象的持久化與傳輸。類必須實現 Serializable 接口(標記接口,無方法):


class Student implements Serializable {
    private String name;
    private int age;
    // 構造器、toString()...
}

public class ObjetStream {
    public static void main(String[] args) {
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.txt"));
             ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.txt"))) {
            oos.writeObject(new Student("gg", 22));
            oos.writeObject(new Student("tt", 18));
            for (int i = 0; i < 2; i++) {
                System.out.println(ois.readObject());
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

5. 基礎類型數據流:DataInputStream / DataOutputStream

當只需存儲對象的字段(基本類型)而非整個對象時,更輕量、跨語言兼容性更好:


DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.dat"));
dos.writeUTF(member.getName());
dos.writeInt(member.getAge());
dos.flush();

DataInputStream dis = new DataInputStream(new FileInputStream("data.dat"));
String name = dis.readUTF();
int age = dis.readInt();

6. PushbackInputStream:一字節「後退」

允許將讀取的字節推回流中,常用於詞法分析器需「向前看一個字符」的場景:


PushbackInputStream push = new PushbackInputStream(new ByteArrayInputStream("hello,world".getBytes()));
int temp;
while ((temp = push.read()) != -1) {
    if (temp == ',') {
        push.unread(temp); // 推回逗號
        temp = push.read(); // 重新讀取
        System.out.print("(回退" + (char)temp + ")");
    } else {
        System.out.print((char)temp);
    }
}

7. SequenceInputStream:合併多個輸入流

將多個 InputStream 串聯成一個邏輯流,順序讀取:


Vector v = new Vector<>();
v.add(new FileInputStream("text1.txt"));
v.add(new FileInputStream("text2.txt"));
SequenceInputStream sis = new SequenceInputStream(v.elements());
// 讀取 sis 即可依次讀完所有文件

三、字符流:專為文本設計

Java 內部用 Unicode,字符流自動處理編碼轉換。核心類:Reader/Writer 及其子類。

1. FileReader / PrintWriter 示例


char[] buffer = new char[512];
try (FileReader reader = new FileReader("copy1.txt");
     PrintWriter writer = new PrintWriter(System.out)) {
    int len;
    while ((len = reader.read(buffer)) != -1) {
        writer.write(buffer, 0, len);
    }
}

2. 緩衝字符流與鏈式包裝:BufferedReader / BufferedWriter

鏈式調用(裝飾器模式)增強功能。BufferedReader.readLine() 按行讀取極其方便:


public static void concatenateFile(String... fileNames) throws IOException {
    try (BufferedWriter writer = new BufferedWriter(new FileWriter("copy2.txt"))) {
        for (String name : fileNames) {
            try (BufferedReader reader = new BufferedReader(new FileReader(name))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    writer.write(line);
                    writer.newLine(); // 跨平台換行
                }
            }
        }
    }
}

3. Console 類:安全讀取密碼

Console.readPassword() 禁用回顯,返回 char[],用完可手動清零,比 String 更安全。

4. StreamTokenizer:文本分詞利器

將字符流解析為 Token(標識符、數字、字符串、註釋)。默認規則可自定義:


StreamTokenizer st = new StreamTokenizer(new BufferedReader(new FileReader("copy1.txt")));
// 將引號、斜杠視為普通字符,不作特殊處理
st.ordinaryChar('\''); st.ordinaryChar('"'); st.ordinaryChar('/');
while (st.nextToken() != StreamTokenizer.TT_EOF) {
    switch (st.ttype) {
        case StreamTokenizer.TT_WORD:  System.out.println("單詞:" + st.sval); break;
        case StreamTokenizer.TT_NUMBER: System.out.println("數字:" + st.nval); break;
        case StreamTokenizer.TT_EOL: break;
        default: System.out.println("標點:" + (char)st.ttype);
    }
}

實測發現: 單獨小數點視為數字 0.0;中文連寫視為一個詞;引號內整體為一 Token(除非調用 ordinaryChar)。

四、進階話題預告

  • Externalizable:比 Serializable 更可控的序列化接口
  • serialVersionUID:對象版本控制
  • Java NIO (New IO):非阻塞、通道、緩衝區、選擇器——高性能網絡編程基石,後續結合多線程專文講解

Java IO 體系龐大但脈絡清晰:文件 → 字節流/字符流 → 裝飾器增強 → 特殊功能流。掌握核心抽象類(InputStream/OutputStream/Reader/Writer)、裝飾器模式(Buffered*/Data*/Object*)及資源關閉模式(try-with-resources),即可應對絕大多數場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved