什么是反射
运行时动态获取对象的全部类型信息。
反射原理-Class类 + 类加载
在markword中保存有对象的类型指针,这个指针会指向Class对象,这个对象保存了类型信息,就是类加载的时候会将对应的类型定义读入到内存中,markword的类型指针会指向这个类的类型定义。
所以可以通过.class来获取对象的类型信息,然后通过Class来进行保存,访问对应的类。
另外加载是动态加载的,不是静态加载的。不是一次性将所有的class读入到内存中,而是在发现缺少某一个class的时候,再把对应的类型定义读入到内存中。
反射的常用接口
- .classs
- .getClass
- Class.forName(“全类名”);
- class.newInstance();
- getMethod(name,参数类型)。通过invoke(对象,参数)进行调用,静态方法使用invoke(null, 参数)
- 非public方法,使用setAccessible(true);调用
- 仍旧遵循堕胎原则
- Field getField(name):根据字段名获取某个 public 的 field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类的某个 field(不包括父类)
- Field[] getFields():获取所有 public 的 field(包括父类)
- Field[] getDeclaredFields():获取当前类的所有 field(不包括父类)
父类的字段无法继承,如果想要获取父类的方法,需要通过不加declared进行访问。
反射的安全性
由于可以通过setAccessible(true)访问私有属性、方法,所以需要进行保证。一般是使用SecurityManager进行访问性的保证。可以自定义用户的SecurityManager,通过继承的方式。下面是一个例子。
- 方法一:继承securityManger
- 方法二:指定安全策略文件,然后通过System.setProperty(”java.security.policy“, 路径); System.setSecurityManager(new SecurityManager());进行保证
方法一:
import java.lang.reflect.ReflectPermission;
public class SecurityManagerExample {
public static void main(String[] args) {
// 创建自定义的安全管理器
SecurityManager securityManager = new CustomSecurityManager();
// 设置自定义的安全管理器
System.setSecurityManager(securityManager);
// 尝试进行反射操作
try {
// 获取Class对象
Class<?> clazz = MyClass.class;
// 通过反射调用私有方法
clazz.getDeclaredMethod("privateMethod").invoke(new MyClass());
} catch (Exception e) {
System.out.println("Security exception: " + e.getMessage());
}
}
static class CustomSecurityManager extends SecurityManager {
@Override
public void checkPermission(java.security.Permission perm) {
// 检查反射相关的权限
if (perm instanceof ReflectPermission) {
String name = perm.getName();
if (name != null && name.startsWith("suppressAccessChecks")) {
throw new SecurityException("Reflective access is not allowed.");
}
}
}
}
static class MyClass {
private void privateMethod() {
System.out.println("This is a private method.");
}
}
}
方法二:
grant {
permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
};
import java.lang.reflect.Method;
public class SecurityPolicyExample {
public static void main(String[] args) {
// 设置安全策略文件路径
System.setProperty("java.security.policy", "path/to/mysecurity.policy");
// 启用安全管理器
System.setSecurityManager(new SecurityManager());
// 尝试进行反射操作
try {
// 获取Class对象
Class<?> clazz = MyClass.class;
// 通过反射调用私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(new MyClass());
} catch (Exception e) {
System.out.println("Security exception: " + e.getMessage());
}
}
static class MyClass {
private void privateMethod() {
System.out.println("This is a private method.");
}
}
}
动态代理
在运行期间创建某一个接口的实例。
原理就是在运行期间动态创建class字节码并加载的过程。
使用proxy创建代理对象,将接口方法代理给InvokeHandler完成的。
动态代理原理是反射。InvokeHandler的调用使用了反射的.class.getMethod方法。