Docker 的優點
882
2025-03-31
文章目錄
1、類加載機制
1.1、類加載運行全過程
1.2、類的加載時機
1.3、不會初始化(可能會加載)
1.4、類加載器和雙親委派機制
1.4.1、類加載器特點
1.4.2、類加載器初始化過程
1.4.3、全盤加載機制
1.4.4、自定義類加載器
1.4.5、打破雙親加載機制
1.4.6、擴展:tomcat如何打破雙親加載機制
1.5、顯示當前ClassLoader加載了哪些Jar
1.6、添加引用類的幾種方式
1、類加載機制
1.1、類加載運行全過程
當我們啟動一個Java文件的時候,比如
點擊main方法時,首先需要通過類加載器把主類加載到JVM,具體如下
粗略地:
地址:https://www.processon.com/view/link/613df3f6e401fd7aedfe372d
詳細地:
細分:
加載
在硬盤上查找并通過IO讀入字節碼文件,使用到類時才會加載,例如調用類的 main()方法,new對象等等,在加載階段會在內存中生成一個代表這個類的 java.lang.Class對象,作為方法區這個類的各種數據的訪問入口 (找 Class 文件)
驗證
校驗字節碼文件的正確性(驗證格式、依賴)
準備
給類的靜態變量分配內存,并賦予默認值(靜態字段、方法表)
解析
將符號引用替換為直接引用,該階段會把一些靜態方法(符號引用,比如 main()方法)替換為指向數據存內存的指針或句柄等(直接引用),這是所謂的靜態鏈接過 程(類加載期間完成),動態鏈接(調方法)是在程序運行間完成的將符號引用替換為直接引用(符號解析為引用)
初始化
對類的靜態變量初始化為指定的值,執行靜態代碼塊(構造器、靜態變量賦值、靜態代碼塊)
使用
卸載
鏈接:https://www.processon.com/view/link/613e05d8e401fd7aedfe52f3
類被加載到方法區中后后主要包含 運行時常量池、類型信息、字段信息、方法信息、類加載器的引用、對應class實例的引用等信息。
**類加載器的引用:**這個類到類加載器實例的引用
**對應class實例的引用:**類加載器在加載類信息放到方法區中后,會創建一個對應的Class 類型的對象實例放到堆(Heap)中, 作為開發人員訪問方法區中類定義的入口和切入點
**注意:**主類在運行過程中如果使用到其它類,會逐步加載這些類。 jar包或war包里的類不是一次性全部加載的,是使用到時才加載。
package com.zhz; /** * @author zhouhengzhe * @Description: 測試動態加載 * @date 2021/9/12下午9:55 * @since */ public class TestDynamicLoad { static { System.out.println("*************load TestDynamicLoad************"); } public static void main(String[] args) { new A(); System.out.println("*************load test************"); B b = null; //B不會加載,除非這里執行new B() } } class A { static { System.out.println("*************load A************"); } public A() { System.out.println("*************initial A************"); } } class B { static { System.out.println("*************load B************"); } public B() { System.out.println("*************initial B************"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

1.2、類的加載時機
當虛擬機啟動時,初始化用戶指定的主類,就是啟動執行的 main 方法所在的類;
當遇到用以新建目標類實例的 new 指令時,初始化 new 指令的目標類,就是 new 一個類的時候要初始化;
當遇到調用靜態方法的指令時,初始化該靜態方法所在的類;
當遇到訪問靜態字段的指令時,初始化該靜態字段所在的類;
子類的初始化會觸發父類的初始化;
如果一個接口定義了 default 方法,那么直接實現或者間接實現該接口的類的初始化,會觸發該接口的初始化;
使用反射 API 對某個類進行反射調用時,初始化這個類,其實跟前面一樣,反射調用要么是已經有實例了,要么是靜態方法,都需要初始化;
當初次調用 MethodHandle 實例時,初始化該 MethodHandle 指向的方法所在的類。
1.3、不會初始化(可能會加載)
通過子類引用父類的靜態字段,只會觸發父類的初始化,而不會觸發子類的初始化。
定義對象數組,不會觸發該類的初始化。
常量在編譯期間會存入調用類的常量池中,本質上并沒有直接引用定義常量的類,不會觸發定義常量所在的類。
通過類名獲取 Class 對象,不會觸發類的初始化,Hello.class 不會讓 Hello 類初始化。
通過 Class.forName 加載指定類時,如果指定參數 initialize 為 false 時,也不會觸發類初始化,其實這個參數是告訴虛擬機,是否要對類進行初始化。(Class.forName(“jvm.Hello”))默認會加載 Hello 類。
通過 ClassLoader 默認的 loadClass 方法,也不會觸發初始化動作(加載了,但是不初始化)。
1.4、類加載器和雙親委派機制
引導類加載器(BootstrapClassLoader):負責加載支撐JVM運行的位于JRE的lib目錄下的核心類庫,比如rt.jar、charsets.jar等
擴展類加載器(ExtClassLoader):負責加載支撐JVM運行的位于JRE的lib目錄下的ext擴展目錄中的JAR類包
應用程序類加載器(AppClassLoader):負責加載ClassPath路徑下的類包,主要就是加載你自己寫的那些類
自定義加載器:負責加載用戶自定義路徑下的類包
雙親委派機制是什么?
1、我們寫的類會由AppClassLoader先去加載,然后AppClassLoader會委托ExtClassLoader去加載,ExtClassLoader會委托BootstrapClassLoader去加載
2、如果BootstrapClassLoader加載找不到目標類,就會回退給ExtClassLoader,讓ExtClassLoader去加載,ExtClassLoader加載找不到目標類,就由AppClassLoader自己加載。
源碼(證據):
//ClassLoader的loadClass方法,里面實現了雙親委派機制 protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded // 檢查當前類加載器是否已經加載了該類 Class> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { 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(); //都會調用URLClassLoader的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) { resolveClass(c);//不會執行 } return c; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
解釋:
1、檢查指定名稱的類是否已經加載過,如果已經加載過,就直接返回。
2、如果沒加載過,就判斷一下是否有父加載器,如果有父加載器,由父加載器加載(即調用parent.loadClass(name, false)),如果沒有父加載器就調用BootstrapClassLoader->findBootstrapClassOrNull(name);來加載。
3、如果父加載器及BootstrapClassLoader都沒有找到指定的類,那么調用當前類加載器的 findClass方法來完成類加載。
1.4.1、類加載器特點
1、雙親委托
2、防止重復加載:當父類加載器加載過后,子類加載器就不需要再次加載了,保證被加載類的唯一性
3、負責依賴
4、緩存加載
5、沙箱安全機制:防止Java核心類被隨意篡改
package java.lang; /** * @author zhouhengzhe * @Description: 沙箱安全機制 * @date 2021/9/18上午2:34 * @since */ public class String { public static void main(String[] args) { System.out.println("aaa"); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.4.2、類加載器初始化過程
//Launcher的構造方法 public Launcher() { Launcher.ExtClassLoader var1; try { //構造擴展類加載器,在構造的過程中將其父加載器設置為null var1 = Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { //構造應用類加載器,在構造的過程中將其父加載器設置為ExtClassLoader, //Launcher的loader屬性值是AppClassLoader,我們一般都是用這個類加載器來加載我們自己寫的應用程序 this.loader = Launcher.AppClassLoader.getAppClassLoader(var1); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } Thread.currentThread().setContextClassLoader(this.loader); String var2 = System.getProperty("java.security.manager"); if (var2 != null) { SecurityManager var3 = null; if (!"".equals(var2) && !"default".equals(var2)) { try { var3 = (SecurityManager)this.loader.loadClass(var2).newInstance(); } catch (IllegalAccessException var5) { } catch (InstantiationException var6) { } catch (ClassNotFoundException var7) { } catch (ClassCastException var8) { } } else { var3 = new SecurityManager(); } if (var3 == null) { throw new InternalError("Could not create SecurityManager: " + var2); } System.setSecurityManager(var3); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
1.4.3、全盤加載機制
顯示指定某一個類加載器加載類
1.4.4、自定義類加載器
package com.zhz.bytecode; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author zhouhengzhe * @Description: 自定義類加載器 * @date 2021/9/22上午11:29 * @since */ public class CustomClassLoaderTest { static class CustomClassLoader extends ClassLoader { private String classpath; public CustomClassLoader(String classpath) { this.classpath = classpath; } @Override protected Class> findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); //defineClass將一個字節數組轉為Class對象,這個字節數組是class文件讀取后最終的字節數組。 return defineClass(name,data,0,data.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(); } } private byte[] loadByte(String name) throws IOException { // name = name.replaceAll("\.", "/"); FileInputStream fis = new FileInputStream(classpath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //初始化自定義類加載器,會先初始化父類ClassLoader,其中會把自定義類加載器的父加載 器設置為應用程序類加載器AppClassLoader CustomClassLoader customClassLoader=new CustomClassLoader("/Users/mac/Documents/ideaproject/Java/Java基礎/jvm-learn-demo/src/main/java/"); //D盤創建 test/com/zhz/bytecode 幾級目錄,將User類的復制類User1.class丟入該目錄 Class> clazz = customClassLoader.loadClass("com.zhz.bytecode.Hello"); Object instance = clazz.newInstance(); Method method = clazz.getDeclaredMethod("hello", null); method.invoke(instance,null); System.out.println(clazz.getClassLoader().getClass().getName()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
1.4.5、打破雙親加載機制
再來一個沙箱安全機制示例,嘗試打破雙親委派機制,用自定義類加載器加載我們自己實現的 java.lang.String.class(失敗,Java不給改核心類)
示例:
package com.zhz.bytecode; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author zhouhengzhe * @Description: 打破雙親加載機制 * @date 2021/9/22下午12:48 * @since */ public class BreakParentLoadingMechanism { static class CustomClassLoader extends ClassLoader { private String classpath; public CustomClassLoader(String classpath) { this.classpath = classpath; } /** * 重寫類加載方法,實現自己的加載邏輯,不委派給雙親加載 * @param name * @return: Class> * @author: zhouhengzhe * @date: 2021/9/22 */ @Override public Class> loadClass(String name,boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class> c = findLoadedClass(name); if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); c = findClass(name); // this is the defining class loader; record the stats sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } if (resolve) { resolveClass(c); } return c; } } @Override protected Class> findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); //defineClass將一個字節數組轉為Class對象,這個字節數組是class文件讀取后最終的字節數組。 return defineClass(name,data,0,data.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(); } } private byte[] loadByte(String name) throws IOException { // name = name.replaceAll("\.", "/"); FileInputStream fis = new FileInputStream(classpath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //初始化自定義類加載器,會先初始化父類ClassLoader,其中會把自定義類加載器的父加載 器設置為應用程序類加載器AppClassLoader CustomClassLoaderTest.CustomClassLoader customClassLoader=new CustomClassLoaderTest.CustomClassLoader("/Users/mac/Documents/ideaproject/Java/Java基礎/jvm-learn-demo/src/main/java/"); //D盤創建 test/com/zhz/bytecode 幾級目錄,將User類的復制類User1.class丟入該目錄 Class> clazz = customClassLoader.loadClass("java.lang.String"); Object instance = clazz.newInstance(); Method method = clazz.getDeclaredMethod("sout", null); method.invoke(instance,null); System.out.println(clazz.getClassLoader().getClass().getName()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

1.4.6、擴展:tomcat如何打破雙親加載機制
Tomcat類加載機制詳解
解釋:
CommonClassLoader能加載的類都可以被CatalinaClassLoader和SharedClassLoader使用, 從而實現了公有類庫的共用,而CatalinaClassLoader和SharedClassLoader自己能加載的類則 與對方相互隔離。
WebAppClassLoader可以使用SharedClassLoader加載到的類,但各個WebAppClassLoader 實例之間相互隔離。
而JasperLoader的加載范圍僅僅是這個JSP文件所編譯出來的那一個.Class文件,它出現的目的 就是為了被丟棄:當Web容器檢測到JSP文件被修改時,會替換掉目前的JasperLoader的實例, 并通過再建立一個新的Jsp類加載器來實現JSP文件的熱加載功能。
tomcat主要類加載器:
commonLoader:Tomcat最基本的類加載器,加載路徑中的class可以被Tomcat容器本身以及各個Webapp訪問;
catalinaLoader:Tomcat容器私有的類加載器,加載路徑中的class對于Webapp不可見;
sharedLoader:各個Webapp共享的類加載器,加載路徑中的class對于所有 Webapp可見,但是對于Tomcat容器不可見;
WebappClassLoader:各個Webapp私有的類加載器,加載路徑中的class只對當前 Webapp可見,比如加載war包里相關的類,每個war包應用都有自己的WebappClassLoader,實現相互隔離,比如不同war包應用引入了不同的spring版本, 這樣實現就能加載各自的spring版本;
注意:
tomcat類加載機制違背了Java推薦的雙親加載機制。
為了實現隔離性,沒有遵守Java推薦的雙親加載機制,,每個webappClassLoader加載自己的目錄下的class文件,不會傳遞給父類加載器,打破了雙親委派機制。
模擬實現—>模擬實現Tomcat的webappClassLoader加載自己war包應用內不同版本類實現相互共存與隔離:
代碼
package com.zhz.bytecode; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * @author zhouhengzhe * @Description: 模擬實現Tomcat的webappClassLoader加載自己war包應用內不同版本類實現相互共存與隔 離 * @date 2021/9/22下午1:12 * @since */ public class SimulateTomcatMultipleVersionIsolation { static class CustomClassLoader extends ClassLoader { private String classpath; public CustomClassLoader(String classpath) { this.classpath = classpath; } /** * 重寫類加載方法,實現自己的加載邏輯,不委派給雙親加載 * @param name * @return: Class> * @author: zhouhengzhe * @date: 2021/9/22 */ @Override public Class> loadClass(String name,boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class> c = findLoadedClass(name); if (c == null) { // If still not found, then invoke findClass in order // to find the class. long t1 = System.nanoTime(); //非自定義的類還是走雙親委派加載---->重要 if (!name.startsWith("com.zhz.jvm")){ c=this.getParent().loadClass(name); }else { c = findClass(name); } // this is the defining class loader; record the stats sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } if (resolve) { resolveClass(c); } return c; } } @Override protected Class> findClass(String name) throws ClassNotFoundException { try { byte[] data = loadByte(name); //defineClass將一個字節數組轉為Class對象,這個字節數組是class文件讀取后最終的字節數組。 return defineClass(name,data,0,data.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(); } } private byte[] loadByte(String name) throws IOException { //name = name.replaceAll("\.", "/"); FileInputStream fis = new FileInputStream(classpath + "/" + name + ".class"); int len = fis.available(); byte[] data = new byte[len]; fis.read(data); fis.close(); return data; } } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { //初始化自定義類加載器,會先初始化父類ClassLoader,其中會把自定義類加載器的父加載 器設置為應用程序類加載器AppClassLoader CustomClassLoaderTest.CustomClassLoader customClassLoader=new CustomClassLoaderTest.CustomClassLoader("D:/test"); //D盤創建 test/com/zhz/bytecode 幾級目錄,將User類的復制類User1.class丟入該目錄 Class> clazz = customClassLoader.loadClass("com.zhz.jvm.User1"); Object instance = clazz.newInstance(); Method method = clazz.getDeclaredMethod("sout", null); method.invoke(instance,null); System.out.println(clazz.getClassLoader().getClass().getName()); //初始化自定義類加載器,會先初始化父類ClassLoader,其中會把自定義類加載器的父加載 器設置為應用程序類加載器AppClassLoader CustomClassLoaderTest.CustomClassLoader customClassLoader1=new CustomClassLoaderTest.CustomClassLoader("D:/test1"); //D盤創建 test/com/zhz/bytecode 幾級目錄,將User類的復制類User1.class丟入該目錄 Class> clazz1 = customClassLoader1.loadClass("com.zhz.jvm.User1"); Object instance1 = clazz1.newInstance(); Method method1 = clazz1.getDeclaredMethod("sout", null); method1.invoke(instance1,null); System.out.println(clazz1.getClassLoader().getClass().getName()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
注意:同一個JVM內,兩個相同包名和類名的類對象可以共存,因為他們的類加載器可以不一 樣,所以看兩個類對象是否是同一個,除了看類的包名和類名是否都相同之外,還需要他們的類加載器也是同一個才能認為他們是同一個。
1.5、顯示當前ClassLoader加載了哪些Jar
方法一:
package com.zhz; import sun.misc.Launcher; import java.net.URL; /** * @author zhouhengzhe * @Description: 方法一:顯示當前ClassLoader加載了哪些Jar * @date 2021/9/12下午10:06 * @since */ public class TestJdkClassLoader { public static void main(String[] args) { System.out.println(String.class.getClassLoader()); System.out.println(com.sun.crypto.provider.DESKeyFactory.class.getClassLoader().getClass().getName()); System.out.println(TestJdkClassLoader.class.getClassLoader().getClass().getName()); System.out.println(); ClassLoader appClassLoader = ClassLoader.getSystemClassLoader(); ClassLoader extClassloader = appClassLoader.getParent(); ClassLoader bootstrapLoader = extClassloader.getParent(); System.out.println("the bootstrapLoader : " + bootstrapLoader); System.out.println("the extClassloader : " + extClassloader); System.out.println("the appClassLoader : " + appClassLoader); System.out.println(); System.out.println("bootstrapLoader加載以下文件:"); URL[] urls = Launcher.getBootstrapClassPath().getURLs(); for (int i = 0; i < urls.length; i++) { System.out.println(urls[i]); } System.out.println(); System.out.println("extClassloader加載以下文件:"); System.out.println(System.getProperty("java.ext.dirs")); System.out.println(); System.out.println("appClassLoader加載以下文件:"); System.out.println(System.getProperty("java.class.path")); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
方法二:
package com.zhz.bytecode; import sun.misc.Launcher; import java.lang.reflect.Field; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Objects; /** * @author zhouhengzhe * @Description: 方法二:顯示當前ClassLoader加載了哪些Jar * @date 2021/9/18上午12:20 * @since */ public class JvmClassLoaderPrintPath { public static void main(String[] args) { //啟動類加載器 URL[] urLs = Launcher.getBootstrapClassPath().getURLs(); System.out.println("啟動類加載器"); for (URL urL : urLs) { System.out.println("==>"+ urL.toExternalForm()); } //擴展類加載器 printClassLoad("擴展類加載器",JvmClassLoaderPrintPath.class.getClassLoader()); //應用類加載器 printClassLoad("應用類加載器",JvmClassLoaderPrintPath.class.getClassLoader()); } private static void printClassLoad(String name, ClassLoader classLoader) { if (Objects.nonNull(classLoader)){ System.out.println(name+" ClassLoader -> "+classLoader.toString()); printURLForClassLoader(classLoader); return; } System.out.println(name+" ClassLoader -> null"); } private static void printURLForClassLoader(ClassLoader classLoader) { Object ucp = insightField(classLoader, "ucp"); Object path = insightField(ucp, "path"); ArrayList ps = (ArrayList) path; for (Object p : ps) { System.out.println(" ==> "+p.toString()); } } private static Object insightField(Object obj, String name){ try { Field field=null; if (obj instanceof URLClassLoader){ field=URLClassLoader.class.getDeclaredField(name); }else { field=obj.getClass().getDeclaredField(name); } field.setAccessible(true); return field.get(obj); }catch (Exception e){ e.printStackTrace(); return null; } } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
結果
1.6、添加引用類的幾種方式
1、放到 JDK 的 lib/ext 下,或者 -Djava.ext.dirs
2、java-cp/classpath 或者 class 文件放到當前路徑
3、自定義 ClassLoader 加載
4、拿到當前執行類的 ClassLoader,反射調用 addUrl 方法添加 Jar 或路徑(JDK9 無效)
Java JVM 容器
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。