Java类型信息

来源:互联网 发布:事业单位考试视频软件 编辑:程序博客网 时间:2024/06/05 16:18

运行时识别对象和类的信息的两种方式:

  • RTTI(Run-Time Type Identification)
  • “反射“机制
    RTTI的含义就是运行时识别一个对象的类型,类型信息是由Class对象表示的,每个Java里面的类都对应一个Class对象(在编写并且编译后),这个对象被保存在这个类的同名class文件里。为了生成Class对象,运行这个程序的Java虚拟机将使用“类加载器”子系统。
    Class对象是由JVM加载的,所有的类都是在对其第一次使用时,动态加载到JVM。当程序创建第一个对类的静态成员的引用,就会加载这个类。使用new关键字创建类的新对象也会被当做对类的静态成员的引用。因此,Java程序在它开始运行之前并非完全被加载,其各个部分是在必须时才被加载的。所以在使用该类时,类加载器首先会检查这个类的Class对象是否已经加载。如果尚未加载,默认的类加载器就会根据类名查找.class文件(编译后Class对象保存在同名的class文件中),当这个类的字节码文件被加载时,它们必须接受验证,以确保其没有被破坏,并且不包含不良Java代码(这是Java的安全机制检测),完全没问题后就会被动态加载到内存中,此时相当于Class对象被载入内存(.class字节码文件保存的就是Class对象),它就用来创建这个类的所有对象。
    如果想要在运行时使用类型信息,就必须首先获得对恰当的Class对象的引用。
    Class对象加载的时机

Class对象仅在需要的时候才被加载,static初始化是在类加载时进行的。
调用Class.forName(“全限定名”)将会导致类被加载,前提是该类从来没有被加载过。

获取Class对象引用的3种方法:
- Class cc=Class.fotName(“全限定名”);
- Class cc=object.getClass();
- FancyToy.class;//类字面常量

使用类字面常量生成对Class对象的引用相对前面两种方法更简单,更安全,因为它在编译期就会受到检查,并且由于无需调用forName()方法而更高效。使用.class来创建对Class对象的引用时,不会自动地初始化该Class对象。
使用类字面常量生成对Class对象的引用不仅可以应用于普通的类,还可以应用于接口、数组以及基本数据类型。对于基本数据类型对应的包装类,还有一个标准字段TYPE。TYPE字段是一个引用,指向对应的基本数据类型的Class对象。建议使用.class的形式,以保持与普通类的一致性。

boolean.class = Boolean.TYPE;char.class = Character.TYPE;byte.class = Byte.TYPE;short.class = Short.TYPE;int.class = Integer.TYPE;long.class = Long.TYPE;float.class = Float.TYPE;double.class = Double.TYPE;void.class = Void.TYPE;

为了使用类而做的准备工作实际包含3个步骤:
加载:由类加载器执行,查找此类字节码文件,并从这些字节码中创建一个Class对象
链接:在链接阶段将验证类中的字节码,为静态域分配空间,不包含实例成员变量,并且如果必需的话,将解析这个类创建的对其他类的所有引用
初始化:如果该类具有超类,则对其初始化,执行静态初始化器和静态初始化块

class Initable {  //编译期静态常量  static final int staticFinal = 47;  //非编期静态常量  static final int staticFinal2 =    ClassInitialization.rand.nextInt(1000);  static {    System.out.println("Initializing Initable");  }}class Initable2 {  //静态成员变量  static int staticNonFinal = 147;  static {    System.out.println("Initializing Initable2");  }}class Initable3 {  //静态成员变量  static int staticNonFinal = 74;  static {    System.out.println("Initializing Initable3");  }}public class ClassInitialization {  public static Random rand = new Random(47);  public static void main(String[] args) throws Exception {    //字面常量获取方式获取Class对象 不触发初始化    Class initable = Initable.class;    System.out.println("After creating Initable ref");    //不触发类初始化     //static final值是编译期常量,那么这个值不需要对Initable类进行初始化就可以被读取    System.out.println(Initable.staticFinal);    //会触发类初始化    //如果只是将一个域设置为static final,还不足以确保这种行为,访问这个域将强制进行类的初始话    System.out.println(Initable.staticFinal2);    //会触发类初始化    //如果一个static域不是final的,那么在对它访问时,总是要求它在被读取之前,要先进行链接(为这个域分配空间)和初始化(初始化该存储空间)    System.out.println(Initable2.staticNonFinal);    //forName方法获取Class对象 触发初始化    Class initable3 = Class.forName("Initable3");    System.out.println("After creating Initable3 ref");    System.out.println(Initable3.staticNonFinal);  }}

执行结果:

After creating Initable ref47Initializing Initable258Initializing Initable2147Initializing Initable3After creating Initable3 ref74

泛化的Class引用
public class ClazzDemo {

public static void main(String[] args){    //没有泛型    Class intClass = int.class;    //带泛型的Class对象    Class<Integer> integerClass = int.class;    integerClass = Integer.class;    //没有泛型的约束,可以随意赋值    intClass= double.class;    //编译期错误,无法编译通过    //integerClass = double.class}

}
普通的类引用不会产生警告信息,尽管泛型类引用只能赋值为指向其声明的类型,但是普通的类引用可以被重新赋值为指向任何其他的Class对象,通过使用泛型语法,可以让编译期强制执行额外的类型检查。

//编译无法通过Class<Number>  numberClass=Integer.class;//Integer继承自NumberInteger Class 不是Number Class对象的子类

为了在使用泛化的Class引用时放松限制,可以使用通配符,它是Java泛型的一部分。通配符就是“?”,表示“任何事物”。因此可以在普通Class引用中添加通配符,并产生相同的结果。

Class<?> intClass=int.class;intClass=double.class;

这样的语句并没有什么问题,毕竟通配符指明所有类型都适用,那么为什么不直接使用Class还要使用Class

//编译通过Class<? extends Number> clazz=Integer.class;clazz=double.class;clazz.Number.class;

向Class引用添加泛型语法的原因仅仅是为了提供编译期类型检查,因此如果你操作有误,稍后立即就会发现这一点。
类型转换/Class引用的转型语法
cast()方法接收参数对象,并将其转型为Class引用的类型。

class Building{}class House extends Building{}public class ClassCasts{Building b=new House();Class<House> houseType=House.class;House h=houseType.cast(b);h=(House)b;}

关于instanceof 关键字,它返回一个boolean类型的值,意在告诉我们对象是不是某个特定的类型实例。如下,在强制转换前利用instanceof检测obj是不是Animal类型的实例对象,如果返回true再进行类型转换,这样可以避免抛出类型转换的异常(ClassCastException)

public void cast2(Object obj){    if(obj instanceof Animal){          Animal animal= (Animal) obj;      }}

而isInstance方法则是Class类中的一个Native方法,也是用于判断对象类型的,看个简单例子:

public void cast2(Object obj){
//instanceof关键字
if(obj instanceof Animal){
Animal animal= (Animal) obj;
}

    //isInstance方法    if(Animal.class.isInstance(obj)){        Animal animal= (Animal) obj;    }

}

事实上instanceOf 与isInstance方法产生的结果是相同的。对于instanceOf是关键字只被用于对象引用变量,检查左边对象是不是右边类或接口的实例化。如果被测对象是null值,则测试结果总是false。一般形式:

//判断这个对象是不是这种类型
obj.instanceof(class)

而isInstance方法则是Class类的Native方法,其中obj是被测试的对象或者变量,如果obj是调用这个方法的class或接口的实例,则返回true。如果被检测的对象是null或者基本类型,那么返回值是false;一般形式如下:

//判断这个对象能不能被转化为这个类
class.inInstance(obj)

最后这里给出一个简单实例,验证isInstance方法与instanceof等价

class A{}class B extends A {}public class C {  static void test(Object x) {    print("Testing x of type " + x.getClass());    print("x instanceof A " + (x instanceof A));    print("x instanceof B "+ (x instanceof B));    print("A.isInstance(x) "+ A.class.isInstance(x));    print("B.isInstance(x) " +      B.class.isInstance(x));    print("x.getClass() == A.class " +      (x.getClass() == A.class));    print("x.getClass() == B.class " +      (x.getClass() == B.class));    print("x.getClass().equals(A.class)) "+      (x.getClass().equals(A.class)));    print("x.getClass().equals(B.class)) " +      (x.getClass().equals(B.class)));  }  public static void main(String[] args) {    test(new A());    test(new B());  } }Testing x of type class com.zejian.Ax instanceof A truex instanceof B false //父类不一定是子类的某个类型A.isInstance(x) trueB.isInstance(x) falsex.getClass() == A.class truex.getClass() == B.class falsex.getClass().equals(A.class)) truex.getClass().equals(B.class)) falseTesting x of type class com.zejian.Bx instanceof A truex instanceof B trueA.isInstance(x) trueB.isInstance(x) truex.getClass() == A.class falsex.getClass() == B.class truex.getClass().equals(A.class)) falsex.getClass().equals(B.class)) true

RTTI3种形式:
1传统的类型转换,由RTTI保证类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个ClassCastException异常
2代表对象类型的Class对象
3关键字instanceof,告诉我们对象是不是某个特定类型的实例。

原文地址:http://blog.csdn.net/javazejian