1. 程式人生 > >Thinking in Java -- 類型信息RTTI

Thinking in Java -- 類型信息RTTI

告訴 包名 clas 內容 磁盤 tty 限定 lines 連接

Thinking in Java – 類型信息

個人感覺 java 中的比較難的部分了,在看了些netty源代碼發現事實上這塊很實用。


這章重點是RTTI和反射。先說下自己的理解
RTTI是執行時識別。在c++中是用virtual來實現的,在編譯期會忽略對象的詳細類型信息,假定我們已經知道,並在執行時詳細識別。

Java反射機制實在執行狀態中,對於隨意一個類,都能夠知道這個類的全部屬性和方法。對於隨意一個對象。都能夠調用它的隨意一個方法和屬性,這樣的動態獲取的信息以及動態調用對 象的方法的功能稱為Java的反射機制


Class對象

Class對象就是用來創建類的全部的“常規”對象的。Java使用Class對象來執行其RTTI.
Class對象是什麽
Java是一門純面向對象的語言。在Java中,一切都是對象,也就是class,對於每一個類文件(每一個對象)編譯後我們都會生成一個.class文件(javac hello.java —> hello.class)。*.class文件就是Class對象,在我們要第一次使用hello這個類文件時。JVM的類載入器會來載入hello.class文件,感覺Class對象就是一個模具。用來生產對象的。
註意:構造器也是靜態方法

樣例:

package onefour_chapter;

/**
 * Created by wwh on 15-8-7.
 */

class Candy{
    static {
        System.out.println("Loading Candy");
    }
}

class Gum{
    static {
        System.out.println("Loading Gum");
    }
}

class Cookie{
    static {
        System.out.println("Loading Cookie"
); } } public class SweetShop { public static void main(String[] args){ System.out.println("inside main"); new Candy(); System.out.println("After creating Candy"); try{ Class.forName("Gum"); }catch (ClassNotFoundException e){ System.out.println("not found Gum"); } System.out.println("After Class.forName(\"Gum\")"); new Cookie(); System.out.println("After create Cookie"); } }

技術分享
我執行完發現結果和書上不一致,原因是Class.forName()參數要求是全然限定名,我的代碼在包中。前面加上包名字就可以,例如以下
Class.forName(“包名.Gum”);

關鍵點:

static代碼塊

static修飾的代碼塊僅僅在類被載入時執行而且僅會被執行一次。一般用來初始化靜態變量和調用靜態方法,無論你new多少次,在jvm的聲明周期裏一個類僅僅被載入一次。
前面說構造器也是靜態方法,所以test t = new test(),在第一次創建對象時jvm就會載入test類的Class對象(.class文件),Class對象就和其它對象一樣,我們能夠操控它的引用。forName()就是獲取Class對象引用的一種方法。
Class.forName()返回對象的Class引用,從Class對象中我們能夠獲得許多的信息從而在執行時來抉擇一些事情。很像c++的type_traits。註意使用newInstance()創造的類必須帶有默認的構造器

static語句塊
static語句塊在類載入時執行。僅僅被執行一次
在用單例模式寫封裝數據庫連接池時,new一個數據庫連接池就被我寫在了static代碼塊中,保證在類載入時就初始化好連接池,且僅僅初始化一次。(註意。訪問靜態常量時。假設編譯器能計算出來。則不會載入)

類載入:在終端下Java載入.class文件。Java命令的作用是啟動虛擬機。虛擬機通過流從磁盤上將字節碼(.class文件)中的內容讀入虛擬機,並保存起來的過程。


類字面常量

Java還提供了一種方法來生成Class對象的引用,即類字面常量,classname.class,比方hello.class。它更安全和高效也不用try語句包裹。所以一般我們不使用forName(),可用於類,接口,數組。基本類型。建議使用.class的形式。.class執行了“盡可能”懶惰初始化,而forName()馬上就初始化
為了使用類而做的準備工作有3個。載入–>連接–>初始化


範化的Class引用

Class引用總是指向某個Class對象,它能夠制造類的實例。並包括可作用於這些實例的全部方法代碼。它還包括該類的靜態成員,因此,Class引用表示的就是它所指向的對象的確切類型。該對象便是Class類的一個對象。
向Class引用加入範型語法原因僅僅是為了編譯器類型檢查。前面說了RTTI和反射都是執行時的。如今我們在編譯時也做了一些限定。更加保證了執行的正確性
為了在使用範化的Class引用時放松限制。能夠使用了通配符,也但是使用通配符和extends關鍵字相結合。創建一個範圍。
Class

import java.util.ArrayList;
import java.util.List;

/**
 * Created by wwh on 15-8-7.
 */

class CountedInteger{
    static {
        System.out.println("Class 載入");
    }
    private static long counter;
    private final long id = counter++;
    public String toString() { return Long.toString(id); }
}

public class FilledList<T> {
    private Class<T> type;
    /* 構造函數。參數為被限定的Class對象,我們能夠在實例化時來指定 */
    public FilledList(Class<T> type){ this.type = type; }
    public List<T> create(int nElements){
        List<T> result = new ArrayList<T>();
        try{
            for(int i = 0; i < nElements; i++){
                result.add(type.newInstance());
            }
        }catch (Exception e){
            throw new RuntimeException();
        }
        return result;
    }

    public static void main(String[] args){
        /* 執行限定的類型,並傳遞.class作為構造函數的參數 */
        FilledList<CountedInteger> fl = new FilledList<CountedInteger>(CountedInteger.class);
        System.out.println(fl.create(15));
    }

} 

RTTI在Java中的第三種形式是instanceof,它返回一個布爾值,告訴我們對象是不是某個特定的實例。
if(obj instanceof Dog) obj.exe();
instanceof有比較嚴格的限制,僅僅能將其與命名類型進行比較。而不能與FClass對象作比較
Class.isInstance()方法提供了一種動態地測試對象的途徑。

Thinking in Java -- 類型信息RTTI