Java类型信息

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

  • 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 ref
47
Initializing Initable
258
Initializing Initable2
147
Initializing Initable3
After creating Initable3 ref
74

泛化的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.A
x instanceof A true
x instanceof B false //父类不一定是子类的某个类型
A.isInstance(x) true
B.isInstance(x) false
x.getClass() == A.class true
x.getClass() == B.class false
x.getClass().equals(A.class)) true
x.getClass().equals(B.class)) false
Testing x of type class com.zejian.B
x instanceof A true
x instanceof B true
A.isInstance(x) true
B.isInstance(x) true
x.getClass() == A.class false
x.getClass() == B.class true
x.getClass().equals(A.class)) false
x.getClass().equals(B.class)) true

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值