JavaIO流的使用和修飾器模式(直擊心靈版)

系列文章目錄

? ?JavaIO流的使用和修飾器模式


文章目錄

  • 系列文章目錄
  • 前言
  • 一、字節流:
    • 1.FileInputStream(讀取文件)
    • 2.FileOutputStream(寫入文件)
  • 二、字符流:
    • 1..基礎字符流:
    • 2.處理流:
    • 3.對象處理流:
    • 4.轉換流:
  • ?三、修飾器模式
  • 總結


前言

? 前面我們講解了Java文件和IO流的基礎部分。把流簡單的分了一下類,但是我們還不知道具體是如何是使用的,下面我們將詳細的講解一下這些個流各自的職責是什么,簡言之就是各自的使用方式。然后我還想給大家強戴一下IO流當中的修飾器模式,因為這個實際上通過封裝真的太牛逼了。


? ? ? ? ? 我先給大家按字節流和字符流的分類方式來進行講述:

一、字節流:

? ? ? ?用于處理二進制數據(如圖片、視頻、任何文件)?,核心類為?InputStream?和?OutputStream

? ? ? ? (1)FileInputStream(讀取文件)

? ? ? ?? ? ?每次調用?read()?方法從磁盤讀取1字節,頻繁IO操作性能差。
? 適用場景:小文件讀取或需要逐字節處理的場景。

try (InputStream in = new FileInputStream("test.jpg")) {int byteData;while ((byteData = in.read()) != -1) { // 每次讀取1字節// 處理字節(例如加密、校驗)System.out.print((char)byteData + " ");}
} catch (IOException e) {e.printStackTrace();
}

? ? ? ??我們要注意,這樣單個字節讀取,如果文件當中有漢字就不行了。? ? ? ? ? ? ? ? ??

?所以進階版可以用int read(byte[] b)方法來讀取,這個方法底層是從該輸入流中讀取最多b.length字節數據到字節數組,如果讀取正常,返回實際讀取字節數, -1表示的是讀取完畢了。但記得最后還要轉換為字符串 new String(buf,0,readlen).

? ? ? ? ? ?(2) FileOutputStream(寫入文件)

? ? ? ??注意點:若文件不存在會自動創建,若存在默認覆蓋(通過構造參數可設置為追加模式)。

// 第二個參數 true 表示追加寫入
try (OutputStream out = new FileOutputStream("log.txt", true)) {String logEntry = "Error occurred at " + new Date() + "\n";out.write(logEntry.getBytes(StandardCharsets.UTF_8)); // 顯式指定編碼
} catch (IOException e) {e.printStackTrace();
}

這里還有一處細節要注意,就是這樣創建,寫入內容會覆蓋原來的內容,但如果是這樣創建的 new FileOutputStream(filepath,true),這樣再寫入內容就會追加到文件后面。

二、字符流

1.基礎字符流:

? (1)FileReader 讀文件

? 這里循環讀取使用read()是單個字符讀取,使用read(buf)返回的是實際取到的字符數.

? (2)FileWriter? ? ? 寫文件

這里面注意一定要關閉流,或者Flush才能真正的把數據寫入到文件

2.處理流:

BufferedReader?和?BufferedWriter:

readLine()?可逐行讀取文本。

// 讀取CSV文件并解析
try (BufferedReader br = new BufferedReader(new FileReader("data.csv"))) {String line;while ((line = br.readLine()) != null) {String[] columns = line.split(",");// 處理每一列數據}
}// 寫入帶換行的文本
try (BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {bw.write("Line 1");bw.newLine();  // 跨平臺換行(Windows為\r\n,Linux為\n)bw.write("Line 2");
}

像BufferedReader類中,有屬性Reader,即可以封裝一個節點流 (該節點流可以是任意的,只要是Reader的子類就行,這個我們下面講修飾器模式再講)。

details:

1.BufferedReader?和?BufferedWriter都是按照字符操作的。

2.不要去操作二進制文件(如聲音,視頻等)可能會造成文件損壞。

? ?總結:

場景正確流類型原因
圖片、視頻、EXE文件字節流直接處理原始字節,避免編解碼干擾
文本文件(.txt)字符流正確處理字符編碼(如UTF-8、GBK)
混合數據(如PDF)字節流PDF包含文本和二進制結構,需精確控制字節
網絡傳輸數據字節流網絡協議基于字節,而非字符

所以說字符流是“文本專用工具”,操作二進制文件就像用剪刀擰螺絲——不僅費力,還可能搞砸!

? 3.對象處理流:

? 能夠將基本數據類型或者對象進行序列化和反序列化的操作。

? ? ? 這里我們需要注意的是如果需要讓某個對象支持序列化機制,則必須讓其類是可序列化的,而為了讓某個類是可序列化的,該類必須實現如下兩個接口之一:

? ? Serializable? 和? ?Externalizable? 我們常用的是Serializable接口,因為它不用再重寫方法了。

? ??

class User implements Serializable {private static final long serialVersionUID = 1L; // 版本號private String name;private transient String password; // transient字段不會被序列化
}// 序列化對象到文件
User user = new User("Alice", "secret");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.dat"))) {oos.writeObject(user);
}// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.dat"))) {User restoredUser = (User) ois.readObject();System.out.println(restoredUser.getName()); // 輸出 "Alice"System.out.println(restoredUser.getPassword()); // 輸出 null(transient字段)
}

? ? ?注意讀取(反序列化)的順序需要和保存數據(序列化)的順序一致,否則會出現異常。

還有最容易忽略的一點就是序列化對象時,要求里面的屬性的類型也需要實現序列化接口。

? 序列化對象時,默認將里面所有屬性都會進序列化,除了static或者transient修飾的成員。

? ? 4.轉換流:

亂碼的本質是 ?字符編碼不匹配

  1. ?寫入時:文本按編碼A(如UTF-8)轉換為字節。
  2. ?讀取時:字節按編碼B(如GBK)解碼為字符。
  3. ?結果:編碼A和編碼B的映射關系不同,導致字符顯示錯誤。

轉換流的作用

類名功能核心價值
InputStreamReader將字節流(InputStream)按指定編碼轉換為字符流?解決讀取時的編碼問題
OutputStreamWriter將字符流按指定編碼轉換為字節流(OutputStream?解決寫入時的編碼問題
try (Reader reader = new InputStreamReader(new FileInputStream("utf8_file.txt"), StandardCharsets.UTF_8)) {// 正確讀取中文字符int data;while ((data = reader.read()) != -1) {System.out.print((char) data);}
}try (Reader reader = new InputStreamReader(new FileInputStream("utf8_file.txt"), StandardCharsets.UTF_8)) {// 正確讀取中文字符int data;while ((data = reader.read()) != -1) {System.out.print((char) data);}
}

綜上可知,學習IO流我們必須要知道什么時候使用什么流。

? ?三、修飾器模式:

? 其實我在學習的過程中也很疑惑這個修飾器模式到底有什么用,不就是像套娃一樣一層套著一層嗎,但是當我們真正理解了才發現Java設計者有多牛逼。

? ? ? ? 像以BufferedInputStream舉例:

BufferedInputStream?的緩沖機制

  • ?內部緩沖區BufferedInputStream?維護一個字節數組(默認大小 8KB),用于臨時存儲從底層流讀取的數據。
  • ?讀取邏輯
    1. 當用戶調用?read()?時,BufferedInputStream?會優先從緩沖區讀取數據
    2. ?如果緩沖區為空,它會一次性從底層?InputStream(如?FileInputStream)讀取一批數據(填滿緩沖區)。
    3. 后續的?read()?直接從緩沖區返回數據,直到緩沖區耗盡,再重復步驟 2。

? ?

  • 數據來源BufferedInputStream?本身不連接任何數據源(如文件、網絡等),它只是一個“功能增強包裝器”。
  • ?依賴關系:緩沖流需要底層流提供原始數據,而?FileInputStream?是唯一能直接讀取文件的節點流。

? ?裝飾器模式(Decorator Pattern)的核心思想是 ?動態地為對象添加功能,同時保持接口的一致性。

? 舉一個咖啡加料的例子:

? ? ?假設你經營一家咖啡店,需要靈活組合咖啡和配料(如牛奶、糖),但不想為每種組合創建子類(如?MilkSugarCoffeeSugarCoffee?等)。裝飾器模式可以完美解決這個問題

? ? ?1.定義基礎組件:

? ? ??

// 基礎接口:咖啡
public interface Coffee {double getCost();String getDescription();
}// 具體組件:基礎咖啡
public class SimpleCoffee implements Coffee {@Overridepublic double getCost() { return 2.0; }@Overridepublic String getDescription() { return "基礎咖啡"; }
}

? ? ?2. 定義裝飾器基類:

? ?

// 裝飾器基類:實現 Coffee 接口,并持有一個 Coffee 對象
public abstract class CoffeeDecorator implements Coffee {protected Coffee decoratedCoffee;public CoffeeDecorator(Coffee coffee) {this.decoratedCoffee = coffee;}// 委托給被裝飾的 Coffee 對象@Overridepublic double getCost() { return decoratedCoffee.getCost(); }@Overridepublic String getDescription() { return decoratedCoffee.getDescription(); }
}

? ? ?3. 具體修飾器:牛奶或糖:

? ? ?

// 牛奶裝飾器
public class MilkDecorator extends CoffeeDecorator {public MilkDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() { return super.getCost() + 0.5; }@Overridepublic String getDescription() { return super.getDescription() + "+牛奶"; }
}// 糖裝飾器
public class SugarDecorator extends CoffeeDecorator {public SugarDecorator(Coffee coffee) {super(coffee);}@Overridepublic double getCost() { return super.getCost() + 0.2; }@Overridepublic String getDescription() { return super.getDescription() + "+糖"; }
}

? ? ?4.使用修飾器的動態組合:?

public class Main {public static void main(String[] args) {// 基礎咖啡Coffee coffee = new SimpleCoffee();System.out.println(cost: " + coffee.getCost() + ", desc: " + coffee.getDescription());// 加牛奶coffee = new MilkDecorator(coffee);System.out.println(cost: " + coffee.getCost() + ", desc: " + coffee.getDescription());// 再加糖coffee = new SugarDecorator(coffee);System.out.println(cost: " + coffee.getCost() + ", desc: " + coffee.getDescription());}
}

而在IO流中:

  • 組件接口InputStream(所有輸入流的基類)。
  • ?具體組件FileInputStream(直接操作文件的節點流)。
  • ?裝飾器基類FilterInputStream(實現?InputStream,并持有?InputStream?對象)。
  • ?具體裝飾器BufferedInputStream(擴展?FilterInputStream,添加緩沖功能)。
// 節點流:直接讀取文件
InputStream fileStream = new FileInputStream("data.txt");// 裝飾器:添加緩沖功能
InputStream bufferedStream = new BufferedInputStream(fileStream);// 可以繼續裝飾:例如添加解密功能(假設有 DecryptInputStream)
InputStream decryptedStream = new DecryptInputStream(bufferedStream);

當調用?bufferedStream.read()?時:

  1. ?檢查緩沖區:如果有數據,直接返回。
  2. ?緩沖區為空:調用底層?fileStream.read(byte[])?批量讀取數據到緩沖區。
  3. ?返回數據:從緩沖區返回一個字節。

其實吧,處理流(如?BufferedInputStream)需要傳入?InputStream?對象的核心目的,正是為了在自己的成員方法中調用底層流的?read?方法,并在其基礎上添加額外功能(如緩沖、編碼轉換等)。這是裝飾器模式的精髓所在。


總結

以上就是今天要講的內容,本文僅簡單的講述了IO流分類后的使用和例子,然后講了一下修飾器模式,接下來我會一直持續更新,謝謝大家。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/73030.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/73030.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/73030.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【設計模式】SOLID 設計原則概述

SOLID 是面向對象設計中的五大原則,不管什么面向對象的語言, 這個準則都很重要,如果你沒聽說過,趕緊先學一下。它可以提高代碼的可維護性、可擴展性和可讀性,使代碼更加健壯、易于測試和擴展。SOLID 代表以下五個設計原…

可發1區的超級創新思路:基于注意力機制的DSD-CNN時間序列預測模型(功率預測、交通流量預測、故障檢測)

首先聲明,該模型為原創!原創!原創! 一、應用場景 該模型主要用于時間序列數據預測問題,包含功率預測、電池壽命預測、電機故障檢測等等 二、模型整體介紹(本文以光伏功率預測為例) DSD-CNN(Depthwise-Spacewise Separable CNN)結合通道注意力機制,通過以下創新提升…

wsl2配置xv6全解(包括22.04Jammy)

文章目錄 獲取xv6源代碼Ubuntu20.04 Version安裝指令成功測試參考MIT2021年官方文檔 24.04 Version安裝指令成功測試參考MIT2024年官方文檔 Ubuntu 22.04沒有官方文檔? 配置大體流程1. 卸載原本qemu(如果之前安裝了)2. clone qemu官方源代碼&…

招聘面試季--一文頓悟,Java中字節流和字符流的區別及使用場景上的差異

?一、核心區別? ?特性??字節流??字符流??數據單位?以字節(8-bit)為單位處理數據(如0xA1)以字符(16-bit Unicode)為單位處理數據(如A, 你)?基類?InputStream / OutputSt…

車載以太網網絡測試-16【傳輸層-UDP】

目錄 1 摘要2 車載以太網傳輸層概述3 車載以太網UDP協議3.1 車載以太網UDP協議的作用3.2 UDP報文幀結構3.3 UDP協議的通信過程3.3.1 通信過程3.3.2 實例示例3.3.3 代碼示例 4 總結 1 摘要 車載以太網的第五層是傳輸層,它在車載網絡架構中扮演著至關重要的角色。主要…

深度強化學習中的深度神經網絡優化策略:挑戰與解決方案

I. 引言 深度強化學習(Deep Reinforcement Learning,DRL)結合了強化學習(Reinforcement Learning,RL)和深度學習(Deep Learning)的優點,使得智能體能夠在復雜的環境中學…

無人機點對點技術要點分析!

一、技術架構 1. 網絡拓撲 Ad-hoc網絡:無人機動態組建自組織網絡,節點自主協商路由,無需依賴地面基站。 混合架構:部分場景結合中心節點(如指揮站)與P2P網絡,兼顧集中調度與分布式協同。 2.…

MQ,RabbitMQ,MQ的好處,RabbitMQ的原理和核心組件,工作模式

1.MQ MQ全稱 Message Queue(消息隊列),是在消息的傳輸過程中 保存消息的容器。它是應用程序和應用程序之間的通信方法 1.1 為什么使用MQ 在項目中,可將一些無需即時返回且耗時的操作提取出來,進行異步處理&#xff0…

django怎么配置404和500

在 Django 中,配置 404 和 500 錯誤頁面需要以下步驟: 1. 創建自定義錯誤頁面模板 首先,創建兩個模板文件,分別用于 404 和 500 錯誤頁面。假設你的模板目錄是 templates/。 404 頁面模板 創建文件 templates/404.html&#x…

各類神經網絡學習:(四)RNN 循環神經網絡(下集),pytorch 版的 RNN 代碼編寫

上一篇下一篇RNN(中集)待編寫 代碼詳解 pytorch 官網主要有兩個可調用的模塊,分別是 nn.RNNCell 和 nn.RNN ,下面會進行詳細講解。 RNN 的同步多對多、多對一、一對多等等結構都是由這兩個模塊實現的,只需要將對輸入…

深度學習篇---深度學習中的范數

文章目錄 前言一、向量范數1.L0范數1.1定義1.2計算式1.3特點1.4應用場景1.4.1特征選擇1.4.2壓縮感知 2.L1范數(曼哈頓范數)2.1定義2.2計算式2.3特點2.4應用場景2.4.1L1正則化2.4.2魯棒回歸 3.L2范數(歐幾里得范數)3.1定義3.2特點3…

星越L_燈光操作使用講解

目錄 1.開啟前照燈 2左右轉向燈、遠近燈 3.auto自動燈光 4.自適應遠近燈光 5.后霧燈 6.調節大燈高度 1.開啟前照燈 2左右轉向燈、遠近燈 3.auto自動燈光 系統根據光線自動開啟燈光

Stable Diffusion lora訓練(一)

一、不同維度的LoRA訓練步數建議 2D風格訓練 數據規模:建議20-50張高質量圖片(分辨率≥10241024),覆蓋多角度、多表情的平面風格。步數范圍:總步數控制在1000-2000步,公式為 總步數 Repeat Image Epoch …

AI 生成 PPT 網站介紹與優缺點分析

隨著人工智能技術不斷發展,利用 AI 自動生成 PPT 已成為提高演示文稿制作效率的熱門方式。本文將介紹幾款主流的 AI PPT 工具,重點列出免費使用機會較多的網站,并對各平臺的優缺點進行詳細分析,幫助用戶根據自身需求選擇合適的工具…

使用Systemd管理ES服務進程

Centos中的Systemd介紹 CentOS 中的 Systemd 詳細介紹 Systemd 是 Linux 系統的初始化系統和服務管理器,自 CentOS 7 起取代了傳統的 SysVinit,成為默認的初始化工具。它負責系統啟動、服務管理、日志記錄等核心功能,顯著提升了系統的啟動速…

【一維前綴和與二維前綴和(簡單版dp)】

1.前綴和模板 一維前綴和模板 1.暴力解法 要求哪段區間,我就直接遍歷那段區間求和。 時間復雜度O(n*q) 2.前綴和 ------ 快速求出數組中某一個連續區間的和。 1)預處理一個前綴和數組 這個前綴和數組設定為dp,dp[i]表示:表示…

在Windows和Linux系統上的Docker環境中使用的鏡像是否相同

在Windows和Linux系統上的Docker環境中使用的鏡像是否相同,取決于具體的運行模式和目標平臺: 1. Linux容器模式(默認/常見場景) Windows系統: 當Windows上的Docker以Linux容器模式運行時(默認方式&#xf…

植物來源藥用天然產物的合成生物學研究進展-文獻精讀121

植物來源藥用天然產物的合成生物學研究進展 摘要 大多數藥用天然產物在植物中含量低微,提取分離困難;而且這些化合物一般結構復雜,化學合成難度大,還容易造成環境污染。基于合成生物學技術獲得藥用天然產物具有綠色環保和可持續發…

JavaScript |(五)DOM簡介 | 尚硅谷JavaScript基礎實戰

學習來源:尚硅谷JavaScript基礎&實戰丨JS入門到精通全套完整版 筆記來源:在這位大佬的基礎上添加了一些東西,歡迎大家支持原創,大佬太棒了:JavaScript |(五)DOM簡介 | 尚硅谷JavaScript基礎…

瀏覽器工作原理深度解析(階段二):HTML 解析與 DOM 樹構建

一、引言 在階段一中,我們了解了瀏覽器通過 HTTP/HTTPS 協議獲取頁面資源的過程。本階段將聚焦于瀏覽器如何解析 HTML 代碼并構建 DOM 樹,這是渲染引擎的核心功能之一。該過程可分為兩個關鍵步驟:詞法分析(Token 化)和…