中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

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

發布時間:2020-03-31 18:24:55 來源:網絡 閱讀:316 作者:Java_老男孩 欄目:編程語言

1. Class 類的原理

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

首先了解 JVM 如何構建實例。

1.1 JVM 構建實例

JVM:Java Virtual Machine,Java 虛擬機。在?JVM?中分為棧、堆、方法區等,但這些都是?JVM?內存,文中所描述的內存指的就是?JVM?內存。.class?文件是字節碼文件,是通過?.java?文件編譯得來的。

知道上面這些內容,我們開始創建實例。我們以創建 Person 對象舉例:

Person?p?=?new?Person()

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

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

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

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

同志們發現沒有,其實這里還是有些區別的,我告訴你區別是下面的字比上面多,你會打我不(別打我臉)。

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

其實通過?new?或者反射創建實例,都需要?Class?對象。

1.2 .class 文件

.class?文件在文章開頭講過,是字節碼文件。.java?是源程序。Java 程序是跨平臺的,一次編譯到處執行,而編譯就是從源文件轉換成字節碼文件。

字節碼無非就是由 0 和 1 構成的文件。

有如下一個類:

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

通過 vim 查看一下字節碼文件:

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

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

1.3 類加載器

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

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

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

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

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

因為 ClassLoader 的 findClass 方法默認拋出異常,需要我們寫一個子類重新覆蓋它,比如:

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

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

defineClass 是通過字節碼獲取 Class 的方法,是 ClassLoader 定義的。我們具體不知道如何實現的,因為最終會調用一個 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);

總結下類加載器加載 .class?文件的步驟:

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

1.4 Class 類

.class?文件已經被類加載器加載到內存中并生成字節數組,JVM?根據字節數組創建了對應的?Class?對象。

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

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

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

  1. 權限修飾符
  2. 類名和泛型信息
  3. 接口
  4. 實體
  5. 注解
  6. 構造函數
  7. 方法

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

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

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

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

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

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

那我們看看?Class?類的方法

1.4.1 構造器

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

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

1.4.2 Class.forName

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

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

1.4.3 newInstance

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

newInstance()?的底層是返回無參構造函數。

2. 總結

我們來梳理下前面的知識點:

反射的關鍵點就是獲取?Class?類,那系統是如何獲取到?Class?類?

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

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

Class?類的構造器是私有的,所以需要通過?JVM?獲取?Class

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

武功县| 太仆寺旗| 武邑县| 通榆县| 马鞍山市| 台中市| 天门市| 台中县| 凤台县| 涟源市| 鹤峰县| 屏山县| 玉田县| 玉溪市| 文昌市| 桂东县| 通河县| 台南县| 雅江县| 山东省| 台中县| 永州市| 化德县| 罗田县| 西乌珠穆沁旗| 郁南县| 乌鲁木齐市| 北流市| 宣威市| 香港| 县级市| 班玛县| 新巴尔虎右旗| 商城县| 西乡县| 星座| 大化| 武乡县| 车险| 寿阳县| 凤山县|