Java反射机制

反射

1. 反射的定义

  • 反射即反向探知,有点类似考古学家根据发掘的物品来探知以前的事情
  • 在Java程序运行状态中:
    1. 对于给定的一个类(Class)对象,可以获得这个类(Class)对象的所有属性和方法;
    2. 对于给定的一个对象( new ClassName < ? extends Object> ),都能够调用它的任意一个属性和方法

这种动态获取类的内容以及动态调用对象的方法和获取属性的机制,就叫做JAVA的反射机制。

2. 反射的优缺点

优点:

  • 增加程序的灵活性,避免固有逻辑写死到程序中
  • 代码相对简洁,可以提高程序的复用性
public interface Ball {
public void playBall();
}
class BasketBall implements Ball {
@Override
public void playBall() {
System.out.println("一起打篮球。。。。");
}
}
class FootBall implements Ball {
@Override
public void playBall() {
System.out.println("一起踢足球。。。。");
}
}
class BallMain {
public static void main(String[] args) throws Exception {
System.out.println(getNewInstanceByKey("BasketBall"));
System.out.println(getNewInstanceByKey("FootBall"));
}
public static Ball getInstanceByKey (String key) {
if ("BasketBall".equals(key)) {
return new BasketBall();
} else if ("FootBall".equals(key)) {
return new FootBall();
}
// 如果要增加排球、羽毛球、、、很多球,下面还要继续判断,创建实例吗?
return null;
}
public static Ball getNewInstanceByKey (String key) throws Exception {
String basePackage = "com.elian.reflex";
Ball ball = null;
Class<?> clazz = Class.forName(basePackage + "." + key);
ball = (Ball) clazz.newInstance();
return ball;
}
}

缺点:

  • 相比于直接调用,反射有比较大的性能消耗
  • 内部暴露和安全隐患

反射到底慢在哪里?

  1. 调用了native方法
  2. 每次new Instance都会做安全检查 比较耗时

3. 深入class内部

image-20220228181503535

3.1 基本操作

1) 获取类对象的4种方式

public class ReflexTest {
public static void main(String[] args) throws Exception {
Class<Person> p1 = Person.class;
Class<?> p2 = Class.forName("com.elian.reflex.Person");
Class<? extends Person> p3 = new Person().getClass();
Class<?> p4 = ReflexTest.class.getClassLoader().loadClass("com.elian.reflex.Person");
}
}

2) 基本操作

// 获取类的相关结构
int modifiers = p1.getModifiers(); // 获取类修饰符
p1.getPackage() // 获取包名
p1.getName() // 获取全路径名
p1.getSuperclass() // 获取父类
p1.getClassLoader() // 获取类加载器
p1.getSimpleName() // 获取类名
p1.getInterfaces() // 获取类型所有实现的接口
p1.getAnnotations() // 获取所有注解

3) 字段的操作

public static void main(String[] args) throws Exception {
// 获取Person对象
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
Field[] clazzFields = clazz.getFields(); // 只能获取当前类及父类public的字段
Field[] declaredFields = clazz.getDeclaredFields(); // 可以获取当前类中所有的字段
for (Field field :
clazzFields) {
System.out.println(field.getModifiers() + " " + field.getName());
}
System.out.println( "========" );
for (Field field :
declaredFields) {
System.out.println(field.getModifiers() + " " + field.getName());
}
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 设置权限,private的字段可以设置值
nameField.set(person, "Elian");
// 对静态字段进行操作
Field addressField = clazz.getDeclaredField("address");
addressField.set(null, "北京通州");
}

4) 对方法的操作

public static void main(String[] args) throws Exception {
Person person = new Person();
Class<Person> clazz = Person.class;
Method[] methods = clazz.getMethods(); // 获取当前类及父类中所有的public修饰的方法
Method[] declaredMethods = clazz.getDeclaredMethods(); // 获取本类中所有方法
for (Method method :
methods) {
System.out.println( method.getModifiers() + " " + method.getName() );
}
System.out.println( "================" );
for (Method method :
declaredMethods) {
System.out.println( method.getModifiers() + " " + method.getName() );
}
Method jump = clazz.getDeclaredMethod("jump");
jump.invoke(person);
Field address = clazz.getDeclaredField("address");
address.setAccessible(true);
address.set(null, "北京通州");
Method getAddress = clazz.getDeclaredMethod("getAddress");
getAddress.setAccessible(true);
System.out.println(getAddress.invoke(null));
}

5) 对构造器的 操作

public static void main(String[] args) throws Exception {
Class<Person> clazz = Person.class;
Constructor<?>[] constructors = clazz.getConstructors();
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor constructor :
constructors) {
System.out.println(constructor.getModifiers() + " " + constructor.getName());
}
System.out.println("===============");
for (Constructor constructor :
declaredConstructors) {
System.out.println(constructor.getModifiers() + " " + constructor.getName());
}
// 创建实例两种方式
// 1. 直接通过newInstance创建对象
Person person1 = clazz.newInstance();
// 2. 获取对应的Constructor对象获取实例
Constructor<Person> declaredConstructor = clazz.getDeclaredConstructor(String.class);
declaredConstructor.setAccessible(true);
Person person2 = declaredConstructor.newInstance("Elian");
System.out.println(person2.getName());
}

漏洞:对于单例的对象来说,能够调用私有的构造器创建新的对象,比较危险,所以应在代码中控制对象的创建。

public class SinglePerson {
private static final SinglePerson instance = new SinglePerson();
public static SinglePerson getInstance() {
return instance;
}
private SinglePerson() {
if (instance != null)
throw new RuntimeException( "The instance of SinglePerson has been created and cannot be created it again" );
}
public static void main(String[] args) {
SinglePerson s1 = SinglePerson.getInstance();
SinglePerson s2 = SinglePerson.getInstance();
System.out.println(s1);
System.out.println(s2);
}
}

3.2 反射的使用场景

  1. jdbc的封装
  2. Spring中的IoC
  3. jdbcTemplate
  4. MyBatis

4. SpringIoC中反射的应用

public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
// 创建Ioc容器对象 BeanFactory 同时解析配置文件
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
// 单例对象的实例化
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
destroyBeans();
cancelRefresh(ex);
throw ex;
}
finally {
resetCommonCaches();
}
}
}
'==========================='
public abstract class BeanUtils {
//....
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException());
}
}
// ....
}
posted @   coderElian  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示