反射必殺技:深入了解Class類,讓你一通百通-創(chuàng)新互聯(lián)

1. Class 類的原理

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

10多年的青岡網(wǎng)站建設(shè)經(jīng)驗(yàn),針對設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整青岡建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“青岡網(wǎng)站設(shè)計(jì)”,“青岡網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

首先了解 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é)碼文件,是通過?.java文件編譯得來的。

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

Person?p?=?new?Person()

簡簡單單通過?new就創(chuàng)建了對象,那流程是什么樣的呢?見下圖

反射必殺技:深入了解Class類,讓你一通百通

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

反射必殺技:深入了解Class類,讓你一通百通

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

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

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

1.2 .class 文件

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

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

有如下一個(gè)類:

反射必殺技:深入了解Class類,讓你一通百通

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

反射必殺技:深入了解Class類,讓你一通百通

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

1.3 類加載器

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

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
    synchronized (getClassLoadingLock(name)) {
        // 1.檢查類是否已經(jīng)加載
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            long t0 = System.nanoTime();
            try {
                // 2.尚未加載,遵循父優(yōu)先的等級加載機(jī)制(雙親委派機(jī)制)
                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) {
                // 3.如果還沒有加載成功,調(diào)用 findClass()
                long t1 = System.nanoTime();
                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;
    }
}

// 需要重寫該方法,默認(rèn)就是拋出異常
protected Class<?> findClass(String name) throws ClassNotFoundException {
    throw new ClassNotFoundException(name);
}

類加載器加載?.class文件主要分位三個(gè)步驟

  1. 檢查類是否已經(jīng)加載,如果有就直接返回
  2. 當(dāng)前不存在該類,遵循雙親委派機(jī)制,加載 .class文件
  3. 上面兩步都失敗,調(diào)用 findClass()

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

@Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        try {
            // 通過IO流從指定位置讀取xxx.class文件得到字節(jié)數(shù)組
            byte[] datas = getClassData(name);
            if (null == datas){
                throw new ClassNotFoundException("類沒有找到:" + name);
            }
            // 調(diào)用類加載器本身的defineClass()方法,由字節(jié)碼得到 class 對象
            return defineClass(name, datas, 0, datas.length);
        }catch (IOException e){
            throw new ClassNotFoundException("類沒有找到:" + name);
        }
    }

    private byte[] getClassData(String name) {
        return byte[] datas;
    }

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

private native Class<?> defineClass0(String name, byte[] b, int off, int len,
                                         ProtectionDomain pd);

    private native Class<?> defineClass1(String name, byte[] b, int off, int len,
                                         ProtectionDomain pd, String source);

    private native Class<?> defineClass2(String name, java.nio.ByteBuffer b,
                                         int off, int len, ProtectionDomain pd,
                                         String source);

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

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

1.4 Class 類

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

接下來我們來分析下?Class對象。

反射必殺技:深入了解Class類,讓你一通百通

我們知道 Java 的對象會有下面的信息:

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

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

在?Class中肯定有保存這些信息的字段,我們來看一下:

反射必殺技:深入了解Class類,讓你一通百通

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

反射必殺技:深入了解Class類,讓你一通百通

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

那我們看看?Class類的方法

1.4.1 構(gòu)造器

反射必殺技:深入了解Class類,讓你一通百通

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

1.4.2 Class.forName

反射必殺技:深入了解Class類,讓你一通百通

Class.forName()方法還是通過類加載器獲取?Class對象。

1.4.3 newInstance

反射必殺技:深入了解Class類,讓你一通百通

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

2. 總結(jié)

我們來梳理下前面的知識點(diǎn):

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

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

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

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

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

創(chuàng)新互聯(lián)www.cdcxhl.cn,專業(yè)提供香港、美國云服務(wù)器,動態(tài)BGP最優(yōu)骨干路由自動選擇,持續(xù)穩(wěn)定高效的網(wǎng)絡(luò)助力業(yè)務(wù)部署。公司持有工信部辦法的idc、isp許可證, 機(jī)房獨(dú)有T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確進(jìn)行流量調(diào)度,確保服務(wù)器高可用性。佳節(jié)活動現(xiàn)已開啟,新人活動云服務(wù)器買多久送多久。

當(dāng)前標(biāo)題:反射必殺技:深入了解Class類,讓你一通百通-創(chuàng)新互聯(lián)
網(wǎng)站路徑:http://muchs.cn/article30/dsjipo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設(shè)、關(guān)鍵詞優(yōu)化靜態(tài)網(wǎng)站、品牌網(wǎng)站建設(shè)、網(wǎng)站導(dǎo)航、企業(yè)建站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化