一、Class对象
- Class对象可用来获取当前类的信息
- 获取Class对象的三种方式
- Class.forName()获取(触发类加载机制)
- 类名.class
- Object.getClass()
- getFields():获取能公开访问的字段
- getDeclarField:获取实际类的字段(包括私有字段)
二、反射获取泛型类型
- 调用Field#getGenericType()并将结果强制转换为ParameteredType
三、JDK动态代理
实现
- JDK动态代理只能是接口实现类才可以使用
- 主要类
- java.lang.reflect.Proxy
- java.lang.reflect.InvocationHandler
public class MainTest {
public static void main(String[] args) {
InterTest interTest = () -> {
System.out.println("say: Hello");
return "success";
};
InterTest o = (InterTest) Proxy.newProxyInstance(MainTest.class.getClassLoader(), new Class[]{InterTest.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.err.println("before");
method.invoke(interTest);
return "null";
}
});
System.out.println("结果:" + o.say());
}
}
interface InterTest {
String say();
}
动态生成代理类
- 怎么调用的?
- 通过继承Proxy类,并调用其中的InvocationHandler对象的invoke()方法进行执行
- 为什么要继承Proxy类?
- 就只是为了调用Proxy中保存的InvocationHandler对象
- 但是我认为也可以将InvocationHandler放到动态代理类中,这样就不用再继承Proxy类,就可以实现代理类
- 怎么保存生成的动态代理类
- 在main方法中加入System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.example.proxy.ProxyInterface;
public final class $Proxy0 extends Proxy implements ProxyInterface {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("org.example.proxy.ProxyInterface").getMethod("method1");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public $Proxy0(InvocationHandler var1) {
super(var1);
}
public final void method1() {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup var0) throws IllegalAccessException {
if (var0.lookupClass() == Proxy.class && var0.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
} else {
throw new IllegalAccessException(var0.toString());
}
}
}
四、反射Modifier类
- 用于解码类和成员变量访问修饰符和final修饰符,可以用于判断是否被static等修饰符修饰。
五、Java类加载机制
类加载流程
加载
- 通过一个类的全限定名获取Class文件的二进制字节流
- 将Class文件描述的静态存储结构转化为方法区的运行时数据结构
- 生成java.lang.Class对象,作为方法区中这个类信息发访问入口
连接
验证(-noverify 可以关闭大部分验证来提高类加载效率)
- 文件格式验证:检查Class文件格式是否正确,需要读取Class的二进制字节流(连接和加载交叉进行部分)
- 元数据验证:对元数据语义检验,保证没有与定义相悖。
- 字节码验证:检查Code属性部分,确保代码中没有对虚拟机产生危害的代码
- JDK6后与Javac进行了联合优化:由javac验证生成StackMapTable,而虚拟机验证时只需检查StackMapTable。
- 符号引用验证:检查类中依赖的外部类、方法、属性等的权限等是合法的
准备
- 为static变量分配空间并设置初始化阶段的初始零值(如果有ConstantValue属性,则设置对应的值)
public static int value = 123;
public static final int value = 123;
解析
初始化
- 定义:根据程序主观代码去进行初始化
- 直接表示:调用进行初始化
- 类中没有static{} 时不会生成,虚拟机会保证执行子类的前执行了父类的
public class RefTest {
static {
i = 10;
System.out.println(i);
}
public static int i = 99;
}
使用
卸载
类加载器
- Bootstrap ClassLoader:根类加载器(由C++实现)
- JDK9之后由JVM和Java协作创造,获取该对象将返回NULL
- Extension ClassLoader:扩展类加载器
- JDK9之后替换为Platform ClassLoader
- Application ClassLoader:应用类加载器(AppClassLoader)
- User ClassLoader:自己实现的类加载器
双亲委派机制
- 双亲委派机制的流程
- 当一个类加载器收到类加载的请求时,会向上委派父类加载器进行加载,一直委派到Bootstrap ClassLoader。到达启动类加载器之后,在依次向下进行类加载请求
- 意义
- 能够保证类不被重复加载
- 保证一些类安全性,如果由恶意的类替换发生,那么从BootStrapClassLoader加载 可以保证类的安全性

protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
long t1 = System.nanoTime();
c = findClass(name);
PerfCounter.getParentDelegationTime().addTime(t1 - t0);
PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
自定义类加载器
类加载时机
- 访问静态方法、变量(非宏定义)
- 创建Class对象
- 静态变量赋值
六、函数式编程的序列化 SerializedLambda
- Java为了使用于函数式编程的Lambda也可以序列化,特地使用SerializedLambda进行序列化
private static SerializedLambda getSerializedLambda(SFunction<?, ?> sFunction) {
Method method;
try {
method = sFunction.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
return (SerializedLambda) method.invoke(sFunction);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private static String getFiledNameBySerializedLambda(SerializedLambda lambda) {
String implMethodName = lambda.getImplMethodName();
if (implMethodName.startsWith(GET_PREFIX)) {
return Introspector.decapitalize(implMethodName.substring(GET_PREFIX.length()));
} else if (implMethodName.startsWith(IS_PREFIX)) {
return Introspector.decapitalize(implMethodName.substring(IS_PREFIX.length()));
} else {
throw new RuntimeException("无法解析字段名获取失败");
}
}
@FunctionalInterface
public interface SFunction<T, R> extends Serializable {
R get(T obj);
}
七、基本类型和包装类的Class对象
- 包装类和基本类,我们知道本质上并不是同一个类型。只是有了自动拆装箱才可以自动相互转换
- 所以包装类和基本类型的Class是不同的,这就可以区分了
| 包装类Class对象 | 对应的基本类型Class对象(通过包装类获取) | 对应的基本类型Class对象(直接获取) |
| --- | --- | --- |
| Integer.class | Integer.TYPE | int.class |
| Xxx.class | Xxx.TYPE | xxx.class |
八、直接获取和Declared的区别
- 反射直接获取:只能获取public权限的属性/方法,但是可以获取父类中的属性/方法
- 带有Declared:可以获取任何权限的属性/方法,但是不可以获取父类的属性/方法
- 示例
- getDeclaredMethods():获取本类中任何权限的方法