反射的理解

反射的理解

1. 反射是什么

反射是 Java 中的一个强大特性,允许程序在运行时检查和操作类、方法、字段等信息。它是一种动态机制,可以在运行时解析类,获取类的成员变量、方法、构造器等,并对它们进行操作。

2. 为什么使用反射

灵活性:反射可以在运行时动态地操作类,而不需要在编译时知道类的具体信息。

可扩展性:通过反射,可以编写通用的代码,支持多种不同的类和对象。

框架和工具:许多框架和工具(如 Spring、Hibernate 等)都广泛使用反射来实现功能,如依赖注入、对象关系映射等。

3. 反射的基本用法

3.1 获取 Class 对象

在 Java 中,每个类都有一个对应的 Class 对象,它是反射的入口。可以通过以下方式获取 Class 对象:

使用类的 .class 属性:Class clazz = Person.class;

使用对象的 .getClass() 方法:Class clazz = personInstance.getClass();

使用类加载器:Class clazz = Class.forName("com.example.Person");

3.2 获取类的信息

一旦获取了 Class 对象,就可以通过它获取类的各种信息,如:

获取类名:clazz.getName();

获取构造器:clazz.getConstructors(); 或 clazz.getConstructor(Class... parameterTypes);

获取方法:clazz.getMethods(); 或 clazz.getMethod(String name, Class... parameterTypes);

获取字段:clazz.getFields(); 或 clazz.getField(String name);

3.3 操作对象

通过反射,可以动态地创建对象、调用方法、访问字段等。

动态创建对象:

Person person = (Person) clazz.newInstance(); // 已过时,推荐使用 Constructor

或者:

Constructor constructor = clazz.getConstructor(String.class, int.class);

Person person = (Person) constructor.newInstance("John", 25);

动态调用方法:

Method method = person.getClass().getMethod("sayHello", String.class);

method.invoke(person, "World");

动态访问字段:

Field field = person.getClass().getField("name");

field.set(person, "Alice");

4. 反射的实际应用

4.1 动态加载和运行代码

例如,在一个插件系统中,可以通过反射动态加载并运行插件的代码:

Class pluginClass = Class.forName("com.example.Plugin");

Object plugin = pluginClass.newInstance();

Method executeMethod = pluginClass.getMethod("execute");

executeMethod.invoke(plugin);

4.2 Java Bean 操作

通过反射可以动态地操作 Java Bean 的属性,如:

Person person = new Person("John", 30);

Field ageField = person.getClass().getField("age");

ageField.set(person, 35);

4.3 测试框架

许多测试框架(如 JUnit)都使用反射来运行测试方法:

TestRunner runner = new TestRunner(MyTest.class);

runner.run();

框架通过反射找到测试类中的测试方法,并调用它们。

4.4 ORM 框架

ORM 框架(如 Hibernate)使用反射将数据库表映射到 Java 类:

Session session = sessionFactory.openSession();

session.save(person);

Hibernate 通过反射操作 Person 类的字段,将其数据存储到数据库中。

5. 反射的优缺点

优点

灵活性:可以在运行时动态地操作类。

可扩展性:支持编写通用代码,适用于多种类和对象。

框架和工具:许多现代框架和工具都依赖反射来实现核心功能。

缺点

性能开销:反射操作的性能比直接调用稍差,因为它需要解析类信息。

安全性问题:可能破坏封装性,访问或修改私有成员。

复杂性:使用反射会使代码变得复杂,难以阅读和维护。

6. 示例代码

以下是个简单的反射示例,展示如何动态创建对象并调用方法:

import java.lang.reflect.Constructor;

import java.lang.reflect.Method;

public class ReflectionExample {

public static void main(String[] args) {

try {

// 获取类

Class clazz = Class.forName("com.example.Person");

// 创建对象

Constructor constructor = clazz.getConstructor(String.class, int.class);

Person person = (Person) constructor.newInstance("John", 30);

// 调用方法

Method sayHelloMethod = person.getClass().getMethod("sayHello", String.class);

sayHelloMethod.invoke(person, "World");

// 访问字段

Field ageField = person.getClass().getField("age");

System.out.println("Age: " + ageField.get(person));

} catch (Exception e) {

e.printStackTrace();

}

}

}

类Person如下:

public class Person {

public String name;

public int age;

public Person(String name, int age) {

this.name = name;

this.age = age;

}

public void sayHello(String target) {

System.out.println("Hello, " + target + "! My name is " + this.name);

}

}

在黑马Java下部中,反射通常的俩中作用:

6.1:保存任意对象数据

6.2:创建对象并运行方法

7.Skyakeout 中的应用

/**

* 功能字段自动填充切面

*/

@Component

@Aspect

@Slf4j

public class AutoFillAspect {

/**

* 切入点

*/

@Pointcut("execution(public * com.sky.mapper.*.*(..))&&@annotation(com.sky.annotation.AutoFill)")

public void autoFillPointCut(){}

/**

* 前置通知,公共字段自动填充

*/

@Before("autoFillPointCut()")

public void autoFill(JoinPoint joinPoint){

//通过 JoinPoint 获取方法参数和上下文信息。

log.info("开始公共字段自动填充...");

//获取当前被拦截的方法上的数据库操作类型

MethodSignature signature = (MethodSignature) joinPoint.getSignature();//方法签名对象

AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class);//获得方法上的注解对象

OperationType operationType = autoFill.value();//获得注解中的数据库操作类型

//获取当前被拦截的方法上的——实体对象

Object[] args = joinPoint.getArgs();//获得方法的参数

if(args.length == 0||args == null){

return;

}

Object entity = args[0];//获得实体对象

//准备赋值的数据

LocalDateTime now = LocalDateTime.now();

Long currentId = BaseContext.getCurrentId();

//根据不同的操作类型,通过反射赋予不同的值

if(operationType == OperationType.INSERT){

//为3个公共字段赋值

try {

//使用反射动态调用实体对象的 setter 方法。

Method setCreateTime = entity.getClass().getDeclaredMethod("setCreateTime",LocalDateTime.class);

Method setCreateUser = entity.getClass().getDeclaredMethod("setCreateUser",Long.class);

Method setUpdateTime = entity.getClass().getDeclaredMethod("setUpdateTime",LocalDateTime.class);

Method setUpdateUser = entity.getClass().getDeclaredMethod("setUpdateUser", Long.class);

//通过反射设置值

//invoke 是 Java 反射机制中的一个核心方法,用于动态调用对象的方法。

//invoke 方法允许你在运行时调用一个对象的任意方法

setCreateTime.invoke(entity,now);

setCreateUser.invoke(entity,currentId);

setUpdateTime.invoke(entity,now);

setUpdateUser.invoke(entity,currentId);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

if(operationType == OperationType.UPDATE){

//为2个公共字段赋值

try {

Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME,LocalDateTime.class);

Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

//通过反射设置值

setUpdateTime.invoke(entity,now);

setUpdateUser.invoke(entity,currentId);

} catch (Exception e) {

throw new RuntimeException(e);

}

}

}

}

相关推荐

汉字:「藩」 字形演变 字源演变
365bet娱乐网站

汉字:「藩」 字形演变 字源演变

📅 07-25 👁️ 1516
上海地区最赞的30家酒店
365bet娱乐网站

上海地区最赞的30家酒店

📅 08-16 👁️ 297
是罪人也可能是英雄——世界大赛“红点套餐”那些事儿
为何足球比赛没有0号球衣?曾是甲A门将号码,国际足联重新规范