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

Java的IO模型、Netty原理解析

 更新時(shí)間:2025年03月31日 08:46:17   作者:卷福同學(xué)  
Java的I/O是以流的方式進(jìn)行數(shù)據(jù)輸入輸出的,Java的類庫涉及很多領(lǐng)域的IO內(nèi)容:標(biāo)準(zhǔn)的輸入輸出,文件的操作、網(wǎng)絡(luò)上的數(shù)據(jù)傳輸流、字符串流、對(duì)象流等,這篇文章主要介紹了Java的IO模型、Netty原理詳解,需要的朋友可以參考下

1.什么是IO

雖然作為Java開發(fā)程序員,很多都聽過IO、NIO這些,但是很多人都沒深入去了解這些內(nèi)容。

  • Java的I/O是以流的方式進(jìn)行數(shù)據(jù)輸入輸出的,Java的類庫涉及很多領(lǐng)域的IO內(nèi)容:標(biāo)準(zhǔn)的輸入輸出,文件的操作、網(wǎng)絡(luò)上的數(shù)據(jù)傳輸流、字符串流、對(duì)象流等

2.同步與異步、阻塞與非阻塞

  • 同步:一個(gè)任務(wù)完成之前不能做其他操作,必須等待。
  • 異步:一個(gè)任務(wù)完成之前,可以進(jìn)行其他操作
  • 阻塞:相對(duì)于CPU來說,掛起當(dāng)前線程,不能做其他操作只能等待
  • 非阻塞:CPU無需掛起當(dāng)前線程,可以執(zhí)行其他操作

3.三種IO模型

BIO(Blocking I/O)

同步并阻塞模式,調(diào)用方在發(fā)起IO操作時(shí)會(huì)被阻塞,直到操作完成才能繼續(xù)執(zhí)行,適用于連接數(shù)較少的場景。

例如:服務(wù)端通過ServerSocket監(jiān)聽端口,accept()阻塞等待客戶端連接。

優(yōu)缺點(diǎn):

  • 優(yōu)點(diǎn):實(shí)現(xiàn)簡單
  • 缺點(diǎn):線程資源開銷大,連接數(shù)多時(shí),每個(gè)線程都要占用CPU資源,容易出現(xiàn)性能瓶頸

適用于低并發(fā)、短連接的場景,如傳統(tǒng)的HTTP服務(wù)

NIO(Non-blocking I/O)

同步非阻塞模型,客戶端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到Selector多路復(fù)用器上,服務(wù)器端通過Selector管理多個(gè)通道Channel,Selector會(huì)輪詢這些連接,當(dāng)輪詢到連接上有IO活動(dòng)就進(jìn)行處理。

NIO基于 Channel 和 Buffer 進(jìn)行操作,數(shù)據(jù)總是從通道讀取到緩沖區(qū)或者從緩沖區(qū)寫入到通道。Selector 用于監(jiān)聽多個(gè)通道上的事件(比如收到連接請(qǐng)求、數(shù)據(jù)達(dá)到等等),因此使用單個(gè)線程就可以監(jiān)聽多個(gè)客戶端通道。

IO多路復(fù)用:一個(gè)線程可對(duì)應(yīng)多個(gè)連接,不用為每個(gè)連接都創(chuàng)建一個(gè)線程

核心組件:

  • Channel:雙向通信通道(如SocketChannel),數(shù)據(jù)可流入流出
  • Buffer:數(shù)據(jù)緩沖區(qū),是雙向的,可讀可寫
  • Selector:一個(gè)Selector對(duì)應(yīng)一個(gè)線程,一個(gè)Selector上可注冊(cè)多個(gè)Channel,并輪詢多個(gè)Channel的就緒事件

優(yōu)缺點(diǎn):

  • 可以減少線程數(shù)量,降低線程切換的開銷,適用于需要處理大量并發(fā)連接的場景
  • 缺點(diǎn):實(shí)現(xiàn)復(fù)雜度高

使用于高并發(fā)、長連接的場景,如即時(shí)通訊場景

AIO(Asynchronous I/O)

異步非阻塞模型,基于事件回調(diào)或Future機(jī)制

  • 調(diào)用方發(fā)起IO請(qǐng)求后,無需等待操作完成,可繼續(xù)執(zhí)行其他任務(wù)。操作系統(tǒng)在IO操作完成后,通過回調(diào)或事件通知的方式告知調(diào)用方
  • Java中AsynchronousSocketChannel是AIO的代表類,通過回調(diào)函數(shù)處理讀寫操作完成后的結(jié)果

優(yōu)缺點(diǎn):

  • IO密集型的應(yīng)用,AIO提供更高的并發(fā)和低延遲,因?yàn)檎{(diào)用方在等待IO時(shí)不會(huì)被阻塞
  • 缺點(diǎn):實(shí)現(xiàn)復(fù)雜

適用于高吞吐、低延遲的場景,如日志批量寫入

4.什么是Netty

說起Java的IO模型,繞不開的就是Netty框架了,那什么是Netty,為什么Netty的性能這么高呢?

  • Netty是由JBOSS提供的一個(gè)Java開源框架。提供異步的、事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用程序框架和工具,用以快速開發(fā)高性能、高可靠性的網(wǎng)絡(luò)服務(wù)器
  • Netty的原理就是NIO,是基于NIO的完美封裝

很多中間件的底層通信框架用的都是它,比如:RocketMQ、Dubbo、Elasticsearch

4.1 Netty的核心要點(diǎn)

核心特點(diǎn):

  • 高并發(fā):通過多路復(fù)用Selector實(shí)現(xiàn)單線程管理大量連接,減少線程開銷
  • 傳輸快:零拷貝技術(shù),減少內(nèi)存拷貝次數(shù)
  • 封裝性:簡化NIO的復(fù)雜API,提供鏈?zhǔn)教幚恚–hannelPipeline)和可擴(kuò)展的編解碼能力(如Protobuf支持)

高性能的核心原因:

  • 主從Reactor線程模型,無鎖化設(shè)計(jì),減少線程競爭
  • 零拷貝技術(shù),堆外內(nèi)存直接操作
  • 高效內(nèi)存管理,對(duì)象池技術(shù),預(yù)分配內(nèi)存塊并復(fù)用,對(duì)象復(fù)用機(jī)制
  • 基于Selector的I/O多路復(fù)用,異步事件驅(qū)動(dòng)機(jī)制
  • Selector空輪詢問題修復(fù)

4.2 零拷貝技術(shù)

Netty的零拷貝體現(xiàn)在操作數(shù)據(jù)時(shí), 不需要將數(shù)據(jù) buffer從 一個(gè)內(nèi)存區(qū)域拷貝到另一個(gè)內(nèi)存區(qū)域。少了一次內(nèi)存的拷貝,CPU 效率就得到的提升。

4.2.1 Linux系統(tǒng)的文件從本地磁盤發(fā)送到網(wǎng)絡(luò)中的零拷貝技術(shù)

  • 內(nèi)核緩沖區(qū)是 Linux 系統(tǒng)的 Page Cahe。為了加快磁盤的 IO,Linux 系統(tǒng)會(huì)把磁盤上的數(shù)據(jù)以 Page 為單位緩存在操作系統(tǒng)的內(nèi)存里
  • 內(nèi)核緩沖區(qū)到 Socket 緩沖區(qū)之間并沒有做數(shù)據(jù)的拷貝,只是一個(gè)地址的映射,底層的網(wǎng)卡驅(qū)動(dòng)程序要讀取數(shù)據(jù)并發(fā)送到網(wǎng)絡(luò)上的時(shí)候,看似讀取的是 Socket 的緩沖區(qū)中的數(shù)據(jù),其實(shí)直接讀的是內(nèi)核緩沖區(qū)中的數(shù)據(jù)。
  • 零拷貝中所謂的“零”指的是內(nèi)存中數(shù)據(jù)拷貝的次數(shù)為 0

4.2.2 Netty零拷貝技術(shù)

  • 使用了堆外內(nèi)存進(jìn)行Socket讀寫,避免JVM堆內(nèi)存到堆外內(nèi)存的數(shù)據(jù)拷貝
  • 提供了CompositeByteBuf合并對(duì)象,可以組合多個(gè)Buffer對(duì)象合并成一個(gè)邏輯上的對(duì)象,用戶可以像操作一個(gè)Buffer那樣對(duì)組合Buffer進(jìn)行操作,避免傳統(tǒng)內(nèi)存拷貝合并
  • 文件傳輸使用FileRegion,封裝FileChannel#transferTo()方法,將文件緩沖區(qū)的內(nèi)容直接傳輸?shù)侥繕?biāo)Channel,避免內(nèi)核緩沖區(qū)和用戶態(tài)緩沖區(qū)間的數(shù)據(jù)拷貝

4.2.3 Netty和操作系統(tǒng)的零拷貝的區(qū)別?

Netty 的 Zero-copy 完全是在用戶態(tài)(Java 應(yīng)用層)的, 更多的偏向于優(yōu)化數(shù)據(jù)操作。而在 OS 層面上的 Zero-copy 通常指避免在用戶態(tài)(User-space)與內(nèi)核態(tài)(Kernel-space)之間來回拷貝數(shù)據(jù)

4.3 Reactor模式

  • 基于IO多路復(fù)用技術(shù),多個(gè)連接共用一個(gè)多路復(fù)用器,程序只需要阻塞等待多路復(fù)用器即可
  • 基于線程池技術(shù)復(fù)用線程資源,程序?qū)⑦B接上的任務(wù)分配給線程池中線程處理,不用為每個(gè)連接單獨(dú)創(chuàng)建線程
  • Reactor是圖中的ServiceHandler,在一個(gè)單獨(dú)線程中運(yùn)行,負(fù)責(zé)監(jiān)聽和分發(fā)事件

Reactor可以分為單Reactor單線程模式、單Reactor多線程模型,主從Reactor多線程模型

4.3.1 單Reactor單線程模式

  • Reactor通過select監(jiān)聽客戶端請(qǐng)求事件,收到事件后通過dispatch分發(fā)

該模式簡單,所有操作都由1個(gè)IO線程處理,缺點(diǎn)是存在性能瓶頸,只有1個(gè)線程工作,無法發(fā)揮多核CPU的性能。

4.3.2 單Reactor多線程模式

  • Reactor主線程負(fù)責(zé)接收建立連接事件和后續(xù)的IO處理,Worker線程池處理具體業(yè)務(wù)邏輯

充分發(fā)揮了多核CPU的處理能力,缺點(diǎn)是用一個(gè)線程接收事件和響應(yīng),高并發(fā)時(shí)仍然會(huì)有性能瓶頸

4.3.3 主從Reactor多線程模式

  • Reactor主線程負(fù)責(zé)通過select監(jiān)聽連接事件,通過acceptor處理連接事件
  • Reactor從線程負(fù)責(zé)處理建立連接后的IO處理事件
  • worker線程池負(fù)責(zé)業(yè)務(wù)邏輯處理,并將結(jié)果返回給Handler

該模式優(yōu)點(diǎn)是主從線程分工明確,能應(yīng)對(duì)更高的并發(fā)。缺點(diǎn)是編程復(fù)雜度較高。

應(yīng)用該模式的中間件有:Dubbo、RocketMQ、Zookeeper

小結(jié)

Reactor模式的核心在于用一個(gè)或少量線程來監(jiān)聽多個(gè)連接上的事件,根據(jù)事件類型分發(fā)調(diào)用相應(yīng)處理邏輯,從而避免為每個(gè)連接都分配一個(gè)線程

4.4 Netty的線程模型

  • BossGroup:boss線程組,負(fù)責(zé)接收客戶端的連接請(qǐng)求,連接來了之后,將其注冊(cè)到Worker線程組的NioEventLoop中
  • WorkerGroup:Worker線程組,每個(gè)線程都是一個(gè)NioEventLoop,負(fù)責(zé)和處理一個(gè)或多個(gè)Channel的I/O讀寫操作。處理邏輯通常是通過ChannelPipeline中的各個(gè)ChannelHandler來完成
  • 業(yè)務(wù)線程組(可選):還可以引入一個(gè)業(yè)務(wù)線程組來處理業(yè)務(wù)邏輯,避免阻塞Worker線程

簡單理解:Boss線程是老板,Worker線程是員工,老板負(fù)責(zé)接收處理的事件請(qǐng)求,Worker負(fù)責(zé)工作,處理請(qǐng)求的I/O事件,并交給對(duì)應(yīng)的Handler處理

本質(zhì)是將線程連接和具體的業(yè)務(wù)處理分開

5.多路復(fù)用I/O的3種機(jī)制

5.1 select

這三種都是操作系統(tǒng)中的多路復(fù)用I/O機(jī)制

輪詢機(jī)制:select使用一個(gè)固定大小的位圖來表示文件描述符集,將文件描述符的狀態(tài)(如可讀、可寫)存儲(chǔ)在一個(gè)數(shù)組中,調(diào)用select時(shí),每次需將完整的位圖從用戶空間拷貝到內(nèi)核空間,內(nèi)核遍歷所有描述符,檢查就緒狀態(tài)

局限:

  • 文件描述符限制通常為1024,限制了并發(fā)處理數(shù)
  • 性能低:搞并發(fā)場景,每次都要遍歷整個(gè)位圖,性能開銷大,時(shí)間負(fù)責(zé)度為O(N)

5.2 poll

poll使用了動(dòng)態(tài)數(shù)組來替代位圖,使用pollfd結(jié)構(gòu)數(shù)組存儲(chǔ)文件描述符和事件,無數(shù)量限制

工作機(jī)制:每次調(diào)用時(shí)仍然需要遍歷所有描述符,即使只有少量描述符修改了,仍然要檢查整個(gè)數(shù)組,時(shí)間復(fù)雜度為O(N)

5.3 epoll

1)事件驅(qū)動(dòng)模型:epoll使用紅黑樹來存儲(chǔ)和管理注冊(cè)的文件描述符,使用就緒事件鏈表來存儲(chǔ)觸發(fā)的事件。當(dāng)某個(gè)文件描述符上的事件就緒時(shí),epoll會(huì)將該文件描述符添加到就緒鏈表中。

2)觸發(fā)模式:支持水平觸發(fā)(LT)和邊緣觸發(fā)(ET),ET模式下事件僅通知一次

  • 水平觸發(fā)(Level Triggered),默認(rèn)模式,只要文件描述符上有未處理的數(shù)據(jù),每次調(diào)用epoll_wait都會(huì)返回該文件描述符
  • 邊緣觸發(fā)(Edge Triggered),僅在狀態(tài)發(fā)生變化時(shí)通知一次,減少重復(fù)事件的通知次數(shù)

3)工作流程

  • epoll_create創(chuàng)建實(shí)例:分配相應(yīng)數(shù)據(jù)結(jié)構(gòu),并返回一個(gè)epoll文件描述符。內(nèi)核分配一棵紅黑樹管理文件描述符,以及一個(gè)就緒事件的鏈表
  • epoll_ctl注冊(cè)、修改、刪除事件:epoll_ctl是用于管理文件描述符與事件關(guān)系的接口
  • epoll_wait等待事件:epoll會(huì)檢查就緒事件鏈表,將鏈表中所有就緒的文件描述符返回給用戶空間。epoll_wait高效體現(xiàn)在它返回的是已經(jīng)發(fā)生事件的文件描述符,而不是遍歷所有注冊(cè)的文件描述符

優(yōu)點(diǎn)是時(shí)間復(fù)雜度O(1),僅處理活躍連接,性能和連接數(shù)無關(guān)

4)零拷貝機(jī)制

  • 通過內(nèi)存映射mmap減少了在內(nèi)核和用戶空間之間的數(shù)據(jù)復(fù)制,進(jìn)一步提高了性能

總結(jié):epoll每次只傳遞發(fā)生的事件,不需要傳遞所有文件描述符,所以提高了效率

6. Netty如何解決JDK NIO空輪詢bug的?

Java NIO在Linux系統(tǒng)下默認(rèn)是epoll機(jī)制,理論上無客戶端連接時(shí)Selector.select()方法是會(huì)阻塞的。

發(fā)生空輪詢bug表現(xiàn)時(shí),即時(shí)select輪詢事件返回?cái)?shù)量是0,Select.select()方法也不會(huì)被阻塞,NIO就會(huì)一直處于while死循環(huán)中,不斷向CPU申請(qǐng)資源導(dǎo)致CPU 100%

底層原因

  • Linux內(nèi)核在某些情況下會(huì)錯(cuò)誤地將Selector的EPOLLUP(連接掛起)和EPOLLERR(錯(cuò)誤)事件標(biāo)記為就緒狀態(tài),JDK中的NIO實(shí)現(xiàn)未正確處理這些事件,導(dǎo)致select()方法誤判事件存在而提前返回

6.1 Netty的解決方式

Netty并沒有解決這個(gè)bug,而是繞開了這個(gè)錯(cuò)誤,具體如下:

1)統(tǒng)計(jì)空輪詢次數(shù):通過selectCnt計(jì)數(shù)器來統(tǒng)計(jì)連續(xù)空輪詢的次數(shù),每次執(zhí)行Selector.select()方法后,如果發(fā)現(xiàn)沒有IO事件,selectCnt就會(huì)遞增

2)設(shè)置閾值:定義了一個(gè)閾值,默認(rèn)為512,當(dāng)空輪詢達(dá)到這個(gè)閾值時(shí),Netty就會(huì)觸發(fā)重建Selector的操作

3)重建Selector:Netty新建一個(gè)Selector,并將所有注冊(cè)的Channel從舊的Selector轉(zhuǎn)移到新的Selector上,過程涉及取消舊Selector上的注冊(cè),以及新Selector上重新注冊(cè)

4)關(guān)閉舊的Selector:重建Selector并將Channel重新注冊(cè)后,Netty關(guān)閉舊的Selector

總結(jié):通過SelectCnt統(tǒng)計(jì)沒有IO事件的次數(shù),來判斷當(dāng)前是否發(fā)生了空輪詢,如果發(fā)生了,就重建一個(gè)Selector來替換之前出問題的Selector

核心代碼如下:

long time = System.nanoTime();
//調(diào)用select方法,阻塞時(shí)間為上面算出的最近一個(gè)將要超時(shí)的定時(shí)任務(wù)時(shí)間
int selectedKeys = selector.select(timeoutMillis);
//計(jì)數(shù)器加1
++selectCnt;
if (selectedKeys != 0 || oldWakenUp || this.wakenUp.get() || this.hasTasks() || this.hasScheduledTasks()) {
   //進(jìn)入這個(gè)分支,表示正常場景     
   //selectedKeys != 0: selectedKeys個(gè)數(shù)不為0, 有io事件發(fā)生
   //oldWakenUp:表示進(jìn)來時(shí),已經(jīng)有其他地方對(duì)selector進(jìn)行了喚醒操作
   //wakenUp.get():也表示selector被喚醒
   //hasTasks() || hasScheduledTasks():表示有任務(wù)或定時(shí)任務(wù)要執(zhí)行
   //發(fā)生以上幾種情況任一種則直接返回
   break;
}
//此處的邏輯就是: 當(dāng)前時(shí)間 - 循環(huán)開始時(shí)間 >= 定時(shí)select的時(shí)間timeoutMillis,說明已經(jīng)執(zhí)行過一次阻塞select(), 有效的select
if (time - TimeUnit.MILLISECONDS.toNanos(timeoutMillis) >= currentTimeNanos) {
   //進(jìn)入這個(gè)分支,表示超時(shí),屬于正常的場景
   //說明發(fā)生過一次阻塞式輪詢, 并且超時(shí)
   selectCnt = 1;
} else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
   //進(jìn)入這個(gè)分支,表示沒有超時(shí),同時(shí) selectedKeys==0
   //屬于異常場景
   //表示啟用了select bug修復(fù)機(jī)制,
   //即配置的io.netty.selectorAutoRebuildThreshold
   //參數(shù)大于3,且上面select方法提前返回次數(shù)已經(jīng)大于
   //配置的閾值,則會(huì)觸發(fā)selector重建
   //進(jìn)行selector重建
   //重建完之后,嘗試調(diào)用非阻塞版本select一次,并直接返回
   selector = this.selectRebuildSelector(selectCnt);
   selectCnt = 1;
   break;
}
currentTimeNanos = time;

到此這篇關(guān)于Java的IO模型、Netty原理詳解的文章就介紹到這了,更多相關(guān)Java IO模型、Netty原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java通過反射注解賦值的方法詳解

    Java通過反射注解賦值的方法詳解

    這篇文章主要為大家詳細(xì)介紹了Java語言如何通過反射實(shí)現(xiàn)注解賦值,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下
    2022-07-07
  • java集成開發(fā)SpringBoot生成接口文檔示例實(shí)現(xiàn)

    java集成開發(fā)SpringBoot生成接口文檔示例實(shí)現(xiàn)

    這篇文章主要為大家介紹了java集成開發(fā)SpringBoot如何生成接口文檔的示例實(shí)現(xiàn)過程,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-10-10
  • java實(shí)現(xiàn)動(dòng)態(tài)上傳多個(gè)文件并解決文件重名問題

    java實(shí)現(xiàn)動(dòng)態(tài)上傳多個(gè)文件并解決文件重名問題

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)動(dòng)態(tài)上傳多個(gè)文件,并解決文件重名問題的方法,感興趣的小伙伴們可以參考一下
    2016-03-03
  • 一文詳解Java項(xiàng)目中如何優(yōu)雅的使用枚舉類型

    一文詳解Java項(xiàng)目中如何優(yōu)雅的使用枚舉類型

    枚舉類型在開發(fā)中是很常見的,有非常多的應(yīng)用場景,這篇文章我們就來學(xué)習(xí)一下項(xiàng)目中如何優(yōu)雅的使用枚舉類型,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-03-03
  • Triple協(xié)議支持Java異?;貍髟O(shè)計(jì)實(shí)現(xiàn)詳解

    Triple協(xié)議支持Java異?;貍髟O(shè)計(jì)實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了Triple協(xié)議支持Java異?;貍髟O(shè)計(jì)實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • Java中@JSONField和@JsonProperty注解的用法及區(qū)別詳解

    Java中@JSONField和@JsonProperty注解的用法及區(qū)別詳解

    @JsonProperty和@JSONField注解都是為了解決obj轉(zhuǎn)json字符串的時(shí)候,將java bean的屬性名替換成目標(biāo)屬性名,下面這篇文章主要給大家介紹了關(guān)于Java中@JSONField和@JsonProperty注解的用法及區(qū)別的相關(guān)資料,需要的朋友可以參考下
    2024-06-06
  • Java中Future接口詳解

    Java中Future接口詳解

    這篇文章主要介紹了Java中Future接口詳解,本文通過案例給大家詳細(xì)講解了Java中Future接口,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-04-04
  • java切分字符串的2種方法實(shí)例

    java切分字符串的2種方法實(shí)例

    在我們?nèi)粘9ぷ髦薪?jīng)常遇到截取字符串的需求,下面這篇文章主要給大家介紹了關(guān)于java切分字符串的2種方法,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • Java使用POI-TL實(shí)現(xiàn)生成有個(gè)性的簡歷

    Java使用POI-TL實(shí)現(xiàn)生成有個(gè)性的簡歷

    POI-TL?是一個(gè)基于?Apache?POI?的?Java?庫,專注于在?Microsoft?Word?文檔(.docx?格式)中進(jìn)行模板填充和動(dòng)態(tài)內(nèi)容生成,下面我們看看如何使用POI-TL生成有個(gè)性的簡歷吧
    2024-11-11
  • 基于java Files類和Paths類的用法(詳解)

    基于java Files類和Paths類的用法(詳解)

    下面小編就為大家分享一篇基于java Files類和Paths類的用法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2017-11-11

最新評(píng)論