Class
# api
# getGenericSuperclass
getGenericSuperclass是 Java 中 java.lang.Class 类的方法之一。它用于获取表示某个类的父类的 Type, 其中 Type 是 Java 泛型类型的表示形式。这个方法通常与 Java 泛型一起使用, 以便在运行时获取类的泛型信息
Type getGenericSuperclass()
返回类型: Type, 表示该类的父类的泛型类型
getGenericSuperclass 的常见使用场景包括:
获取父类的泛型类型信息: 通过 getGenericSuperclass, 您可以在运行时获取类的父类的泛型类型信息。这对于实现泛型工具类或进行运行时类型检查非常有用。例如, 可以获取某个类的直接父类的泛型类型, 然后进一步分析这些泛型参数
实现通用的序列化和反序列化: 当您需要将对象序列化为字节数组或反序列化为对象时, getGenericSuperclass 可以帮助您获取类的父类的泛型信息, 以便正确地序列化和反序列化泛型对象
构建通用的数据访问层(DAO): 在数据访问层中, 您可能需要动态地根据模型类来构建 SQL 查询。通过 getGenericSuperclass, 您可以获取模型类的泛型信息, 以便根据泛型类型生成合适的 SQL 查询
动态代理: 使用 Java 动态代理机制时, 您可能需要获取被代理类的泛型信息。getGenericSuperclass 可以帮助您确定代理类需要实现的接口和泛型参数
反射操作: 对于任何需要在运行时进行反射操作的情况, getGenericSuperclass 可以提供更多有关类层次结构和泛型类型的信息
需要注意的是, getGenericSuperclass 方法只能获取直接父类的泛型信息, 如果类的层次结构更加复杂, 您可能需要进一步递归检查超类以获取完整的泛型信息。此外, 泛型类型的擦除规则也会影响到 getGenericSuperclass 返回的类型。因此, 在使用这个方法时, 要仔细考虑泛型类型的实际情况
示例: 获取参数的Class类文件对象public class Base<T,R> {
T t;
R r;
}
public class A extends Base<String,Integer> {
}
2
3
4
5
6
7
8
获取 A 文件第一个参数 String 的类对象可以这样做, 如下:
// => getParamTypeForSuper(Base.class,1);
public <T> Class<T> getParamTypeForSuper(Class<?> clazz, int index) {
while (clazz != null) {
Type superclass = clazz.getGenericSuperclass();
if (superclass instanceof Class) {
clazz = clazz.getSuperclass();
continue;
}
ParameterizedType parameterized = (ParameterizedType) superclass;
Type type = parameterized.getActualTypeArguments()[index];
if (type instanceof ParameterizedType)
return (Class<T>) ((ParameterizedType) type).getRawType();
else
return (Class<T>) type;
}
throw new RuntimeException("Missing type parameter.");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# getSuperclass
getSuperclass返回直接继承的父类(由于编译擦除, 没有显示泛型参数)
# getDeclaredField/getField
- getDeclaredField: 获取 Class 类对象所有声明的 field,不包含父类声明的 field
- getField: 获取当前类对象申明的 field,包含父类的非 private 的 field。 如果要获取父类 private field,需要通过 superClass 获取
# snip
# access static field
val c = Class.forName("android.app.ActivityThread")
val currentActivityThread = c.getDeclaredMethod("currentActivityThread")
var acc = currentActivityThread.isAccessible
if (!acc) {
currentActivityThread.isAccessible = true
}
// 调用static方法
val o = currentActivityThread.invoke(null)
if (!acc) {
currentActivityThread.isAccessible = acc
}
val f = c.getDeclaredField("mInstrumentation")
acc = f.isAccessible
if (!acc) {
f.isAccessible = true
}
val currentInstrumentation = f[o] as Instrumentation
val ins: Instrumentation = ApmInstrumentation(currentInstrumentation)
f[o] = ins
if (!acc) {
f.isAccessible = false
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# FAQ
# Java 反射真的很慢吗?
- 反射调用过程中会产生大量的临时对象, 这些对象会占用内存, 可能会导致频繁 gc, 从而影响性能
- 反射调用方法时会从方法数组中遍历查找, 并且会检查可见性等操作会耗时
- 反射在达到一定次数时, 会动态编写字节码并加载到内存中, 这个字节码没有经过编译器优化, 也不能享受 JIT 优化
- 反射一般会涉及自动装箱/拆箱和类型转换, 都会带来一定的资源开销
- 不要在性能敏感的应用中, 频繁调用反射
- 如果反射执行的次数小于 1000 这个数量级, 反射的耗时实际上与正常调用无太大差异
- 反射对内存占用还有一定影响的, 在内存敏感的场景下, 谨慎使用反射
# Class.forName vs ClassLoader.loadClass
Class.forName- 装载、链接、初始化;静态代码块会被执行.
ClassLoader.loadClass- 装载