最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?IO流實(shí)戰(zhàn)之文件復(fù)制、文本讀寫、對(duì)象序列化詳細(xì)解析

 更新時(shí)間:2025年11月07日 10:36:10   作者:梵得兒SHI  
Java?IO提供了對(duì)象序列化機(jī)制,可以將對(duì)象轉(zhuǎn)換為字節(jié)流,并從字節(jié)流還原對(duì)象,這篇文章主要介紹了Java?IO流實(shí)戰(zhàn)之文件復(fù)制、文本讀寫、對(duì)象序列化的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

IO 流是 Java 操作數(shù)據(jù)傳輸?shù)暮诵墓ぞ?,從?jiǎn)單的文件復(fù)制到復(fù)雜的對(duì)象持久化,都離不開 IO 流的靈活運(yùn)用。本文將通過三個(gè)實(shí)戰(zhàn)場(chǎng)景 ——文件復(fù)制、文本讀寫對(duì)象序列化存儲(chǔ),帶你從原理到代碼徹底掌握 IO 流的實(shí)戰(zhàn)技巧,每個(gè)場(chǎng)景都配備直觀圖示和優(yōu)化方案,讓你在實(shí)際開發(fā)中少走彎路。

一、實(shí)戰(zhàn)場(chǎng)景一:文件復(fù)制 —— 字節(jié)流的核心應(yīng)用

文件復(fù)制是 IO 流最基礎(chǔ)也最常用的場(chǎng)景,無論是圖片、視頻還是文檔,本質(zhì)上都是字節(jié)數(shù)據(jù)的傳輸。我們需要解決的核心問題是:如何高效地將源文件的字節(jié)數(shù)據(jù)傳輸?shù)侥繕?biāo)文件

1.1 基礎(chǔ)實(shí)現(xiàn):字節(jié)流直接讀寫

最原始的文件復(fù)制可以通過FileInputStream(字節(jié)輸入流)和FileOutputStream(字節(jié)輸出流)實(shí)現(xiàn),原理是逐字節(jié)或按字節(jié)數(shù)組讀取源文件,再寫入目標(biāo)文件。

public class FileCopyBasic {
    public static void main(String[] args) {
        // 源文件和目標(biāo)文件路徑
        String sourcePath = "source.jpg";
        String targetPath = "target_basic.jpg";
        
        // 聲明流對(duì)象(try-with-resources語法自動(dòng)關(guān)閉資源)
        try (FileInputStream fis = new FileInputStream(sourcePath);
             FileOutputStream fos = new FileOutputStream(targetPath)) {
            
            byte[] buffer = new byte[1024]; // 緩沖區(qū)(一次讀1024字節(jié))
            int len; // 實(shí)際讀取的字節(jié)數(shù)
            
            // 循環(huán)讀?。寒?dāng)len=-1時(shí)表示讀取完畢
            while ((len = fis.read(buffer)) != -1) {
                // 寫入緩沖區(qū)中的有效數(shù)據(jù)(避免寫入空字節(jié))
                fos.write(buffer, 0, len);
            }
            System.out.println("基礎(chǔ)字節(jié)流復(fù)制完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

原理圖示:字節(jié)流通過緩沖區(qū)批量傳輸數(shù)據(jù),減少直接操作磁盤的次數(shù)(直接逐字節(jié)讀寫效率極低,必須用數(shù)組緩沖)。

1.2 優(yōu)化方案:緩沖流提升效率

基礎(chǔ)字節(jié)流雖然能完成復(fù)制,但read()write()仍會(huì)頻繁觸發(fā)系統(tǒng)調(diào)用。BufferedInputStreamBufferedOutputStream通過內(nèi)置緩沖區(qū)(默認(rèn) 8KB)進(jìn)一步減少 IO 次數(shù),效率更高。

public class FileCopyWithBuffer {
    public static void main(String[] args) {
        String sourcePath = "source.jpg";
        String targetPath = "target_buffer.jpg";
        
        // 緩沖流包裝基礎(chǔ)字節(jié)流
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourcePath));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath))) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
                // 緩沖流會(huì)自動(dòng)刷新,大文件可手動(dòng)調(diào)用bos.flush()避免數(shù)據(jù)滯留
            }
            System.out.println("緩沖流復(fù)制完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

為什么緩沖流更快?基礎(chǔ)字節(jié)流的read()每次都從磁盤讀數(shù)據(jù),而緩沖流會(huì)一次性讀取大量數(shù)據(jù)到內(nèi)置緩沖區(qū),后續(xù)read()直接從內(nèi)存緩沖區(qū)獲取,大幅減少磁盤 IO 次數(shù)(磁盤 IO 速度遠(yuǎn)低于內(nèi)存)。

1.3 進(jìn)階方案:NIO 的零拷貝復(fù)制

Java NIO 的FileChannel提供了transferTo()方法,支持零拷貝(數(shù)據(jù)直接從內(nèi)核緩沖區(qū)傳輸?shù)侥繕?biāo)文件,不經(jīng)過用戶態(tài)),是大文件復(fù)制的最優(yōu)選擇。

public class FileCopyWithChannel {
    public static void main(String[] args) {
        String sourcePath = "source.mp4";
        String targetPath = "target_channel.mp4";
        
        try (FileChannel inChannel = new FileInputStream(sourcePath).getChannel();
             FileChannel outChannel = new FileOutputStream(targetPath).getChannel()) {
            
            // 傳輸數(shù)據(jù):從輸入通道到輸出通道,每次最多傳輸Integer.MAX_VALUE字節(jié)
            long position = 0;
            long size = inChannel.size();
            while (position < size) {
                position += inChannel.transferTo(position, Integer.MAX_VALUE, outChannel);
            }
            System.out.println("NIO通道復(fù)制完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

三種方案對(duì)比

實(shí)現(xiàn)方式核心類優(yōu)點(diǎn)缺點(diǎn)適用場(chǎng)景
基礎(chǔ)字節(jié)流FileInputStream簡(jiǎn)單直接效率低(頻繁 IO)小文件、簡(jiǎn)單場(chǎng)景
緩沖字節(jié)流BufferedInputStream內(nèi)置緩沖區(qū),效率較高仍有用戶態(tài) / 內(nèi)核態(tài)切換中大型文件
NIO 通道FileChannel零拷貝,效率最高代碼稍復(fù)雜超大文件、性能敏感場(chǎng)景

二、實(shí)戰(zhàn)場(chǎng)景二:文本讀寫 —— 字符流與編碼處理

文本文件(如.txt、.java)由字符組成,直接用字節(jié)流讀寫可能因編碼問題導(dǎo)致亂碼。字符流(Reader/Writer)專門處理字符數(shù)據(jù),能自動(dòng)完成字節(jié)與字符的轉(zhuǎn)換。

2.1 基礎(chǔ)字符流:FileReader/FileWriter

   FileReaderFileWriter是字符流的基礎(chǔ)實(shí)現(xiàn),默認(rèn)使用系統(tǒng)編碼(可能導(dǎo)致跨平臺(tái)亂碼),適合簡(jiǎn)單場(chǎng)景。

public class TextReadWriteBasic {
    public static void main(String[] args) {
        String sourceTxt = "source.txt";
        String targetTxt = "target_basic.txt";
        
        // 字符流讀寫文本
        try (FileReader fr = new FileReader(sourceTxt);
             FileWriter fw = new FileWriter(targetTxt)) {
            
            char[] buffer = new char[1024]; // 字符緩沖區(qū)
            int len;
            while ((len = fr.read(buffer)) != -1) {
                fw.write(buffer, 0, len);
            }
            System.out.println("基礎(chǔ)字符流讀寫完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 解決編碼問題:InputStreamReader/OutputStreamWriter

當(dāng)文本文件編碼(如 UTF-8)與系統(tǒng)默認(rèn)編碼不一致時(shí),必須用InputStreamReaderOutputStreamWriter指定編碼,避免亂碼。

public class TextReadWriteWithCharset {
    public static void main(String[] args) {
        String sourceTxt = "source_utf8.txt";
        String targetTxt = "target_utf8.txt";
        String charset = "UTF-8"; // 明確指定編碼
        
        // 字節(jié)流→字符流(指定編碼)
        try (InputStreamReader isr = new InputStreamReader(
                 new FileInputStream(sourceTxt), charset);
             OutputStreamWriter osw = new OutputStreamWriter(
                 new FileOutputStream(targetTxt), charset)) {
            
            char[] buffer = new char[1024];
            int len;
            while ((len = isr.read(buffer)) != -1) {
                osw.write(buffer, 0, len);
            }
            System.out.println("指定編碼的字符流讀寫完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

編碼轉(zhuǎn)換原理:文本文件存儲(chǔ)的是字節(jié)(如 UTF-8 編碼的漢字占 3 字節(jié)),InputStreamReader按指定編碼將字節(jié)轉(zhuǎn)換為字符,OutputStreamWriter再將字符轉(zhuǎn)換為目標(biāo)編碼的字節(jié)。

2.3 高效文本處理:緩沖字符流與特殊操作

BufferedReaderBufferedWriter是字符流的緩沖增強(qiáng)版,提供readLine()(逐行讀取)和newLine()(跨平臺(tái)換行)等實(shí)用方法,是文本處理的首選。

public class TextReadWriteWithBuffer {
    public static void main(String[] args) {
        String sourceTxt = "article.txt";
        String targetTxt = "article_copy.txt";
        
        try (BufferedReader br = new BufferedReader(
                 new InputStreamReader(new FileInputStream(sourceTxt), "UTF-8"));
             BufferedWriter bw = new BufferedWriter(
                 new OutputStreamWriter(new FileOutputStream(targetTxt), "UTF-8"))) {
            
            String line; // 存儲(chǔ)每行內(nèi)容
            // 逐行讀?。╮eadLine()不包含換行符)
            while ((line = br.readLine()) != null) {
                bw.write(line); // 寫入一行內(nèi)容
                bw.newLine(); // 換行(自動(dòng)適配系統(tǒng)換行符:\n或\r\n)
            }
            System.out.println("緩沖字符流逐行讀寫完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

關(guān)鍵技巧

  • readLine()適合處理按行結(jié)構(gòu)化的文本(如日志、CSV);
  • newLine()比硬編碼\n更通用,避免跨平臺(tái)問題;
  • 大文件讀寫時(shí),緩沖字符流的效率遠(yuǎn)高于基礎(chǔ)字符流。

三、實(shí)戰(zhàn)場(chǎng)景三:對(duì)象序列化 —— 對(duì)象的持久化存儲(chǔ)

序列化是將對(duì)象轉(zhuǎn)換為字節(jié)序列以便存儲(chǔ)或傳輸?shù)倪^程,反序列化則是將字節(jié)序列恢復(fù)為對(duì)象。Java 通過Serializable接口和對(duì)象流(ObjectInputStream/ObjectOutputStream)實(shí)現(xiàn)這一功能。

3.1 序列化的基本實(shí)現(xiàn)

步驟 1:定義可序列化的類(實(shí)現(xiàn)Serializable接口)

import java.io.Serializable;
import java.util.Date;

// 實(shí)現(xiàn)Serializable接口(標(biāo)記接口,無方法)
public class User implements Serializable {
    // 序列化版本號(hào)(強(qiáng)烈建議顯式聲明,避免類結(jié)構(gòu)變化導(dǎo)致反序列化失?。?
    private static final long serialVersionUID = 1L;
    
    private String username;
    private int age;
    private transient String password; // transient修飾的字段不參與序列化
    private Date registerTime;
    
    // 構(gòu)造器、getter、setter、toString()
    public User(String username, int age, String password) {
        this.username = username;
        this.age = age;
        this.password = password;
        this.registerTime = new Date();
    }
    
    @Override
    public String toString() {
        return "User{username='" + username + "', age=" + age + 
               ", password='" + password + "', registerTime=" + registerTime + "}";
    }
}

步驟 2:使用對(duì)象流進(jìn)行序列化和反序列化

import java.io.*;

public class ObjectSerialization {
    public static void main(String[] args) {
        String filePath = "user.ser";
        User user = new User("zhangsan", 25, "123456");
        
        // 1. 序列化:將對(duì)象寫入文件
        try (ObjectOutputStream oos = new ObjectOutputStream(
                 new FileOutputStream(filePath))) {
            oos.writeObject(user);
            System.out.println("序列化完成:" + user);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 2. 反序列化:從文件恢復(fù)對(duì)象
        try (ObjectInputStream ois = new ObjectInputStream(
                 new FileInputStream(filePath))) {
            User deserializedUser = (User) ois.readObject();
            System.out.println("反序列化結(jié)果:" + deserializedUser);
            // 注意:password為null(transient字段未被序列化)
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

輸出結(jié)果

序列化完成:User{username='zhangsan', age=25, password='123456', registerTime=...}
反序列化結(jié)果:User{username='zhangsan', age=25, password='null', registerTime=...}

3.2 序列化的核心知識(shí)點(diǎn)

  1. Serializable 接口:是一個(gè)標(biāo)記接口(無任何方法),僅用于告訴 JVM 該類可以被序列化。若類未實(shí)現(xiàn)此接口,序列化時(shí)會(huì)拋出NotSerializableException

  2. serialVersionUID 的作用:用于驗(yàn)證序列化和反序列化的類版本是否一致。若不顯式聲明,JVM 會(huì)根據(jù)類結(jié)構(gòu)自動(dòng)生成,類結(jié)構(gòu)(如增減字段)變化會(huì)導(dǎo)致版本號(hào)改變,反序列化失敗。建議所有可序列化類顯式聲明此常量。

  3. transient 關(guān)鍵字:被transient修飾的字段不參與序列化,反序列化時(shí)會(huì)被賦予默認(rèn)值(如 null、0)。適合存儲(chǔ)敏感信息(如密碼)或無需持久化的臨時(shí)數(shù)據(jù)。

  4. 父類序列化規(guī)則:若父類未實(shí)現(xiàn)Serializable,則父類必須有默認(rèn)無參構(gòu)造器,否則反序列化時(shí)會(huì)報(bào)錯(cuò)(無法初始化父類字段)。

序列化流程圖示

3.3 序列化的應(yīng)用場(chǎng)景與限制

應(yīng)用場(chǎng)景

  • 對(duì)象持久化(如存儲(chǔ)到文件、數(shù)據(jù)庫 BLOB 字段);
  • 網(wǎng)絡(luò)傳輸(如 RPC 框架中對(duì)象的跨服務(wù)傳輸);
  • 深拷貝(通過序列化 + 反序列化實(shí)現(xiàn)對(duì)象的完全復(fù)制)。

限制

  • 不能序列化靜態(tài)字段(靜態(tài)字段屬于類,不屬于對(duì)象);
  • 循環(huán)引用的對(duì)象可序列化(JVM 會(huì)處理引用關(guān)系);
  • 序列化后的字節(jié)流與 JVM 相關(guān),跨語言兼容性差(可考慮 JSON、Protobuf 等跨語言格式)。

四、IO 流實(shí)戰(zhàn)總結(jié)與最佳實(shí)踐

通過三個(gè)場(chǎng)景的實(shí)戰(zhàn),我們可以總結(jié)出 IO 流使用的核心原則:

  1. 選擇合適的流類型

    • 字節(jié)流:處理非文本文件(圖片、視頻、二進(jìn)制數(shù)據(jù));
    • 字符流:處理文本文件(需注意編碼);
    • 對(duì)象流:處理對(duì)象的序列化 / 反序列化。
  2. 優(yōu)先使用緩沖流:緩沖流(BufferedXXX)通過內(nèi)置緩沖區(qū)減少 IO 次數(shù),效率遠(yuǎn)高于基礎(chǔ)流,幾乎所有場(chǎng)景都應(yīng)優(yōu)先使用。

  3. 資源管理必須嚴(yán)謹(jǐn):始終使用try-with-resources 語法(自動(dòng)關(guān)閉資源),避免流未關(guān)閉導(dǎo)致的資源泄漏(尤其是文件流和網(wǎng)絡(luò)流)。

  4. 關(guān)注編碼問題:文本處理時(shí)必須明確指定編碼(如 UTF-8),避免依賴系統(tǒng)默認(rèn)編碼導(dǎo)致的亂碼。

  5. 大文件優(yōu)化:大文件復(fù)制優(yōu)先用 NIO 的FileChannel.transferTo(),利用零拷貝提升性能;大文件讀寫避免一次性加載到內(nèi)存,應(yīng)分塊處理。

IO 流是 Java 開發(fā)的基礎(chǔ)技能,掌握這些實(shí)戰(zhàn)技巧不僅能解決日常開發(fā)問題,更能幫助你理解 IO 操作的底層原理。希望本文的三個(gè)實(shí)戰(zhàn)場(chǎng)景能讓你對(duì) IO 流的運(yùn)用更加得心應(yīng)手,歡迎在評(píng)論區(qū)分享你的實(shí)戰(zhàn)經(jīng)驗(yàn)!

總結(jié) 

到此這篇關(guān)于Java IO流實(shí)戰(zhàn)之文件復(fù)制、文本讀寫、對(duì)象序列化詳細(xì)解析的文章就介紹到這了,更多相關(guān)Java IO流文件復(fù)制、文本讀寫、對(duì)象序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Springboot配置文件Nacos和環(huán)境變量?jī)?yōu)先級(jí)詳解

    Springboot配置文件Nacos和環(huán)境變量?jī)?yōu)先級(jí)詳解

    這篇文章主要介紹了Springboot配置文件Nacos和環(huán)境變量?jī)?yōu)先級(jí),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-06-06
  • 使用maven構(gòu)建java9 service實(shí)例詳解

    使用maven構(gòu)建java9 service實(shí)例詳解

    本篇文章主要介紹了使用maven構(gòu)建java9 service實(shí)例詳解,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-02-02
  • Spring詳細(xì)講解循環(huán)依賴是什么

    Spring詳細(xì)講解循環(huán)依賴是什么

    這篇文章主要介紹了Java中的Spring循環(huán)依賴詳情,文章基于Java的相關(guān)資料展開詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • Java+Freemarker實(shí)現(xiàn)根據(jù)XML模板文件生成Word文檔

    Java+Freemarker實(shí)現(xiàn)根據(jù)XML模板文件生成Word文檔

    這篇文章主要為大家詳細(xì)介紹了Java如何使用Freemarker實(shí)現(xiàn)根據(jù)XML模板文件生成Word文檔,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以學(xué)習(xí)一下
    2023-11-11
  • JVM核心教程之JVM運(yùn)行與類加載全過程詳解

    JVM核心教程之JVM運(yùn)行與類加載全過程詳解

    我們都知道一個(gè)java程序運(yùn)行要經(jīng)過編譯和執(zhí)行,但是這太概括了,中間還有很多步驟,下面這篇文章主要給大家介紹了關(guān)于JVM核心教程之JVM運(yùn)行與類加載全過程的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2018-04-04
  • Java并發(fā)中死鎖、活鎖和饑餓是什么意思

    Java并發(fā)中死鎖、活鎖和饑餓是什么意思

    今天看到的一篇文章,說的很好,再敲了一遍,分享一下有關(guān)于死鎖、活鎖及饑餓的概念和區(qū)別,感興趣的可以了解一下
    2021-11-11
  • JVM Metaspace內(nèi)存溢出問題解決方案

    JVM Metaspace內(nèi)存溢出問題解決方案

    這篇文章主要介紹了JVM Metaspace內(nèi)存溢出排查總結(jié)過程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 2023最新版IDEA創(chuàng)建javaweb項(xiàng)目的詳細(xì)圖文教程

    2023最新版IDEA創(chuàng)建javaweb項(xiàng)目的詳細(xì)圖文教程

    之前用的社區(qū)版IDEA無法部署JavaWeb項(xiàng)目,于是裝了一個(gè)最新版的IDEA,下面這篇文章主要給大家介紹了關(guān)于2023最新版IDEA創(chuàng)建javaweb項(xiàng)目的詳細(xì)圖文教程,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • java web實(shí)現(xiàn)用戶權(quán)限管理

    java web實(shí)現(xiàn)用戶權(quán)限管理

    這篇文章主要介紹了java web實(shí)現(xiàn)用戶權(quán)限管理,設(shè)計(jì)并實(shí)現(xiàn)一套簡(jiǎn)單的權(quán)限管理功能,感興趣的小伙伴們可以參考一下
    2015-11-11
  • 在?Java?中將Object?轉(zhuǎn)換為?Int的四種方法

    在?Java?中將Object?轉(zhuǎn)換為?Int的四種方法

    這篇文章主要介紹了在Java中如何將?Object?轉(zhuǎn)換為Int,本文研究了在?Java中將Object轉(zhuǎn)換為int的四種不同方法,結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05

最新評(píng)論

准格尔旗| 呼伦贝尔市| 河西区| 普安县| 灵璧县| 景洪市| 嘉禾县| 民和| 获嘉县| 长沙县| 桐乡市| 社旗县| 监利县| 白城市| 饶平县| 广南县| 南丰县| 岱山县| 南昌市| 福安市| 安陆市| 长沙县| 饶平县| 竹山县| 抚宁县| 姚安县| 无为县| 扬中市| 缙云县| 清水县| 历史| 西平县| 淮阳县| 通城县| 高安市| 沈阳市| 南江县| 化德县| 赤壁市| 中方县| 沙田区|