RTTI:即 Run-Time Type Identification,程序在运行时,通过运行时类型信息来检查某个引用所指的实际派生类型。其中运行时类型信息被包含在一个称为Class对象的特殊对象中,Java使用Class对象来执行其RTTI。
Class.forName("ClassName")和ClassName.class主要有以下几点区别:
- Class.forName("ClassName")方式必须要置于try语句中,而ClassName.class则不需要置于try语句块中,因为它在编译时会受到检查,相对来说更简单和安全;
- ClassName.class没有forName()方法的调用,相对更加高效;
- Class.forName("ClassName")方式会执行类加载的加载、链接、初始化三个步骤,而ClassName.class则实现了“惰性加载”,不会立马执行初始化步骤,初始化被延迟到了对静态方法(包括构造方法)等首次引用时才被执行。
其中类加载的三个步骤:
- 加载。该步骤主要查找对应的字节码文件,并从该文件中创建一个Class对象;
- 链接。该步骤检查载入CLass文件数据的正确性,为静态变量分配存储空间;
- 初始化。该步骤对类的静态变量,静态代码块执行初始化操作。
Class.forName("ClassName")和ClassName.class用法比较的示例代码:
package rtti;
class Cat {
static {
System.out.print("Loading Cat");
}
static int num = 10;
}
class Dog {
static {
System.out.print("Loading Dog");
}
static int num = 10;
}
public class Test {
public static void main(String[] args) {
System.out.print("ClassName.class的输出为:{");
Class cat = Cat.class;
System.out.println("}");
System.out.print("调用static变量的输出为:{");
System.out.print(" Cat.num: "+Cat.num);
System.out.println("}");
try {
System.out.print("Class.forName(\"ClassName\")的输出为:{");
Class.forName("rtti.Dog");
System.out.println("}");
System.out.print("调用static变量的输出为:{");
System.out.print(" Dog.num: "+Dog.num);
System.out.println("}");
} catch (ClassNotFoundException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
/*程序输出:
ClassName.class的输出为:{}
调用static变量的输出为:{Loading Cat Cat.num: 10}
Class.forName("ClassName")的输出为:{Loading Dog}
调用static变量的输出为:{ Dog.num: 10}
*/
从程序的输出结果中可以明显的看出:
- 使用ClassName.class这种方式是不会调用static代码块的,也就是说没有执行初始化步骤,但是在调用static变量的时候执行了static代码块;
- 而使用Class.forName("ClassName")这种方式是执行了初始化步骤的;
- 我们要分情况选择合适的方法,例如在加载数据库驱动的时候,可以选择使用Class.forName("com.mysql.jdbc.Driver")来让jvm执行相应的static代码块。