RTTI:
运行时的对象类型信息识别,大方向可以分为两种
最传统的RTTI就是静态识别,程序编译之后,编译器就知道该对象的类型信息 运行时识别,需要用到反射机制
Class对象
每个类都拥有一个Class
对象,这个Class
对象会被存储到与类名相同的.class
文件里面 这个对象的存在,让反射机制得以实现 为了生成某个类的对象,JVM将使用其中的类加载器
类加载器首先检查对应类的Class对象是否已经加载,如果没有,默认的加载器就会根据类名查找.class
文件(字节码)。 当Class
对象被加载到内存的时候,就会被用来创建这个类的所有对象。
class Candy { static print ( "Loading Candy" ) ; }
class Gum { static print ( "Loading Gum" ) ; }
class Cookie { static print ( "Loading Cookei" ) ; }
public class SweetShop {
public static void main ( String[ ] args) {
print ( "inside main" ) ;
new Candy ( ) ;
print ( "After creatingcandy" ) ;
try {
Class. forName ( "Gum" ) ;
} catch ( ClassNotFOundException e) {
print ( "Couldn't find Gum" ) ;
}
print ( "After Class.ForName(\"Gum\")" ) ;
new Cookie ( ) ;
print ( "After creating Cookie" ) ;
}
}
上面代码的输出中,会发现每一个类在第一次加载的时候,static
成员都会被执行。 Class.forName(String s)
在找到s
对应的类的时候,如果对应类没有被加载,那么就加载它,该函数的返回值是Class
对象的引用。此外,这个s
必须包含完整的类名(加上包名)只要想在运行时使用类型信息,就需要先获得对恰当的Class
对象的引用,Class.forName
是一种获取途径,在感兴趣的类已经创建了一个对象之后,也可以使用obj.getClass()
的方式获得对应对象的Class
引用。
类字面常量
.class
被称为类字面常量,这里千万不要和文件的.class
弄混了,这个相当于数组的.length
这样的成员,表示的是类对象的而引用信息。使用.class
来创建对应类Class
对象的引用的时候,并不会自动初始化Class
对象。 实际上,类对象的创建分为三个部分
加载:通过类加载器执行。查找字节码(一般是在classpath指定的路径中查找),从字节码中创建一个Class
对象 链接:验证类中的字节码,为静态域分配存储空间,解析这个类创建的对其他类的所有引用 初始化(惰性):如果有父类,那么对其进行初始化,执行静态初始化器 和静态初始化块 。这一个步骤不会在链接之后立马执行,而是延迟到对静态方法(static
如构造器),或者非final
静态域首次引用是才执行。
泛化的class引用
Class
引用总是指向某个Class
对象,可以制造实例,并包含可以作用域这些事例的所有方法的代码。
public class GenericClassReferences {
public static void main ( ) {
Class intClass = int . class ;
Class< Integer> genericIntClas = int . class ;
genericIntClass = Integer. class ;
intClass = double . class ;
}
}
泛型类引用只能赋值为指向其声明的类型,但是普通的类引用可以被重新赋值为指向任何其他类型的Class
对象;换句话说,其实泛型语法会让编译器更进一步检查类引用的类型。 虽然包装类Integer
是继承自Number
类,但是如果写
Class< Number> genericNumberClass = int . class ;
会出现问题,因为Integer.class
并非Number.class
的子类,这点要且介。 那么如果我们想要和多态一样,通过Number.class
来表示各种继承类的Class
对象,比如Integer.class, Double.class
,要怎么写呢?java提供了?
通配符以及extends
关键字
其中通配符意味着,Class<?>
作用等价于非泛型Class
extends
意味着,Class<? extends Obj>
表示的类引用是Obj
类的子类对象的类信息
public class BoundedClassReferences {
public static void main ( String[ ] args) {
Class< ? extends NUmber > bounded = int . class ;
bounded = double . class ;
bound = Number. class ;
}
}
Class
对象可以通过调用newInstance()
方法来产生对象
class CountedInteger {
private static long counter;
private final long id = count++ ;
public String toString ( ) { return Long. toString ( id) ; }
}
public class FilledList < T> {
private Class< T> type;
public FilledList ( Class< T> type) { this . type = type; }
public List< T> create ( int nEle) {
List< T> result = new ArrayList < T> ( ) ;
try {
for ( int i = 0 ; i < nEle; i++ ) result. add ( type. newInstance ( ) ) ;
} catch ( Exception e) {
throw new RuntimeException ( e) ;
}
return result;
}
public static void main ( String[ ] args) {
FilledList< CountedInteger> fl= new FilledList < CountedInteger> ( CountedInteger. class ) ;
System. out. println ( fl. create ( 15 ) ) ;
}
}
新的转型方法.cast()
,对无法使用普通括号转型的情况很有用,调用者为想要编程的Class
对象引用,传入参数为想要转型的对象obj
,感觉有点类似于String.asValue(int)
class Building { }
class House extends Building { }
public class ClassCasts {
public static void main ( String [ ] args) {
Building b = new House ( ) ;
Class< House> houseType = House. class ;
House h = houseType. cast ( b) ;
h = ( House) b;
}
}
参考书