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对象,从而实现了创建一个编译时完全未知的对象,并查看此对象的所有公有的成员变量、构造器、方法。

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