激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java中的類加載器_動力節點Java學院整理

Java中的類加載器_動力節點Java學院整理

2020-11-19 10:20動力節點 Java教程

這篇文章主要為大家詳細介紹了Java中類加載器的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下

從java的動態性到類加載機制

Java是一種動態語言。那么怎樣理解這個“動態”呢?或者說一門語言具備了什么特性,才能稱之為動態語言呢?對于java,我是這樣理解的。 

JVM(java虛擬機)執行的不是本地機器碼指令,而是執行一種稱之為字節碼的指令(存在于class文件中)。這就要求虛擬機在真正執行字節碼之前,先把相關的class文件加載到內存中。虛擬機不是一次性加載所有需要的class文件,因為它在執行的時候根本不會知道以后會用到哪些class文件。它是每用到一個類,就會在運行時“動態地”加載和這個類相關的class文件。這就是java被稱之為動態性語言的根本原因。除了動態加載類之外,還會動態的初始化類,對類進行動態鏈接。動態初始化和動態鏈接放在其他文章中進行介紹。本文中只關心類的加載。 

在JVM中負責對類進行加載的正是本文要介紹的類加載器(ClassLoader),所以,類加載器是JVM不可或缺的重要組件。 

java中的類加載器及類加載器工作原理

java中(指的是javase)有三種類加載器。每個類加載器在創建的時候已經指定他們對應的目錄, 也就是說每個類加載器去哪里加載類是確定的,我認為在ClassLoader類中應該會有getTargetPath()之類的方法, 得到他們對應的路徑,找了找jdk的文檔,發現是沒有的。以下是這三種類加載器和他們對應的路徑: 

 * AppClassLoader  --   加載classpath指定的路徑中的類 
 * ExtClassLoader   --   加載jre/lib/ext目錄下或者java.ext.dirs系統屬性定義的目錄下的類 
 * BootStrap           --   加載JRE/lib/rt.jar中的類 

那么類加載器是如何工作的呢?可以參看jdk中ClassLoader類的源碼。這個類的實現使用了模板方法模式,首先是loadClass方法來加載類,loadClass方法又調用了findClass方法,該方法讀取并返回類文件的數據,findClass方法返回后,loadClass方法繼續調用defineClass方法,將返回的數據加工成虛擬機運行時可識別的類型信息。所以,我們如果開發自己的類加載器,只需要繼承jdk中的ClassLoader類,并覆蓋findClass方法就可以了,剩下的而工作,父類會完成。其他java平臺有的根據自己的需求,實現了自己特定的類加載器,例如javaee平臺中的tomcat服務器,Android平臺中的dalvik虛擬機也定義了自己的類加載器。 

虛擬機加載類有兩種方式,一種方式就是上面提到的ClassLoader.loadClass()方法,另一種是使用反射API,Class.forName()方法,其實Class.forName()方法內部也是使用的ClassLoader。Class類中forName方法的實現如下: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static Class<?> forName(String name, boolean initialize,
  ClassLoader loader)
  throws ClassNotFoundException
 {
 if (loader == null) {
  SecurityManager sm = System.getSecurityManager();
  if (sm != null) {
 ClassLoader ccl = ClassLoader.getCallerClassLoader();
 if (ccl != null) {
  sm.checkPermission(
 SecurityConstants.GET_CLASSLOADER_PERMISSION);
 }
  }
 }
 return forName0(name, initialize, loader);
 }
 
 /** Called after security checks have been made. */
 private static native Class forName0(String name, boolean initialize,
   ClassLoader loader)
 throws ClassNotFoundException;

類加載器的三個特性 

類加載器有三個特性,分別為委派,可見性和單一性,其他文章上對這三個特性的介紹如下: 

  * 委托機制是指將加載一個類的請求交給父類加載器,如果這個父類加載器不能夠找到或者加載這個類,那么再加載它。
  * 可見性的原理是子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。
  * 單一性原理是指僅加載一個類一次,這是由委托機制確保子類加載器不會再次加載父類加載器加載過的類。 

其中,委派機制是基礎,在其他資料中也把這種機制叫做類加載器的雙親委派模型,其實說的是同一個意思。可加性和單一性是依賴于委派機制的。

以下代碼測試類加載器的委派機制: 

?
1
2
3
4
5
6
7
8
9
10
ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 
 
ClassLoader extClassLoader = appClassLoader.getParent();
System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@addbf1
//AppClassLoader的父加載器是ExtClassLoader
 
System.out.println(extClassLoader.getParent()); //null
//ExtClassLoader的父加載器是null, 也就是BootStrap,這是由c語言實現的

由打印結果可知,加載我們自己編寫的類的加載器是AppClassLoader,AppClassLoader的父加載器是ExtClassLoader,在而ExtClassLoader的父加載器返回結果為null,這說明他的附加載器是BootStrap,這個加載器是和虛擬機緊密聯系在一起的,在虛擬機啟動時,就會加載jdk中的類。它是由C實現的,沒有對應的java對象,所以返回null。但是在邏輯上,BootStrap仍是ExtClassLoader的父加載器。也就是說每當ExtClassLoader加載一個類時,總會委托給BootStrap加載。 

系統類加載器和線程上下文類加載器 

在java中,還存在兩個概念,分別是系統類加載器和線程上下文類加載器。 

其實系統類加載器就是AppClassLoader應用程序類加載器,它兩個值得是同一個加載器,以下代碼可以驗證: 

?
1
2
3
4
5
6
ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 
ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(sysClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
//由上面的驗證可知, 應用程序類加載器和系統類加載器是相同的, 因為地址是一樣的

 這兩個類加載器對應的輸出,不僅類名相同,連對象的哈希值都是一樣的,這充分說明系統類加載器和應用程序類加載器不僅是同一個類,更是同一個類的同一個對象。

每個線程都會有一個上下文類加載器,由于在線程執行時加載用到的類,默認情況下是父線程的上下文類加載器, 也就是AppClassLoader。 

?
1
2
3
4
5
6
7
8
new Thread(new Runnable() {
 
@Override
public void run() {
ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
System.out.println(threadcontextClassLosder); //sun.misc.Launcher$AppClassLoader@19821f
}
}).start();

這個子線程在執行時打印的信息為sun.misc.Launcher$AppClassLoader@19821f,可以看到和主線程中的AppClassLoader是同一個對象(哈希值相同)。 

也可以為線程設置特定的類加載器,這樣的話,線程在執行時就會使用這個特定的類加載器來加載使用到的類。如下代碼: 

?
1
2
3
4
5
6
7
8
9
10
11
12
Thread th = new Thread(new Runnable() {
 
@Override
public void run() {
ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
System.out.println(threadcontextClassLosder); //jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74
}
});
 
th.setContextClassLoader(new ClassLoader() {});
 
th.start();

在線程運行之前,為它設置了一個匿名內部類的類加載器對象,線程運行時,輸出的信息為:jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74,也就是我們設置的那個類加載器對象。 

類加載器的可見性

下面驗證類加載器的可見性,也就是 子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。 

以下代碼使用父加載器ExtClassLoader加載子加載器AppClassLoader路徑下的類,由輸出可知,是不可能實現的。 

?
1
2
3
4
5
6
7
8
try {
Class.forName("jg.zhang.java.testConcurrent.Person", true,
 ClassLoaderTest.class.getClassLoader().getParent());
System.out.println("1 -- 類被加載");
} catch (ClassNotFoundException e) {
//e.printStackTrace();
System.out.println("1 -- 未找到類");
}

輸出為 :1 -- 未找到類 。說明拋出了ClassNotFoundException異常。原因是讓ExtClassLoader加載 jg.zhang.java.testConcurrent.Person這個類因為這個類不在jre/lib/ext目錄下或者java.ext.dirs系統屬性定義的目錄下,所以拋出ClassNotFoundException。所以父加載器不能加載應該被子加載器加載的類。也就是說這個類在父加載器中不可見。這種機制依賴于委派機制。 

下面代碼使用子加載器AppClassLoader 加載父加載器BootStrap中的類,這是可以實現的。

?
1
2
3
4
5
6
7
8
try {
Class.forName("java.lang.String", true,
 ClassLoaderTest.class.getClassLoader());
System.out.println("2 -- 類被加載");
} catch (ClassNotFoundException e) {
//e.printStackTrace();
System.out.println("2 -- 未找到類");
}

 輸出為:2 -- 類被加載。說明成功加載了String類。是因為在指定由AppClassLoader加載String類時,由AppClassLoader一直委派到BootStrap加載。雖然是由子加載器的父加載器加載的,但是也可以說,父加載器加載的類對于子加載器來說是可見的。這同樣依賴于委派機制。其實在虛擬機啟動初期,java.lang.String已經被BootStrap預加載了,這時再次加載,虛擬機發現已經加載,不會再重復加載。這同時也證明了類加載器的單一性。 

測試代碼

到此為止,類加載器的知識就全部講完了。以下是整個測試代碼: 

?
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package jg.zhang.java.testclassloader;
 
 
/**
 * 參考ImportNew上的一篇文章<<類加載器的工作原理>>,
 * 文章地址:http://www.importnew.com/6581.html
 *
 * Java類加載器基于三個機制:委托、可見性和單一性。
 * 委托機制是指將加載一個類的請求交給父類加載器,如果這個父類加載器不能夠找到或者加載這個類,那么再加載它。
 * 可見性的原理是子類的加載器可以看見所有的父類加載器加載的類,而父類加載器看不到子類加載器加載的類。
 * 單一性原理是指僅加載一個類一次,這是由委托機制確保子類加載器不會再次加載父類加載器加載過的類。
 *
 * 三種類加載器: 每個類加載器在創建的時候已經指定他們對應的目錄, 也就是說每個類加載器去哪里加載類是確定的
 * 我認為在ClassLoader類中應該會有getTargetPath()之類的方法, 得到他們對應的路徑,找了找jdk的文檔,發現是沒有的.
 * AppClassLoader -- 加載classpath指定的路徑中的類
 * ExtClassLoader -- 加載jre/lib/ext目錄下或者java.ext.dirs系統屬性定義的目錄下的類
 * BootStrap  -- 加載JRE/lib/rt.jar中的類
 *
 *
 *
 * @author zhangjg
 *
 */
public class ClassLoaderTest {
 
 
 public static void main(String[] args) {
 test1();
 test2();
 test3();
 }
 
 /**
 * 驗證線程上下文類加載器
 */
 private static void test3() {
 /**
 * 1 每個線程都會有一個上下文類加載器,由于在線程執行時加載用到的類,默認情況下是父線程
 * 的上下文類加載器, 也就是AppClassLoader
 */
 new Thread(new Runnable() {
 
 @Override
 public void run() {
 ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
 System.out.println(threadcontextClassLosder); //sun.misc.Launcher$AppClassLoader@19821f
 }
 }).start();
 
 /**
 * 2 也可以給創建的線程設定特定的上下文類加載器
 */
 Thread th = new Thread(new Runnable() {
 
 @Override
 public void run() {
 ClassLoader threadcontextClassLosder = Thread.currentThread().getContextClassLoader();
 System.out.println(threadcontextClassLosder); //jg.zhang.java.testclassloader.ClassLoaderTest$3@1b67f74
 }
 });
 
 th.setContextClassLoader(new ClassLoader() {});
 
 th.start();
 }
 
 /**
 * 測試可見性,可見性依賴于委托機制
 */
 private static void test2() {
 
 /**
 * 1 讓ExtClassLoader加載 jg.zhang.java.testConcurrent.Person這個類
 * 因為這個類不在jre/lib/ext目錄下或者java.ext.dirs系統屬性定義的目錄下
 * 所以拋出ClassNotFoundException
 *
 * 所以父加載器不能加載應該被子加載器加載的類,這個類在父加載器中不可見
 * 這種機制依賴于委派機制
 */
 
 try {
 Class.forName("jg.zhang.java.testConcurrent.Person", true,
  ClassLoaderTest.class.getClassLoader().getParent());
 System.out.println("1 -- 類被加載");
 } catch (ClassNotFoundException e) {
 //e.printStackTrace();
 System.out.println("1 -- 未找到類");
 }
 
 
 /**
 * 2 讓AppClassLoader加載java.lang.String類
 * 沒有拋出異常,說明類被正常加載了
 * 雖然是由AppClassLoader一直委派到BootStrap而加載的
 * 所以可以說,父加載器加載的類對于子加載器來說是可見的,這同樣依賴于委派機制
 *
 * 其實在虛擬機啟動初期,java.lang.String已經被BootStrap預加載了
 * 這時再次加載,虛擬機發現已經加載,不會再重復加載
 */
 try {
 Class.forName("java.lang.String", true,
  ClassLoaderTest.class.getClassLoader());
 System.out.println("2 -- 類被加載");
 } catch (ClassNotFoundException e) {
 //e.printStackTrace();
 System.out.println("2 -- 未找到類");
 }
 
 }
 
 /**
 * 驗證三種類加載器的父子關系
 */
 private static void test1() {
 ClassLoader appClassLoader = ClassLoaderTest.class.getClassLoader();
 System.out.println(appClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 
 ClassLoader sysClassLoader = ClassLoader.getSystemClassLoader();
 System.out.println(sysClassLoader); //sun.misc.Launcher$AppClassLoader@19821f
 //由上面的驗證可知, 應用程序類加載器和系統類加載器是相同的, 因為地址是一樣的
 
 ClassLoader extClassLoader = appClassLoader.getParent();
 System.out.println(extClassLoader); //sun.misc.Launcher$ExtClassLoader@addbf1
 //AppClassLoader的父加載器是ExtClassLoader
 
 System.out.println(extClassLoader.getParent()); //null
 //ExtClassLoader的父加載器是null, 也就是BootStrap,這是由c語言實現的
 
 }
 
}

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 狠狠干五月天 | 欧美性受xxxx人人本视频 | 亚洲射逼 | 国产亚洲欧美在线视频 | 国产91在线亚洲 | 亚洲电影在线观看高清免费 | 91精品久久久久久久久网影视 | 亚洲成人播放 | 成人在线免费观看小视频 | 12av毛片 | 一区视频 | 国产午夜精品久久久久久免费视 | 亚洲第一成人在线视频 | 一区二区三区精品国产 | 爽毛片| 午夜在线观看视频网站 | 福利一区二区三区视频在线观看 | 久久精品re | 一级国产免费 | 哪里可以看免费的av | 欧美一区2区三区4区公司二百 | 露脸各种姿势啪啪的清纯美女 | 92看片淫黄大片一级 | 色七七亚洲 | 成人综合区一区 | 日本娇小18xxxⅹhd | 成人毛片免费在线 | 国产一级不卡毛片 | 欧美成人一区二区三区 | 久久国产精品一区 | 国产精品一二三区 | 久久精品中文字幕 | 一级黄色免费观看 | 黄色试看视频 | 午夜免费一区 | 国产成人精品区一区二区不卡 | 91精品国产777在线观看 | 91精品国产99久久久久久红楼 | 欧美日韩在线播放 | 国产一级大片 | 国产色91|