Java中的IO讀寫(xiě)原理詳解
1. Java IO讀寫(xiě)原理
無(wú)論是Socket的讀寫(xiě)還是文件的讀寫(xiě),在Java層面的應(yīng)用開(kāi)發(fā)或者是linux系統(tǒng)底層開(kāi)發(fā),都屬于輸入input和輸出output的處理,簡(jiǎn)稱(chēng)為IO讀寫(xiě)。
在原理上和處理流程上,都是一致的。區(qū)別在于參數(shù)的不同。
用戶(hù)程序進(jìn)行IO的讀寫(xiě),基本上會(huì)用到read&write兩大系統(tǒng)調(diào)用。
可能不同操作系統(tǒng),名稱(chēng)不完全一樣,但是功能是一樣的。
先強(qiáng)調(diào)一個(gè)基礎(chǔ)知識(shí):read系統(tǒng)調(diào)用,并不是把數(shù)據(jù)直接從物理設(shè)備,讀數(shù)據(jù)到內(nèi)存。
write系統(tǒng)調(diào)用,也不是直接把數(shù)據(jù),寫(xiě)入到物理設(shè)備。
read系統(tǒng)調(diào)用,是把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到進(jìn)程緩沖區(qū);
而write系統(tǒng)調(diào)用,是把數(shù)據(jù)從進(jìn)程緩沖區(qū)復(fù)制到內(nèi)核緩沖區(qū)。
這個(gè)兩個(gè)系統(tǒng)調(diào)用,都不負(fù)責(zé)數(shù)據(jù)在內(nèi)核緩沖區(qū)和磁盤(pán)之間的交換。
底層的讀寫(xiě)交換,是由操作系統(tǒng)kernel內(nèi)核完成的。
1.1. 內(nèi)核緩沖與進(jìn)程緩沖區(qū)
緩沖區(qū)的目的,是為了減少頻繁的系統(tǒng)IO調(diào)用。大家都知道,系統(tǒng)調(diào)用需要保存之前的進(jìn)程數(shù)據(jù)和狀態(tài)等信息,而結(jié)束調(diào)用之后回來(lái)還需要恢復(fù)之前的信息
為了減少這種損耗時(shí)間、也損耗性能的系統(tǒng)調(diào)用,于是出現(xiàn)了緩沖區(qū)。
有了緩沖區(qū),操作系統(tǒng)使用read函數(shù)把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到進(jìn)程緩沖區(qū),write把數(shù)據(jù)從進(jìn)程緩沖區(qū)復(fù)制到內(nèi)核緩沖區(qū)中。
等待緩沖區(qū)達(dá)到一定數(shù)量的時(shí)候,再進(jìn)行IO的調(diào)用,提升性能。至于什么時(shí)候讀取和存儲(chǔ)則由內(nèi)核來(lái)決定,用戶(hù)程序不需要關(guān)心。
在linux系統(tǒng)中,系統(tǒng)內(nèi)核也有個(gè)緩沖區(qū)叫做內(nèi)核緩沖區(qū)。每個(gè)進(jìn)程有自己獨(dú)立的緩沖區(qū),叫做進(jìn)程緩沖區(qū)。
所以,用戶(hù)程序的IO讀寫(xiě)程序,大多數(shù)情況下,并沒(méi)有進(jìn)行實(shí)際的IO操作,而是在讀寫(xiě)自己的進(jìn)程緩沖區(qū)。
1.2. java IO讀寫(xiě)的底層流程
用戶(hù)程序進(jìn)行IO的讀寫(xiě),基本上會(huì)用到系統(tǒng)調(diào)用read&write,read把數(shù)據(jù)從內(nèi)核緩沖區(qū)復(fù)制到進(jìn)程緩沖區(qū),write把數(shù)據(jù)從進(jìn)程緩沖區(qū)復(fù)制到內(nèi)核緩沖區(qū)
它們不等價(jià)于數(shù)據(jù)在內(nèi)核緩沖區(qū)和磁盤(pán)之間的交換。
首先看看一個(gè)典型Java 服務(wù)端處理網(wǎng)絡(luò)請(qǐng)求的典型過(guò)程:
(1)客戶(hù)端請(qǐng)求
Linux通過(guò)網(wǎng)卡,讀取客戶(hù)斷的請(qǐng)求數(shù)據(jù),將數(shù)據(jù)讀取到內(nèi)核緩沖區(qū)。
(2)獲取請(qǐng)求數(shù)據(jù)
服務(wù)器從內(nèi)核緩沖區(qū)讀取數(shù)據(jù)到Java進(jìn)程緩沖區(qū)。
(3)服務(wù)器端業(yè)務(wù)處理
Java服務(wù)端在自己的用戶(hù)空間中,處理客戶(hù)端的請(qǐng)求。
(4)服務(wù)器端返回?cái)?shù)據(jù)
Java服務(wù)端已構(gòu)建好的響應(yīng),從用戶(hù)緩沖區(qū)寫(xiě)入系統(tǒng)緩沖區(qū)。
(5)發(fā)送給客戶(hù)端
Linux內(nèi)核通過(guò)網(wǎng)絡(luò) I/O ,將內(nèi)核緩沖區(qū)中的數(shù)據(jù),寫(xiě)入網(wǎng)卡,網(wǎng)卡通過(guò)底層的通訊協(xié)議,會(huì)將數(shù)據(jù)發(fā)送給目標(biāo)客戶(hù)端。
1.2. 四種主要的IO模型
服務(wù)器端編程經(jīng)常需要構(gòu)造高性能的IO模型,常見(jiàn)的IO模型有四種:
同步阻塞IO(Blocking IO)
首先,解釋一下這里的阻塞與非阻塞:
阻塞IO,指的是需要內(nèi)核IO操作徹底完成后,才返回到用戶(hù)空間,執(zhí)行用戶(hù)的操作。阻塞指的是用戶(hù)空間程序的執(zhí)行狀態(tài),用戶(hù)空間程序需等到IO操作徹底完成。傳統(tǒng)的IO模型都是同步阻塞IO。在java中,默認(rèn)創(chuàng)建的socket都是阻塞的。
其次,解釋一下同步與異步:
同步IO,是一種用戶(hù)空間與內(nèi)核空間的調(diào)用發(fā)起方式。同步IO是指用戶(hù)空間線(xiàn)程是主動(dòng)發(fā)起IO請(qǐng)求的一方,內(nèi)核空間是被動(dòng)接受方。異步IO則反過(guò)來(lái),是指內(nèi)核kernel是主動(dòng)發(fā)起IO請(qǐng)求的一方,用戶(hù)線(xiàn)程是被動(dòng)接受方。
同步非阻塞IO(Non-blocking IO)
非阻塞IO,指的是用戶(hù)程序不需要等待內(nèi)核IO操作完成后,內(nèi)核立即返回給用戶(hù)一個(gè)狀態(tài)值,用戶(hù)空間無(wú)需等到內(nèi)核的IO操作徹底完成,可以立即返回用戶(hù)空間,執(zhí)行用戶(hù)的操作,處于非阻塞的狀態(tài)。
簡(jiǎn)單的說(shuō):阻塞是指用戶(hù)空間(調(diào)用線(xiàn)程)一直在等待,而且別的事情什么都不做;非阻塞是指用戶(hù)空間(調(diào)用線(xiàn)程)拿到狀態(tài)就返回,IO操作可以干就干,不可以干,就去干的事情。
非阻塞IO要求socket被設(shè)置為NONBLOCK。
強(qiáng)調(diào)一下,這里所說(shuō)的NIO(同步非阻塞IO)模型,并非Java的NIO(New IO)庫(kù)。
IO多路復(fù)用(IO Multiplexing)
即經(jīng)典的Reactor設(shè)計(jì)模式,有時(shí)也稱(chēng)為異步阻塞IO,Java中的Selector和Linux中的epoll都是這種模型。
異步IO(Asynchronous IO)
異步IO,指的是用戶(hù)空間與內(nèi)核空間的調(diào)用方式反過(guò)來(lái)。用戶(hù)空間線(xiàn)程是變成被動(dòng)接受的,內(nèi)核空間是主動(dòng)調(diào)用者。
這一點(diǎn),有點(diǎn)類(lèi)似于Java中比較典型的模式是回調(diào)模式,用戶(hù)空間線(xiàn)程向內(nèi)核空間注冊(cè)各種IO事件的回調(diào)函數(shù),由內(nèi)核去主動(dòng)調(diào)用。
1.3. 同步阻塞IO(Blocking IO)
在linux中的Java進(jìn)程中,默認(rèn)情況下所有的socket都是blocking IO。在阻塞式 I/O 模型中,應(yīng)用程序在從IO系統(tǒng)調(diào)用開(kāi)始,一直到到系統(tǒng)調(diào)用返回,這段時(shí)間是阻塞的。
返回成功后,應(yīng)用進(jìn)程開(kāi)始處理用戶(hù)空間的緩存數(shù)據(jù)。
舉個(gè)栗子,發(fā)起一個(gè)blocking socket的read讀操作系統(tǒng)調(diào)用,流程大概是這樣:
- 當(dāng)用戶(hù)線(xiàn)程調(diào)用了read系統(tǒng)調(diào)用,內(nèi)核(kernel)就開(kāi)始了IO的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)。很多時(shí)候,數(shù)據(jù)在一開(kāi)始還沒(méi)有到達(dá)(比如,還沒(méi)有收到一個(gè)完整的Socket數(shù)據(jù)包),這個(gè)時(shí)候kernel就要等待足夠的數(shù)據(jù)到來(lái)。
- 當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從kernel內(nèi)核緩沖區(qū),拷貝到用戶(hù)緩沖區(qū)(用戶(hù)內(nèi)存),然后kernel返回結(jié)果。
- 從開(kāi)始IO讀的read系統(tǒng)調(diào)用開(kāi)始,用戶(hù)線(xiàn)程就進(jìn)入阻塞狀態(tài)。一直到kernel返回結(jié)果后,用戶(hù)線(xiàn)程才解除block的狀態(tài),重新運(yùn)行起來(lái)。
所以,blocking IO的特點(diǎn)就是在內(nèi)核進(jìn)行IO執(zhí)行的兩個(gè)階段,用戶(hù)線(xiàn)程都被block了。
BIO的優(yōu)點(diǎn):
程序簡(jiǎn)單,在阻塞等待數(shù)據(jù)期間,用戶(hù)線(xiàn)程掛起。用戶(hù)線(xiàn)程基本不會(huì)占用 CPU 資源。
BIO的缺點(diǎn):
一般情況下,會(huì)為每個(gè)連接配套一條獨(dú)立的線(xiàn)程,或者說(shuō)一條線(xiàn)程維護(hù)一個(gè)連接成功的IO流的讀寫(xiě)。在并發(fā)量小的情況下,這個(gè)沒(méi)有什么問(wèn)題。但是,當(dāng)在高并發(fā)的場(chǎng)景下,需要大量的線(xiàn)程來(lái)維護(hù)大量的網(wǎng)絡(luò)連接,內(nèi)存、線(xiàn)程切換開(kāi)銷(xiāo)會(huì)非常巨大。因此,基本上,BIO模型在高并發(fā)場(chǎng)景下是不可用的。
1.4. 同步非阻塞NIO(None Blocking IO)
在linux系統(tǒng)下,可以通過(guò)設(shè)置socket使其變?yōu)閚on-blocking。NIO 模型中應(yīng)用程序在一旦開(kāi)始IO系統(tǒng)調(diào)用,會(huì)出現(xiàn)以下兩種情況:
(1)在內(nèi)核緩沖區(qū)沒(méi)有數(shù)據(jù)的情況下,系統(tǒng)調(diào)用會(huì)立即返回,返回一個(gè)調(diào)用失敗的信息。
(2)在內(nèi)核緩沖區(qū)有數(shù)據(jù)的情況下,是阻塞的,直到數(shù)據(jù)從內(nèi)核緩沖復(fù)制到用戶(hù)進(jìn)程緩沖。復(fù)制完成后,系統(tǒng)調(diào)用返回成功,應(yīng)用進(jìn)程開(kāi)始處理用戶(hù)空間的緩存數(shù)據(jù)。
舉個(gè)栗子。發(fā)起一個(gè)non-blocking socket的read讀操作系統(tǒng)調(diào)用,流程是這個(gè)樣子:
- 在內(nèi)核數(shù)據(jù)沒(méi)有準(zhǔn)備好的階段,用戶(hù)線(xiàn)程發(fā)起IO請(qǐng)求時(shí),立即返回。用戶(hù)線(xiàn)程需要不斷地發(fā)起IO系統(tǒng)調(diào)用。
- 內(nèi)核數(shù)據(jù)到達(dá)后,用戶(hù)線(xiàn)程發(fā)起系統(tǒng)調(diào)用,用戶(hù)線(xiàn)程阻塞。內(nèi)核開(kāi)始復(fù)制數(shù)據(jù)。它就會(huì)將數(shù)據(jù)從kernel內(nèi)核緩沖區(qū),拷貝到用戶(hù)緩沖區(qū)(用戶(hù)內(nèi)存),然后kernel返回結(jié)果。
- 用戶(hù)線(xiàn)程才解除block的狀態(tài),重新運(yùn)行起來(lái)。經(jīng)過(guò)多次的嘗試,用戶(hù)線(xiàn)程終于真正讀取到數(shù)據(jù),繼續(xù)執(zhí)行。
NIO的特點(diǎn):
應(yīng)用程序的線(xiàn)程需要不斷的進(jìn)行 I/O 系統(tǒng)調(diào)用,輪詢(xún)數(shù)據(jù)是否已經(jīng)準(zhǔn)備好,如果沒(méi)有準(zhǔn)備好,繼續(xù)輪詢(xún),直到完成系統(tǒng)調(diào)用為止。
NIO的優(yōu)點(diǎn):
每次發(fā)起的 IO 系統(tǒng)調(diào)用,在內(nèi)核的等待數(shù)據(jù)過(guò)程中可以立即返回。用戶(hù)線(xiàn)程不會(huì)阻塞,實(shí)時(shí)性較好。
NIO的缺點(diǎn):
需要不斷的重復(fù)發(fā)起IO系統(tǒng)調(diào)用,這種不斷的輪詢(xún),將會(huì)不斷地詢(xún)問(wèn)內(nèi)核,這將占用大量的 CPU 時(shí)間,系統(tǒng)資源利用率較低。
總之,NIO模型在高并發(fā)場(chǎng)景下,也是不可用的。一般 Web 服務(wù)器不使用這種 IO 模型。一般很少直接使用這種模型,而是在其他IO模型中使用非阻塞IO這一特性。java的實(shí)際開(kāi)發(fā)中,也不會(huì)涉及這種IO模型。
再次說(shuō)明,Java NIO(New IO) 不是IO模型中的NIO模型,而是另外的一種模型,叫做IO多路復(fù)用模型( IO multiplexing )。
1.5. IO多路復(fù)用模型(I/O multiplexing)
如何避免同步非阻塞NIO模型中輪詢(xún)等待的問(wèn)題呢?這就是IO多路復(fù)用模型。
IO多路復(fù)用模型,就是通過(guò)一種新的系統(tǒng)調(diào)用,一個(gè)進(jìn)程可以監(jiān)視多個(gè)文件描述符,一旦某個(gè)描述符就緒(一般是內(nèi)核緩沖區(qū)可讀/可寫(xiě)),內(nèi)核kernel能夠通知程序進(jìn)行相應(yīng)的IO系統(tǒng)調(diào)用。
目前支持IO多路復(fù)用的系統(tǒng)調(diào)用,有 select,epoll等等。select系統(tǒng)調(diào)用,是目前幾乎在所有的操作系統(tǒng)上都有支持,具有良好跨平臺(tái)特性。epoll是在linux 2.6內(nèi)核中提出的,是select系統(tǒng)調(diào)用的linux增強(qiáng)版本。
IO多路復(fù)用模型的基本原理就是select/epoll系統(tǒng)調(diào)用,單個(gè)線(xiàn)程不斷的輪詢(xún)select/epoll系統(tǒng)調(diào)用所負(fù)責(zé)的成百上千的socket連接,當(dāng)某個(gè)或者某些socket網(wǎng)絡(luò)連接有數(shù)據(jù)到達(dá)了,就返回這些可以讀寫(xiě)的連接。因此,好處也就顯而易見(jiàn)了——通過(guò)一次select/epoll系統(tǒng)調(diào)用,就查詢(xún)到到可以讀寫(xiě)的一個(gè)甚至是成百上千的網(wǎng)絡(luò)連接。
舉個(gè)栗子。發(fā)起一個(gè)多路復(fù)用IO的的read讀操作系統(tǒng)調(diào)用,流程是這個(gè)樣子:
在這種模式中,首先不是進(jìn)行read系統(tǒng)調(diào)動(dòng),而是進(jìn)行select/epoll系統(tǒng)調(diào)用。當(dāng)然,這里有一個(gè)前提,需要將目標(biāo)網(wǎng)絡(luò)連接,提前注冊(cè)到select/epoll的可查詢(xún)socket列表中。然后,才可以開(kāi)啟整個(gè)的IO多路復(fù)用模型的讀流程。
(1)進(jìn)行select/epoll系統(tǒng)調(diào)用,查詢(xún)可以讀的連接。kernel會(huì)查詢(xún)所有select的可查詢(xún)socket列表,當(dāng)任何一個(gè)socket中的數(shù)據(jù)準(zhǔn)備好了,select就會(huì)返回。
當(dāng)用戶(hù)進(jìn)程調(diào)用了select,那么整個(gè)線(xiàn)程會(huì)被block(阻塞掉)。
(2)用戶(hù)線(xiàn)程獲得了目標(biāo)連接后,發(fā)起read系統(tǒng)調(diào)用,用戶(hù)線(xiàn)程阻塞。內(nèi)核開(kāi)始復(fù)制數(shù)據(jù)。它就會(huì)將數(shù)據(jù)從kernel內(nèi)核緩沖區(qū),拷貝到用戶(hù)緩沖區(qū)(用戶(hù)內(nèi)存),然后kernel返回結(jié)果。
(3)用戶(hù)線(xiàn)程才解除block的狀態(tài),用戶(hù)線(xiàn)程終于真正讀取到數(shù)據(jù),繼續(xù)執(zhí)行。
多路復(fù)用IO的特點(diǎn):
IO多路復(fù)用模型,建立在操作系統(tǒng)kernel內(nèi)核能夠提供的多路分離系統(tǒng)調(diào)用select/epoll基礎(chǔ)之上的。多路復(fù)用IO需要用到兩個(gè)系統(tǒng)調(diào)用(system call), 一個(gè)select/epoll查詢(xún)調(diào)用,一個(gè)是IO的讀取調(diào)用。
和NIO模型相似,多路復(fù)用IO需要輪詢(xún)。負(fù)責(zé)select/epoll查詢(xún)調(diào)用的線(xiàn)程,需要不斷的進(jìn)行select/epoll輪詢(xún),查找出可以進(jìn)行IO操作的連接。
另外,多路復(fù)用IO模型與前面的NIO模型,是有關(guān)系的。對(duì)于每一個(gè)可以查詢(xún)的socket,一般都設(shè)置成為non-blocking模型。只是這一點(diǎn),對(duì)于用戶(hù)程序是透明的(不感知)。
多路復(fù)用IO的優(yōu)點(diǎn):
用select/epoll的優(yōu)勢(shì)在于,它可以同時(shí)處理成千上萬(wàn)個(gè)連接(connection)。與一條線(xiàn)程維護(hù)一個(gè)連接相比,I/O多路復(fù)用技術(shù)的最大優(yōu)勢(shì)是:系統(tǒng)不必創(chuàng)建線(xiàn)程,也不必維護(hù)這些線(xiàn)程,從而大大減小了系統(tǒng)的開(kāi)銷(xiāo)。
Java的NIO(new IO)技術(shù),使用的就是IO多路復(fù)用模型。在linux系統(tǒng)上,使用的是epoll系統(tǒng)調(diào)用。
多路復(fù)用IO的缺點(diǎn):
本質(zhì)上,select/epoll系統(tǒng)調(diào)用,屬于同步IO,也是阻塞IO。都需要在讀寫(xiě)事件就緒后,自己負(fù)責(zé)進(jìn)行讀寫(xiě),也就是說(shuō)這個(gè)讀寫(xiě)過(guò)程是阻塞的。
如何充分的解除線(xiàn)程的阻塞呢?那就是異步IO模型。
1.6. 異步IO模型(asynchronous IO)
如何進(jìn)一步提升效率,解除最后一點(diǎn)阻塞呢?這就是異步IO模型,全稱(chēng)asynchronous I/O,簡(jiǎn)稱(chēng)為AIO。
AIO的基本流程是:
- 用戶(hù)線(xiàn)程通過(guò)系統(tǒng)調(diào)用,告知kernel內(nèi)核啟動(dòng)某個(gè)IO操作
- 用戶(hù)線(xiàn)程返回。kernel內(nèi)核在整個(gè)IO操作(包括數(shù)據(jù)準(zhǔn)備、數(shù)據(jù)復(fù)制)
- 完成后,通知用戶(hù)程序,用戶(hù)執(zhí)行后續(xù)的業(yè)務(wù)操作。
kernel的數(shù)據(jù)準(zhǔn)備是將數(shù)據(jù)從網(wǎng)絡(luò)物理設(shè)備(網(wǎng)卡)讀取到內(nèi)核緩沖區(qū);kernel的數(shù)據(jù)復(fù)制是將數(shù)據(jù)從內(nèi)核緩沖區(qū)拷貝到用戶(hù)程序空間的緩沖區(qū)。
(1)當(dāng)用戶(hù)線(xiàn)程調(diào)用了read系統(tǒng)調(diào)用,立刻就可以開(kāi)始去做其它的事,用戶(hù)線(xiàn)程不阻塞。
(2)內(nèi)核(kernel)就開(kāi)始了IO的第一個(gè)階段:準(zhǔn)備數(shù)據(jù)。當(dāng)kernel一直等到數(shù)據(jù)準(zhǔn)備好了,它就會(huì)將數(shù)據(jù)從kernel內(nèi)核緩沖區(qū),拷貝到用戶(hù)緩沖區(qū)(用戶(hù)內(nèi)存)。
(3)kernel會(huì)給用戶(hù)線(xiàn)程發(fā)送一個(gè)信號(hào)(signal),或者回調(diào)用戶(hù)線(xiàn)程注冊(cè)的回調(diào)接口,告訴用戶(hù)線(xiàn)程read操作完成了。
(4)用戶(hù)線(xiàn)程讀取用戶(hù)緩沖區(qū)的數(shù)據(jù),完成后續(xù)的業(yè)務(wù)操作。
異步IO模型的特點(diǎn):
在內(nèi)核kernel的等待數(shù)據(jù)和復(fù)制數(shù)據(jù)的兩個(gè)階段,用戶(hù)線(xiàn)程都不是block(阻塞)的。
用戶(hù)線(xiàn)程需要接受kernel的IO操作完成的事件,或者說(shuō)注冊(cè)IO操作完成的回調(diào)函數(shù),到操作系統(tǒng)的內(nèi)核。
所以說(shuō),異步IO有的時(shí)候,也叫做信號(hào)驅(qū)動(dòng) IO 。
?異步IO模型缺點(diǎn):
需要完成事件的注冊(cè)與傳遞,這里邊需要底層操作系統(tǒng)提供大量的支持,去做大量的工作。
目前來(lái)說(shuō), Windows 系統(tǒng)下通過(guò) IOCP 實(shí)現(xiàn)了真正的異步 I/O。
但是,就目前的業(yè)界形式來(lái)說(shuō),Windows 系統(tǒng),很少作為百萬(wàn)級(jí)以上或者說(shuō)高并發(fā)應(yīng)用的服務(wù)器操作系統(tǒng)來(lái)使用。
而在 Linux 系統(tǒng)下,異步IO模型在2.6版本才引入,目前并不完善。
所以,這也是在 Linux 下,實(shí)現(xiàn)高并發(fā)網(wǎng)絡(luò)編程時(shí)都是以 IO 復(fù)用模型模式為主。
小結(jié)一下:
四種IO模型,理論上越往后,阻塞越少,效率也是最優(yōu)。
在這四種 I/O 模型中,前三種屬于同步 I/O,因?yàn)槠渲姓嬲?I/O 操作將阻塞線(xiàn)程。
只有最后一種,才是真正的異步 I/O 模型,可惜目前Linux 操作系統(tǒng)尚欠完善。
到此這篇關(guān)于Java中的IO讀寫(xiě)原理詳解的文章就介紹到這了,更多相關(guān)Java的IO讀寫(xiě)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java字符串的大寫(xiě)字母右移實(shí)現(xiàn)方法
下面小編就為大家?guī)?lái)一篇java字符串的大寫(xiě)字母右移實(shí)現(xiàn)方法。小編覺(jué)得聽(tīng)不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04淺談一個(gè)基礎(chǔ)的SpringBoot項(xiàng)目該包含哪些
這篇文章主要介紹了淺談一個(gè)基礎(chǔ)的SpringBoot項(xiàng)目該包含哪些,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10java 中JFinal getModel方法和數(shù)據(jù)庫(kù)使用出現(xiàn)問(wèn)題解決辦法
這篇文章主要介紹了java 中JFinal getModel方法和數(shù)據(jù)庫(kù)使用出現(xiàn)問(wèn)題解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04idea如何快速查找一個(gè)類(lèi)或類(lèi)中方法名和變量
這篇文章主要介紹了idea如何快速查找一個(gè)類(lèi)或類(lèi)中方法名和變量問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11關(guān)于SpringBoot靜態(tài)資源路徑管理問(wèn)題
這篇文章主要介紹了SpringBoot靜態(tài)資源路徑管理,主要包括默認(rèn)靜態(tài)資源路徑,增加靜態(tài)資源路徑前綴的相關(guān)操作,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05JAVA參數(shù)傳遞方式實(shí)例淺析【按值傳遞與引用傳遞區(qū)別】
這篇文章主要介紹了JAVA參數(shù)傳遞方式,結(jié)合實(shí)例形式分析了java按值傳遞與引用傳遞區(qū)別及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2020-05-05創(chuàng)建好SpringBoot項(xiàng)目后但是找不到Maven的解決方法
在使用IDEA專(zhuān)業(yè)版創(chuàng)建好SpringBoot項(xiàng)目后,發(fā)現(xiàn)上方導(dǎo)航欄的運(yùn)行按鈕是灰色的,而且左側(cè)導(dǎo)航欄的pom.xml的圖標(biāo)顏色也不是正常的,點(diǎn)開(kāi)右側(cè)導(dǎo)航欄的Maven后,發(fā)現(xiàn)Maven找不到,所以本文介紹了創(chuàng)建好SpringBoot項(xiàng)目后但是找不到Maven的解決方法,需要的朋友可以參考下2024-10-10