由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。
这种通过Class实例获取class信息的方法称为反射(Reflection)。
如何获取一个class的Class实例?有三个方法:
方法一:直接通过一个class的静态变量class获取: 类字面常量
这种方法比forName安全,因为不用放入try块中
类字面常量不仅可以用于普通的类,还可以用于接口、数组和基本类型
Class cls = String.class;
方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:
String s = "Hello";
Class cls = s.getClass();
方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:
try {
Class cls = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
类字面常量
这种方法比forName安全,因为不用放入try块中
类字面常量不仅可以用于普通的类,还可以用于接口、数组和基本类型
当使用 .class来创建对Class对象的引用时,不会自动的初始化该Class对象
参考:
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明
chapter7-7.2
class Initable {
static final int staticFinal = 47;
static final int staticFinal2 =
ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
public class ClassInitialization {
public static Random rand = new Random(47);
public static void main(String[] args) throws Exception {
Class initable = Initable.class; //不会触发初始化
System.out.println(Initable.staticFinal); //只会打印出47
//会触发初始化,打印一个随机数和Initializing Initable
System.out.println(Initable.staticFinal2);
static final是一个 编译期常量 这个值不需要初始化Initable类就可以被读取 但是只设置为static或者fianl不可以,会触发该类的初始化
class Initable2 {
static int staticNonFinal = 147;
static {
System.out.println("Initializing Initable2");
}
}
在main方法里
System.out.println(Initable2.staticNonFinal);
输出:
Initializing Initable2
147
getName()和getCanonicalName()和getSimpleName()三个方法
getSimpleName()返回的只是类名
getCanonicalName()返回的是更容易理解的表示
getName()返回的是虚拟机里面的class的表示
对于普通类没有区别,对于内部类和数组类才会有区别
User user = new User();
String canonicalName1 = user.getClass().getCanonicalName();
System.out.println(canonicalName1);
String name1 = user.getClass().getName();
System.out.println(name1);
运行后:
泛化的Class引用
可见通过使用泛型语法,可以让编译器强制执行额外的类型检查
Class类没有继承,虽然Integer是Number则子类,但是Integer.class不是Number.class的子类