主頁 > 後端開發 > 從零造就JVM大牛(一)

從零造就JVM大牛(一)

2020-10-27 01:27:24 後端開發

引言

從事java的小伙伴大家好,如果你是一名從事java行業的程式員,無論你是小白還是作業多年的老司機,我相信這篇文章一定會給你帶來

不同程度的識訓不敢說你看完我的文章從此精通jvm打遍天下無對手,但我能保證的是看完我的文章并且實踐操作加以理解,至少在jvm

的這個領域碾壓百分之80以上的程式員,廢話不多說我們進入正題,(此文連載,請持續關注!!!)

一:類加載

我相信很多從事java的小伙伴兒在網上或者視頻上也看了很多關于jvm的文章和講解,但總覺得缺少點兒什么,那么今天我來告訴你為什么

會有這種感覺,因為很多人或者說很多文章講的jvm都沒有從最底層,沒有從人的習慣性思維上去剖析,從今天開始,我就要把jvm那點兒

事兒給小伙伴兒們說清楚,保證你會見到jvm領域的另一番美妙的天地,

廢話不多說直接進入正題,講類加載器之前,咱們先從類加載開始說起,那么java底層是如何加載一個類的呢,他的順序是怎么樣的呢?下面

我會細致的手把手帶你分析,

在java代碼中,型別的加載,連接,初始化程序都是在程式運行期間完成的.

型別加載:這里的型別指的的是什么呢?

答:型別就是指的我們Java源代碼通過編譯后的class檔案

型別的來源有哪些?型別的來源我總結了以下7點內容

答:

1:本地磁盤

2:網路下載.class檔案

3:war,jar下加載.class檔案

4:從專門的資料庫中讀取.class檔案(少見)

5:將java源檔案動態編譯成class檔案

6:典型的就是動態代理,通過運行期生成class檔案

7:我們的jsp會被轉換成servlet,而我們的serlvet是一個java檔案,會被編譯成class檔案

那么我們的是通過什么進行加載的呢?是如何被加載到jvm中的呢?

答:通過我們的類加載器(classLoader)進行加載

首先我們來了解以下類加載器的種類?系統級別的類加載器如下

1:啟動類加載器 (Bootstrap Classloader)

2:擴展類加載器(Extention ClassLoader)

3:應用類加載器

非系統級別類加載器如下:

1:自定義類加載器

接下來我們一個一個類加載剖析,好好的講講他們的前世今生,以及作用,讓你徹底的了解類加載器到底是什么,

一:啟動類加載器(Bootstrap Classloader)重點剖析

加載的職責:負責加載JAVA_HOME/jre/lib/rt.jar,該加載器是有C++實作,不是ClassLoader類的子類,

接下來我們會以代碼穿插的方式來講清楚類加載器到底是什么,很多人只是用嘴說,這樣當時聽懂了,然而并不能

真正的體會到類加載器以及類加載的精髓,只有用示例去驗證你的說法,這才是正確的學習姿勢,廢話不多說開搞,

首先創建一個java工程 jvm-classloader jdk版本采用1.8.0_144 包名為src/com.test 創建類 MainClass01,結構如下圖:

首先我們通過代碼的形式來看看,我們的啟動類加載器到底加載了哪些包

package com.test;

import java.util.Arrays;
import java.util.List;

/**
 * jvm 類加載器 第一章
 * @author 奇客時間-時光
 * 列印啟動類加載器加載的路徑
 */
public class MainClass01 {
    
    public static void main(String[] args) {

        String bootStrapLoadingPath = System.getProperty("sun.boot.class.path");
        List<String> bootLoadingPathList = Arrays.asList(bootStrapLoadingPath.split(";"));
        for(String bootPath:bootLoadingPathList) {
            System.out.println("啟動類加載器加載的目錄:{}"+bootPath);
        }
    }
}



"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=60329:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;I:\jvm\out\production\jvm-classloader" com.test.MainClass01
啟動類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar
啟動類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar
啟動類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\sunrsasign.jar
啟動類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar
啟動類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar
啟動類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar
啟動類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar
啟動類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\classes

Process finished with exit code 0


通過以上運行代碼可知,啟動類加載器加載如下的這些jar包,包含了大家常說的rt.jar,那么現在有個問題,從運行結果上我們看啟動類加載加載了C:\Program Files\Java\jdk1.8.0_144\jre\classes這個目錄下的類,那么我們自己寫一個類,編譯成Class檔案以后丟到這個目錄下它會被加載嘛?我們不妨來試一下,我們在com.test包下面創建一個Cat類,然后找到編譯好的Cat.class檔案,扔到C:\Program Files\Java\jdk1.8.0_144\jre\classes目錄下,

如圖可知,我們jre目錄并沒有classes目錄,所以我們手動創建一個,并且把Cat.class檔案扔進去,然后在測驗類中獲取一下Cat.classLoader是什么,由此就可以知道Cat是不是被啟動類加載器加載了,

由上面的運行結果可知,我們的Cat類確實被啟動類加載器給加載了,為什么是null?當一個類被啟動類加載器加載以后,那么他的getClassLoader回傳的結果就是null,這也是啟動類加載器和其他類加載器不一樣的地方,

補充說明:Bootstrap Classloader加載器是由C++去加載的,然后Bootstrap Classloader加載rt等jar包,Bootstrap Classloader加載器也加載了系統類加載器和擴展類類加載器他們都位于sun.boot.class.path這個路徑,

二:擴展類加載器(Extention ClassLoader)重點剖析

加載的職責:加載java平臺擴展的jar包,\lib\ext,可以通過-Djava.ext.dirs指定加載的路徑

該加載器是有java代碼撰寫的,并且是ClassLoader的子類,位于sun.misc.Launcher$ExtClassLoader 是我們launch類的一個內部類

同樣的我們在MainClass01中測驗一下

package com.test;

import java.util.Arrays;
import java.util.List;

/**
 * jvm 類加載器 第一章
 * @author 奇客時間-時光
 * 列印啟動類加載器加載的目錄
 * 列印擴展類加載器加載的目錄
 */
public class MainClass01 {

    public static void main(String[] args) {

        String extLoadingPath = System.getProperty("java.ext.dirs");
        List<String> list = Arrays.asList(extLoadingPath.split(";"));
        for(String extpath:list) {
            System.out.println("擴展類加載器加載的目錄:{}"+extpath);
        }
    }
}


"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=59761:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;I:\jvm\out\production\jvm-classloader" com.test.MainClass01
擴展類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext
擴展類加載器加載的目錄:{}C:\WINDOWS\Sun\Java\lib\ext

Process finished with exit code 0

三:應用類加載器(APP ClassLoader)重點剖析

加載的職責:負責加載我們工程目錄下classpath下的檔案下.class以及jar包

該加載器也是由Java代碼撰寫的也是我們ClassLoader的一個子類,sun.misc.Launcher$AppClassLoader 是我們的launcher的內部類

String appLoadingPath = System.getProperty("java.class.path");

同樣的,我們在MainClass01中運行如下代碼:

package com.test;

import java.util.Arrays;
import java.util.List;

/**
 * jvm 類加載器 第一章
 * @author 奇客時間-時光
 * 列印啟動類加載器加載的目錄
 * 列印擴展類加載器加載的目錄
 * 列印應用類加載器加載的目錄
 */
public class MainClass01 {

    public static void main(String[] args) {

        String appLoadingPath = System.getProperty("java.class.path");
        List<String> appLoadingPathList = Arrays.asList(appLoadingPath.split(";"));
        for(String appPath:appLoadingPathList) {
            System.out.println("應用類加載器加載的目錄:{}"+appPath);
        }

    }
}


"C:\Program Files\Java\jdk1.8.0_144\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=52479:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar;I:\jvm\out\production\jvm-classloader" com.test.MainClass01
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\charsets.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\deploy.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\access-bridge-64.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\cldrdata.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\dnsns.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jaccess.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\jfxrt.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\localedata.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\nashorn.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunec.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunjce_provider.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunmscapi.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\sunpkcs11.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\ext\zipfs.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\javaws.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\jce.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfr.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\jfxswt.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\jsse.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\management-agent.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\plugin.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\resources.jar
應用類加載器加載的目錄:{}C:\Program Files\Java\jdk1.8.0_144\jre\lib\rt.jar
應用類加載器加載的目錄:{}I:\jvm\out\production\jvm-classloader
應用類加載器加載的目錄:{}C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar

Process finished with exit code 0

四:我們自定義的類加載器

由此可知,三種類加載所加載對應的目錄已經介紹完畢,后面還會對這三種類加載器進行深入的決議,下面我們再看看自定義類加載器,為什么需要自定義類加載器呢?因為有些場景下jdk給我們提供的三種類加載器沒有辦法實作我們的個性化需求,關于為什么,這個我后面會詳細闡述,我們首先看看自定義類加載器以及原始碼,

如何寫自定義類加載器呢?首先我們看看jdk 提供的java原版 doc檔案是如何描述的,這里要說一下,無論各位小伙伴以后學什么技術,第一手資料很重要,因為是最權威的,也沒有經過任何加工的,也是最準確的,所以我們直接看原始碼中的doc檔案即可,

那么我們要寫自己的類加載器,對于小白來說肯定是不清楚如何下手的,這個時候,我們就要想,自定義類加載肯定是和類加載器有關系,不放我們看看java有沒有提供關于類加載器相關的類,于是我們可以通過IDEA的編輯器進行搜索一下,類加載器顧名思義 ClassLoader先搜索一下,結果還真有,說明這是一個跟類加載器有關的類,我們直接閱讀他的java doc,

我把之前編輯好的中文注釋貼在下面,大家可以閱讀,不過這里還是建議學習jdk原始碼的小伙伴還是自己閱讀比較好,這樣記得也比較深刻,

4.1:ClassLoader抽象類 檔案解讀

/**
* A class loader is an object that is responsible for loading classes(類加載器是一個物件,作用就是用來加載器類的). The
* class ClassLoader is an abstract class(本類是一個抽象的類). Given the binary name of a class
(給定一個類的二進制名字,比如java.lang.String就是String類的二進制名字)
, a class loader should attempt to
* locate or generate data that constitutes a definition for the class(那么類加載器嘗試去定位(已經存在我們的磁盤檔案中)
或者生成(為啥有生成,存在動態代理)構成class的定義資料).
A typical strategy is to transform the name into a file name and then read a
* "class file" of that name from a file system.(通常的策略就是把我們的類的二進制名稱轉換為我們的檔案名稱 
比如java.lang.String 轉換為java/lang/String)
那么就會根據這個檔案名稱去檔案系統找到該class檔案,
*
* <p> Every {@link Class <tt>Class</tt>} object contains a {@link
* Class#getClassLoader() reference} to the <tt>ClassLoader</tt> that defined
* it. (每一個通過類加載器加載的class檔案后會回傳一個Class物件,該Class物件會包含一個加載他的ClassLoader的物件參考)
*
* <p> <tt>Class</tt> objects for array classes are not created by class
* loaders, but are created automatically as required by the Java runtime.
我們的陣列物件的Class物件不是由我們的類加載器創建的,而是由我們的jvm根據需要在運行時間創建出來的.
* The class loader for an array class, as returned by {@link
* Class#getClassLoader()} is the same as the class loader for its element
* type;(我們獲取到了陣列型別的CLass物件,通過該Class物件呼叫getClassLoader()回傳的是跟我們陣列元素中的類加載器是一樣的)
if the element type is a primitive type, then the array class has no
* class loader.(如果我們原生的陣列 那么該陣列的Class沒有類加載器)
* <p> Applications implement subclasses of <tt>ClassLoader</tt> in order to
* extend the manner in which the Java virtual machine dynamically loads
* classes.(我們通過實作ClassLoader,可以動態來擴展我們類加載的方式)
* <p> Class loaders may typically be used by security managers to indicate
* security domains.
* <p> The <tt>ClassLoader</tt> class uses a delegation model to search for
* classes and resources.(ClassLoader使用雙親委派模型來尋找類和資源) Each instance of <tt>ClassLoader</tt> has an
* associated parent class loader(classloader的每一個實體都會有一個parent的classLoader). When requested to find a class or
* resource, a <tt>ClassLoader</tt> instance will delegate the search for the
* class or resource to its parent class loader before attempting to find the
* class or resource itself(當我們發起類加載的請求,那么類加載器自己去尋找資源之前委托給父類). The virtual machine's built-in class loader,
* called the "bootstrap class loader", (虛擬機內嵌的classLoader是叫做啟動類加載器)does not itself have a parent but may
* serve as the parent of a <tt>ClassLoader</tt> instance.(啟動類加載器是沒有雙親的,但是他可以作為其他類加載器的雙親)
* <p> Class loaders that support concurrent loading of classes are known as
* <em>parallel capable</em> class loaders(若一個類加載器支持并行加載的話,那么這個類加載器就叫做并行類加載器) and are required to register
* themselves at their class initialization time by invoking the
* {@link
* #registerAsParallelCapable <tt>ClassLoader.registerAsParallelCapable</tt>}
* method.(若一個類加載器想要成為一個并行類加載器的話,那么在該類加載器初始化的時候要求去呼叫ClassLoader.registerAsParallelCapable方法)
Note that the <tt>ClassLoader</tt> class is registered as parallel
* capable by default(默認情況下當前的這個類ClassLoader這個抽象類模式是并行加載器器). However, its subclasses still need to register themselves
* if they are parallel capable.(然而我們的子加載器需要成為并行的內加載器,需要注冊自己為并行的類加載器) <br>
* In environments in which the delegation model is not strictly
* hierarchical(若我們的類加載器不是屬于雙親委派的模型情況下), class loaders need to be parallel capable(類加載器需要注冊為并行的加載器), otherwise class
* loading can lead to deadlocks because the loader lock is held for the
* duration of the class loading process (see {@link #loadClass
* <tt>loadClass</tt>} methods).(不然在內加載的階段會導致死鎖)
* <p> Normally, the Java virtual machine loads classes from the local file
* system in a platform-dependent manner(通常情況下 jvm從本地磁盤下去加載類). For example, on UNIX systems, the
* virtual machine loads classes from the directory defined by the
* <tt>CLASSPATH</tt> environment variable.(在unix系統中,虛擬機從classpath下加載類)
* <p> However, some classes may not originate from a file(然而,有些class檔案不是存在我們的磁盤檔案中); they may originate
* from other sources, such as the network(比如從網路上), or they could be constructed by an
* application(動態代理生產的). The method {@link #defineClass(String, byte[], int, int)
* <tt>defineClass</tt>} converts an array of bytes into an instance of class
* <tt>Class</tt>. (我們的defineClass方法會把位元組陣列轉為我們一個class的實體)Instances of this newly defined class can be created using
* {@link Class#newInstance <tt>Class.newInstance</tt>}.這個實體可以通過我們的newInstance來呼叫
全盤委托模型:由我們classloader加載出來的class那么該類中的方法或建構式可能參考其他類,jvm會呼叫同一個類加載器去加載被應用的類
* <p> The methods and constructors of objects created by a class loader may
* reference other classes(). To determine the class(es) referred to, the Java
* virtual machine invokes the {@link #loadClass <tt>loadClass</tt>} method of
* the class loader that originally created the class.
* <p> For example, an application could create a network class loader to
* download class files from a server. Sample code might look like:
* <blockquote><pre>
* ClassLoader loader&nbsp;= new NetworkClassLoader(host,&nbsp;port);
* Object main&nbsp;= loader.loadClass("Main", true).newInstance();
* &nbsp;.&nbsp;.&nbsp;.
* </pre></blockquote>
我們自定義的class laoder 需要重寫我們的ClassLoader類的findClass 和我們的loadClassData方法
* <p> The network class loader subclass must define the methods {@link
* #findClass <tt>findClass</tt>} and <tt>loadClassData</tt> to load a class
* from the network. Once it has downloaded the bytes that make up the class,
* it should use the method {@link #defineClass <tt>defineClass</tt>} to
* create a class instance. A sample implementation is:
* <blockquote><pre>
* class NetworkClassLoader extends ClassLoader {
* String host;
* int port;
* public Class findClass(String name) {
* byte[] b = loadClassData(name);
* return defineClass(name, b, 0, b.length);
* }
* private byte[] loadClassData(String name) {
* // load the class data from the connection
* &nbsp;.&nbsp;.&nbsp;.
* }
* }
* </pre></blockquote>
* <h3> <a name="name">Binary names</a> </h3>
* <p> Any class name provided as a {@link String} parameter to methods in
* <tt>ClassLoader</tt> must be a binary name as defined by
* <cite>The Java&trade; Language Specification</cite>.
* <p> Examples of valid class names include:
* <blockquote><pre>
* "java.lang.String" 表示我們的String類的二進制名稱
* "javax.swing.JSpinner$DefaultEditor" 表示JSpinner的內部類DefaultEditor的二進制名稱
* "java.security.KeyStore$Builder$FileBuilder$1" 表示java.security.KeyStore類的內部類Builder類的內部類的FileBuilder的第一個內部類
* "java.net.URLClassLoader$3$1" 表示java.net.URLClassLoader類中第三個內部類中的第一個內部類
* </pre></blockquote>
* @see #resolveClass(Class)
* @since 1.0
*/
public abstract class ClassLoader {}

通過上面檔案的描述我們可以實作ClassLoader這個類就可以動態來擴展我們類加載的方式,從上面的檔案中也可以看出,加載一個class檔案需要用到loadClass方法,并且還要重寫ClassLoader類的findClass 和我們的loadClassData方法

接下來對上述的幾個重要的方法的java doc進行翻譯

4.2:我們的ClassLoader類加載器重要方法詳解

  • loadClass方法詳解
/**
* Loads the class with the specified <a href="https://www.cnblogs.com/tomakemyself/p/#name">binary name</a>. (根據二進制名稱進行加載) The
* default implementation of this method searches for classes in the(下面是呼叫方法的順序)
* following order:
* <ol>
* <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
* has already been loaded. </p></li> 呼叫findloaderClass檢查類是否被加載過
* <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
* on the parent class loader(首先會呼叫父類的loaderClass來加載). If the parent is <tt>null</tt> the class
* loader built-in to the virtual machine is used(若發現父類是null,那就說明已經到了頂層的啟動類加載器),
instead. </p></li>
* <li><p> Invoke the {@link #findClass(String)} method to find the
* class. </p></li> 接下來就呼叫我們的findClass方法來查找class
* </ol>
* <p> If the class was found using the above steps(若通過上述步驟找到了class檔案), and the
* <tt>resolve</tt> flag is true(那么就設定resolve為true), this method will then invoke the {@link
* #resolveClass(Class)} method on the resulting <tt>Class</tt> object.(決議來那么就通過呼叫resolverClass
回傳物件)
* <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
* #findClass(String)}, rather than this method. </p>
強烈的要求我們子類實作ClassLoader 那么我們必須要從寫findClass方法
* <p> Unless overridden, this method synchronizes on the result of
* {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
* during the entire class loading process.
* @param name
* The <a href="https://www.cnblogs.com/tomakemyself/p/#name">binary name</a> of the class
* @param resolve
* If <tt>true</tt> then resolve the class
* @return The resulting <tt>Class</tt> object
* @throws ClassNotFoundException
* If the class could not be found
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
//檢查根據二進制名稱是否被加載過
Class<?> c = findLoadedClass(name);
//沒有加載過
if (c == null) {
long t0 = System.nanoTime();
try {
// 判斷有沒有父類,有父類 呼叫父類的loadClass
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//沒有父類,表面當前的類加載器已經到了啟動類加載器了,那么通過啟動類加載器去找
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//還沒找到
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//呼叫findClass去找
c = findClass(name);

// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
//決議我們的class進行連接 等操作
resolveClass(c);
}
return c;
}
}

上面這段java doc檔案中有幾個重要的點需要注意一下

1:根據二進制名稱進行加載

2:呼叫findloaderClass檢查類是否被加載過

3:首先會呼叫父類的loaderClass來加載

4:若發現父類是null,那就說明已經到了頂層的啟動類加載器

5:接下來就呼叫我們的findClass方法來查找class

這幾部是類加載器加載類的加載程序,比較重要的程序,希望各位小伙伴兒一定要結合原始碼牢記,

上述又提到了另一個方法

  • findClass()方法
/**
* Finds the class with the specified <a href="https://www.cnblogs.com/tomakemyself/p/#name">binary name</a>.通過給定的二進制名稱查找出class檔案
* This method should be overridden by class loader implementations(這個方法應該被遵循雙親委托模型的子類重寫) that
* follow the delegation model for loading classes(), and will be invoked by
* the {@link #loadClass <tt>loadClass</tt>} method after checking the
* parent class loader for the requested class(這個方法會被loadClass方法呼叫,在父類加載器檢查了是否加載過后). The default implementation
* throws a <tt>ClassNotFoundException</tt>.
*
* @param name
* The <a href="https://www.cnblogs.com/tomakemyself/p/#name">binary name</a> of the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*
* @since 1.2
*/
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
  • defineClass()方法
/**
這個方法的作用就是用來把位元組陣列轉為一個Class物件,這個物件
* Converts an array of bytes into an instance of class <tt>Class</tt>.
* Before the <tt>Class</tt> can be used it must be resolved(這個Class物件能被使用必須是經過決議的因為在決議階段會進行驗證). This method
* is deprecated in favor of the version that takes a <a
* href="https://www.cnblogs.com/tomakemyself/p/#name">binary name</a> as its first argument, and is more secure. 就是不推薦用這個方法,而是
推薦用它重寫的方法,因為重寫方法安全些(因為重寫方法中一個引數是我們的是傳入二進制模型,他是通過安全域進行保護的)
*
* @param b
* The bytes that make up the class data. The bytes in positions
* <tt>off</tt> through <tt>off+len-1</tt> should have the format
* of a valid class file as defined by
* <cite>The Java&trade; Virtual Machine Specification</cite>.
*
* @param off
* The start offset in <tt>b</tt> of the class data
*
* @param len
* The length of the class data
*
* @return The <tt>Class</tt> object that was created from the specified
* class data
*
* @throws ClassFormatError
* If the data did not contain a valid class
*
* @throws IndexOutOfBoundsException
* If either <tt>off</tt> or <tt>len</tt> is negative, or if
* <tt>off+len</tt> is greater than <tt>b.length</tt>.
*
* @throws SecurityException
* If an attempt is made to add this class to a package that
* contains classes that were signed by a different set of
* certificates than this class, or if an attempt is made
* to define a class in a package with a fully-qualified name
* that starts with "{@code java.}".
*
* @see #loadClass(String, boolean)
* @see #resolveClass(Class)
*
* @deprecated Replaced by {@link #defineClass(String, byte[], int, int)
* defineClass(String, byte[], int, int)}
*/
@Deprecated
protected final Class<?> defineClass(byte[] b, int off, int len)
throws ClassFormatError
{
return defineClass(null, b, off, len, null);
}

以上兩個方法在注釋中均已經解釋,

看完以上的java doc檔案,那么我們就可以來寫一個自定義的類加載器了,

創建自定義類加載的步驟如下:

1:創建一個類名為Test01ClassLoader,這個是我們自己的類加載器

2:繼承ClassLoader類,并且重寫findClass方法,

3:創建方法loadClassData構建一個byte[]陣列,

4:在findClass中呼叫父類的defineClass方法,傳入相關資訊(引數name是名稱,傳入構建好的byte陣列,起始下標從0開始,最后是陣列長度)

5:建構式不能少,如果帶引數,意為你指定雙親加載器是誰(類加載器雙親委托模型后面文章中介紹,先記住自定義類加載器的寫法和步驟即可)

6:最后就是在main函式中用我們自定義的類加載器去加載我們指定的類來測驗,,,

package com.test;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * jvm 類加載器 第一章
 * @author 奇客時間-時光
 * 自定義類加載器
 * 1,繼承ClassLoader
 * 2,通過構造器指定當前類加載器的父加載器
 * 3,重寫findClass方法
 * 4,構造一個byte[]字數
 * 5,呼叫this.defineClass方法創建classLoader的實體
 */
public class Test01ClassLoader extends ClassLoader {

    private String classLoaderName;

    //宣告檔案后綴
    private final String x = ".class";

    //宣告檔案路徑
    private String path;

    public void setPath(String path) {
        this.path = path;
    }

    /**
     * 使用默認的類加載器作為當前類加載器的雙親{APPClassLoader作為雙親}
     * @param classLoaderName
     */
    public Test01ClassLoader(String classLoaderName){
        super();//指定系統類加載器為我們的父加載器
        this.classLoaderName = classLoaderName;
    }

    /**
     * 使用我們自己指定的類加載器作為當前類加載器的雙親
     * @param parentClassLoaderNane
     * @param classLoaderName
     */
    public Test01ClassLoader(ClassLoader parentClassLoaderNane,String classLoaderName){
        super(parentClassLoaderNane);
        this.classLoaderName = classLoaderName;
    }

    /**
     * 重寫findClass方法,這一步非常重要必不可少
     * @param name
     * @return
     * @throws ClassNotFoundException
     */
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("自己的類加載器被加載了");
        byte[] bytes = this.loadClassData(name);
        return this.defineClass(name,bytes,0,bytes.length);
    }

    /**
     * 回傳一個位元組數字通過給定的類名
     * @param name
     * @return
     */
    private byte[] loadClassData(String name ){

        InputStream inputStream = null;
        byte[] bytes = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            name = name.replace(".","\\");
            inputStream= new FileInputStream(this.path+new File(name+this.x));
            byteArrayOutputStream = new ByteArrayOutputStream();

            int ch = 0;
            while(-1 !=(ch= inputStream.read())){
                byteArrayOutputStream.write(ch);
            }
            bytes=byteArrayOutputStream.toByteArray();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
                byteArrayOutputStream.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return bytes;
    }


    public static void main(String[] args) throws Exception {
        //創建自定義類加載器的一個實體,并且通過構造器指定名稱
        Test01ClassLoader myClassLoader = new Test01ClassLoader("loader1");
        //設定加載路徑
        myClassLoader.setPath("I:\\jvm\\out\\production\\jvm-classloader\\");
        //呼叫loadClass方法來加載我們的class檔案
        Class<?> classz = myClassLoader.loadClass("com.test.Dog");
        //通過構造器創建實體
        Object object = classz.getDeclaredConstructor().newInstance();
        //查看我們創建的實體是由哪個類加載器加載的
        System.out.println(object.getClass().getClassLoader());

        /*myClassLoader.setPath("D:\\classes\\");
        Class classz1 = myClassLoader.loadClass("com.jdyun.jvm05.Test");
        Object object1 = classz1.getDeclaredConstructor().newInstance();
        System.out.println(classz1.getClassLoader());
        */
        /*System.out.println(System.getProperty("sun.boot.class.path"));
        System.out.println(System.getProperty("java.ext.dirs"));
        System.out.println(System.getProperty("java.class.path"));
        System.out.println(Test12.getSystemClassLoader());*/

        /*MyTest02[] list = new MyTest02[1];
        System.out.println(list.getClass().getClassLoader());*/

    }

}

上述代碼運行結果顯示還是通過AppClassLoader來加載的Dog類,那么這是為什么呢?因為我們在建構式中指定的將系統類加載器也就是我們的AppClassLoader作為我們的雙親,那么根據雙親委托模型,父加載器能加載的類,就由父加載器來加載,所以我們自定義的類加載沒有加載到Dog類,(雙親委托模型后面文章會詳細介紹,先記住這個結論就好,)

接下來我們對代碼進行一點兒改造,來測驗我們自定義的類加載是否生效了,

我在G:\jdyun-jvm\out\production\jdyun-jvm\\com\\jdyun\\jvm05\\目錄下創建一個class檔案Test.class,運行結果如下圖所示

我把加載的路徑改變,這樣我們我們的AppClassloader他只會加載classPath路徑下的檔案,而我們在外部指定的檔案,就自然而然的被我們的自定義類加載器給加載了,

接下來我們對代碼進行一下小小的改動,我們創建兩個自定義類加載器的實體,然后看看加載出來的Class物件是不是一樣的,

public static void main(String[] args) throws Exception {
        //創建自定義類加載器的一個實體,并且通過構造器指定名稱
        Test01ClassLoader myClassLoader = new Test01ClassLoader("loader1");
        //設定加載路徑
        myClassLoader.setPath("G:\\jdyun-jvm\\out\\production\\jdyun-jvm\\");
        //呼叫loadClass方法來加載我們的class檔案
        Class<?> classz = myClassLoader.loadClass("com.jdyun.jvm05.Test");
        //通過構造器創建實體
        Object object = classz.getDeclaredConstructor().newInstance();
        //查看我們創建的實體是由哪個類加載器加載的
        System.out.println(classz.getClassLoader());
        System.out.println(classz.hashCode());

        //創建自定義類加載器的一個實體,并且通過構造器指定名稱
        Test01ClassLoader myClassLoader2 = new Test01ClassLoader("loader1");
        //設定加載路徑
        myClassLoader2.setPath("G:\\jdyun-jvm\\out\\production\\jdyun-jvm\\");
        //呼叫loadClass方法來加載我們的class檔案
        Class<?> classz2 = myClassLoader2.loadClass("com.jdyun.jvm05.Test");
        //通過構造器創建實體
        Object object2 = classz2.getDeclaredConstructor().newInstance();
        //查看我們創建的實體是由哪個類加載器加載的
        System.out.println(classz2.getClassLoader());
        System.out.println(classz2.hashCode());

    }


列印結果:
自己的類加載器被加載了
com.test.Test01ClassLoader@1540e19d
21685669
自己的類加載器被加載了
com.test.Test01ClassLoader@7f31245a
325040804

通過上面代碼可知,同一個類加載器加載的同一個Class檔案加載出來的Class物件不是同一個,并且兩個class物件之間也是不能相互轉換的,

好了各位小伙伴兒,第一篇文章咱們就先介紹到這里,大家現在了解一下4種類加載器即可,此專題的文章后面還會陸續連載,期待各位小伙伴的閱讀,

另外筆者在公眾號:奇客時間,給大家收錄了1000多道今年互聯網公司的面試真題
面試真題-回復關鍵字形式:公司-部門-面試輪次,例如:阿里-螞蟻金服-一面,自動回復面試真題;

當前已經收錄如下:
位元組跳動-抖音-面試輪次, 搜狐-搜索組-面試輪次, OPPO-商城-面試輪次, 58同城-基礎架構部-面試輪次,湖南臺-芒果TV-面試輪次 , 騰訊-乘車碼-面試輪次 , 騰訊-微信支付-面試輪次 , 騰訊-零售新業務-面試輪次 , 騰訊-直播平臺-面試輪次, 快手-廣告業務部-面試輪次 , 貝殼找房-商品組-面試輪次 , 百度-資訊流-面試輪次 , 京東-零售-面試輪次 , 京東-物流-面試輪次 , 京東-電商-面試輪次 , 滴滴-小桔車服-面試輪次 , 滴滴-金融-面試輪次 , 阿里-高德-面試輪次 , 阿里-大文娛-面試輪次 , 阿里-健康-面試輪次 , 阿里-螞蟻金服-面試輪次 , 美團-外賣-面試輪次 , 美團-風控-面試輪次

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/192825.html

標籤:Java

上一篇:MyBatis不太好用,我自己造了一個輪子

下一篇:【java】錯誤提示java.lang.ArithmeticException: / by zero

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more