类型信息
RTTI(RunTime Type Information,运行时类型信息)能够在程序运行时发现和使用类型信息。
Java在运行时,识别对象和类信息,主要有两种方式:
- “传统的” RTTI:假定我们在编译时已经知道了所有的类型;
- “反射”机制:允许我们在运行时发现和使用类的信息。
Class 对象
运行时的类型信息,由Class
对象表示。每一个类都有一个Class
对象。
当生成类对象时,JVM
会先调用类加载器,将类加载到内存中。
所有的类都是第一次使用时,动态加载到 JVM
中的。当程序创建第一个对类的静态成员的引用时,就会加载这个类。
类加载器首先会检查这个类的 Class
对象是否已经加载,如果尚未加载,默认的类加载器就会根据类名查找 .class
文件。这个类的字节码被加载后,JVM
会对其进行验证,确保它没有损坏,并且不包含不良的 Java 代码。
想在运行时使用类型信息,必须先得到Class
引用。Class.forName(全限定类名)
或者 对象.getClass()
。
另外一种获取类对象引用的方法是,类字面常量。例如Status.calss
。当使用.calss
时,不会自动初始化该Class
对象。直到第一次引用static
方法或非常量(非final)的static字段,才会进行类初始化。
一般类的初始化分三个步骤:
- 加载。类加载器,查找字节码,从字节码中创建
Class
对象。 - 链接。验证字节码,为
static
字段分配存储空间,并且解析类中创建的对其他类的引用。 - 初始化。如果有超类,先初始化超类。
Class 泛型
Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
// 为了使用Class对象时,放松限制,可使用通配符。通配符就是 ?,表示“任何事物”。
Class<?> intClass = int.class;
复制代码
反射
类 Class
支持反射操作, java.lang.reflect
库中包含类 Field
、Method
和 Constructor
。这些类型的对象由 JVM 在运行时创建,以表示未知类中的对应成员。
Constructor
用于创建新对象,get()
和 set()
方法读取和修改与 Field
对象关联的字段,invoke()
方法调用与 Method
对象关联的方法。
此外,还可以调用便利方法 getFields()
、getMethods()
、getConstructors()
等,以返回表示字段、方法和构造函数的对象数组。因此,匿名对象的类信息可以在运行时完全确定,编译时不需要知道任何信息。
泛型
多态是一种面向对象思想的泛化机制。
简单泛型
促成泛型出现的最主要的动机之一是为了创建集合类。约定集合要存储什么类型的对象,并且通过编译器确保规约得以满足。避免了手动类型转化。
List<String> list = new ArrayList<>();
list.add("abc");
复制代码
泛型也同样适用于接口和方法。
泛型擦除
编译后,泛型就会被擦除掉,例如List<String>
编译后会变为List
。所以实际上,Java编译后,是没有泛型这一概念的。
Class c1 = new ArrayList<String>().getClass();
Class c2 = new ArrayList<Integer>().getClass();
System.out.println(c1 == c2); // true
复制代码