第十四章類型信息

java在運行時識別對象和類信息主要有2中方式:
1.傳統(tǒng)的RTTI(Run-Time Type Indentification) ,他假定在編譯時已經(jīng)知道所有的類型.
2.java反射,允許在運行時發(fā)現(xiàn)和使用類信息.

RTTI :主要解決傳統(tǒng)意義上的多態(tài)向上轉型問題.事實上List<> 容器保存多態(tài)對象時,對于容器來說他始終是當成Object對象保存,在讀取容器的數(shù)據(jù)時在轉成基類.運行時就是多態(tài)的需要解決的問題.

14.2 Class對象

class 是表示類信息的對象.運行class對象的系統(tǒng)成為類加載器.
類加載器子系統(tǒng)包含一個原生類加載器,但他只加載可信類.包括java API類,通常他是從本地加載的.當程序第一次創(chuàng)建其靜態(tài)成員引用時,就會加載這個類.從此說名類的構造器是靜態(tài)方法,new 操作符會被認為創(chuàng)建對靜態(tài)成員的引用.

Class.forName("com.xxx.className");//使用的是全限定名
class.newInstance(),方法的類必須還有默認構造器.

14.2字面常量

例如 Test.class. 字面常量更簡潔,更安全,高效.不需要進行異常檢車.普通類,接口,和數(shù)組,基本類型都可以使用字面常量.包裝器類還定義了特殊的字段.TYPE,表示字面常量.

采用.class 創(chuàng)建對象的引用時候不會自動初始化該class對象.為了實用類準備工作實際上有經(jīng)過3個階段

1.加載:類加載器執(zhí)行,創(chuàng)建一個class對象
2.鏈接:驗證類中的字節(jié)碼,為靜態(tài)域分配存儲空間,如果有必要會創(chuàng)建對其他類的引用.
3.初始化:初始化靜態(tài)域,被延遲到靜態(tài)變量,靜態(tài)域被首次引用時.如果沒有調用 new XXX(),其構造函數(shù)不會執(zhí)行.

package tinking_in_java.runTimeTypeInfo;
/**
 * Created by leon on 17-12-17.
 */
class Initable {
    static final int a = 47;
    static {
        int j = 100;
        System.out.println("static block");
    }
    public Initable() {
        System.out.println("construction Initable");
    }
}
class Initable2 extends Initable {
    public static final int b = 100;
    static {
        System.out.println("Initable2");
    }
    public Initable2() {
        System.out.println("construction Initable2");
    }
}
public class rttiTest {
    public static void main(String[] args) {
        Class initable2 = Initable2.class;
        Class innn = int.class;
        try {
            Class inn2 = Class.forName("tinking_in_java.runTimeTypeInfo.Initable2");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        int c = Initable2.b;
    }
}
//output----
static block
Initable2
Process finished with exit code 0

14.2.2 泛型class引用

在泛型中,如果需要有繼承關系 需要采用extends 關鍵字,不能直接用基類名字.
例如 :

Class<Number> intNumber=int.class; //這樣是不正確的,雖然Integer 是繼承Number,
                                   //但是Integer class 不是繼承 Number Class .
Class<? extends Number> intNumber=int.class;//這樣才是合法的

14.3 類型檢測

instanceof :只能比較是否屬于 某class
isAssignableFrom()判斷是否是超類,或者同類

14.5反射:運行時的類信息

package tinking_in_java.runTimeTypeInfo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * Created by leon on 17-12-17.
 */
interface Interface {
    void dosomething();
    void dosomethingEles(String arg);
}
class RealObject implements Interface {
    @Override
    public void dosomething() {
        System.out.println("real object");
    }
    @Override
    public void dosomethingEles(String arg) {
        System.out.println("real object" + arg);
    }
}
class DynamicHandle implements InvocationHandler {
    private Object proxyed;
    public DynamicHandle(Object proxyed) {
        this.proxyed = proxyed;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (args != null)
            for (Object arg : args)
                System.out.println("" + arg);
        if (method != null) {
            System.out.println(method.toString());
        }
        //如果返回null 則表示攔截方法,如果返回method.invoke() 表示不攔截
        return null;//method.invoke(proxyed, args);
    }
}
public class SimpleDynamicProxy {
    public static void cusume(Interface intf) {
        intf.dosomething();
        intf.dosomethingEles("booooo");
    }
    public static void main(String[] args) {
        RealObject realObject = new RealObject();
//        cusume(realObject);
        Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(),
                new Class[]{Interface.class},
                new DynamicHandle(realObject));
        cusume(proxy);
    }
}
//output----
public abstract void tinking_in_java.runTimeTypeInfo.Interface.dosomething()
booooo
public abstract void tinking_in_java.runTimeTypeInfo.Interface.dosomethingEles(java.lang.String)

java的動態(tài)代理缺陷,只能針對實現(xiàn)接口類的代理做修改.不支持對class 進行代理.如果需要針對class代理,則需要采用cglib,CGLib采用了非常底層的字節(jié)碼技術,其原理是通過字節(jié)碼技術為一個類創(chuàng)建子類,并在子類中采用方法攔截的技術攔截所有父類方法的調用,順勢織入橫切邏輯

類加載過程:

image.png

加載,驗證,準備,初始化,卸載 這幾部分的順序是按部就班的開始.
但是解析的順序是不確定的,java為了支持動態(tài)綁定機制,有時候可以在初始化之后在進行.
1.什么時候開始加載? jvm規(guī)范并沒有嚴格要求,有虛擬機具體實現(xiàn)決定.
2.初始化有嚴格要求:
(1) new,getstatic, putstatic 或者invokestatic 4條指令是.會觸發(fā)初始化.(但是讀寫 static final 靜態(tài)常量池中字段不會觸發(fā)初始化).
(2) 使用java.lang.refelect包的方法進行反射調用時,如果沒有初始化會觸發(fā)初始化.
(3) 當初始化一個類時,發(fā)現(xiàn)其父類還沒有初始化,會先觸發(fā)器父類初始化.
(4) 當虛擬機啟動是,用戶需要執(zhí)行主類(包含main()的那個類),會初始化這個主類.
(5) 當使用jdk動態(tài)語言支持時,如果一個java.lang.invoke.methodHandle實例最后結果返回是REF_getStatic,REF_putStatic,REF_invokeStaic句柄時,當句柄所對應的類沒有初始化,會初始化.

對于這5中情況的觸發(fā)稱為主動引用,虛擬機規(guī)范中定義”有且只有”,處理這5種情況之外的所有引用,都不會觸發(fā)初始化,成為被動引用.

接口加載過程和類加載過程稍有些不同.接口也有初始化過程,和類初始化的區(qū)別在于上述5點的第三點.接口在初始化時,并不會把父接口全部初始化,只有在用到父接口的時候才會(把父接口)初始化.

類的加載過程:

(一) 加載(class loading) :
1.采用權限定符(com.xxx.xxx)讀取class文件
 2.將靜態(tài)存儲結構轉換成方法區(qū)的運行時數(shù)據(jù)結構
 3.在內(nèi)存中生成java.lang.Class對象,作為方法區(qū)這個類各種數(shù)據(jù)的訪問入口.
虛擬機的這3點要求并不是很嚴格,所以在加載階段可以有很多靈活的方法:例如
 a)從zip,jar,war,ear,apk等包讀取
 b)從網(wǎng)絡中讀取 applet
c)運行時計算生成,動態(tài)代理技術.Proxy 類就是通過ProxyGenerator.generateProxyClass 生成$Proxy形式的代理類.
數(shù)組類和一般類的加載不太一樣.
(二) 驗證:
  1)文件驗證(驗證文件的格式是否符合jvm規(guī)范要求)
   2)元數(shù)據(jù)驗證(保證類,方法,字段符合java規(guī)范)
   3)字節(jié)碼驗證(保證代碼數(shù)據(jù)流和控制流,包括類型轉化保證不會危害虛擬機)
   4)符號引用驗證(驗證類引用其他類,方法,字段的可見性)這個過程不是一定必須,如果不需要驗證符號可以設置 -Xverify:none
(三) 準備:
正式為類變量分配內(nèi)存,并初始值階段.這里進行內(nèi)存分配的僅僅是static 變量,而不包括實例變量,實例變量會在初始化時候隨對象一起分配到java堆中.這里說的初始階段通常是初始為0 的."通常情況"指的是不是靜態(tài)常量,僅僅是靜態(tài)變量.如果是靜態(tài)常量在準備階段就會設置常量值.
例如: static int i=100;//準備階段 時 i=0
static final int I =100;//準備階段時 i=100,這個是靜態(tài)常量
(四) 解析:
主要將符號引用解析成直接引用的過程,轉化成具體的機器對應的執(zhí)行地址.
(五) 初始化:
執(zhí)行類構造器<clinit>()方法過程,<init>()實例構造器, <clinit> 和<init>這兩個是不同的,類構造器中,靜態(tài)域,變量是按java代碼中的排列順序賦值.靜態(tài)語句塊只能訪問在他之前定義的變量,在靜態(tài)語句塊之后的靜態(tài)變量可以賦值,但無法訪問.

  public class Test{
  static{
        i=0;//給變量賦值可以正常編譯通過
        System.out.print(i);//這句編譯器會提示"非法向前引用"
         }
    static int i=1;
 }
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容