JAVA I/O(一)

来源:https://www.cnblogs.com/shuimuzhushui/archive/2019/01/14/10269203.html
-Advertisement-
Play Games

最近再看I/O這一塊,故作為總結記錄於此。JDK1.4引入NIO後,原來的I/O方法都基於NIO進行了優化,提高了性能。I/O操作類都在java.io下,大概將近80個,大致可以分為4類: 基於位元組操作的I/O介面:以InputStream和OutputStream為基類,也是I/O操作的基礎。 基 ...


最近再看I/O這一塊,故作為總結記錄於此。JDK1.4引入NIO後,原來的I/O方法都基於NIO進行了優化,提高了性能。I/O操作類都在java.io下,大概將近80個,大致可以分為4類:

  • 基於位元組操作的I/O介面:以InputStream和OutputStream為基類,也是I/O操作的基礎。
  • 基於字元操作的I/O介面:以Reader和Writer為基類,字元的讀寫是基於位元組進行的,中間進行了轉換。
  • 基於磁碟操作的I/O介面:主要是File,代表目錄下的所有文件。
  • 基於網路操作的I/O介面:主要是Socket,實現網路數據的傳輸。

本文大致總結一下基於位元組和字元的I/O操作,主要理清JAVA I/O中的類關係。

摘自《Java編程思想》:類庫中常使用“流”這個抽象的概念,代表任何有能力產出數據的數據源對象或有能力接收數據的接收端對象。其屏蔽了I/O設備中數據處理的細節。I/O類分為輸入和輸出兩類。通過 繼承,任何InputStream或Reader的派生類都含有read()方法,用於讀取單個位元組或字元,任何OutputStream或Writer的派生類都含有write()方法,用於寫單個位元組或字元。通常不會使用單一的類創建流對象,而是通過疊合多個對象提供期望的功能(即採用裝飾器模式)。

一、基於位元組的I/O操作

1. InputStream類型

InputStream的作用表示那些從不同數據源產生輸入的類,即其派生類多是不同數據源對應的流對象。如下:

  ByteArrayInputStream:從記憶體緩衝區讀取位元組數組

  FileInputStream:從文件中讀取位元組,其構造參數可以是文件名、File對象或FileDescriptor

  ObjectInputStream:主要用於反序列化,讀取基本數據類型或對象

  PipedInputStream:產生用於寫入相關PipedOutputStream的數據,實現“管道化”概念,多用於多線程中。

  FilterInputStream:作為裝飾器類,其子類與上述不同流對象疊合使用,以控制特定輸入流。

其中,FilterInputStream的子類通過添加屬性或有用的介面控制位元組輸入流,其構造函數為InputStream,常見的幾個如下:

  DataInputStream:與DataOutputStream搭配使用,讀取基本類型數據及String對象。

  BufferdInputStream:使用緩衝區的概念,避免每次都進行實際讀操作,提升I/O性能。

  InflaterInputStream:其子類GZIPInputStream和ZipInputStream可以讀取GZIP和ZIP格式的數據。

2. OutputStream類型

與InputStream相對應,OutputStream的作用表示將數據寫入不同的數據源,常用的輸出流對象如下:

  ByteArrayOutputStream:在記憶體中創建緩衝區,寫入位元組數組

  FileOutputStream:將位元組數據寫入文件中,其構造參數可以是文件名、File對象或FileDescriptor

  ObjectOutputStream:主要用於序列化,作用於基本數據類型或對象

  PipedOutputStream:任何寫入其中的數據,都會自動作為相關PipedInputStream的輸出,實現“管道化”概念,多用於多線程中。

  FilterOutputStream:作為裝飾器類,其子類與上述不同流對象疊合使用,以控制特定輸出流。

其中,FilterOutputStream的子類通過添加屬性或有用的介面控制位元組輸入流,其構造函數為InputStream,常見的幾個如下:

  DataOutputStream:與DataInputStream搭配使用,寫入基本類型數據及String對象。

  PrintStream:用於格式化輸出顯示。

  BufferdOutputStream:使用緩衝區的概念,避免每次都進行實際寫操作,提升I/O性能。

  DeflaterOutputStream:其子類GZIPOutputStream和ZipOutputStream可以寫GZIP和ZIP格式的數據。

二、基於字元的I/O操作

不管是磁碟還是網路傳輸,數據處理的最小單元都是位元組,而不是字元。故所有I/O操作的都是位元組而不是字元。為了方便引入了字元操作,其中涉及位元組到字元的轉換適配,InputStreamReader可以把InputStream轉為Reader,OutputStreamWriter可以把OutputStream轉為Writer。對上述按位元組操作的流對象,可以採用FilterInputStream和FilterOutputStream的裝飾器子類控制流。Reader和Writer沿用相似的思想,但不完全相同。

1. Reader類型

繼承自Reader類的,字元型數據來源常用類,如下:

  InputStreamReader:位元組與字元適配器,子類包含FileReader(以字元形式讀取文件) 

  CharArrayReader:讀取字元數組

  StringReader:數據源是字元串

  BufferedReader:讀取字元輸入流,併進行緩存,常用法:BufferedReader in = new BufferedReader(new FileReader("foo.in")); 表示採用緩存的方式從文件讀取數據

  PipedReader:管道形式讀取字元  

  FilterReader:對Reader裝飾,直接使用的不多,如PushbackReader

2. Writer類型

繼承自Writer類的,字元型數據來源常用類,如下:

  OutputStreamReader:位元組與字元適配器,子類包含FileWriter(以字元形式寫文件) 

  CharArrayWriter:寫字元數組

  StringWriter:內部有StringBuffer,用於緩存構造字元串

  BufferedWriter:字元輸出流,常用法:PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out"))); 表示將數據格式化並用緩存的方式寫入文件

  PipedWriter:管道形式輸出字元  

  FilterWriter:對Writer裝飾,如XMLWriter

三、自我獨立的類RandomAccessFile

該類可以隨機訪問文件,實現了DataOutput, DataInput,不是InputStream或OutputStream繼承層次結構的一部分。與其他I/O類本質有所不同,可以在一個文件內向前或向後移動。

工作方式類似與DataOutputStream和 DataInputStream,用法如下:

  RandomAccessFile randomAccessFile = new RandomAccessFile("data.dat", "rw")

其中,r代表讀,w代表寫。

四、常用實例

1. 緩存輸入文件

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**
 * 緩存輸入文件,防止頻繁與文件交互
 * 1.採用裝飾器模式,BufferedReader從FileReader中讀取字元,FileReader為字元數據源
 * 2.FileReader繼承InputStreamReader,實例化一個FileInputStream對象作為位元組數據源,
 * 3.InputStreamReader繼承Reader,包含StreamDecoder,將位元組數據轉換為字元;編碼格式沒有指定時採用預設編碼。
 * 4.Reader可以實現對FileInputStream加鎖*/
public class BufferedInputFile {

    public static String read(String filename) throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new FileReader(filename));
        String s;
        StringBuilder sb = new StringBuilder();
        while((s = bufferedReader.readLine()) != null) {
            sb.append(s + "\n");
        }
        bufferedReader.close();
        return sb.toString();
    }
    
    public static void main(String[] args) throws IOException {
        System.out.println(read("src/com/test/io/BufferedInputFile.java"));
    }
}
//輸出類文件到控制台

2.從記憶體輸入

import java.io.IOException;
import java.io.StringReader;
/**
 * 將文件讀入記憶體
 * 具體形式:new StringReader(new BufferdReader(new FileReader(filename)))
 * 通過緩存讀文件,防止每讀一個位元組,都與文件直接交互*/
public class MemoryINput {

    static String filename = "src/com/test/io/BufferedInputFile.java";
    
    public static void main(String[] args) throws IOException{
        StringReader in = new StringReader(BufferedInputFile.read(filename));
        int c;
        while((c = in.read()) != -1) {
            System.out.println((char)c);
        }
    }
}

3.格式化記憶體輸入

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
/**
 * 格式化的記憶體輸入
 * 1.in.readByte()讀取位元組,無法判斷位元組是否有效合法,因此無法判斷結尾,報java.io.EOFException
 * 2.採用available()方法預估還有多少位元組可存取*/
public class FormattedMemoryInput {
    
    public static void main(String[] args) throws IOException {
        DataInputStream in = new DataInputStream(
                new ByteArrayInputStream(BufferedInputFile.read("src/com/test/io/BufferedInputFile.java").getBytes()));
//        byte c;
//        while((c = in.readByte()) !=-1) {
//            System.out.print((char) c);
//        }
        while(in.available() != 0) {
            System.out.print((char) in.readByte());
        }
    }
}

4.基本文件輸出

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
/**
 * 基本文件輸出
 * 1.先利用BufferedInputFile和StringReader將數據讀到記憶體,記住輸入流已經關閉
 * 2.new PrintWriter(new BufferedWriter(new FileWriter(outfile)))輸出字元到文件
 * 註意,此處使用BufferedWriter進行緩衝,防止每個位元組都與文件交互
 * 3.文本文件輸出快捷方式 PrintWriter out = new PrintWriter(outfile);
 * 底層實現了緩存new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName)))*/
public class BasicFIleOutput {

    static String filename = "src/com/test/io/BufferedInputFile.java";
    static String outfile = "BasicFIleOutput.out";
    
    public static void main(String[] args) throws IOException {
        BufferedReader in = new BufferedReader(new StringReader(BufferedInputFile.read(filename)));
//        PrintWriter out = new PrintWriter(new FileWriter(outfile));
//        PrintWriter out = new PrintWriter(outfile);
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(outfile)));
        int lineCount = 1;
        String s;
        while((s = in.readLine()) != null) {
            out.println(lineCount++ + ":" +s);
        }
        out.close();
    }
}

5.存儲與恢複數據

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * 1.DataInputStream能從DataOutputStream中準確讀取數據
 * 2.讀數據時,必須知道數據精確的位置,否則會報錯
 * 3.writeUTF與readUTF採用UTF-8的變體進行編碼
 *
 */
public class StoringAndRecoveringData {

    public static void main(String[] args) throws IOException {
        
        DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")));
        out.writeDouble(3.12159);
        out.writeUTF("this is pi");
        out.writeDouble(1.4414);
        out.writeUTF("this is root of 2");
        out.close();
        
        DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")));
        System.out.println(in.readDouble());
        System.out.println(in.readUTF());
        System.out.println(in.readDouble());
        in.close();
    }
} 

五、總結

1.  I/O操作本質是基於位元組流的操作,InputStream和OutputStream對輸入和輸出源進行了抽象,其子類代表不同的數據源。

2. FilterInputStream和FilterOutputStream採用裝飾器模式,對輸入和輸出流進行控制,如採用緩衝器、讀基本數據類型等。

3. Reader和Writer代表基於字元的操作,底層是基於位元組操作,經過InputStreamReader和OutputStreamWriter,採用StreamEncoder和StreamDecoder,將輸入輸出流,按Charset進行轉換

4. 所有基於位元組或字元的操作,基本都採用疊合的方式。如輸入流採用緩存的方式從文件中讀取,輸出流採用緩存的方式按格式輸出到文件。

5. 理清他們之間的關係,有利於瞭解I/O的操作過程。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 基本數據類型和string之間的轉換 (1) 基本類型轉string 使用 fmt.Sprintf(“%參數”, 表達式) a. 通用: 參數含義 %v 值的預設格式表示 %+v 類似%v,但輸出結構體時會添加欄位名 %#v 值的Go語法表示 %T 值的類型的Go語法表示 %% 百分號 b. 布爾值 ...
  • Django 系列博客(十一) 前言 本篇博客介紹使用 ORM 來進行多表的操作,當然重點在查詢方面。 創建表 實例: 作者模型:一個作者有姓名和年齡。 作者詳細模型:把作者的詳情放到詳情表,包含生日,手機號,家庭住址等信息。作者詳情模型和作者模型之間是一對一的關係(one to one)。 出版商 ...
  • 本篇主要介紹的是SpringCloud相關知識、微服務架構以及搭建一個高可用的服務註冊與發現的服務模塊(Eureka)。 ...
  • Werkzeug:response,request,routing 獲取請求參數:data,form,args,files,cookies,headers,method,url routing:Rule類-用來構造不同的URL模式的對象,路由URL規則 Map類-存儲所有的URL規則和一些配置參數 ...
  • 項目說明: Python新手,第一個爬蟲項目,網路爬蟲算是一項能提升編程學習興趣的活動了,讓Python學習不再枯燥。 Python版本3.7.2 需要模塊:requests,os,beautifulsoup 爬蟲目標地址https://www.mzitu.com/xinggan/ 項目實現: 首先 ...
  • 使用CBrother腳本做TCP伺服器與C++客戶端通信   工作中總是會遇到一些對於伺服器壓力不是特別大,但是代碼量比較多,用C++寫起來很不方便。對於這種需求,我選擇用CBrother腳本做伺服器,之所以不選擇Python是因為python的語法我實在是適應不了,再來CBrother的網路框架也... ...
  • # 使用selenium和phantomJS瀏覽器獲取網頁內容的小演示 # 導入包 from selenium import webdriver # 使用selenium庫里的webdriver方法調用PhantomJS瀏覽器實例一個瀏覽器的操作對象 # 括弧里的參數為PhantomJS瀏覽器在電腦... ...
  • 新聞 "Rider上的拼寫助手,對未使用引用的代碼分析及更多F 相關特性的更新" ".NET開源革命開始" "ML.NET 0.9發佈記錄" "測試驅動的集成開發環境" "Giraffe在GitHub上超過了1000個星" "用於Giraffe,Saturn和ASP.NET Core中間件適配器的函 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...