文章目录
RRTI的概念
RTTI(Run-Time Type Identification)运行时类型识别,其作用是在运行时识别一个对象的类型和类的信息。这里分两种情况:
- 传统的
RRTI
,它假定我们在编译期已知道了所有类型,在没有反射机制创建和使用类对象时,一般都是编译期已确定其类型,如new对象
时该类必须已定义好 - 另外一种是反射机制,它允许我们在运行时发现和使用类型的信息。
Class 类
-
在 Java 中用来表示运行时类型信息的对应类就是 Class 类,Class 类也是一个实实在在的类,存在于JDK 的java.lang包中。
class
是关键字,Class
是一个类 -
Class 是 Java 中类的类型抽象。Java 程序中的各个类,属于同一类事物,可以用一个类来描述,这个类就是 Class。比如创建一个
Person
类,那么,JVM 就会创建一个Person
对应 Class 类的 Class 对象,该 Class 对象保存了 Person 类相关的类型信息。如Person
类的名称、类的属性、类所属的包名、字段名称列表、方法的列表等类型信息。 -
Java 中每个类都有一个 Class 对象,当我们编写并且编译一个新创建的类就会产生一个对应 Class 对象并且这个 Class 对象会被保存在同名
.class
文件里,编译后的字节码文件保存的就是 Class 对象。每个Java类,无论创建多少个实例对象,在JVM中都只有一个Class
对象,即在内存中每个类有且只有一个相对应的 Class 对象(.class只有一份)
Class对象的加载及其获取方式
Class对象的加载
- Class 是由Jvm创建的,内部只有一个私有的构造函数。
- 只会被JVM加载一次,也就是只有一份
- 编译后Class对象被保存在同名的.class文件中,所以JVM的类加载器根据类名查找.class文件
- Java安全机制检测。类的字节码文件被加载时,它们必须接受相关验证,以确保其没有被破坏并且不包含不良Java代码
- 类的实例对象创建是依据在内存中的 Class 对象(class字节码)类型信息完成的
- 简单看一个例子:static语句,会在类第一次被加载时执行
new
一个对象,构造函数将被调用,属于静态方法的引用,Class 对象肯定会被加载。因为实例对象的创建依据其Class对象。forName
方法是 Class类的一个 static 成员方法,表明调用Class.forName()
方法获取Class对象的引用。
class Candy {
static {
System.out.println("Loading Candy");
}
}
class Gum {
static {
System.out.println("Loading Gum");
}
}
class Cookie {
static {
System.out.println("Loading Cookie");
}
}
/**
* static语句,会在类第一次被加载时执行
*/
public static void test() {
print("inside main");
new Candy();
print("After creating Candy");
try {
Class.forName("com.learning.optimize.jdk.reflect.clazz.Gum");
} catch (ClassNotFoundException e) {
print("Couldn't find Gum");
}
print("After Class.forName(\"com.learning.optimize.jdk.reflect.Gum\")");
new Cookie();
print("After creating Cookie");
// 字面常量的方式获取Class对象,不会初始化该类
Class<Gum> gumClass = Gum.class;
}
输出:
inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("com.learning.optimize.jdk.reflect.Gum")
Loading Cookie
After creating Cookie
Class获取方式
- Class 对象的加载方法,加载是同一份
- Class.forName(“类名”),例如:
Class.forName("java.lang.Date")
- Class字面常量,类名.class,例如:
Date.class
- 对象.getClass(),例如:
New Date().getClass()
String str1 = "abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
// 三种方式加载的代码是同一份字节码
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
Class字面常量与基本类型
- 相对其他两种方法更加简单,更安全,无需初始化该类。(这个很重要)
// 字面常量的方式获取Class对象,不会初始化该类。(没有打印输出)
Class<Gum> gumClass = Gum.class;
class Gum {
static {
System.out.println("Loading Gum");
}
}
- Class字面常量方法可以应用于普通的类、接口、数组以及基本数据类型等
- 基本类型(包装类)与void
- void:
void.class
、Void.TYPE
- 基本类型、数组类型的判断:
isPrimitive()
和isArray()
方法 - 基本类型的包装类:常量
type
代表包装类型的字节码,如Integer.TYPE
System.out.println(String.class.isPrimitive()); // false
System.out.println(int.class.isPrimitive()); // true
System.out.println(int[].class.isPrimitive()); // false
System.out.println(int[].class.isArray()); // true,数组类型
// 注意基本类型的包装类
System.out.println(int.class == Integer.class); // false
System.out.println(int.class == Integer.TYPE); // true(常量type代表包装类型的字节码)
理解泛型化的Class对象引用
- 由于Class的引用总数指向某个类的Class对象,利用Class对象可以创建实例类,这也就足以说明Class对象的引用指向的对象确切的类型。
- 在Java5 引入泛型后,使用我们可以利用泛型来表示Class对象更具体的类型,即使在运行期间会被擦除,但编译期足以确保我们使用正确的对象类型
static void test6() {
//没有泛型
Class intClass = int.class;
//带泛型的Class对象
Class<Integer> integerClass = int.class;
integerClass = Integer.class;
//没有泛型的约束,可以随意赋值
intClass = double.class;
//编译期错误,无法编译通过
//integerClass = double.class
}
instanceof 关键字与 isInstance方法
instanceof
:表明对象是不是某个特定的类型实例isInstance
:Class类中的一个Native方法,用于判断对象类型
static void test(Object x) {
print("Testing x of type " + x.getClass());
print("x instanceof A " + (x instanceof A));
print("x instanceof B " + (x instanceof B));
print("A.isInstance(x) " + A.class.isInstance(x));
print("B.isInstance(x) " + B.class.isInstance(x));
print("x.getClass() == A.class " + (x.getClass() == A.class));
print("x.getClass() == B.class " + (x.getClass() == B.class));
print("x.getClass().equals(A.class)) " + (x.getClass().equals(A.class)));
print("x.getClass().equals(B.class)) " + (x.getClass().equals(B.class)));
}
输出:两个方法的结果是一致的,使用不一样而已
x instanceof A true
x instanceof B false // 父类不一定是子类的某个类型
A.isInstance(x) true
B.isInstance(x) false
x.getClass() == A.class true
x.getClass() == B.class false
x.getClass().equals(A.class)) true
x.getClass().equals(B.class)) false
------------------------------------------------
Testing x of type class com.learning.optimize.jdk.reflect.clazz.B
x instanceof A true
x instanceof B true
A.isInstance(x) true
B.isInstance(x) true
x.getClass() == A.class false
x.getClass() == B.class true
x.getClass().equals(A.class)) false
x.getClass().equals(B.class)) true
获取父类 Class
- 方法
方法返回值 | 方法名称 | 方法说明 |
---|---|---|
Class<? super T> | getSuperclass() | 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。 |
Type | getGenericSuperclass() | 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。 |
- 实例
class Cookie<T> {
static {
System.out.println("Loading Cookie");
}
}
class FruitsCookie extends Cookie<Fruits> {
}
class Fruits {
}
static void test6() {
Class<FruitsCookie> clazz = FruitsCookie.class;
// getSuperclass(): 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class
// Object 类、一个接口、一个基本类型或 void,则返回 null。
Class<? super FruitsCookie> superclass = clazz.getSuperclass();
print(superclass.getName());
// getGenericSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。
Type genericSuperclass = clazz.getGenericSuperclass();
print(genericSuperclass instanceof ParameterizedType);
// 获取父类的信息
print(genericSuperclass.getTypeName());
// 封装了父类及其注解(JDK1.8 新增)
AnnotatedType annotatedSuperclass = clazz.getAnnotatedSuperclass();
print(annotatedSuperclass instanceof AnnotatedParameterizedType);
// 父类的信息
Type type = annotatedSuperclass.getType();
print(type instanceof ParameterizedType);
print(type.getTypeName());
}
输出:
com.learning.optimize.jdk.clazz.Cookie
true
com.learning.optimize.jdk.clazz.Cookie<com.learning.optimize.jdk.clazz.Fruits>
true
true
com.learning.optimize.jdk.clazz.Cookie<com.learning.optimize.jdk.clazz.Fruits>
true
com.learning.optimize.jdk.clazz.Fruits
- ParameterizedType 参数化类型
getActualTypeArguments()
:获取泛型的类型, 如 Map<String,Person> map 这个 ParameterizedType 返回的是 String 类,Person 类的全限定类名的 Type ArraygetRawType()
:返回的是当前这个 ParameterizedType 的类型getOwnerType()
:返回 Type 对象,表示此类型是其成员之一的类型。例如,如果此类型为 O.I,则返回 O 的表示形式。
static void test7() {
Class<FruitsCookie> clazz = FruitsCookie.class;
// getGenericSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type。
Type genericSuperclass = clazz.getGenericSuperclass();
print(genericSuperclass instanceof ParameterizedType);
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
// 获取泛型的类型: 如 Map<String,Person> map 这个 ParameterizedType 返回的是 String 类,Person 类的全限定类名的 Type Array
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Type actualTypeArgument = actualTypeArguments[0];
System.out.println(actualTypeArgument.getTypeName());
// 返回的是当前这个 ParameterizedType 的类型
Type rawType = parameterizedType.getRawType();
// Type getOwnerType()返回 Type 对象,表示此类型是其成员之一的类型。例如,如果此类型为 O<T>.I<S>,则返回 O<T> 的表示形式。
// 如果此类型为顶层类型,则返回 null。
Type ownerType = parameterizedType.getOwnerType();
}