通过前面,我们了解了类的加载,同时会提取一个类的信息生成Class对象存放在内存中,而反射机制其实就是利用这些存放的类信息,来获取类的信息和操作类。那么如何获取到每个类对应的Class对象呢,我们可以通过以下方式:
public static void main(String[] args) throws ClassNotFoundException {
Class<String> clazz = String.class; //使用class关键字,通过类名获取
Class<?> clazz2 = Class.forName("java.lang.String"); //使用Class类静态方法forName(),通过包名.类名获取,注意返回值是Class<?>
Class<?> clazz3 = new String("cpdd").getClass(); //通过实例对象获取
}
注意Class类也是一个泛型类,只有第一种方法,能够直接获取到对应类型的Class对象,而以下两种方法使用了?
通配符作为返回值,但是实际上都和第一个返回的是同一个对象:
Class<String> clazz = String.class; //使用class关键字,通过类名获取
Class<?> clazz2 = Class.forName("java.lang.String"); //使用Class类静态方法forName(),通过包名.类名获取,注意返回值是Class<?>
Class<?> clazz3 = new String("cpdd").getClass();
System.out.println(clazz == clazz2);
System.out.println(clazz == clazz3);
通过比较,验证了我们一开始的结论,在JVM中每个类始终只存在一个Class对象,无论通过什么方法获取,都是一样的。现在我们再来看看这个问题:
public static void main(String[] args) {
Class<?> clazz = int.class; //基本数据类型有Class对象吗?
System.out.println(clazz);
}
迷了,不是每个类才有Class对象吗,基本数据类型又不是类,这也行吗?实际上,基本数据类型也有对应的Class对象(反射操作可能需要用到),而且我们不仅可以通过class关键字获取,其实本质上是定义在对应的包装类中的:
/**
* The {@code Class} instance representing the primitive type
* {@code int}.
*
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
/*
* Return the Virtual Machine's Class object for the named
* primitive type
*/
static native Class<?> getPrimitiveClass(String name); //C++实现,并非Java定义
每个包装类中(包括Void),都有一个获取原始类型Class方法,注意,getPrimitiveClass获取的是原始类型,并不是包装类型,只是可以使用包装类来表示。
public static void main(String[] args) {
Class<?> clazz = int.class;
System.out.println(Integer.TYPE == int.class);
}
通过对比,我们发现实际上包装类型都有一个TYPE,其实也就是基本类型的Class,那么包装类的Class和基本类的Class一样吗?
public static void main(String[] args) {
System.out.println(Integer.TYPE == Integer.class);
}
我们发现,包装类型的Class对象并不是基本类型Class对象。数组类型也是一种类型,只是编程不可见,因此我们可以直接获取数组的Class对象:
public static void main(String[] args) {
Class<String[]> clazz = String[].class;
System.out.println(clazz.getName()); //获取类名称(得到的是包名+类名的完整名称)
System.out.println(clazz.getSimpleName());
System.out.println(clazz.getTypeName());
System.out.println(clazz.getClassLoader()); //获取它的类加载器
System.out.println(clazz.cast(new Integer("10"))); //强制类型转换
}
再谈instanceof
正常情况下,我们使用instanceof进行类型比较:
public static void main(String[] args) {
String str = "";
System.out.println(str instanceof String);
}
它可以判断一个对象是否为此接口或是类的实现或是子类,而现在我们有了更多的方式去判断类型:
public static void main(String[] args) {
String str = "";
System.out.println(str.getClass() == String.class); //直接判断是否为这个类型
}
如果需要判断是否为子类或是接口/抽象类的实现,我们可以使用asSubClass()
方法:
public static void main(String[] args) {
Integer i = 10;
i.getClass().asSubclass(Number.class); //当Integer不是Number的子类时,会产生异常
}
获取父类信息
通过getSuperclass()
方法,我们可以获取到父类的Class对象:
public static void main(String[] args) {
Integer i = 10;
System.out.println(i.getClass().getSuperclass());
}
也可以通过getGenericSuperclass()
获取父类的原始类型的Type:
public static void main(String[] args) {
Integer i = 10;
Type type = i.getClass().getGenericSuperclass();
System.out.println(type);
System.out.println(type instanceof Class);
}
我们发现Type实际上是Class类的父接口,但是获取到的Type的实现并不一定是Class。
同理,我们也可以像上面这样获取父接口:
public static void main(String[] args) {
Integer i = 10;
for (Class<?> anInterface : i.getClass().getInterfaces()) {
System.out.println(anInterface.getName());
}
for (Type genericInterface : i.getClass().getGenericInterfaces()) {
System.out.println(genericInterface.getTypeName());
}
}