反射
Java反射(Reflection)是一种允许程序在运行时动态地获取有关类、方法和属性的信息,甚至可以对它们进行操作的机制。通过反射,程序可以在编译时并不知道一个类的具体信息的情况下,运行时获取该类的结构,并进行相应的操作。反射的核心是在运行时操作类和对象的元信息,这为开发提供了更大的灵活性。
1.Java反射的基本概念
反射是通过 Java 提供的一组类和接口来实现的,这些类和接口位于 java.lang.reflect
包下,主要包括以下几个核心类:
- Class类:表示正在运行的 Java 应用程序中的类或接口。每个类或接口都有一个与之关联的
Class
对象,可以通过它获取类的元数据。 - Constructor类:表示类的构造方法对象。通过反射,你可以调用一个类的构造方法,实例化该类。
- Field类:表示类的字段(成员变量)。通过反射,可以获取类的属性,并对属性进行读写操作。
- Method类:表示类的方法对象。可以通过反射调用一个类的具体方法。
2.反射的主要功能
- 在运行时获取类的结构信息:
- 可以获取类的包、类名、父类、接口等信息。
- 可以获取类的字段(属性)、方法、构造方法等。
- 动态创建类的实例:
- 即使在编译时不知道具体类,也可以通过反射在运行时根据类名实例化对象。
- 动态调用方法:
- 通过反射,可以在运行时调用对象的方法,甚至可以调用私有方法。
- 动态访问和修改字段:
- 通过反射,可以访问类的属性(包括私有属性),并且可以修改它们的值。
3.反射的使用
1. 获取 Class
对象
Class
对象是反射的基础,Java 反射的所有操作都从 Class
对象开始。获取 Class
对象的方式有以下几种:
1.1 通过类名获取 Class
对象
这是在编译时就知道类名的常用方式。
Class<?> clazz = SomeClass.class;
1.2 通过对象获取 Class
对象
通过对象的 getClass()
方法可以获取该对象所属类的 Class
对象。
SomeClass obj = new SomeClass();
Class<?> clazz = obj.getClass();
1.3 通过 Class.forName()
获取 Class
对象
通过类的全限定名(带包名的类名)可以动态加载类。
Class<?> clazz = Class.forName("com.example.SomeClass");
2. 通过反射创建对象
使用 Constructor
类可以动态创建对象。可以通过 Class
对象获取类的构造方法,并使用它创建对象。
2.1 获取无参构造器并创建对象
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
2.2 获取带参构造器并创建对象
// 获取带一个String参数的构造器
Constructor<?> constructor = clazz.getConstructor(String.class);
// 传入参数并创建对象
Object obj = constructor.newInstance("参数值");
2.3 处理私有构造器
如果构造方法是私有的,可以用getDeclaredConstructor()
创建
并通过 setAccessible(true)
绕过访问控制。
//获取全部构造器,包括私有的
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true); // 绕过访问权限
Object obj = constructor.newInstance();
3. 通过反射操作成员变量
使用 Field
类可以操作类的属性。可以获取类的属性,并对其进行读写。
3.1 获取成员变量
// 获取某个public字段
Field field = clazz.getField("fieldName");
3.2 访问和修改成员变量值
通过反射可以直接访问和修改类的字段值,即使是私有字段。
// 获取某个字段的值
Object value = field.get(obj);
// 修改字段的值
field.set(obj, "新值");
3.3 访问私有成员变量
如果字段是私有的,可以通过 setAccessible(true)
来访问它。
Field field = clazz.getDeclaredField("privateField");
field.setAccessible(true); // 允许访问私有字段
Object value = field.get(obj); // 获取字段值
field.set(obj, "新值"); // 修改字段值
4. 通过反射操作成员方法(Method)
使用 Method
类可以动态调用类的方法。可以获取类的方法对象,并在运行时调用它。
4.1 获取成员方法
通过 getMethod()
或 getDeclaredMethod()
可以获取方法,前者只能获取 public
方法,而后者可以获取包括私有方法在内的所有方法。
// 获取public方法
Method method = clazz.getMethod("methodName", String.class);
// 获取私有方法
Method method = clazz.getDeclaredMethod("privateMethod", String.class);
method.setAccessible(true); // 允许访问私有方法
4.2 调用成员方法
通过 Method.invoke()
可以调用对象的方法,参数为方法所属的对象和实际参数。
// 调用方法并传入参数
Object returnValue = method.invoke(obj, "参数值");
4.3 静态方法的调用
调用静态方法时,invoke()
方法的第一个参数传 null
。
Method staticMethod = clazz.getMethod("staticMethod", int.class);
Object returnValue = staticMethod.invoke(null, 123);
5. 通过反射操作数组
反射不仅可以操作类、字段、方法,还可以用于操作数组。
5.1 创建数组
可以通过 Array.newInstance()
方法动态创建数组。
// 创建一个包含10个String元素的数组
Object array = Array.newInstance(String.class, 10);
5.2 操作数组元素
可以使用反射方法访问和修改数组中的元素。
// 设置数组中的某个元素
Array.set(array, 0, "第一个元素");
// 获取数组中的某个元素
Object value = Array.get(array, 0);
6. 获取类的完整信息
反射还可以用于获取类的所有成员信息,如构造方法、成员变量和成员方法。
6.1 获取类的所有构造器
Constructor<?>[] constructors = clazz.getConstructors(); // 获取所有public构造器
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors(); // 获取所有构造器(包括私有的)
6.2 获取类的所有成员变量
Field[] fields = clazz.getFields(); // 获取所有public成员变量
Field[] allFields = clazz.getDeclaredFields(); // 获取所有字段(包括私有的)
6.3 获取类的所有成员
//getMethods可以获取父类中public修饰的方法
Method[] methods = clazz.getMethods(); // 获取所有public方法
Method[] allMethods = clazz.getDeclaredMethods(); // 获取所有方法(包括私有的,不包括父类中的)
4. 动态代理(Dynamic Proxy)
反射是 Java 动态代理机制的基础。通过动态代理可以在运行时创建接口的代理类,拦截对接口方法的调用,并执行自定义逻辑。
1.动态代理三要素:
1.真正干活的对象
2.代理对象
3.利用代理调用方法
切记一点:代理可以增强或者拦截的方法都在接口中,接口需要写在newProxyInstance的第二个参数里。
2.代码实现:
public class Test {
public static void main(String[] args) {
/*
需求:
外面的人想要大明星唱一首歌
1. 获取代理的对象
代理对象 = ProxyUtil.createProxy(大明星的对象);
2. 再调用代理的唱歌方法
代理对象.唱歌的方法("只因你太美");
*/
//1. 获取代理的对象
BigStar bigStar = new BigStar("鸡哥");
Star proxy = ProxyUtil.createProxy(bigStar);
//2. 调用唱歌的方法
String result = proxy.sing("只因你太美");
System.out.println(result);
}
}
/*
*
* 类的作用:
* 创建一个代理
*
* */
public class ProxyUtil {
/*
*
* 方法的作用:
* 给一个明星的对象,创建一个代理
*
* 形参:
* 被代理的明星对象
*
* 返回值:
* 给明星创建的代理
*
*
*
* 需求:
* 外面的人想要大明星唱一首歌
* 1. 获取代理的对象
* 代理对象 = ProxyUtil.createProxy(大明星的对象);
* 2. 再调用代理的唱歌方法
* 代理对象.唱歌的方法("只因你太美");
* */
public static Star createProxy(BigStar bigStar){
/* java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情*/
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),//参数一:用于指定用哪个类加载器,去加载生成的代理类
new Class[]{Star.class},//参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
//参数三:用来指定生成的代理对象要干什么事情
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
* 参数一:代理的对象
* 参数二:要运行的方法 sing
* 参数三:调用sing方法时,传递的实参
* */
if("sing".equals(method.getName())){
System.out.println("准备话筒,收钱");
}else if("dance".equals(method.getName())){
System.out.println("准备场地,收钱");
}
//去找大明星开始唱歌或者跳舞
//代码的表现形式:调用大明星里面唱歌或者跳舞的方法
return method.invoke(bigStar,args);
}
}
);
return star;
}
}
public interface Star {
//我们可以把所有想要被代理的方法定义在接口当中
//唱歌
public abstract String sing(String name);
//跳舞
public abstract void dance();
}
public class BigStar implements Star {
private String name;
public BigStar() {
}
public BigStar(String name) {
this.name = name;
}
//唱歌
@Override
public String sing(String name){
System.out.println(this.name + "正在唱" + name);
return "谢谢";
}
//跳舞
@Override
public void dance(){
System.out.println(this.name + "正在跳舞");
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "BigStar{name = " + name + "}";
}
}
3.额外扩展
动态代理,还可以拦截方法
比如:
在这个故事中,经济人作为代理,如果别人让邀请大明星去唱歌,打篮球,经纪人就增强功能。
但是如果别人让大明星去扫厕所,经纪人就要拦截,不会去调用大明星的方法。
/*
* 类的作用:
* 创建一个代理
* */
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
Star star = (Star) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
new Class[]{Star.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("cleanWC".equals(method.getName())){
System.out.println("拦截,不调用大明星的方法");
return null;
}
//如果是其他方法,正常执行
return method.invoke(bigStar,args);
}
}
);
return star;
}
}
标签:反射,Java,对象,代理,Class,获取,概述,方法,public
From: https://blog.csdn.net/qq_74181819/article/details/142335302