欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java?IO流實(shí)戰(zhàn)之文件復(fù)制、文本讀寫(xiě)、對(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ù)制、文本讀寫(xiě)、對(duì)象序列化的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下

前言

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

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

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

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

最原始的文件復(fù)制可以通過(guò)FileInputStream(字節(jié)輸入流)和FileOutputStream(字節(jié)輸出流)實(shí)現(xiàn),原理是逐字節(jié)或按字節(jié)數(shù)組讀取源文件,再寫(xiě)入目標(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語(yǔ)法自動(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) {
                // 寫(xiě)入緩沖區(qū)中的有效數(shù)據(jù)(避免寫(xiě)入空字節(jié))
                fos.write(buffer, 0, len);
            }
            System.out.println("基礎(chǔ)字節(jié)流復(fù)制完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

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

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

基礎(chǔ)字節(jié)流雖然能完成復(fù)制,但read()write()仍會(huì)頻繁觸發(fā)系統(tǒng)調(diào)用。BufferedInputStreamBufferedOutputStream通過(guò)內(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()每次都從磁盤(pán)讀數(shù)據(jù),而緩沖流會(huì)一次性讀取大量數(shù)據(jù)到內(nèi)置緩沖區(qū),后續(xù)read()直接從內(nèi)存緩沖區(qū)獲取,大幅減少磁盤(pán) IO 次數(shù)(磁盤(pán) 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)過(guò)用戶態(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)方式核心類(lèi)優(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)景二:文本讀寫(xiě) —— 字符流與編碼處理

文本文件(如.txt、.java)由字符組成,直接用字節(jié)流讀寫(xiě)可能因編碼問(wèn)題導(dǎo)致亂碼。字符流(Reader/Writer)專(zhuān)門(mén)處理字符數(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";
        
        // 字符流讀寫(xiě)文本
        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ǔ)字符流讀寫(xiě)完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 解決編碼問(wèn)題: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("指定編碼的字符流讀寫(xiě)完成!");
        } 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()(逐行讀?。┖?code>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)容
            // 逐行讀取(readLine()不包含換行符)
            while ((line = br.readLine()) != null) {
                bw.write(line); // 寫(xiě)入一行內(nèi)容
                bw.newLine(); // 換行(自動(dòng)適配系統(tǒng)換行符:\n或\r\n)
            }
            System.out.println("緩沖字符流逐行讀寫(xiě)完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

關(guān)鍵技巧

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

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

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

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

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

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

// 實(shí)現(xiàn)Serializable接口(標(biāo)記接口,無(wú)方法)
public class User implements Serializable {
    // 序列化版本號(hào)(強(qiáng)烈建議顯式聲明,避免類(lèi)結(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ì)象寫(xiě)入文件
        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)記接口(無(wú)任何方法),僅用于告訴 JVM 該類(lèi)可以被序列化。若類(lèi)未實(shí)現(xiàn)此接口,序列化時(shí)會(huì)拋出NotSerializableException。

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

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

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

序列化流程圖示

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

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

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

限制

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

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

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

  1. 選擇合適的流類(lèi)型

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

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

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

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

IO 流是 Java 開(kāi)發(fā)的基礎(chǔ)技能,掌握這些實(shí)戰(zhàn)技巧不僅能解決日常開(kāi)發(fā)問(wèn)題,更能幫助你理解 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ù)制、文本讀寫(xiě)、對(duì)象序列化詳細(xì)解析的文章就介紹到這了,更多相關(guān)Java IO流文件復(fù)制、文本讀寫(xiě)、對(duì)象序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring 父類(lèi)變量注入失敗的解決

    Spring 父類(lèi)變量注入失敗的解決

    這篇文章主要介紹了Spring 父類(lèi)變量注入失敗的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • elasticsearch索引index之Translog數(shù)據(jù)功能分析

    elasticsearch索引index之Translog數(shù)據(jù)功能分析

    這篇文章主要為大家介紹了elasticsearch索引index之Translog數(shù)據(jù)功能分析,主要分析translog的結(jié)構(gòu)及寫(xiě)入方式,有需要的朋友可以借鑒參考下
    2022-04-04
  • 深入理解Maven的坐標(biāo)與依賴

    深入理解Maven的坐標(biāo)與依賴

    這篇文章主要介紹了深入理解Maven的坐標(biāo)與依賴,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-01-01
  • Java8中stream和functional interface的配合使用詳解

    Java8中stream和functional interface的配合使用詳解

    這篇文章主要給大家介紹了關(guān)于Java8中stream和functional interface配合使用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java8具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-11-11
  • Spring 使用Validation 驗(yàn)證框架的問(wèn)題詳解

    Spring 使用Validation 驗(yàn)證框架的問(wèn)題詳解

    Spring Boot在內(nèi)部通過(guò)集成hibernate-validation已經(jīng)實(shí)現(xiàn)了JSR-349驗(yàn)證規(guī)范接口,在Spring Boot項(xiàng)目中只要直接使用就行了。 一般用在Controller中用于驗(yàn)證前端傳來(lái)的參數(shù)。這篇文章給大家介紹Spring Validation 驗(yàn)證框架的相關(guān)知識(shí),感興趣的朋友一起看看吧
    2021-07-07
  • 劍指Offer之Java算法習(xí)題精講數(shù)組與字符和等差數(shù)列

    劍指Offer之Java算法習(xí)題精講數(shù)組與字符和等差數(shù)列

    跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過(guò)之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化
    2022-03-03
  • Java二叉樹(shù)路徑和代碼示例

    Java二叉樹(shù)路徑和代碼示例

    這篇文章主要介紹了Java二叉樹(shù)路徑和代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。
    2017-12-12
  • Java中Controller、Service、Dao/Mapper層的區(qū)別與用法

    Java中Controller、Service、Dao/Mapper層的區(qū)別與用法

    在Java開(kāi)發(fā)中,通常會(huì)采用三層架構(gòu)(或稱MVC架構(gòu))來(lái)劃分程序的職責(zé)和功能,分別是Controller層、Service層、Dao/Mapper層,本文將詳細(xì)給大家介紹了三層的區(qū)別和用法,需要的朋友可以參考下
    2023-05-05
  • 深入講解java線程與synchronized關(guān)鍵字

    深入講解java線程與synchronized關(guān)鍵字

    Java 中多線程的同步依靠的是對(duì)象鎖機(jī)制,synchronized關(guān)鍵字就是利用了封裝對(duì)象鎖來(lái)實(shí)現(xiàn)對(duì)共享資源的互斥訪問(wèn)。下面這篇文章主要介紹了java線程與synchronized關(guān)鍵字的相關(guān)資料,需要的朋友可以參考下。
    2017-03-03
  • springboot實(shí)現(xiàn)jar運(yùn)行復(fù)制resources文件到指定的目錄(思路詳解)

    springboot實(shí)現(xiàn)jar運(yùn)行復(fù)制resources文件到指定的目錄(思路詳解)

    這篇文章主要介紹了springboot實(shí)現(xiàn)jar運(yùn)行復(fù)制resources文件到指定的目錄,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04

最新評(píng)論