国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

這篇具有很好參考價值的文章主要介紹了到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。


1、引言


關(guān)于Java網(wǎng)絡(luò)編程中的同步IO和異步IO的區(qū)別及原理的文章非常的多,具體來說主要還是在討論Java BIO和Java NIO這兩者,而關(guān)于Java AIO的文章就少之又少了(即使用也只是介紹了一下概念和代碼示例)。

在深入了解AIO之前,我注意到以下幾個現(xiàn)象:

  • 1)2011年Java 7發(fā)布,它增加了AIO(號稱異步IO網(wǎng)絡(luò)編程模型),但12年過去了,平時使用的開發(fā)框架和中間件卻還是以NIO為主(例如網(wǎng)絡(luò)框架Netty、Mina,Web容器Tomcat、Undertow),這是為什么?
  • 2)Java AIO又稱為NIO 2.0,難道它也是基于NIO來實現(xiàn)的?
  • 3)Netty為什么會舍去了AIO的支持?
  • 4)AIO看起來貌似只是解決了有無,實際是發(fā)布了個寂寞?

Java AIO的這些不合常理的現(xiàn)象難免會令人心存疑惑。所以決定寫這篇文章時,我不想只是簡單的把AIO的概念再復述一遍,而是要透過現(xiàn)象,深入分析、思考和并理解Java AIO的本質(zhì)。


2、我們所理解的異步


AIO的A是Asynchronous(即異步)的意思,在了解AIO的原理之前,我們先理清一下“異步”到底是怎樣的一個概念。

說起異步編程,在平時的開發(fā)還是比較常見的。

例如以下的代碼示例:

@Async

public void create() {
????//TODO
}
?
public void build() {
????executor.execute(() -> build());
}
?
不管是用@Async注解,還是往線程池里提交任務(wù),他們最終都是同一個結(jié)果,就是把要執(zhí)行的任務(wù),交給另外一個線程來執(zhí)行。

這個時候,我們可以大致的認為,所謂的“異步”,就是用多線程的方式去并行執(zhí)行任務(wù)。

3、Java BIO和NIO到底是同步還是異步?


Java BIO和NIO到底是同步還是異步,我們先按照異步這個思路,做異步編程。

3.1BIO代碼示例

byte [] data = new byte[1024];

InputStream in = socket.getInputStream();
in.read(data);
// 接收到數(shù)據(jù),異步處理
executor.execute(() -> handle(data));
?
public void handle(byte [] data) {
????// TODO
}
?
如上:BIO在read()時,雖然線程阻塞了,但在收到數(shù)據(jù)時,可以異步啟動一個線程去處理。

3.2NIO代碼示例

selector.select();

Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> iterator = keys.iterator();
while (iterator.hasNext()) {
????SelectionKey key = iterator.next();
????if (key.isReadable()) {
????????SocketChannel channel = (SocketChannel) key.channel();
????????ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
????????executor.execute(() -> {
????????????try {
????????????????channel.read(byteBuffer);
????????????????handle(byteBuffer);
????????????} catch (Exception e) {
?
????????????}
????????});
????}
}
?
public static void handle(ByteBuffer buffer) {
????// TODO
}
?
同理:NIO雖然read()是非阻塞的,通過select()可以阻塞等待數(shù)據(jù),在有數(shù)據(jù)可讀的時候,異步啟動一個線程,去讀取數(shù)據(jù)和處理數(shù)據(jù)。

3.3產(chǎn)生的理解偏差


此時我們信誓旦旦地說,Java的BIO和NIO是異步還是同步,取決你的心情,你高興給它個多線程,它就是異步的。

但果真如此么?

在翻閱了大量博客文章之后,基本一致的闡明了——BIO和NIO是同步的。

那問題點出在哪呢,是什么造成了我們理解上的偏差呢?

那就是參考系的問題,以前學物理時,公交車上的乘客是運動還是靜止,需要有參考系前提,如果以地面為參考,他是運動的,以公交車為參考,他是靜止的。

Java IO也是一樣,需要有個參考系,才能定義它是同步還是異步。

既然我們討論的是關(guān)于Java IO是哪一種模式,那就是要針對IO讀寫操作這件事來理解,而其他的啟動另外一個線程去處理數(shù)據(jù),已經(jīng)是脫離IO讀寫的范圍了,不應(yīng)該把他們扯進來。

3.4嘗試定義異步


所以以IO讀寫操作這事件作為參照,我們先嘗試的這樣定義,就是:發(fā)起IO讀寫的線程(調(diào)用read和write的線程),和實際操作IO讀寫的線程,如果是同一個線程,就稱之為同步,否則是異步。

按上述定義:

  • 1)顯然BIO只能是同步,調(diào)用in.read()當前線程阻塞,有數(shù)據(jù)返回的時候,接收到數(shù)據(jù)的還是原來的線程;
  • 2)而NIO也稱之為同步,原因也是如此,調(diào)用channel.read()時,線程雖然不會阻塞,但讀到數(shù)據(jù)的還是當前線程。

按照這個思路,AIO應(yīng)該是發(fā)起IO讀寫的線程,和實際收到數(shù)據(jù)的線程,可能不是同一個線程。

是不是這樣呢?我們將在上一節(jié)直接上Java AIO的代碼,我們從 實際代碼中一窺究竟吧。

4、一個Java AIO的網(wǎng)絡(luò)編程示例


4.1AIO服務(wù)端程序代碼

?public class AioServer {
?
????public static void main(String[] args) throws IOException {
????????System.out.println(Thread.currentThread().getName() + " AioServer start");
????????AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open()
????????????????.bind(new InetSocketAddress("127.0.0.1", 8080));
????????serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
?
????????????@Override
????????????public void completed(AsynchronousSocketChannel clientChannel, Void attachment) {
????????????????System.out.println(Thread.currentThread().getName() + " client is connected");
????????????????ByteBuffer buffer = ByteBuffer.allocate(1024);
????????????????clientChannel.read(buffer, buffer, new ClientHandler());
????????????}
?
????????????@Override
????????????public void failed(Throwable exc, Void attachment) {
????????????????System.out.println("accept fail");
????????????}
????????});
????????System.in.read();
????}
}
?
public class ClientHandler implements CompletionHandler<Integer, ByteBuffer> {
????@Override
????public void completed(Integer result, ByteBuffer buffer) {
????????buffer.flip();
????????byte [] data = new byte[buffer.remaining()];
????????buffer.get(data);
????????System.out.println(Thread.currentThread().getName() + " received:"? + new String(data, StandardCharsets.UTF_8));
????}
?
????@Override
????public void failed(Throwable exc, ByteBuffer buffer) {
?
????}
}
?

4.2AIO客戶端程序

public class AioClient {

????public static void main(String[] args) throws Exception {
????????AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();
????????channel.connect(new InetSocketAddress("127.0.0.1", 8080));
????????ByteBuffer buffer = ByteBuffer.allocate(1024);
????????buffer.put("Java AIO".getBytes(StandardCharsets.UTF_8));
????????buffer.flip();
????????Thread.sleep(1000L);
????????channel.write(buffer);
?}
}
?

4.3異步的定義猜想結(jié)論

?文章來源地址http://www.zghlxwxcb.cn/news/detail-693438.html

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?

在服務(wù)端運行結(jié)果里:

1)main線程發(fā)起serverChannel.accept的調(diào)用,添加了一個CompletionHandler監(jiān)聽回調(diào),當有客戶端連接過來時,Thread-5線程執(zhí)行了accep的completed回調(diào)方法。

2)緊接著Thread-5又發(fā)起了clientChannel.read調(diào)用,也添加了個CompletionHandler監(jiān)聽回調(diào),當收到數(shù)據(jù)時,是Thread-1的執(zhí)行了read的completed回調(diào)方法。

這個結(jié)論和上面異步猜想一致:發(fā)起IO操作(例如accept、read、write)調(diào)用的線程,和最終完成這個操作的線程不是同一個,我們把這種IO模式稱之AIO。

當然了,這樣定義AIO只是為了方便我們理解,實際中對異步IO的定義可能更抽象一點。
?

5、 AIO示例引發(fā)思考1:“執(zhí)行completed()方法的線程是誰創(chuàng)建、什么時候創(chuàng)建?”


一般,這樣的問題,需要從程序的入口的開始了解,但跟線程相關(guān),其實是可以從線程棧的運行情況來定位線程是怎么運行。

只運行AIO服務(wù)端程序,客戶端不運行,打印一下線程棧(備注:程序在Linux平臺上運行,其他平臺略有差異)。如下圖所示。

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?分析線程棧,發(fā)現(xiàn),程序啟動了那么幾個線程:

  • 1)線程Thread-0阻塞在EPoll.wait()方法上;
  • 2)線程Thread-1、Thread-2~Thread-n(n和CPU核心數(shù)量一致)從阻塞隊列里take()任務(wù),阻塞等待有任務(wù)返回。


此時可以暫定下一個結(jié)論:AIO服務(wù)端程序啟動之后,就開始創(chuàng)建了這些線程,且線程都處于阻塞等待狀態(tài)。

另外:發(fā)現(xiàn)這些線程的運行都跟epoll有關(guān)系!

提到epoll,我們印象中,Java NIO在Linux平臺底層就是用epoll來實現(xiàn)的,難道Java AIO也是用epoll來實現(xiàn)么?

為了證實這個結(jié)論,我們從下一個問題來展開討論。

?

6、 AIO示例引發(fā)思考2:AIO注冊事件監(jiān)聽和執(zhí)行回調(diào)是如何實現(xiàn)的?


帶著這個問題,去閱讀JDK分析源碼時,發(fā)現(xiàn)源碼特別的長,而源碼解析是一項枯燥乏味的過程,很容易把閱讀者給逼走勸退掉。

對于長流程和邏輯復雜的代碼的理解,我們可以抓住它幾個脈絡(luò),找出哪幾個核心流程。

以注冊監(jiān)聽read為例clientChannel.read(...),它主要的核心流程是:注冊事件 -> 監(jiān)聽事件 -> 處理事件。

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?注:注冊事件調(diào)用EPoll.ctl(...)函數(shù),這個函數(shù)在最后的參數(shù)用于指定是一次性的,還是永久性。上面代碼events | EPOLLONSHOT字面意思看來,是一次性的。

監(jiān)聽事件:

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?

處理事件:

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?

核心流程總結(jié):

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?在分析完上面的代碼流程后會發(fā)現(xiàn):每一次IO讀寫都要經(jīng)歷的這三個事件是一次性的,也就是在處理事件完,本次流程就結(jié)束了,如果想繼續(xù)下一次的IO讀寫,就得從頭開始再來一遍。這樣就會存在所謂的死亡回調(diào)(回調(diào)方法里再添加下一個回調(diào)方法),這對于編程的復雜度大大提高了。

7、 AIO示例引發(fā)思考3:監(jiān)聽回調(diào)的本質(zhì)是什么?

?

7.1概述


先說一下結(jié)論:所謂監(jiān)聽回調(diào)的本質(zhì),就是用戶態(tài)線程調(diào)用內(nèi)核態(tài)的函數(shù)(準確的說是API,例如read、write、epollWait),該函數(shù)還沒有返回時,用戶線程被阻塞了。當函數(shù)返回時,會喚醒阻塞的線程,執(zhí)行所謂回調(diào)函數(shù)。

對于這個結(jié)論的理解,要先引入幾個概念。

7.2系統(tǒng)調(diào)用與函數(shù)調(diào)用


函數(shù)調(diào)用:找到某個函數(shù),并執(zhí)行函數(shù)里的相關(guān)命令。
系統(tǒng)調(diào)用:操作系統(tǒng)對用戶應(yīng)用程序提供了編程接口,所謂API。

系統(tǒng)調(diào)用執(zhí)行過程:

  • 1)傳遞系統(tǒng)調(diào)用參數(shù);
  • 2)執(zhí)行陷入指令,用用戶態(tài)切換到核心態(tài)(這是因為系統(tǒng)調(diào)用一般都需要再核心態(tài)下執(zhí)行);
  • 3)執(zhí)行系統(tǒng)調(diào)用程序;
  • 4)返回用戶態(tài)。

?

7.3用戶態(tài)和內(nèi)核態(tài)之間的通信


用戶態(tài)->內(nèi)核態(tài):通過系統(tǒng)調(diào)用方式即可。

內(nèi)核態(tài)->用戶態(tài):內(nèi)核態(tài)根本不知道用戶態(tài)程序有什么函數(shù),參數(shù)是啥,地址在哪里。所以內(nèi)核是不可能去調(diào)用用戶態(tài)的函數(shù),只能通過發(fā)送信號,比如kill 命令關(guān)閉程序就是通過發(fā)信號讓用戶程序優(yōu)雅退出的。

既然內(nèi)核態(tài)是不可能主動去調(diào)用用戶態(tài)的函數(shù),為什么還會有回調(diào)呢,只能說這個所謂回調(diào)其實就是用戶態(tài)的自導自演。它既做了監(jiān)聽,又做了執(zhí)行回調(diào)函數(shù)。

7.4用實際例子驗證結(jié)論


為了驗證這個結(jié)論是否有說服力,舉個例子:平時開發(fā)寫代碼用的IntelliJ IDEA,它是如何監(jiān)聽鼠標、鍵盤事件和處理事件的。

按照慣例,先打印一下線程棧,會發(fā)現(xiàn)鼠標、鍵盤等事件的監(jiān)聽是由“AWT-XAWT”線程負責的,處理事件則是“AWT-EventQueue”線程負責。如下圖所示。

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?定位到具體的代碼上:可以看到“AWT-XAWT”正在做while循環(huán),調(diào)用waitForEvents函數(shù)等待事件返回。如果沒有事件,線程就一直阻塞在那邊。如下圖所示。

到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!

?

8、Java AIO的本質(zhì)是什么?

?

8.1Java AIO的本質(zhì),就是只在用戶態(tài)實現(xiàn)了異步


由于內(nèi)核態(tài)無法直接調(diào)用用戶態(tài)函數(shù),Java AIO的本質(zhì),就是只在用戶態(tài)實現(xiàn)異步,并沒有達到理想意義上的異步。

1)理想中的異步:

何謂理想意義上的異步?這里舉個網(wǎng)購的例子。

兩個角色,消費者A、快遞員B:

  • 1)A在網(wǎng)上購物時,填好家庭地址付款提交訂單,這個相當于注冊監(jiān)聽事件;
  • 2)商家發(fā)貨,B把東西送到A家門口,這個相當于回調(diào)。


A在網(wǎng)上下完單,后續(xù)的發(fā)貨流程就不用他來操心了,可以繼續(xù)做其他事。B送貨也不關(guān)心A在不在家,反正就把貨扔到家門口就行了,兩個人互不依賴,互不相干擾。

假設(shè)A購物是用戶態(tài)來做,B送快遞是內(nèi)核態(tài)來做,這種程序運行方式過于理想了,實際中實現(xiàn)不了。

2)現(xiàn)實中的異步:

A住的是高檔小區(qū),不能隨意進去,快遞只能送到小區(qū)門口。

A買了一件比較重的商品,比如一臺電視,因為A要上班不在家里,所以找了一個好友C幫忙把電視搬到他家。

A出門上班前,跟門口的保安D打聲招呼,說今天有一臺電視送過來,送到小區(qū)門口時,請電話聯(lián)系C,讓他過來拿。

具體就是:

  • 1)此時,A下單并跟D打招呼,相當于注冊事件。在AIO中就是EPoll.ctl(...)注冊事件;
  • 2)保安在門口蹲著相當于監(jiān)聽事件,在AIO中就是Thread-0線程,做EPoll.wait(..);
  • 3)快遞員把電視送到門口,相當于有IO事件到達;
  • 4)保安通知C電視到了,C過來搬電視,相當于處理事件(在AIO中就是Thread-0往任務(wù)隊列提交任務(wù),Thread-1 ~n去取數(shù)據(jù),并執(zhí)行回調(diào)方法)。


整個過程中,保安D必須一直蹲著,寸步不能離開,否則電視送到門口,就被人偷了。

好友C也必須在A家待著,受人委托,東西到了,人卻不在現(xiàn)場,這有點失信于人。

所以實際的異步和理想中的異步,在互不依賴,互不干擾,這兩點相違背了。保安的作用最大,這是他人生的高光時刻。

異步過程中的注冊事件、監(jiān)聽事件、處理事件,還有開啟多線程,這些過程的發(fā)起者全是用戶態(tài)一手操辦。所以說Java AIO本質(zhì)只是在用戶態(tài)實現(xiàn)了異步,這個和BIO、NIO先阻塞,阻塞喚醒后開啟異步線程處理的本質(zhì)一致。

8.2Java AIO的其它真相


Java AIO跟NIO一樣:在各個平臺的底層實現(xiàn)方式也不同,在Linux是用epoll、Windows是IOCP、Mac OS是KQueue。原理是大同小異,都是需要一個用戶線程阻塞等待IO事件,一個線程池從隊列里處理事件。

Netty之所以移除掉AIO:很大的原因是在性能上AIO并沒有比NIO高。Linux雖然也有一套原生的AIO實現(xiàn)(類似Windows上的IOCP),但Java AIO在Linux并沒有采用,而是用epoll來實現(xiàn)。

Java AIO不支持UDP。

AIO編程方式略顯復雜,比如“死亡回調(diào)”。

?

到了這里,關(guān)于到底什么是Java AIO?為什么Netty會移除AIO?一文搞懂AIO的本質(zhì)!的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 報表VS分析:為什么報表做不完?老板到底想要什么?

    報表VS分析:為什么報表做不完?老板到底想要什么?

    各位數(shù)據(jù)的朋友,大家好,我是老周道數(shù)據(jù),和你一起,用常人思維+數(shù)據(jù)分析,通過數(shù)據(jù)講故事。 上一講和大家講了分析模型中的戰(zhàn)斗機——財務(wù)分析模型。通過奧威BI軟件的行計算模型來開發(fā)財務(wù)分析報表異常地簡單,前端的組合也非常靈活,財務(wù)分析人員就可以搞定。

    2024年02月01日
    瀏覽(21)
  • 公司為什么選擇云數(shù)據(jù)庫?它的魅力到底是什么!

    亞馬遜云科技提供了100余種產(chǎn)品免費套餐。其中,計算資源Amazon EC2首年12個月免費,750小時/月;存儲資源 Amazon S3 首年12個月免費,5GB標準存儲容量;數(shù)據(jù)庫資源 Amazon RDS 首年12個月免費,750小時;Amazon Dynamo DB 25GB存儲容量 永久免費。) 談到數(shù)據(jù)庫想必我們都不陌生,其中主流

    2024年02月04日
    瀏覽(24)
  • Redis為什么會這么快?Redis到底有多快?

    Redis為什么會這么快?Redis到底有多快?

    官方文檔:https://redis.io/docs/management/optimization/benchmarks/ 我們使用redis自帶的benchmark腳本測試: 我們發(fā)現(xiàn),每秒可以執(zhí)行11萬多次set、lpush命令。 執(zhí)行Lua腳本也能達到每秒10萬多次,按照這個測試結(jié)果,redis的10萬qps還是比較準確的,在高性能服務(wù)器上性能還能更強。 總結(jié)起來主

    2024年02月07日
    瀏覽(21)
  • 一個操作讓數(shù)組處理速度快了5倍,到底是為什么

    一個操作讓數(shù)組處理速度快了5倍,到底是為什么

    ? 概述: 通過對數(shù)組進行排序,代碼更好地利用了緩存,從而提高了程序的性能。這種現(xiàn)象通常被稱為\\\"緩存友好\\\"(cache-friendly)或\\\"空間局部性\\\"(spatial locality) 今天做一個數(shù)組數(shù)據(jù)計算時,發(fā)現(xiàn)一個效率問題,給大家分享一下 一個數(shù)組排序和不排序時同樣的邏輯處理速度是

    2024年03月24日
    瀏覽(24)
  • 【Kubernetes】神乎其技的K8s到底是什么,為什么被越來越多人使用

    【Kubernetes】神乎其技的K8s到底是什么,為什么被越來越多人使用

    ??歡迎來到本文?? ??個人簡介:陳童學哦,目前學習C/C++、算法、Python、Java等方向,一個正在慢慢前行的普通人。 ??系列專欄:陳童學的日記 ??其他專欄:C++STL,感興趣的小伙伴可以看看。 ??希望各位→點贊?? + 收藏?? + 留言?? ? ??萬物從心起,心動則萬物動??

    2024年02月13日
    瀏覽(26)
  • 為什么需要設(shè)置System.setProperty(“es.set.netty.runtime.available.processors“, “false“);

    引用:https://blog.csdn.net/weixin_41800884/article/details/107885391

    2024年02月12日
    瀏覽(12)
  • 神秘的IP地址8.8.8.8地址到底是什么?為什么會被用作DNS服務(wù)器地址呢?

    神秘的IP地址8.8.8.8地址到底是什么?為什么會被用作DNS服務(wù)器地址呢?

    當我們在配置網(wǎng)絡(luò)連接或者路由器時,經(jīng)常會遇到需要填寫DNS服務(wù)器地址的情況。而在這些情況下,很多人都會聽到一個神秘的數(shù)字地址:8.8.8.8。那么,這個地址到底是什么,為什么會被用作DNS服務(wù)器地址呢?本文將詳細解釋這個問題。 首先,我們需要了解一下什么是DNS。

    2024年02月03日
    瀏覽(25)
  • 一篇帶你肝完P(guān)ython逆向為什么要學webpack,學完之后到底又該怎么用?

    一篇帶你肝完P(guān)ython逆向為什么要學webpack,學完之后到底又該怎么用?

    大家好,我是辣條哥! 之前講了很多關(guān)于基礎(chǔ)方面的內(nèi)容,從本章開始辣條我會開始慢慢開始跟大家解析一些進階知識以及案例 廢話不多說今天我們就深入解析一下webpack,我們先聊一下Python逆向工程為什么還要學習前端的webpack呢? 首先, 學習webpack對于Python逆向工程師來說

    2024年02月12日
    瀏覽(21)
  • 程序員自學編程的一些誤區(qū),你中招了沒?自學編程到底為什么重要?給你分享我的5個高效學習方法

    今天又來分享干貨了 今天講一下 就是之前朋友們提的一個問題 就是如何來自學編程 我今天來講一些 自學編程的話其實最重要的是什么 其實就是一個驅(qū)動力 也就是你學習他的目標是什么 很多人沒想明白這點就開始去跟著學 最終學的比較痛苦又沒有結(jié)果 我在高中的時候家里

    2023年04月21日
    瀏覽(26)
  • 為什么我心中TOP1的編程語言是Java?我為什么選擇Java?

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包