一、RTTI
RTTI(Run_Time Type Identification 运行时类型识别):
RTTI是任何一门面对对象语言都必须提供的功能。不仅系统本身要利用该功能来识别目前正在运行的对象真正所属的类别,程序员有时候也需要利用这一机制来识别对象,以设计程序作出适当的反应。
Java在运行期间查找对象和类的信息,主要采取以下两种形式:
(1)采用传统的RTTI,它假定我们已在编译和运行期间拥有所有类型
(2)采用Java特有的“反射”机制,利用它可在运行期独立查找类信息。
说简单点,Java提供了两种方式来获取对象的信息:一种是利用传统的方法;一种是利用反射机制。
二、Class
类
Java API中这么解释Class类:
Class
类的实例表示正在运行的 Java 应用程序中的类和接口, 枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class
对象。 基本的 Java 类型(boolean
、byte
、char
、short
、int
、long
、float
和double
)和关键字 void
也表示为 Class
对象。
注意:Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass
方法自动构造的
为了理解RTTI在Java例如何工作,首先必须了解类型信息在运行期间是如何表示的。只要用到一个名为"Class"的特殊形式的对象,这个对象包含了与类有关的信息(有时候也把它叫作“元类”)。事实上,要用Class类创建属于某个类的全部“常规”或“普通”对象。
任何一个作为程序一部分的类,都有一个Class对象。换言之,每次写一个新类时,同时也会创建一个Class对象(更恰当的说,是保存在一个完全同名的.class文件中)。在运行期间,一旦程序员想要生成某一个类的对象,用于执行程序的Java虚拟机(JVM)首先就会检查该类型的Class对象是否已经载入。若尚未载入,JVM就会查找同名的.class文件,并将其载入。所以,Java程序启动时并不是完全载入的,这一点与许多传统语言不同。一旦该类型的Class对象进入内存,就用它创建该类型的所有对象。
三、Class动态加载类的三种方法
(1)类名.Class
(2)类型.getClass()
(3)Class.forName("类名") (这里的类名表示具体类名,所以要加上包名)
补充:
静态加载类和动态加载类:
编译时刻加载的类是静态加载类,例如用new关键字创建的对象要通过编译器的静态检查,如果编译时该类不存在,那么使用该对象的类也无法通过编译。
运行时刻加载的类是动态类,在编译的时候不会进行判断,只有在运行时才会进行判断,假设该类不存在,在运行时才会报错。
例如下面的例子:
Candy.java文件
package com.test;
public class Candy {
static{
System.out.println("Loading Candy in static block");
}
public static void main(String[] args){
System.out.println("Loading Candy in main method");
}
}
LoadClass.java文件:
package com.test;
public class LoadClass {
public static void main(String[] args){
System.out.println("Before loding Candy");
try {
//加载Candy对象
Class.forName("com.test.Candy");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
运行结果如下:Before loding Candy
Loading Candy in static block
从程序的输出结果来看,用这种方式和new 来创建对象没什么区别。其实,二者之间有很大的区别,用new 关键字创建对象要通过编译器静态检查,如果编译时Candy类不存在,那么使用Candy类对象的类LoadClass也无法通过编译。而forName()方法时动态加载,即便编译时Candy类不存在,编译也是可以通过的,只是在运行时刻会抛出异常。
注意:使用forName()方法存在一个问题,它返回的是一个Class类型,而不是加载的那个类型。所以,无法做出下面的声明:
Candy candy=Class.forName("com.test.Candy");
而只能写为:Class candy=Class.forName("com.test.Candy");
也就是说,candy无法直接使用Candy类中定义的方法。解决的办法是利用反射机制,不过这比直接用new来创建对象要麻烦得多。所以,用forName()加载对象多用在加载驱动程序的情况下(例如,加载数据库驱动)下面举个例子熟悉一下Class动态加载类的方法:
package com.baseType;
public class baseClass {
public static void main(String[] args){
Class c1=int.class; //int的类类型
Class c2=String.class; //String类的类类型
Class c3=double.class; //double的类类型
Class c4=Double.class; //Double类的类类型
Class c5=void.class; //关键字void的类类型
//打印类类型的具体名称
System.out.println(c1.getName()); //int
System.out.println(c2.getName()); //java.lang.String
System.out.println(c3.getName()); //double
System.out.println(c4.getName()); //java.lang.Double
System.out.println(c5.getName()); //void
//不带包名的类名称
System.out.println(c2.getSimpleName()); //String
System.out.println(c4.getSimpleName()); //Double
}
}