偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

一篇帶你徹底讀懂 IO 流技術(shù)!

存儲(chǔ) 存儲(chǔ)架構(gòu)
本文闡述的內(nèi)容較多,整合了很多有用的信息,從 Java 基本的 I/O 類(lèi)庫(kù)結(jié)構(gòu)開(kāi)始說(shuō)起,主要介紹了 IO 的傳輸格式和傳輸方式,包括字節(jié)流和字符流接口相關(guān)的分類(lèi)介紹,以及磁盤(pán) I/O 和網(wǎng)絡(luò) I/O 的基本工作方式。

一、摘要

說(shuō)到 IO,相信大家都不陌生,英文全稱(chēng):Input/Output,即輸入/輸出,通常指數(shù)據(jù)在內(nèi)部存儲(chǔ)器和外部存儲(chǔ)器或其他周邊設(shè)備之間的輸入和輸出。

比如我們常用的SD卡、U盤(pán)、移動(dòng)硬盤(pán)等等存儲(chǔ)文件的硬件設(shè)備,當(dāng)我們將其插入電腦的 usb 硬件接口時(shí),我們就可以從電腦中讀取設(shè)備中的信息或者寫(xiě)入信息,這個(gè)過(guò)程就涉及到 I/O 的操作。

當(dāng)然,涉及 I/O 的操作,也不僅僅局限于硬件設(shè)備的讀寫(xiě),還有網(wǎng)絡(luò)數(shù)據(jù)的傳輸。比如,我們?cè)陔娔X上用瀏覽器搜索互聯(lián)網(wǎng)上的信息,這個(gè)信息的過(guò)程也涉及到 I/O 的操作。

無(wú)論是從磁盤(pán)中讀寫(xiě)文件,還是在網(wǎng)絡(luò)中傳輸數(shù)據(jù),可以說(shuō) I/O 主要為處理人機(jī)交互、機(jī)與機(jī)交互中獲取和交換信息提供的一套解決方案。

在 Java 的 IO 體系中,類(lèi)將近有 80 個(gè),位于java.io包下,初步看起來(lái)感覺(jué)非常復(fù)雜,但是經(jīng)過(guò)一番梳理之后,你會(huì)發(fā)現(xiàn)還是有規(guī)律可循的。

從傳輸數(shù)據(jù)的格式角度看,可以大致分為兩組:

  • 基于字節(jié)操作的 I/O 接口:InputStream 和 OutputStream
  • 基于字符操作的 I/O 接口:Reader 和 Writer

從傳輸數(shù)據(jù)的方式角度看,也可以大致分為兩組:

  • 基于磁盤(pán)操作的 I/O 接口:File
  • 基于網(wǎng)絡(luò)操作的 I/O 接口:Socket

雖然 Socket 類(lèi)并不在java.io包下,但是我們?nèi)匀话阉鼈儎澐衷谝黄穑驗(yàn)?I/O 的核心問(wèn)題,要么是數(shù)據(jù)格式影響 I/O 操作,要么是傳輸方式影響 I/O 操作,也就是將什么樣的數(shù)據(jù)寫(xiě)到什么地方的問(wèn)題。

I/O 只是人與機(jī)器或者機(jī)器與機(jī)器交互的手段,除了在它們能夠完成這個(gè)交互功能外,我們關(guān)注的就是如何提高它的運(yùn)行效率,而數(shù)據(jù)格式和傳輸方式是影響效率最關(guān)鍵的因素。

下面我們基于這兩點(diǎn),來(lái)展開(kāi)分析!

二、傳輸格式的分類(lèi)

從傳輸格式角度看,可以分兩類(lèi):字節(jié)流和字符流。

  • 基于字節(jié)的輸入和輸出操作接口分別是:InputStream 和 OutputStream
  • 基于字符的輸入和輸出操作接口分別是:Reader 和 Writer 。

圖片

2.1、字節(jié)流接口

字節(jié)流,是 I/O 流中最底層的流,能處理任何類(lèi)型的數(shù)據(jù)傳輸,比如文字、圖片、視頻、文件等。

2.1.1、基于字節(jié)輸入流的接口

打開(kāi) JDK 源碼,整理之后,InputStream 輸入流接口的類(lèi)繼承層次如下圖所示:

圖片

這些輸入流類(lèi),根據(jù)角色不同,還可以進(jìn)行分類(lèi),分為:節(jié)點(diǎn)流和處理流。

  • 節(jié)點(diǎn)流:指的是向指定的設(shè)備,比如磁盤(pán)、網(wǎng)絡(luò),進(jìn)行讀/寫(xiě)數(shù)據(jù),也被稱(chēng)為底層流,直接和數(shù)據(jù)源相接
  • 處理流:指的是在已存在的節(jié)點(diǎn)流或者處理流基礎(chǔ)上,包裝一些更加方便操作 io 流的功能,比如壓縮、序列化、緩沖操作等,也被稱(chēng)為包裝流

輸入流類(lèi),根據(jù)角色的劃分類(lèi)別如下:

圖片

OutputStream 輸出流的類(lèi)層次結(jié)構(gòu)也是類(lèi)似。

2.1.2、基于字節(jié)輸出流的接口

OutputStream 輸入流接口的類(lèi)繼承層次如下圖所示:

圖片

字節(jié)輸出流類(lèi),根據(jù)角色的劃分類(lèi)別如下:

圖片

這里就不詳細(xì)的介紹各個(gè)子類(lèi)的使用方法,有興趣的朋友可以查看 JDK 的 API 說(shuō)明文檔,筆者也會(huì)在后期的系列文章會(huì)進(jìn)行詳細(xì)的介紹。

這里只是重點(diǎn)想說(shuō)一下,無(wú)論是輸入還是輸出,操作數(shù)據(jù)的方式可以組合使用,各個(gè)處理流的類(lèi)并不是只操作固定的節(jié)點(diǎn)流,比如如下輸出方式:

//將文件輸出流包裝到序列化輸出流中,再將序列化輸出流包裝到緩沖中
OutputStream out = new BufferedOutputStream(new ObjectOutputStream(new FileOutputStream(new File("fileName")));

另外,輸出流最終寫(xiě)到什么地方必須要指定,要么是寫(xiě)到硬盤(pán)中,要么是寫(xiě)到網(wǎng)絡(luò)中,從圖中可以發(fā)現(xiàn),寫(xiě)網(wǎng)絡(luò)實(shí)際上也是寫(xiě)文件,只不過(guò)寫(xiě)到網(wǎng)絡(luò)中,需要經(jīng)過(guò)底層操作系統(tǒng)將數(shù)據(jù)發(fā)送到其他指定的計(jì)算機(jī)中,而不是寫(xiě)入到本地硬盤(pán)中。

2.2、字符流接口

不管是磁盤(pán)還是網(wǎng)絡(luò)傳輸,最小的存儲(chǔ)單元都是字節(jié),而不是字符,所以 I/O 操作的都是字節(jié)而不是字符。

那為什么要有操作字符的 I/O 接口呢?

這是因?yàn)槲覀兊某绦蛑型ǔ2僮鞯臄?shù)據(jù)都是以字符形式,為了程序操作更方便而提供一個(gè)直接寫(xiě)字符的 I/O 接口,僅此而已!

除此之外,使用字節(jié)流操控文字時(shí)不是很方便,容易亂碼,由此誕生了不同的字符集以及對(duì)應(yīng)的字符編碼規(guī)則!

由于全世界的文字博大精深,不同的字符集,占用的字節(jié)位數(shù)不同,以中文為例,在GBK編碼規(guī)則中,一個(gè)中文使用二個(gè)字節(jié)存儲(chǔ);而在UTF-8編碼規(guī)則中,一個(gè)中文使用三個(gè)字節(jié)存儲(chǔ),如果寫(xiě)入和讀取的編碼規(guī)則不一樣,讀取的字節(jié)數(shù)很容易裂開(kāi),導(dǎo)致出現(xiàn)亂碼。

比如以下案例:

public static void main(String[] args) throws Exception {
    byte[] bytes = "學(xué)習(xí)Java語(yǔ)言".getBytes("ISO8859-1");
    File file = new File("encoding.txt");
    OutputStream out = new FileOutputStream(file);
    out.write(bytes);
    out.close();
}

文件的內(nèi)容如下:

??Java??

為了更方便地處理中文這些字符,計(jì)算機(jī)就推出了字符編碼規(guī)則。

實(shí)現(xiàn)原理:字節(jié)流 + 編碼表。

  • 當(dāng)寫(xiě)入一段文字時(shí),會(huì)使用指定的字符集,將該 String 編碼為一系列字節(jié),將結(jié)果存儲(chǔ)到新的字節(jié)數(shù)組中,進(jìn)行傳輸
  • 當(dāng)讀取一段文字時(shí),通過(guò)指定的字符集,解碼指定的字節(jié)數(shù)組來(lái)構(gòu)造新的 String,從而解決文字亂碼的問(wèn)題。
2.2.1、基于字符輸入流的接口

Reader 輸入流接口的類(lèi)繼承層次如下圖所示:

圖片

同樣的,字符輸入流類(lèi),根據(jù)角色的劃分類(lèi)別如下:

圖片

2.2.2、基于字符輸出流的接口

Writer 輸出流的類(lèi)繼承層次如下圖所示:

圖片

字符輸出流類(lèi),根據(jù)角色的劃分類(lèi)別如下:

圖片

2.3、字節(jié)與字符的轉(zhuǎn)化

剛剛我們說(shuō)到,不管是磁盤(pán)還是網(wǎng)絡(luò)傳輸,最小的存儲(chǔ)單元都是字節(jié),而不是字符,設(shè)計(jì)字符的原因是為了程序更方便的操作文本。

那么怎么將字符轉(zhuǎn)化成字節(jié)或者將字節(jié)轉(zhuǎn)化成字符呢?

其中,InputStreamReader和OutputStreamWriter就是轉(zhuǎn)化橋梁。

2.3.1、輸入流轉(zhuǎn)換方案

輸入流字符解碼相關(guān)類(lèi)結(jié)構(gòu)的轉(zhuǎn)化過(guò)程如下圖所示:

圖片

從圖上可以看到,InputStreamReader類(lèi)是字節(jié)到字符的轉(zhuǎn)化橋梁, 其中StreamDecoder指的是一個(gè)解碼操作類(lèi),Charset指的是字符集。

InputStream到Reader的過(guò)程需要指定編碼字符集,否則將采用操作系統(tǒng)默認(rèn)字符集,很可能會(huì)出現(xiàn)亂碼問(wèn)題,StreamDecoder則是完成字節(jié)到字符的解碼的實(shí)現(xiàn)類(lèi)。

案例如下:

File file = new File("encoding.txt");
FileInputStream inputStream =new FileInputStream(file);
//字節(jié)輸入流轉(zhuǎn)為字符輸入流
InputStreamReader streamReader =new InputStreamReader(inputStream, Charset.forName("UTF-8"));
2.3.2、輸出流轉(zhuǎn)換方案

輸出流轉(zhuǎn)化過(guò)程也是類(lèi)似,如下圖所示:

圖片

通過(guò)OutputStreamWriter類(lèi)完成字符到字節(jié)的編碼過(guò)程,由StreamEncoder 完成編碼過(guò)程。

案例如下:

File file = new File("output.txt");
FileOutputStream outputStream =new FileOutputStream(file);
//字符輸出流轉(zhuǎn)字節(jié)輸出流
OutputStreamWriter streamWriter =new OutputStreamWriter(outputStream, Charset.forName("UTF-8"));

三、傳輸方式的分類(lèi)

上文我們介紹了數(shù)據(jù)的傳輸格式,可以通過(guò)字節(jié)流和字符流接口來(lái)完成數(shù)據(jù)的傳輸,至于數(shù)據(jù)寫(xiě)到何處,主要取決于數(shù)據(jù)的傳輸方式。

從傳輸方式角度看,可以分兩類(lèi):磁盤(pán)和網(wǎng)絡(luò)。

  • 基于磁盤(pán)操作的操作接口是:File
  • 基于網(wǎng)絡(luò)操作的操作接口是:Socket

3.1、文件接口

我們知道數(shù)據(jù)在磁盤(pán)的唯一最小描述就是文件,也就是說(shuō)上層應(yīng)用程序只能通過(guò)文件來(lái)操作磁盤(pán)上的數(shù)據(jù),文件也是操作系統(tǒng)和磁盤(pán)驅(qū)動(dòng)器交互的一個(gè)最小單元。

圖片

在 Java I/O 體系中,**File類(lèi)是唯一代表磁盤(pán)文件本身的對(duì)象**。

File 類(lèi)定義了一些與平臺(tái)無(wú)關(guān)的方法來(lái)操作文件,包括檢查一個(gè)文件是否存在、創(chuàng)建、刪除文件、重命名文件、判斷文件的讀寫(xiě)權(quán)限是否存在、設(shè)置和查詢(xún)文件的最近修改時(shí)間等等操作。

值得注意的是 Java 中通常的 File 并不代表一個(gè)真實(shí)存在的文件對(duì)象,當(dāng)你通過(guò)指定一個(gè)路徑描述符時(shí),它就會(huì)返回一個(gè)代表這個(gè)路徑相關(guān)聯(lián)的一個(gè)虛擬對(duì)象,這個(gè)可能是一個(gè)真實(shí)存在的文件或者是一個(gè)包含多個(gè)文件的目錄。

例如,讀取一個(gè)文件內(nèi)容,程序如下:

public static void main(String[] args) throws Exception {
    StringBuilder str = new StringBuilder();
    char[] buf = new char[1024];

    // 讀取文件的內(nèi)容
    FileReader f = new FileReader("input.txt");
    while(f.read(buf)>0){
        str.append(buf);
    }
    str.toString();
}

以上面的程序?yàn)槔?,從硬盤(pán)中讀取一段文本字符,操作流程如下圖:

圖片

當(dāng)我們傳入一個(gè)指定的文件名來(lái)創(chuàng)建File對(duì)象,通過(guò)FileReader來(lái)讀取文件內(nèi)容時(shí),會(huì)自動(dòng)創(chuàng)建一個(gè)FileInputStream對(duì)象來(lái)讀取文件內(nèi)容,也就是我們上文中所說(shuō)的字節(jié)流來(lái)讀取文件。

緊接著,會(huì)創(chuàng)建一個(gè)FileDescriptor的對(duì)象,其實(shí)這個(gè)對(duì)象就是真正代表一個(gè)存在的文件對(duì)象的描述。

由于我們需要讀取的是字符格式,所以需要StreamDecoder類(lèi)通過(guò)解碼方法decode,將字節(jié)轉(zhuǎn)字符,至于如何從磁盤(pán)驅(qū)動(dòng)器上讀取一段數(shù)據(jù),由操作系統(tǒng)幫我們完成。

3.2、網(wǎng)絡(luò)接口

繼續(xù)來(lái)說(shuō)說(shuō)數(shù)據(jù)傳輸?shù)牧硪环N處理方式:網(wǎng)絡(luò)通信。

3.2.1、Socket 簡(jiǎn)介

在 Java 網(wǎng)絡(luò)體系中,Socket是描述計(jì)算機(jī)之間完成相互通信一種抽象定義。

光從描述看可能很難理解,打個(gè)比方,可以把Socket比作為兩個(gè)城市之間的交通工具,有了它,就可以在城市之間來(lái)回穿梭了;并且,交通工具有多種,每種交通工具也有相應(yīng)的交通規(guī)則。

Socket 也一樣,也有多種,大部分情況下我們使用的都是基于 TCP/IP 的流套接字,它是一種穩(wěn)定的通信協(xié)議。

比較典型的基于 Socket 通信的應(yīng)用程序場(chǎng)景,如下圖:

圖片

主機(jī) A 的應(yīng)用程序要想和主機(jī) B 的應(yīng)用程序通信,必須通過(guò) Socket 建立連接,而建立 Socket 連接必須需要底層 TCP/IP 協(xié)議來(lái)建立 TCP 連接。

3.2.2、建立通信鏈路

我們知道網(wǎng)絡(luò)層使用的 IP 協(xié)議可以幫助我們根據(jù) IP 地址來(lái)找到目標(biāo)主機(jī),但是一臺(tái)主機(jī)上可能運(yùn)行著多個(gè)應(yīng)用程序,如何才能與指定的應(yīng)用程序通信呢?

這個(gè)時(shí)候需要通過(guò) TCP 或 UPD 協(xié)議,也就是指定對(duì)應(yīng)的端口號(hào)。

通過(guò) IP + 端口號(hào),就可以創(chuàng)建一個(gè)代表唯一一個(gè)主機(jī)上的一個(gè)應(yīng)用程序的通信鏈路了,創(chuàng)建后的通信鏈路我們稱(chēng)它為 Socket 實(shí)例。

以 TCP 協(xié)議為例,為了準(zhǔn)確無(wú)誤地把數(shù)據(jù)送達(dá)目標(biāo)處,TCP 協(xié)議采用了三次握手策略,如下圖:

圖片

其中,SYN 全稱(chēng)為 Synchronize Sequence Numbers,表示同步序列編號(hào),是 TCP/IP 建立連接時(shí)使用的握手信號(hào)。

ACK 全稱(chēng)為 Acknowledge character,即確認(rèn)字符,表示發(fā)來(lái)的數(shù)據(jù)已確認(rèn)接收無(wú)誤。

在客戶機(jī)和客戶機(jī)之間建立正常的 TCP 網(wǎng)絡(luò)連接時(shí),發(fā)送端首先發(fā)出一個(gè) SYN 消息,接收端使用 SYN + ACK 應(yīng)答表示接收到了這個(gè)消息,最后發(fā)送端再以 ACK 消息響應(yīng)。

整體流程如下:

  • 發(fā)送端 –(發(fā)送帶有 SYN 標(biāo)志的數(shù)據(jù)包 )–> 接受端(第一次握手);
  • 接受端 –(發(fā)送帶有 SYN + ACK 標(biāo)志的數(shù)據(jù)包)–> 發(fā)送端(第二次握手);
  • 發(fā)送端 –(發(fā)送帶有 ACK 標(biāo)志的數(shù)據(jù)包) –> 接受端(第三次握手);

完成三次握手之后,發(fā)送端和接收端之間建立起可靠的 TCP 連接,客戶端應(yīng)用程序與服務(wù)器應(yīng)用程序就可以開(kāi)始傳送數(shù)據(jù)了。

3.2.3、傳輸數(shù)據(jù)

當(dāng)客戶端要與服務(wù)端通信時(shí),客戶端首先要?jiǎng)?chuàng)建一個(gè) Socket 實(shí)例,也就是指定目標(biāo)服務(wù)器的 IP 和端口。

默認(rèn)操作系統(tǒng)將為這個(gè) Socket 實(shí)例分配一個(gè)沒(méi)有被使用的本地端口號(hào),并創(chuàng)建一個(gè)包含本地、遠(yuǎn)程地址和端口號(hào)的套接字?jǐn)?shù)據(jù)結(jié)構(gòu),這個(gè)數(shù)據(jù)結(jié)構(gòu)將一直保存在系統(tǒng)中直到這個(gè)連接關(guān)閉。

  • 客戶端簡(jiǎn)單示例
public static void main(String[] args) throws IOException {
    //通過(guò)IP和端口與服務(wù)端建立連接
    Socket socket =new Socket("127.0.0.1",8080);
    //將字符流轉(zhuǎn)化成字節(jié)流,并輸出
    BufferedWriter bufferedWriter =new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    String str="Hello,我是客戶端!";
    bufferedWriter.write(str);
    bufferedWriter.flush();
    bufferedWriter.close();
}
  • 服務(wù)端簡(jiǎn)單示例
public static void main(String[] args) throws Exception {
    //初始化服務(wù)端socket并且綁定 8080 端口
    ServerSocket serverSocket = new ServerSocket(8080);
    //循環(huán)監(jiān)聽(tīng)所有連接的客戶端請(qǐng)求
    while (true){
        try {
            //等待客戶端的連接
            Socket socket = serverSocket.accept();
            //將字節(jié)流轉(zhuǎn)化成字符流,讀取客戶端輸入的內(nèi)容
            BufferedReader bufferedReader =new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //讀取一行數(shù)據(jù)
            String str = bufferedReader.readLine();
            //輸出打印
            System.out.println("服務(wù)端收到客戶端發(fā)送的信息:" + str);
        } catch (Exception e) {
        }
    }
}

我們先啟動(dòng)服務(wù)端程序,再運(yùn)行客戶端,服務(wù)端收到客戶端發(fā)送的信息,打印結(jié)果如下:

服務(wù)端收到客戶端發(fā)送的信息:Hello,我是客戶端!

注意,客戶端只有與服務(wù)端建立三次握手成功之后,才會(huì)發(fā)送數(shù)據(jù),而 TCP/IP 握手過(guò)程,底層操作系統(tǒng)已經(jīng)幫我們實(shí)現(xiàn)了!

當(dāng)連接已經(jīng)建立成功,服務(wù)端和客戶端都會(huì)擁有一個(gè)Socket實(shí)例,每個(gè)Socket實(shí)例都有一個(gè)InputStream和OutputStream,正如我們前面所說(shuō)的,網(wǎng)絡(luò) I/O 都是以字節(jié)流傳輸?shù)?,Socket正是通過(guò)這兩個(gè)對(duì)象來(lái)交換數(shù)據(jù)。

當(dāng)Socket對(duì)象創(chuàng)建時(shí),操作系統(tǒng)同時(shí)將會(huì)為InputStream和OutputStream分別分配一定大小的緩沖區(qū),數(shù)據(jù)的寫(xiě)入和讀取都是通過(guò)這個(gè)緩存區(qū)完成的。

發(fā)送端將數(shù)據(jù)寫(xiě)到OutputStream對(duì)應(yīng)的SendQ隊(duì)列中,當(dāng)隊(duì)列填滿時(shí),數(shù)據(jù)將被發(fā)送到另一端InputStream的RecvQ隊(duì)列中,如果這時(shí)RecvQ已經(jīng)滿了,那么OutputStream的write方法將會(huì)阻塞直到RecvQ隊(duì)列有足夠的空間容納SendQ發(fā)送的數(shù)據(jù)。

值得特別注意的是,緩存區(qū)的大小以及寫(xiě)入端的速度和讀取端的速度非常影響這個(gè)連接的數(shù)據(jù)傳輸效率,由于可能會(huì)發(fā)生阻塞,所以網(wǎng)絡(luò) I/O 和磁盤(pán) I/O 在數(shù)據(jù)的寫(xiě)入和讀取還要有一個(gè)協(xié)調(diào)的過(guò)程,如果兩邊同時(shí)傳送數(shù)據(jù),可能會(huì)產(chǎn)生死鎖的問(wèn)題。

如何提高網(wǎng)絡(luò) IO 傳輸效率、保證數(shù)據(jù)傳輸?shù)目煽浚@個(gè)我們后面單獨(dú)開(kāi)篇進(jìn)行講解。

四、小結(jié)

本文闡述的內(nèi)容較多,整合了很多有用的信息,從 Java 基本的 I/O 類(lèi)庫(kù)結(jié)構(gòu)開(kāi)始說(shuō)起,主要介紹了 IO 的傳輸格式和傳輸方式,包括字節(jié)流和字符流接口相關(guān)的分類(lèi)介紹,以及磁盤(pán) I/O 和網(wǎng)絡(luò) I/O 的基本工作方式。

五、參考

1、https://developer.ibm.com/zh/articles/j-lo-javaio/

責(zé)任編輯:武曉燕 來(lái)源: Java極客技術(shù)
相關(guān)推薦

2023-02-28 23:04:15

2021-05-09 09:06:24

Python批處理命令

2018-04-09 16:35:10

數(shù)據(jù)庫(kù)MySQLInnoDB

2015-10-22 14:32:44

微服務(wù)PaaS應(yīng)用開(kāi)發(fā)

2019-11-14 05:22:41

Javascript語(yǔ)言this

2023-04-20 08:00:00

ES搜索引擎MySQL

2021-05-20 06:57:16

RabbitMQ開(kāi)源消息

2021-07-03 08:04:10

io_uringNode.js異步IO

2023-05-05 06:39:52

Java工廠設(shè)計(jì)模式

2022-07-07 12:01:43

ATTCALDERA框架

2021-06-16 08:28:25

unary 方法函數(shù)技術(shù)

2021-05-17 05:51:31

KubeBuilderOperator測(cè)試

2021-05-18 05:40:27

kubebuilderwebhook進(jìn)階

2025-01-17 07:00:00

2021-05-12 06:18:19

KubeBuilderOperatork8s

2022-03-10 08:31:51

REST接口規(guī)范設(shè)計(jì)Restful架構(gòu)

2022-02-24 07:56:42

開(kāi)發(fā)Viteesbuild

2020-10-23 07:56:04

Java中的IO流

2022-03-11 10:21:30

IO系統(tǒng)日志

2024-06-25 08:18:55

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)