由于JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等,因此,如果获取了某个Class实例,我们就可以通过这个Class实例获取到该实例对应的class的所有信息。
这种通过Class实例获取class信息的方法称为反射(Reflection)。

如何获取一个class的Class实例?有三个方法:
方法一:直接通过一个class的静态变量class获取: 类字面常量
这种方法比forName安全,因为不用放入try块中
类字面常量不仅可以用于普通的类,还可以用于接口、数组和基本类型

  1. Class cls = String.class;

方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:

  1. String s = "Hello";
  2. Class cls = s.getClass();

方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:

  1. try {
  2. Class cls = Class.forName("java.lang.String");
  3. } catch (ClassNotFoundException e) {
  4. e.printStackTrace();
  5. }

类字面常量

这种方法比forName安全,因为不用放入try块中
类字面常量不仅可以用于普通的类,还可以用于接口、数组和基本类型
当使用 .class来创建对Class对象的引用时,不会自动的初始化该Class对象
参考:
深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明
chapter7-7.2

  1. class Initable {
  2. static final int staticFinal = 47;
  3. static final int staticFinal2 =
  4. ClassInitialization.rand.nextInt(1000);
  5. static {
  6. System.out.println("Initializing Initable");
  7. }
  8. }
  9. public class ClassInitialization {
  10. public static Random rand = new Random(47);
  11. public static void main(String[] args) throws Exception {
  12. Class initable = Initable.class; //不会触发初始化
  13. System.out.println(Initable.staticFinal); //只会打印出47
  14. //会触发初始化,打印一个随机数和Initializing Initable
  15. System.out.println(Initable.staticFinal2);

static final是一个 编译期常量 这个值不需要初始化Initable类就可以被读取 但是只设置为static或者fianl不可以,会触发该类的初始化

  1. class Initable2 {
  2. static int staticNonFinal = 147;
  3. static {
  4. System.out.println("Initializing Initable2");
  5. }
  6. }
  7. main方法里
  8. System.out.println(Initable2.staticNonFinal);
  9. 输出:
  10. Initializing Initable2
  11. 147

getName()和getCanonicalName()和getSimpleName()三个方法

getSimpleName()返回的只是类名

getCanonicalName()返回的是更容易理解的表示

getName()返回的是虚拟机里面的class的表示

对于普通类没有区别,对于内部类和数组类才会有区别

  1. User user = new User();
  2. String canonicalName1 = user.getClass().getCanonicalName();
  3. System.out.println(canonicalName1);
  4. String name1 = user.getClass().getName();
  5. System.out.println(name1);

运行后:
image.png

泛化的Class引用

image.png
image.png
可见通过使用泛型语法,可以让编译器强制执行额外的类型检查

Class类没有继承,虽然Integer是Number则子类,但是Integer.class不是Number.class的子类
image.png