RTTI即run-time type identification,当你只有一个指向对象的基类引用时,RTTI机制可以让你找出这个对象确切的类型.
在运行期获得对象和类的信息有两钟方式:
传统的RTTI,它假定我们在编译期和运行期已经知道了所有的类型
反射机制则允许我们在运行期获得类的信息.
使用(TYPE)进行强制类型转换是RTTI最基本的使用方式,在Java中,所以的类型转换都是在运行期检查的,这也是RTTI名字的来源,在运行期,识别一个对象的类型.
类型信息在运行期的表示,是由一个Class对象来完成,每个类都有一个Class对象,它包含了与类相关的信息,Class对象用来创建"常规"对象.
关于ClassLoader:ClassLoader负责装载(创建)类的对象,并让你可以获得并操作这个Class对象的引用.
[align=center][img]http://juiccee.iteye.com/upload/picture/pic/22097/ed599853-8dda-3bc9-b59e-271403eb58f3.gif [/img][/align]如上图,ClassLoader可以分为三类:
首先是引导(bootstrap)类装入器(也称作基本(primordial) 类装入器),它与其它ClassLoader不同,不能由 Java 代码实例化。(通常是因为它是作为 VM 本身的一部分实现的(使用C++实现)这个类装入器可以从启动的类路径装入核心系统类,通常是位于 jre/lib 目录的 JAR 文件。但是能用 -Xbootclasspath 命令行选项修改这个类路径.
扩展(extension) 类装入器(也称作标准扩展类装入器)是引导类装入器的一个孩子。它的主要职责是从扩展目录装入类,通常位于 jre/lib/ext 目录。这提供了简单地访问新扩展的能力,例如不同的安全扩展,不需要修改用户的类路径即可实现。
系统(system) 类装入器(也称作应用程序(application)类装入器)负责从 CLASSPATH 环境变量指定的路径装入代码。默认情况下,这个类装入器是用户创建的任何类装入器的父类。这也是 ClassLoader.getSystemClassLoader() 方法返回的类装入器。
[align=center][img]http://juiccee.iteye.com/upload/picture/pic/22099/88664499-24fd-36b3-9941-9d792fdf620f.gif [/img][/align]类装载器采用双亲委派模型。在建立用户自定义的类装载器时可以指定其双亲。如果没有指定,则默认的是把系统类装载器作为其双亲。如果向用户自定义的装载器的构造方法里传递null,则引导装载器就是双亲。
classloader依次从缓存,双亲,自己来寻找类。classloader首先判断要求它装入的类是否与过去装入的类相同。如果相同,就返回上次返回的类(即保存在缓存中的类)。如果不是,就把装入类的机会交给父类。这两步递归地以深度优先的方式重复。如果父类返回 null(或抛出 ClassNotFoundException),那么类装载器会在自己的路径中寻找类的源。
因为父类类装入器总是先得到装入类的机会,所以classloader装入的类最靠近根。这意味着所有核心引导类都是由引导装入器装入的,这就保证装入了类(例如java.lang.Object)的正确版本。这也可以让类装入器看到自己或父类或祖先装入的类,但是不能看到子女装入的类。
使用双亲委派模式,避免了重复加载,同时保证加载入系统的核心类不被其它自定义的类非法替换,实现了良好的安全性
动态加载:
在JVM加载类的时候,需要经过三个步骤,装载、连接、初始化。装载就是找到相应的class文件,读入JVM .加载由三个基本动作组成:
1 通过该类型的完全限定名产生一个代表该类型的二进制数据流
2 解析这个二进制数据流为方法区内的内部数据结构
3 创建一个表示该类型的java.lang.Class类的实例
连接又分三步,
第一步是验证class是否符合规格,
第二步是准备,就是为类变量分配内存同时设置默认初始值,
第三步就是解析(resloving),这步是可选的,下文的loadClass方法的第二个参数来判定是否需要解析,所谓的解析根据《深入JVM》这本书的定义就是根据类中的符号引用查找相应的实体,再把符号引用替换成一个直接引用的过程。
众所周知,java采用的是动态加载机制,既在第一次使用的时候(程序第一次调用该类的非常量的静态成员变量或静态方法的时候,需要注意的是构造函数是一个静态方法)加载该类,
加载由三个基本动作组成:
在程序第一次调用用该类的情况下,装载完成以后进行连接,初始化
类字面常量
使用类字面常量生成一个该类的类对象的应用,如ClassName.class
有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float、double和void。
有几种方法可以生成Class对象的引用.
Class.forname("className")调用 forName("X") 将导致命名为 X 的类被初始化
ClassLoader下有两个loadClass方法:Class<?> loadClass(String name) ;Class<?> loadClass(String name, boolean resolve),第二个参数判定是否需要解释
最后就是使用类字面常量,这种情况下初始化将延迟到程序第一次调用该类的非常量的(non-constant)静态成员变量或静态方法的时候.之所以是非常量的静态成员是因为如果一个static final是一个编译期常量,例如:staitc final int foo =47;则不会导致类被初始化.但并不是所有static final都是编译期常量,例如:staitc final int bar =random.nextInt(1000);总之,访问任何非编译期常量都回导致初始化.
带泛型的Class对象引用:
使用带泛型的Class对象引用,你可以具体的指定这个Class对象的类型约束,以提供编译期间的类型检查
"?"通配符号
若要表示类型约束为任何Number类的子类的时,使用以下代码将报错
因为虽然Integer是Number的子类,但是Integer Class object的类型 不是 Number Class object 类型的字类
正确的写法应该是:
"?"是java generics的一部分,表示"anything"即使某个Class对象不包含任何具体的类型信息,也推荐使用Class<?>也不是单独使用Class.这表明你不是忘了约束类型.
声明为Class<T> type的class对象调用type.newInstance()返回一个类型为T的对象.
同样,要表示类型约束为某类的超类时应使用:
但在这种情况下,调用类型约束为<? super xxx>的class对象的newInstance()方法只能返回Object.
SE5中,Class类增加了一个新的方法:public T cast(Object obj),这个方法将一个对象强制转换成此 Class 对象所表示的类或接口。
Class有一个isInstance()此方法是 Java 语言 instanceof 运算符的动态等效方法。如果指定的 Object 参数非空,且能够在不引发 ClassCastException 的情况下被强制转换成该 Class 对象所表示的引用类型,则该方法返回 true;否则返回 false。
在运行期获得对象和类的信息有两钟方式:
传统的RTTI,它假定我们在编译期和运行期已经知道了所有的类型
反射机制则允许我们在运行期获得类的信息.
使用(TYPE)进行强制类型转换是RTTI最基本的使用方式,在Java中,所以的类型转换都是在运行期检查的,这也是RTTI名字的来源,在运行期,识别一个对象的类型.
类型信息在运行期的表示,是由一个Class对象来完成,每个类都有一个Class对象,它包含了与类相关的信息,Class对象用来创建"常规"对象.
关于ClassLoader:ClassLoader负责装载(创建)类的对象,并让你可以获得并操作这个Class对象的引用.
[align=center][img]http://juiccee.iteye.com/upload/picture/pic/22097/ed599853-8dda-3bc9-b59e-271403eb58f3.gif [/img][/align]如上图,ClassLoader可以分为三类:
首先是引导(bootstrap)类装入器(也称作基本(primordial) 类装入器),它与其它ClassLoader不同,不能由 Java 代码实例化。(通常是因为它是作为 VM 本身的一部分实现的(使用C++实现)这个类装入器可以从启动的类路径装入核心系统类,通常是位于 jre/lib 目录的 JAR 文件。但是能用 -Xbootclasspath 命令行选项修改这个类路径.
扩展(extension) 类装入器(也称作标准扩展类装入器)是引导类装入器的一个孩子。它的主要职责是从扩展目录装入类,通常位于 jre/lib/ext 目录。这提供了简单地访问新扩展的能力,例如不同的安全扩展,不需要修改用户的类路径即可实现。
系统(system) 类装入器(也称作应用程序(application)类装入器)负责从 CLASSPATH 环境变量指定的路径装入代码。默认情况下,这个类装入器是用户创建的任何类装入器的父类。这也是 ClassLoader.getSystemClassLoader() 方法返回的类装入器。
[align=center][img]http://juiccee.iteye.com/upload/picture/pic/22099/88664499-24fd-36b3-9941-9d792fdf620f.gif [/img][/align]类装载器采用双亲委派模型。在建立用户自定义的类装载器时可以指定其双亲。如果没有指定,则默认的是把系统类装载器作为其双亲。如果向用户自定义的装载器的构造方法里传递null,则引导装载器就是双亲。
classloader依次从缓存,双亲,自己来寻找类。classloader首先判断要求它装入的类是否与过去装入的类相同。如果相同,就返回上次返回的类(即保存在缓存中的类)。如果不是,就把装入类的机会交给父类。这两步递归地以深度优先的方式重复。如果父类返回 null(或抛出 ClassNotFoundException),那么类装载器会在自己的路径中寻找类的源。
因为父类类装入器总是先得到装入类的机会,所以classloader装入的类最靠近根。这意味着所有核心引导类都是由引导装入器装入的,这就保证装入了类(例如java.lang.Object)的正确版本。这也可以让类装入器看到自己或父类或祖先装入的类,但是不能看到子女装入的类。
使用双亲委派模式,避免了重复加载,同时保证加载入系统的核心类不被其它自定义的类非法替换,实现了良好的安全性
动态加载:
在JVM加载类的时候,需要经过三个步骤,装载、连接、初始化。装载就是找到相应的class文件,读入JVM .加载由三个基本动作组成:
1 通过该类型的完全限定名产生一个代表该类型的二进制数据流
2 解析这个二进制数据流为方法区内的内部数据结构
3 创建一个表示该类型的java.lang.Class类的实例
连接又分三步,
第一步是验证class是否符合规格,
第二步是准备,就是为类变量分配内存同时设置默认初始值,
第三步就是解析(resloving),这步是可选的,下文的loadClass方法的第二个参数来判定是否需要解析,所谓的解析根据《深入JVM》这本书的定义就是根据类中的符号引用查找相应的实体,再把符号引用替换成一个直接引用的过程。
众所周知,java采用的是动态加载机制,既在第一次使用的时候(程序第一次调用该类的非常量的静态成员变量或静态方法的时候,需要注意的是构造函数是一个静态方法)加载该类,
加载由三个基本动作组成:
在程序第一次调用用该类的情况下,装载完成以后进行连接,初始化
类字面常量
使用类字面常量生成一个该类的类对象的应用,如ClassName.class
有九种预定义的 Class 对象,表示八个基本类型和 void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即 boolean、byte、char、short、int、long、float、double和void。
System.out.println(Boolean.class.isPrimitive());//false
System.out.println(Boolean.TYPE.isPrimitive());//true
System.out.println(boolean.class.isPrimitive());//true
有几种方法可以生成Class对象的引用.
Class.forname("className")调用 forName("X") 将导致命名为 X 的类被初始化
ClassLoader下有两个loadClass方法:Class<?> loadClass(String name) ;Class<?> loadClass(String name, boolean resolve),第二个参数判定是否需要解释
最后就是使用类字面常量,这种情况下初始化将延迟到程序第一次调用该类的非常量的(non-constant)静态成员变量或静态方法的时候.之所以是非常量的静态成员是因为如果一个static final是一个编译期常量,例如:staitc final int foo =47;则不会导致类被初始化.但并不是所有static final都是编译期常量,例如:staitc final int bar =random.nextInt(1000);总之,访问任何非编译期常量都回导致初始化.
带泛型的Class对象引用:
使用带泛型的Class对象引用,你可以具体的指定这个Class对象的类型约束,以提供编译期间的类型检查
public class GenericClassReferences {
public static void main(String[] args) {
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class; // Same thing
intClass = double.class;
// genericIntClass = double.class; // Illegal
}
}
"?"通配符号
若要表示类型约束为任何Number类的子类的时,使用以下代码将报错
Class<Number> numberClass = int.class;
因为虽然Integer是Number的子类,但是Integer Class object的类型 不是 Number Class object 类型的字类
正确的写法应该是:
Class<? extends Number> numberClass = int.class
"?"是java generics的一部分,表示"anything"即使某个Class对象不包含任何具体的类型信息,也推荐使用Class<?>也不是单独使用Class.这表明你不是忘了约束类型.
声明为Class<T> type的class对象调用type.newInstance()返回一个类型为T的对象.
同样,要表示类型约束为某类的超类时应使用:
Class<? super FancyToy> up = FancyToy.class.getSuperClass();
而不是
//FancyToy inherite from Toy
Class<Toy> up =FancyToy.class.getSuperClass();//won't compile!
但在这种情况下,调用类型约束为<? super xxx>的class对象的newInstance()方法只能返回Object.
Class<? super FancyToy> up = FancyToy.class.getSuperClass();
up.newInstance();//返回Object
SE5中,Class类增加了一个新的方法:public T cast(Object obj),这个方法将一个对象强制转换成此 Class 对象所表示的类或接口。
Building b = new House();
Class<House> houseType = House.class;
House h = houseType.cast(b);
h = (House)b; // ... or just do this.
Class有一个isInstance()此方法是 Java 语言 instanceof 运算符的动态等效方法。如果指定的 Object 参数非空,且能够在不引发 ClassCastException 的情况下被强制转换成该 Class 对象所表示的引用类型,则该方法返回 true;否则返回 false。