日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
反射必殺技:深入了解Class類(lèi),讓你一通百通

1. Class 類(lèi)的原理

孟子曰:得人心者得天下。而在 Java 中,這個(gè)「人心」就是 Class 類(lèi),獲取到 Class 類(lèi)我們就可以為所欲為之為所欲為。下面讓我們深入「人心」,去探索 Class 類(lèi)的原理。

首先了解 JVM 如何構(gòu)建實(shí)例。

1.1 JVM 構(gòu)建實(shí)例

JVM:Java Virtual Machine,Java 虛擬機(jī)。在 JVM 中分為棧、堆、方法區(qū)等,但這些都是 JVM 內(nèi)存,文中所描述的內(nèi)存指的就是 JVM 內(nèi)存。.class 文件是字節(jié)碼文件,是通過(guò) .java 文件編譯得來(lái)的。

知道上面這些內(nèi)容,我們開(kāi)始創(chuàng)建實(shí)例。我們以創(chuàng)建 Person 對(duì)象舉例:

 
 
 
 
  1. Person p = new Person() 

簡(jiǎn)簡(jiǎn)單單通過(guò) new 就創(chuàng)建了對(duì)象,那流程是什么樣的呢?見(jiàn)下圖: 

這也太粗糙了一些,那在精致一下吧。 

同志們發(fā)現(xiàn)沒(méi)有,其實(shí)這里還是有些區(qū)別的,我告訴你區(qū)別是下面的字比上面多,你會(huì)打我不(別打我臉)。

粗糙的那個(gè)是通過(guò) new 創(chuàng)建的對(duì)象,而精致的是通過(guò) ClassLoader 操作 .class 文件生成 Class 類(lèi),然后創(chuàng)建的對(duì)象。

其實(shí)通過(guò) new 或者反射創(chuàng)建實(shí)例,都需要 Class 對(duì)象。

1.2 .class 文件

.class 文件在文章開(kāi)頭講過(guò),是字節(jié)碼文件。.java 是源程序。Java 程序是跨平臺(tái)的,一次編譯到處執(zhí)行,而編譯就是從源文件轉(zhuǎn)換成字節(jié)碼文件。

字節(jié)碼無(wú)非就是由 0 和 1 構(gòu)成的文件。

有如下一個(gè)類(lèi): 

通過(guò) vim 查看一下字節(jié)碼文件: 

這啥玩意,看不懂。咱也不需要看懂,反正 JVM 對(duì) .class 文件有它自己的讀取規(guī)則。

1.3 類(lèi)加載器

還記得上面的精致圖中,我們知道是通過(guò)類(lèi)加載器把 .class 文件加載到內(nèi)存中。具體的類(lèi)加載器內(nèi)容,我會(huì)另寫(xiě)一篇文章講解(寫(xiě)完鏈接會(huì)更新到這里)。但是核心方法就是 loadClass(),只需要告訴它要加載的 name,它就會(huì)幫你加載:

 
 
 
 
  1. protected Class loadClass(String name, boolean resolve) 
  2.     throws ClassNotFoundException 
  3.     synchronized (getClassLoadingLock(name)) { 
  4.         // 1.檢查類(lèi)是否已經(jīng)加載 
  5.         Class c = findLoadedClass(name); 
  6.         if (c == null) { 
  7.             long t0 = System.nanoTime(); 
  8.             try { 
  9.                 // 2.尚未加載,遵循父優(yōu)先的等級(jí)加載機(jī)制(雙親委派機(jī)制) 
  10.                 if (parent != null) { 
  11.                     c = parent.loadClass(name, false); 
  12.                 } else { 
  13.                     c = findBootstrapClassOrNull(name); 
  14.                 } 
  15.             } catch (ClassNotFoundException e) { 
  16.                 // ClassNotFoundException thrown if class not found 
  17.                 // from the non-null parent class loader 
  18.             } 
  19.  
  20.             if (c == null) { 
  21.                 // 3.如果還沒(méi)有加載成功,調(diào)用 findClass() 
  22.                 long t1 = System.nanoTime(); 
  23.                 c = findClass(name); 
  24.  
  25.                 // this is the defining class loader; record the stats 
  26.                 sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); 
  27.                 sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); 
  28.                 sun.misc.PerfCounter.getFindClasses().increment(); 
  29.             } 
  30.         } 
  31.         if (resolve) { 
  32.             resolveClass(c); 
  33.         } 
  34.         return c; 
  35.     } 
  36.  
  37. // 需要重寫(xiě)該方法,默認(rèn)就是拋出異常 
  38. protected Class findClass(String name) throws ClassNotFoundException { 
  39.     throw new ClassNotFoundException(name); 
  1. 類(lèi)加載器加載 .class 文件主要分位三個(gè)步驟
  2. 檢查類(lèi)是否已經(jīng)加載,如果有就直接返回
  3. 當(dāng)前不存在該類(lèi),遵循雙親委派機(jī)制,加載 .class 文件

上面兩步都失敗,調(diào)用 findClass()

因?yàn)?ClassLoader 的 findClass 方法默認(rèn)拋出異常,需要我們寫(xiě)一個(gè)子類(lèi)重新覆蓋它,比如:

 
 
 
 
  1. @Override 
  2.     protected Class findClass(String name) throws ClassNotFoundException { 
  3.         try { 
  4.             // 通過(guò)IO流從指定位置讀取xxx.class文件得到字節(jié)數(shù)組 
  5.             byte[] datas = getClassData(name); 
  6.             if (null == datas){ 
  7.                 throw new ClassNotFoundException("類(lèi)沒(méi)有找到:" + name); 
  8.             } 
  9.             // 調(diào)用類(lèi)加載器本身的defineClass()方法,由字節(jié)碼得到 class 對(duì)象 
  10.             return defineClass(name, datas, 0, datas.length); 
  11.         }catch (IOException e){ 
  12.             throw new ClassNotFoundException("類(lèi)沒(méi)有找到:" + name); 
  13.         } 
  14.     } 
  15.  
  16.     private byte[] getClassData(String name) { 
  17.         return byte[] datas; 
  18.     } 

defineClass 是通過(guò)字節(jié)碼獲取 Class 的方法,是 ClassLoader 定義的。我們具體不知道如何實(shí)現(xiàn)的,因?yàn)樽罱K會(huì)調(diào)用一個(gè) native 方法:

 
 
 
 
  1. private native Class defineClass0(String name, byte[] b, int off, int len, 
  2.                                          ProtectionDomain pd); 
  3.  
  4.     private native Class defineClass1(String name, byte[] b, int off, int len, 
  5.                                          ProtectionDomain pd, String source); 
  6.  
  7.     private native Class defineClass2(String name, java.nio.ByteBuffer b, 
  8.                                          int off, int len, ProtectionDomain pd, 
  9.                                          String source); 

總結(jié)下類(lèi)加載器加載 .class 文件的步驟:

  • 通過(guò) ClassLoader 類(lèi)中 loadClass() 方法獲取 Class
  • 從緩存中查找,直接返回
  • 緩存中不存在,通過(guò)雙親委派機(jī)制加載
  • 上面兩步都失敗,調(diào)用 findClass()通過(guò) IO 流從指定位置獲取到 .class 文件得到字節(jié)數(shù)組調(diào)用類(lèi)加載器 defineClass() 方法,由字節(jié)數(shù)組得到 Class 對(duì)象

1.4 Class 類(lèi)

.class 文件已經(jīng)被類(lèi)加載器加載到內(nèi)存中并生成字節(jié)數(shù)組,JVM 根據(jù)字節(jié)數(shù)組創(chuàng)建了對(duì)應(yīng)的 Class 對(duì)象。

接下來(lái)我們來(lái)分析下 Class 對(duì)象。 

我們知道 Java 的對(duì)象會(huì)有下面的信息:

  1. 權(quán)限修飾符
  2. 類(lèi)名和泛型信息
  3. 接口
  4. 實(shí)體
  5. 注解
  6. 構(gòu)造函數(shù)
  7. 方法

這些信息在 .class 文件以 0101 表示,最后 JVM 會(huì)把 .class 文件的信息通過(guò)它的方式保存到 Class 中。

在 Class 中肯定有保存這些信息的字段,我們來(lái)看一下: 

Class 類(lèi)中用 ReflectionData 里面的字段來(lái)與 .class 的內(nèi)容映射,分別映射了字段、方法、構(gòu)造器和接口。 

通過(guò) annotaionData 映射了注解數(shù)據(jù),其它的就不展示了,大家可以自行打開(kāi) IDEA 查看下 Class 的源碼。

那我們看看 Class 類(lèi)的方法

1.4.1 構(gòu)造器 

Class 類(lèi)的構(gòu)造器是私有的,只能通過(guò) JVM 創(chuàng)建 Class 對(duì)象。所以就有了上面通過(guò)類(lèi)加載器獲取 Class 對(duì)象的過(guò)程。

1.4.2 Class.forName 

Class.forName() 方法還是通過(guò)類(lèi)加載器獲取 Class 對(duì)象。

1.4.3 newInstance 

newInstance() 的底層是返回?zé)o參構(gòu)造函數(shù)。

2. 總結(jié)

我們來(lái)梳理下前面的知識(shí)點(diǎn):

反射的關(guān)鍵點(diǎn)就是獲取 Class 類(lèi),那系統(tǒng)是如何獲取到 Class 類(lèi)?

是通過(guò)類(lèi)加載器 ClassLoader 將 .class 文件通過(guò)字節(jié)數(shù)組的方式加載到 JVM 中,JVM 將字節(jié)數(shù)組轉(zhuǎn)換成 Class 對(duì)象。那類(lèi)加載器是如何加載的呢?

  • 通過(guò) ClassLoader 的 loadClass() 方法
  • 從緩存中查找,直接返回
  • 緩存中不存在,通過(guò)雙親委派機(jī)制加載
  • 上面兩步都失敗,調(diào)用 findClass()通過(guò) IO 流從指定位置獲取到 .class 文件得到字節(jié)數(shù)組調(diào)用類(lèi)加載器 defineClass() 方法,由字節(jié)數(shù)組得到 Class 對(duì)象

Class 類(lèi)的構(gòu)造器是私有的,所以需要通過(guò) JVM 獲取 Class。

Class.forName() 也是通過(guò)類(lèi)加載器獲取的 Class 對(duì)象。newInstance 方法的底層也是返回的無(wú)參構(gòu)造函數(shù)。


分享文章:反射必殺技:深入了解Class類(lèi),讓你一通百通
本文來(lái)源:http://m.5511xx.com/article/djoigeg.html