Java編程思想第十四章讀書筆記

在運行時識別對象和類的信息有兩種方式,一種是RTTI,它要求在編譯時就知道了所有的類型信息;另一種是“反射”機制,允許在運行時發現和使用類型信息。

class對象

每當編寫並編譯了一個新的類就會產生一個同名的.class文件,這就對應着一個class對象。爲了生成這個類的對象,java虛擬機會使用被稱爲“類加載器”的子系統。當程序創建第一個對類的靜態成員的引用時(構造器也是類的靜態方法),類加載器首先會檢查這個類的class對象是否已經加載,如果沒有加載,默認的類加載器就會根據類名查找.class文件,將這個類的class對象載入內存,用來創建這個類的所有“常規”對象。
獲得class對象的引用的三種方式

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) {
        // Class.forName()
        Class candy = null;
        try {
            candy = Class.forName("type.information.Candy");
        } catch (ClassNotFoundException e) {
            System.out.println("Couldn't find Candy");
        }
        System.out.println(candy.getName());
        // 類字面常量
        Class gum = Gum.class;
        System.out.println(gum.getName());
        // object.getClass()
        Cookie cookie = new Cookie();
        Class c = cookie.getClass();
        System.out.println(c.getName());
    }
}/*output
Loading Candy
type.information.Candy
type.information.Gum
Loading Cookie
type.information.Cookie
*/

可以看到類字面常量在創建class對象的引用時,不會自動初始化該class對象。實際上,類字面常量爲了使用類而做的準備工作實際包含三個步驟:
1、加載,這是由類的加載器執行的,該步驟將查找字節碼,並從這些字節碼中創建一個class對象。
2、鏈接,在鏈接階段將驗證類中的字節碼,爲靜態域分配存儲空間,並且如果必須的話,將解析這個類創建的對其他類的所有引用。
3、初始化,如果該類具有超類,則對其初始化,執行靜態初始化器和靜態初始化塊。而當前類的初始化被延遲到了對靜態方法或者非常數靜態域進行首次引用時才執行。
打印class對象可以獲得的所有信息

interface A {}
class B {
    @Override
    public String toString() { return "B{}"; }
}
class C {}
class D extends B implements A{
    public int a;
    public C obj = new C();
    public void f() {}
}
public class ClassDump {
    public static void disply(String msg, Object[] vals) {
        System.out.println(msg);
        for (Object val : vals)
            System.out.println("  " + val);
    }
    public static void classInfo(Class<?> c) {
        System.out.println("c.getName():" + c.getName());
        System.out.println("c.getPackage():" + c.getPackage());
        System.out.println("c.getSuperclass():" + c.getSuperclass());
        disply("c.getDeclaredClasses()", c.getDeclaredClasses());
        disply("c.getClasses()", c.getClasses());
        disply("c.getInterfaces()", c.getInterfaces());
        disply("c.getDeclaredMethods()", c.getDeclaredMethods());
        disply("c.getDeclaredConstructors()", c.getDeclaredConstructors());
        disply("c.getDeclaredFields()", c.getDeclaredFields());
        disply("c.getFields()", c.getFields());
        if (c.getSuperclass() != null) {
            System.out.println("**********Superclass***********");
            classInfo(c.getSuperclass());
        }
    }
    public static void main(String[] args) {
        classInfo(D.class);
        try {
            System.out.println(D.class.getSuperclass().newInstance());
        } catch (IllegalAccessException | InstantiationException e) {
            System.out.println("Couldn't find constructor");
        }
    }
}/*output
c.getName():type.information.D
c.getPackage():package type.information
c.getSuperclass():class type.information.B
c.getDeclaredClasses()
c.getClasses()
c.getInterfaces()
  interface type.information.A
c.getDeclaredMethods()
  public void type.information.D.f()
c.getDeclaredConstructors()
  type.information.D()
c.getDeclaredFields()
  public int type.information.D.a
  public type.information.C type.information.D.obj
c.getFields()
  public int type.information.D.a
  public type.information.C type.information.D.obj
**********Superclass***********
c.getName():type.information.B
c.getPackage():package type.information
c.getSuperclass():class java.lang.Object
c.getDeclaredClasses()
c.getClasses()
c.getInterfaces()
c.getDeclaredMethods()
  public java.lang.String type.information.B.toString()
c.getDeclaredConstructors()
  type.information.B()
c.getDeclaredFields()
c.getFields()
**********Superclass***********
c.getName():java.lang.Object
c.getPackage():package java.lang, Java Platform API Specification, version 1.8
c.getSuperclass():null
c.getDeclaredClasses()
c.getClasses()
c.getInterfaces()
c.getDeclaredMethods()
  protected void java.lang.Object.finalize() throws java.lang.Throwable
  public final void java.lang.Object.wait() throws java.lang.InterruptedException
  public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
  public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
  public boolean java.lang.Object.equals(java.lang.Object)
  public java.lang.String java.lang.Object.toString()
  public native int java.lang.Object.hashCode()
  public final native java.lang.Class java.lang.Object.getClass()
  protected native java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException
  public final native void java.lang.Object.notify()
  public final native void java.lang.Object.notifyAll()
  private static native void java.lang.Object.registerNatives()
c.getDeclaredConstructors()
  public java.lang.Object()
c.getDeclaredFields()
c.getFields()
B{}

*/

類型轉換前先做檢查

判斷一個對象是否是某類型的實例的三種方式

class Circle  {}
public class DownwardTransformation {
    public static void main(String[] args) {
        // instanceof
        Circle c1 = new Circle();
        if (c1 instanceof Circle)
            System.out.println("This is a circle");
        // isInstance
        Class<Circle> c2 = Circle.class;
        if (c2.isInstance(c1))
            System.out.println("This is a circle");
        // isAssignableFrom
        if (c2.isAssignableFrom(c1.getClass()))
            System.out.println("This is a circle");
    }
}/*output
This is a circle
This is a circle
This is a circle
*/

反射

使用RTTI有一個限制就是這個類型在編譯時必須已知。當在編譯時不能獲知一個對象所屬的類就需要用到反射機制。Class與java.lang.reflect類庫一起對反射的概念進行了支持,該類庫包含Field、Method以及Constructor類,可以調用getFields()、getMethods()和getConstructors()方法返回表示字段、方法以及構造器的對象的數組,這樣未知對象的類信息就能在運行時被完全確定下來,而在編譯時不需要知道任何事情。因此RTTI和反射之間的真正區別在於:對RTTI來說,編譯器在編譯時打開和檢查.class文件。而對於反射機制來說,.class文件在編譯時是不可獲取的,所以是在運行時打開和檢查.class文件。

class Dog {
    public int age;
    public String name;
    public Dog() {}
    public Dog(int age, String name) {
        this.age = age;
        this.name = name;
    }
    public void bark() {
        System.out.println("汪汪汪");
    }
    public void printInfo() {
        System.out.println("age:" + age);
    }
}
public class ReflectTest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("請輸入類的全限定名:");
        String s = scanner.next();
        Class c =null;
        try {
            c = Class.forName(s);
        } catch (ClassNotFoundException e) {
            System.out.println("couldn't find this class");
        }
        System.out.println("c.getName():" + c.getName() + "\nc.getSimpleName():" + c.getSimpleName());
        Field[] fields = c.getFields();
        System.out.print("c.getFields():");
        for (Field field : fields)
            System.out.println(field);
        System.out.print("c.getMethods():");
        Method[] methods = c.getMethods();
        for (Method method : methods)
            System.out.println(method);
        System.out.print("c.getConstructors():");
        Constructor[] constructors = c.getConstructors();
        for (Constructor constructor : constructors)
            System.out.println(constructor);
    }
}/*output
請輸入類的全限定名:
type.information.Dog
c.getName():type.information.Dog
c.getSimpleName():Dog
c.getFields():public int type.information.Dog.age
public java.lang.String type.information.Dog.name
c.getMethods():public void type.information.Dog.bark()
public void type.information.Dog.printInfo()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
c.getConstructors():public type.information.Dog()
public type.information.Dog(int,java.lang.String)
*/

可以看到在編譯時程序不知道類的信息,在執行時通過客戶端輸入類的全限定名,然後類加載器去尋找對應的.class文件,加載class對象,從而實現了創建一個編譯時完全未知的對象,並查看此對象的所有公有的成員變量、構造器、方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章