溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

07.Java類加載問題

發(fā)布時間:2020-08-13 17:20:07 來源:網(wǎng)絡(luò) 閱讀:233 作者:楊充 欄目:移動開發(fā)
目錄介紹
  • 7.0.0.1 Java內(nèi)存模型里包含什么?程序計數(shù)器的作用是什么?常量池的作用是什么?
  • 7.0.0.2 什么是類加載器?類加載器工作機制是什么?類加載器種類?什么是雙親委派機制?
  • 7.0.0.3 什么時候發(fā)生類初始化?類初始化后對類的做了什么,加載變量,常量,方法都內(nèi)存那個位置?
  • 7.0.0.4 通過下面一個代碼案例理解類加載順序?當(dāng)遇到 類名.變量 加載時,只加載變量所在類嗎?
  • 7.0.0.5 看下面這段代碼,說一下準(zhǔn)備階段和初始化階段常量變化的原理?變量初始化過程?
  • 7.0.0.7 說收垃圾回收機制?為什么引用計數(shù)器判定對象是否回收不可行?有哪些引用類型?
  • 7.0.0.8 談?wù)凧ava的類加載過程?加載做了什么?驗證做了什么?準(zhǔn)備做了什么?解析做了什么?初始化做了什么?

好消息

  • 博客筆記大匯總【15年10月到至今】,包括Java基礎(chǔ)及深入知識點,Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時開發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護(hù)并且修正,持續(xù)完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計500篇[近100萬字],將會陸續(xù)發(fā)表到網(wǎng)上,轉(zhuǎn)載請注明出處,謝謝!
  • 鏈接地址:https://github.com/yangchong211/YCBlogs
  • 如果覺得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬事起于忽微,量變引起質(zhì)變!
7.0.0.1 Java內(nèi)存模型里包含什么?程序計數(shù)器的作用是什么?常量池的作用是什么?
  • Java內(nèi)存模型里包含什么?
    • JVM會用一段空間來存儲執(zhí)行程序期間需要用到的數(shù)據(jù)和相關(guān)信息,這段空間就是運行時數(shù)據(jù)區(qū)(Runtime Data Area),也就是常說的JVM內(nèi)存。JVM會將它所管理的內(nèi)存劃分為線程私有數(shù)據(jù)區(qū)和線程共享數(shù)據(jù)區(qū)兩大類。
    • 線程私有數(shù)據(jù)區(qū)包含:
      • 1.程序計數(shù)器:是一個數(shù)據(jù)結(jié)構(gòu),用于保存當(dāng)前正常執(zhí)行的程序的內(nèi)存地址。Java虛擬機的多線程就是通過線程輪流切換并分配處理器時間來實現(xiàn)的,為了線程切換后能恢復(fù)到正確的位置,每條線程都需要一個獨立的程序計數(shù)器,互不影響,該區(qū)域為“線程私有”。
      • 2.Java虛擬機棧:線程私有的,與線程生命周期相同,用于存儲局部變量表,操作棧,方法返回值。局部變量表放著基本數(shù)據(jù)類型,還有對象的引用。
      • 3.本地方法棧:跟虛擬機棧很像,不過它是為虛擬機使用到的Native方法服務(wù)。
    • 線程共享數(shù)據(jù)區(qū)包含:
    • 技術(shù)博客大總結(jié)
      • 4.Java堆:所有線程共享的一塊內(nèi)存區(qū)域,用于存放幾乎所有的對象實例和數(shù)組;是垃圾收集器管理的主要區(qū)域,也被稱做“GC堆”;是Java虛擬機所管理的內(nèi)存中最大的一塊。
      • 5.方法區(qū):各個線程共享的區(qū)域,儲存虛擬機加載的類信息,常量,靜態(tài)變量,編譯后的代碼。
      • 6.運行時常量池:代表運行時每個class文件中的常量表。包括幾種常量:編譯時的數(shù)字常量、方法或者域的引用。
  • 程序計數(shù)器的作用是什么?
  • 常量池的作用是什么?
7.0.0.2 什么是類加載器?類加載器工作機制是什么?類加載器種類?什么是雙親委派機制?
  • 什么是類加載器?
    • 負(fù)責(zé)讀取 Java 字節(jié)代碼,并轉(zhuǎn)換成java.lang.Class類的一個實例;
  • 類加載器工作機制是什么
    • 是虛擬機把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存,并對數(shù)據(jù)進(jìn)行校驗、轉(zhuǎn)換解析和初始化,最終形成可被虛擬機直接使用的Java類型的過程。另外,類型的加載、連接和初始化過程都是在程序運行期完成的,從而通過犧牲一些性能開銷來換取Java程序的高度靈活性。下面介紹類加載每個階段的任務(wù):
      • 加載(Loading):通過類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流;將該二進(jìn)制字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu),該數(shù)據(jù)存儲數(shù)據(jù)結(jié)構(gòu)由虛擬機實現(xiàn)自行定義;在內(nèi)存中生成一個代表這個類的java.lang.Class對象,它將作為程序訪問方法區(qū)中的這些類型數(shù)據(jù)的外部接口
      • 驗證(Verification):確保Class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機的要求,包括文件格式驗證、元數(shù)據(jù)驗證、字節(jié)碼驗證和符號引用驗證
      • 準(zhǔn)備(Preparation):為類變量分配內(nèi)存,因為這里的變量是由方法區(qū)分配內(nèi)存的,所以僅包括類變量而不包括實例變量,后者將會在對象實例化時隨著對象一起分配在Java堆中;設(shè)置類變量初始值,通常情況下零值
      • 解析(Resolution):虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程
      • 初始化(Initialization):是類加載過程的最后一步,會開始真正執(zhí)行類中定義的Java字節(jié)碼。而之前的類加載過程中,除了在『加載』階段用戶應(yīng)用程序可通過自定義類加載器參與之外,其余階段均由虛擬機主導(dǎo)和控制
  • 類加載器種類?
    • 啟動類加載器,Bootstrap ClassLoader,加載JACA_HOME\lib,或者被-Xbootclasspath參數(shù)限定的類
    • 擴展類加載器,Extension ClassLoader,加載\lib\ext,或者被java.ext.dirs系統(tǒng)變量指定的類
    • 應(yīng)用程序類加載器,Application ClassLoader,加載ClassPath中的類庫
    • 自定義類加載器,通過繼承ClassLoader實現(xiàn),一般是加載我們的自定義類
    • 技術(shù)博客大總結(jié)
  • 什么是雙親委派機制?
    • 主要是表示類加載器之間的層次關(guān)系
      • 前提:除了頂層啟動類加載器外,其余類加載器都應(yīng)當(dāng)有自己的父類加載器,且它們之間關(guān)系一般不會以繼承(Inheritance)關(guān)系來實現(xiàn),而是通過組合(Composition)關(guān)系來復(fù)用父加載器的代碼。
      • 工作過程:若一個類加載器收到了類加載的請求,它先會把這個請求委派給父類加載器,并向上傳遞,最終請求都傳送到頂層的啟動類加載器中。只有當(dāng)父加載器反饋自己無法完成這個加載請求時,子加載器才會嘗試自己去加載。
7.0.0.3 什么時候發(fā)生類初始化?類初始化后對類的做了什么,加載變量,常量,方法都內(nèi)存那個位置?
  • 什么時候發(fā)生類初始化
    • 遇到new、getstatic、putstatic或invokestatic這4條字節(jié)碼指令時,如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。生成這4條指令的最常見的Java代碼場景是:使用new關(guān)鍵字實例化對象的時候,讀取或設(shè)置一個類的靜態(tài)字段(被final修飾、已在編譯期把結(jié)果放入常量池的靜態(tài)字段除外)的時候,以及調(diào)用一個類的靜態(tài)方法的時候。
      • 調(diào)用一個類型的靜態(tài)方法時(即在字節(jié)碼中執(zhí)行invokestatic指令)
      • 調(diào)用一個類型或接口的靜態(tài)字段,或者對這些靜態(tài)字段執(zhí)行賦值操作時(即在字節(jié)碼中,執(zhí)行g(shù)etstatic或者putstatic指令),不過用final修飾的靜態(tài)字段除外,它被初始化為一個編譯時常量表達(dá)式
    • 使用java.lang.reflect包的方法對類進(jìn)行反射調(diào)用的時候,如果類沒有進(jìn)行過初始化,則需要先觸發(fā)其初始化。
    • 當(dāng)初始化一個類的時候,如果發(fā)現(xiàn)其父類還沒有進(jìn)行過初始化,則需要先觸發(fā)其父類的初始化。
    • 當(dāng)虛擬機啟動時,用戶需要指定一個要執(zhí)行的主類(包含main()方法的那個類),虛擬機會先初始化這個主類。
    • 當(dāng)使用JDK 1.7的動態(tài)語言支持時,如果一個java.lang.invoke.MethodHandle實例左后的解析結(jié)果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個方法句柄鎖對應(yīng)的類沒有進(jìn)行過初始化時。
  • 類初始化后對類的做了什么技術(shù)博客大總結(jié)
    • 這個階段主要是對類變量初始化,是執(zhí)行類構(gòu)造器的過程。
    • 換句話說,只對static修飾的變量或語句進(jìn)行初始化。
    • 如果初始化一個類的時候,其父類尚未初始化,則優(yōu)先初始化其父類。
    • 如果同時包含多個靜態(tài)變量和靜態(tài)代碼塊,則按照自上而下的順序依次執(zhí)行。
7.0.0.4 通過下面一個代碼案例理解類加載順序?當(dāng)遇到 類名.變量 加載時,只加載變量所在類嗎?
  • 代碼案例如下所示

    class A{
        public static int value = 134;
        static{
            System.out.println("A");
        }
    }
    
    class B extends  A{
        static{
            System.out.println("B");
        }
    }
    
    public class Demo {
       public static void main(String args[]){
           int s = B.value;
           System.out.println(s);
       }
    }
  • a.打印錯誤結(jié)果
    A 
    B
    134 
  • b.打印正確結(jié)果
    A
    134 
    • 觀察代碼,發(fā)現(xiàn)B.value中的value變量是A類的。所以,幫主在這里大膽的猜測一下,當(dāng)遇到 類名.變量 加載時,只加載變量所在類。
  • 如何做才能打印a這種結(jié)果呢?

    class A{
        public static int valueA = 134;
        static{
            System.out.println("A");
        }
    }
    
    class B extends  A{
        public static int valueB = 245;
        static{
            System.out.println("B");
        }
    }
    
    public class Demo {
       public static void main(String args[]){
           int s = B.valueB;
           System.out.println(s);
       }
    }
    • 得到數(shù)據(jù)技術(shù)博客大總結(jié)
      A
      B
      245 
7.0.0.5 看下面這段代碼,說一下準(zhǔn)備階段和初始化階段常量變化的原理?
  • 看下面這段代碼
    public static int value1  = 5;
    public static int value2  = 6;
    static{
        value2 = 66;
    }
  • 準(zhǔn)備階段和初始化階段常量變化?
    • 結(jié)果
      • 在準(zhǔn)備階段value1和value2都等于0;
      • 在初始化階段value1和value2分別等于5和66;
  • 變量初始化過程?
    • 所有類變量初始化語句和靜態(tài)代碼塊都會在編譯時被前端編譯器放在收集器里頭,存放到一個特殊的方法中,這個方法就是<clinit>方法,即類/接口初始化方法,該方法只能在類加載的過程中由JVM調(diào)用;
    • 編譯器收集的順序是由語句在源文件中出現(xiàn)的順序所決定的,靜態(tài)語句塊中只能訪問到定義在靜態(tài)語句塊之前的變量;
    • 如果超類還沒有被初始化,那么優(yōu)先對超類初始化,但在<clinit>方法內(nèi)部不會顯示調(diào)用超類的<clinit>方法,由JVM負(fù)責(zé)保證一個類的<clinit>方法執(zhí)行之前,它的超類<clinit>方法已經(jīng)被執(zhí)行。
    • JVM必須確保一個類在初始化的過程中,如果是多線程需要同時初始化它,僅僅只能允許其中一個線程對其執(zhí)行初始化操作,其余線程必須等待,只有在活動線程執(zhí)行完對類的初始化操作之后,才會通知正在等待的其他線程。(所以可以利用靜態(tài)內(nèi)部類實現(xiàn)線程安全的單例模式)
    • 如果一個類沒有聲明任何的類變量,也沒有靜態(tài)代碼塊,那么可以沒有類<clinit>方法;
7.0.0.7 說收垃圾回收機制?為什么引用計數(shù)器判定對象是否回收不可行?
  • 判定對象可回收有兩種方法:
    • 引用計數(shù)算法:
      • 給對象中添加一個引用計數(shù)器,每當(dāng)有一個地方引用它時,計數(shù)器值就加1;當(dāng)引用失效時,計數(shù)器值就減1;任何時刻計數(shù)器為0的對象就是不可能再被使用的。然而在主流的Java虛擬機里未選用引用計數(shù)算法來管理內(nèi)存,主要原因是它難以解決對象之間相互循環(huán)引用的問題,所以出現(xiàn)了另一種對象存活判定算法。
    • 可達(dá)性分析法:
      • 通過一系列被稱為『GC Roots』的對象作為起始點,從這些節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的。其中可作為GC Roots的對象:虛擬機棧中引用的對象,主要是指棧幀中的本地變量、本地方法棧中Native方法引用的對象、方法區(qū)中類靜態(tài)屬性引用的對象、方法區(qū)中常量引用的對象
  • 回收算法有以下四種:
    • 分代收集算法:是當(dāng)前商業(yè)虛擬機都采用的一種算法,根據(jù)對象存活周期的不同,將Java堆劃分為新生代和老年代,并根據(jù)各個年代的特點采用最適當(dāng)?shù)氖占惴?。技術(shù)博客大總結(jié)
      • 新生代:大批對象死去,只有少量存活。使用『復(fù)制算法』,只需復(fù)制少量存活對象即可。
      • 老年代:對象存活率高。使用『標(biāo)記—清理算法』或者『標(biāo)記—整理算法』,只需標(biāo)記較少的回收對象即可。
    • 復(fù)制算法:把可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用盡后,把還存活著的對象『復(fù)制』到另外一塊上面,再將這一塊內(nèi)存空間一次清理掉。
    • 標(biāo)記-清除算法:首先『標(biāo)記』出所有需要回收的對象,然后統(tǒng)一『清除』所有被標(biāo)記的對象。
    • 標(biāo)記-整理算法:首先『標(biāo)記』出所有需要回收的對象,然后進(jìn)行『整理』,使得存活的對象都向一端移動,最后直接清理掉端邊界以外的內(nèi)存。
  • 垃圾收集算法分類
    • 標(biāo)記-清楚算法(Mark-Sweep)
      • 在標(biāo)記階段,確定所有要回收的對象,并做標(biāo)記。清除階段緊隨標(biāo)記階段,將標(biāo)記階段確定不可用的對象清除。標(biāo)記—清除算法是基礎(chǔ)的收集算法,有兩個不足:1)標(biāo)記和清除階段的效率不高;2)清除后回產(chǎn)生大量的不連續(xù)空間,這樣當(dāng)程序需要分配大內(nèi)存對象時,可能無法找到足夠的連續(xù)空間。
    • 復(fù)制算法(Copying)
      • 復(fù)制算法是把內(nèi)存分成大小相等的兩塊,每次使用其中一塊,當(dāng)垃圾回收的時候,把存活的對象復(fù)制到另一塊上,然后把這塊內(nèi)存整個清理掉。復(fù)制算法實現(xiàn)簡單,運行效率高,但是由于每次只能使用其中的一半,造成內(nèi)存的利用率不高。現(xiàn)在的JVM 用復(fù)制方法收集新生代,由于新生代中大部分對象(98%)都是朝生夕死的,所以會分成1塊大內(nèi)存Eden和兩塊小內(nèi)存Survivor(大概是8:1:1),每次使用1塊大內(nèi)存和1塊小內(nèi)存,當(dāng)回收時將2塊內(nèi)存中存活的對象賦值到另一塊小內(nèi)存中,然后清理剩下的。
    • 標(biāo)記—整理算法(Mark-Compact)
      • 標(biāo)記—整理算法和復(fù)制算法一樣,但是標(biāo)記—整理算法不是把存活對象復(fù)制到另一塊內(nèi)存,而是把存活對象往內(nèi)存的一端移動,然后直接回收邊界以外的內(nèi)存。標(biāo)記—整理算法提高了內(nèi)存的利用率,并且它適合在收集對象存活時間較長的老年代。
    • 分代收集(Generational Collection)
      • 分代收集是根據(jù)對象的存活時間把內(nèi)存分為新生代和老年代,根據(jù)各代對象的存活特點,每個代采用不同的垃圾回收算法。新生代采用復(fù)制算法,老年代采用標(biāo)記—整理算法。
  • 為什么引用計數(shù)器判定對象是否回收不可行?
    • 實現(xiàn)簡單,判定效率高,但不能解決循環(huán)引用問題,同時計數(shù)器的增加和減少帶來額外開銷。
  • 引用類型有哪些種
    • 強引用:默認(rèn)的引用方式,不會被垃圾回收,JVM寧愿拋出OutOfMemory錯誤也不會回收這種對象。
    • 軟引用(SoftReference):如果一個對象只被軟引用指向,只有內(nèi)存空間不足夠時,垃圾回收器才會回收它;
    • 弱引用(WeakReference):如果一個對象只被弱引用指向,當(dāng)JVM進(jìn)行垃圾回收時,無論內(nèi)存是否充足,都會回收該對象。
    • 虛引用(PhantomReference):虛引用和前面的軟引用、弱引用不同,它并不影響對象的生命周期。如果一個對象與虛引用關(guān)聯(lián),則跟沒有引用與之關(guān)聯(lián)一樣,在任何時候都可能被垃圾回收器回收。虛引用通常和ReferenceQueue配合使用。
7.0.0.8 談?wù)凧ava的類加載過程?加載做了什么?驗證做了什么?準(zhǔn)備做了什么?解析做了什么?初始化做了什么?
  • Java文件從編碼完成到最終執(zhí)行過程
    • 編譯:編譯,即把我們寫好的java文件,通過javac命令編譯成字節(jié)碼,也就是我們常說的.class文件。
    • 運行:運行,則是把編譯聲稱的.class文件交給Java虛擬機(JVM)執(zhí)行。
    • 舉個通俗點的例子來說,JVM在執(zhí)行某段代碼時,遇到了classA,然而此時內(nèi)存中并沒有classA的相關(guān)信息,于是JVM就會到相應(yīng)的class文件中去尋找classA的類信息,并加載進(jìn)內(nèi)存中,這就是我們所說的類加載過程。
  • 談?wù)凧ava的類加載過程?
    • 類加載的過程主要分為三個部分:
    • 加載
    • 鏈接
      • 而鏈接又可以細(xì)分為三個小部分:
      • 驗證
      • 準(zhǔn)備
      • 解析
    • 初始化
  • 加載做了什么?
    • 加載指的是把class字節(jié)碼文件從各個來源通過類加載器裝載入內(nèi)存中。
      • 這里有兩個重點:
      • 字節(jié)碼來源。一般的加載來源包括從本地路徑下編譯生成的.class文件,從jar包中的.class文件,從遠(yuǎn)程網(wǎng)絡(luò),以及動態(tài)代理實時編譯
      • 類加載器。一般包括啟動類加載器,擴展類加載器,應(yīng)用類加載器,以及用戶的自定義類加載器。
    • 在加載階段(可以參考java.lang.ClassLoader的loadClass()方法),虛擬機需要完成以下3件事情:
      • 通過一個類的全限定名來獲取定義此類的二進(jìn)制字節(jié)流(并沒有指明要從一個Class文件中獲取,可以從其他渠道,譬如:網(wǎng)絡(luò)、動態(tài)生成、數(shù)據(jù)庫等);
      • 將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運行時數(shù)據(jù)結(jié)構(gòu);
      • 在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)的訪問入口;
    • 加載階段和連接階段(Linking)的部分內(nèi)容(如一部分字節(jié)碼文件格式驗證動作)是交叉進(jìn)行的,加載階段尚未完成,連接階段可能已經(jīng)開始,但這些夾在加載階段之中進(jìn)行的動作,仍然屬于連接階段的內(nèi)容,這兩個階段的開始時間仍然保持著固定的先后順序。
  • 驗證做了什么?技術(shù)博客大總結(jié)
    • 主要是為了保證加載進(jìn)來的字節(jié)流符合虛擬機規(guī)范,不會造成安全錯誤。
    • 包括對于文件格式的驗證,比如常量中是否有不被支持的常量?文件中是否有不規(guī)范的或者附加的其他信息?
    • 對于元數(shù)據(jù)的驗證,比如該類是否繼承了被final修飾的類?類中的字段,方法是否與父類沖突?是否出現(xiàn)了不合理的重載?
    • 對于字節(jié)碼的驗證,保證程序語義的合理性,比如要保證類型轉(zhuǎn)換的合理性。
    • 對于符號引用的驗證,比如校驗符號引用中通過全限定名是否能夠找到對應(yīng)的類?校驗符號引用中的訪問性(private,public等)是否可被當(dāng)前類訪問?
  • 準(zhǔn)備做了什么?
    • 主要是為類變量(注意,不是實例變量)分配內(nèi)存,并且賦予初值。
    • 特別需要注意,初值,不是代碼中具體寫的初始化的值,而是Java虛擬機根據(jù)不同變量類型的默認(rèn)初始值。
    • 比如8種基本類型的初值,默認(rèn)為0;引用類型的初值則為null;常量的初值即為代碼中設(shè)置的值,final static a = 123, 那么該階段a的初值就是123
  • 解析做了什么?
    • 將常量池內(nèi)的符號引用替換為直接引用的過程。
    • 兩個重點:
      • 符號引用。即一個字符串,但是這個字符串給出了一些能夠唯一性識別一個方法,一個變量,一個類的相關(guān)信息。
      • 直接引用??梢岳斫鉃橐粋€內(nèi)存地址,或者一個偏移量。比如類方法,類變量的直接引用是指向方法區(qū)的指針;而實例方法,實例變量的直接引用則是從實例的頭指針開始算起到這個實例變量位置的偏移量
    • 舉個例子來說,現(xiàn)在調(diào)用方法hello(),這個方法的地址是1234567,那么hello就是符號引用,1234567就是直接引用。
    • 在解析階段,虛擬機會把所有的類名,方法名,字段名這些符號引用替換為具體的內(nèi)存地址或偏移量,也就是直接引用。
  • 初始化做了什么?
    • 這個階段主要是對類變量初始化,是執(zhí)行類構(gòu)造器的過程。
    • 換句話說,只對static修飾的變量或語句進(jìn)行初始化。
    • 如果初始化一個類的時候,其父類尚未初始化,則優(yōu)先初始化其父類。
    • 如果同時包含多個靜態(tài)變量和靜態(tài)代碼塊,則按照自上而下的順序依次執(zhí)行。

其他介紹

01.關(guān)于博客匯總鏈接
  • 1.技術(shù)博客匯總
  • 2.開源項目匯總
  • 3.生活博客匯總
  • 4.喜馬拉雅音頻匯總
  • 5.其他匯總
02.關(guān)于我的博客
  • 我的個人站點:www.yczbj.org,www.ycbjie.cn
  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
  • 簡書:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜馬拉雅聽書:http://www.ximalaya.com/zhubo/71989305/
  • 開源中國:https://my.oschina.net/zbj1618/blog
  • 泡在網(wǎng)上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 郵箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles
  • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI