天天看點

Thinking in java-37 類加載器與RTTI

1. 運作時類型确定RTTI

這裡可以找到關于該内容的詳細資料。

Intent: Let you find the exact type of an object even if your handle is a base of your object.

目的:RTTI可以在我們用基類引用處理對象時,運作時确認該對象的實際類型。

class Shape{
    public void draw(){
        System.out.println("Shape drawing.");
    }
}
class Square extends Shape{
    @Override
    public void draw(){
        System.out.println("Square drawing.");
    }
}
class Triangle extends Shape{
    @Override
    public void draw(){
        System.out.println("Triangle drawing.");
    }
}
public class TestRTTI{
    public static void main(String[] args){
        ArrayList<Shape> list = new ArrayList<>();
        list.add(new Square());
        list.add(new Triangle());
        list.add(new Square());
        for(Shape s: list)
            s.draw();
    }
}
           

2. Class 對象

點選檢視詳細内容。

每個Java程式中的對象都是某個類的執行個體instance。在我們裝載了給定類的代碼後,關于該類本身的呈現方式就是一個class對象。 class對象是Class類的一個執行個體。

類對象建立有2個條件:

1). 該類的一個執行個體對象第一次被建立的時候;

2). 當我們顯式建立一個類對象的時候。

顯式地建立類對象有2種方式:

1). 通過Class類的靜态方法:Class.forName():

Class cl = Class.forName("fully_qualified_classname");
           

2). 通過: Classname.class方式(class literal)

Class cl = Classname.class;
           

兩者比較:

1). 通過類的字面值方式(class literal): 該方式建立Class對象是在編譯時被編譯器檢查的,因而更加安全;缺點是:缺乏靈活性。

2). Class.forName()方式,優點:更加靈活。比如,我們可以在執行時才提供類的字元串名稱。缺點:Class.forName(String className) 可能抛出一個checked 異常ClassNotFoundException。

3. Java ClassLoader

我們知道,Java語言的一大優勢是它具備跨平台的特性。當我們編譯一個java檔案時,JVM把它轉換為與平台和機器獨立的位元組碼檔案,并把這些檔案存儲為 .class檔案。之後,當我們想要使用類的時候,Java ClassLoader會把該class檔案裝載入記憶體中。

Java自帶有三種類型的built-in ClassLoader:

Thinking in java-37 類加載器與RTTI

1. Bootstrap ClassLoader: 它裝載的是JDK的内部檔案,典型的如: rt.jar和其他核心的類檔案,比如java.lang.*包中的類檔案。

2. Extension ClassLoader: 它裝載的是JDK擴充目錄中的類檔案,java.ext.dirs路徑下的檔案是由該裝載器裝載的。

3. System ClassLoader: 裝載的是目前classpath中的檔案,一般可以通過設定-cp或 -classpath 指令行指定。從開發者角度來講,是最重要的classloader, 裝載的是由classpath環境變量指定的目錄和jar檔案下的class檔案。

package com.fqyuan.test;

import java.util.HashMap;

import sun.net.spi.nameservice.dns.DNSNameService;

public class TestClassLoader {

    public static void main(String[] args) {
        System.out.println(HashMap.class.getClassLoader());
        System.out.println(DNSNameService.class.getClassLoader());
        System.out.println(TestClassLoader.class.getClassLoader());
    }
}
//Running result:
null
sun.misc.Launcher$ExtClassLoader@75b84c92
sun.misc.Launcher$AppClassLoader@2a139a55
           

4. AppClassLoader 和 SystemClassLoader 關系

Q: Difference between AppClassLoader and SystemClassLoader? 類裝載器中的AppClassLoader 和 SystemClassLoader 有什麼差別?

A: AppClassLoader and SystemClassLoader are the same.

5. 類加載器遵循的三個原則

  • 指派原則(Delegation principle)
    Thinking in java-37 類加載器與RTTI

    Bootstrap ClassLoader: 負責從rt.jar中裝載标準的JDK類檔案,該類裝載器是java中所有其他類裝載器的父類。

    Extension ClassLoader: 會指派類裝載請求給它的父類裝載器Bootstrap, 如果指派不成功,則從jre/lib/ext目錄中裝載類檔案。

    System ClassLoader/Application ClassLoader: Application classloader是Extension ClassLoader的子類裝載器,實作的是sun.misc.Launcher$AppClassLoader類。

  • 可視性原則(Visibility principle):根據可見性原則,子類類加載器可以看到由父類類加載器所加載的類,但是反之不成立。
  • 唯一性原則(Uniqueness principle):被父類類加載器所加載的類,不應該再次被子類加載器再次加載,即一個類隻需要加載一次。

6. RTTI & Reflection兩者的差别是什麼呢?和Class的關系是什麼呢?

Java語言中有一類特殊的類Class, 基于Class類,java實作了RTTI和Reflection機制。我們可以把’Class’類看作’.class’檔案的通常形式,這類檔案中通常包含了attribute/methods/name/type(屬性、方法、姓名、類别,這些都是中繼資料)。是以可以說,每個’.class’檔案都是這個特殊的類’Class’的一個執行個體對象。

  1. 你所寫程式中的每一個類都有一個’Class’對象與之對應,每當我們編輯并編譯一個新的類時,可以看作一個Class對象也被建立并存儲在以.class命名的檔案中。
  2. 所有的類檔案被動态裝載如JVM中,發生時間是在第一次使用該類時。
  3. 某個.class 檔案被裝載如記憶體的時間是,當程式第一次引用該類的靜态成員時,注:構造方法也是一個類的靜态方法。

Java RTTI

  1. 我們首先得到一個Class類型的引用:
Class c = Class.forName(String strName);
c.isIterface(); //determine if it is an interface
c.getSuperCLass(); //Return the class representing the super classes of the entity.
           
  1. 三種不同形式的 RTTI。

    a). down cast: (Circle)shape[I]

    b). 代表該對象Class可以用于查詢運作時資訊,比如getSuperClass().

    c). instanceof方法,确定某個執行個體對象是否為某個類的執行個體。

Java Reflection

  1. 反射機制其實也是關于運作時類資訊的。一種定義式解釋:反射是用來代指能檢視系統中其他代碼(包括其自身)的一種機制。
  2. RTTI和Reflection不同之處在于:RTTI機制,在編譯時編譯器會會打開并檢視class檔案,也就是說:我們可以以一種正常的方式調用一個對象的所有方法。而對于反射機制而言,.class 檔案在編譯時不可用,class檔案是被Runtime environment所打開并檢查的。
  3. 如下例子:使用反射機制得到運作時方法資訊,并在編譯時不知道該方法簽名的基礎上運作該方法。

    假設:java中有一個不知道其具體類型的對象,并且想要調用一個該對象的某個方法。正常的調用方式是不可用的,因為并不知道具體的類類型。

//Traditonal way not applicable.
UnknownClass instance = new UnknownClass();
instance.unknownMethod();

//Use Reflection:
Method method = unknownObject.getClass().getMethod("unknownMethod",null);
method.invoke(unknownObject, null);
           

繼續閱讀