反射
----文章根据b站求知讲堂av76235341编写
问题1:为什么要有反射?
1.在运行时判断任意一个对象所属的类
2.在运行时构造任意一个类的对象
3.在运行时判断任意一个类所具有的成员变量和方法
4.在运行时调用任意一个对象的成员变量和方法
5.生成动态代理
问题2:怎么使用?
反射相关的主要API:
java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法
1.实例化Class类对象的3种方法:
//1.第一种(通过类名获取,最安全,最常用)
Class clazz1=Student.class;
//2.第二种(通过类的相对位置获取)
try {
Class clazz2=Class.forName("反射.Student");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//第三种(通过对象获取)
Student s=new Student();
Class clazz3=s.getClass();
2.一些常用方法
使用反射可以取得:
1.实现的全部接口
public Class<?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。
2.所继承的父类
public Class<? Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。
3.全部的构造器
public Constructor[] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法。
public Constructor[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法
Constructor类中:
取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();
4.全部的方法
public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法
public Method[] getMethods()
返回此Class对象所表示的类或接口的public的方法
5.全部的Field
public Field[] getFields()
返回此Class对象所表示的类或接口的public的Field。
public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field。
三、通过反射调用类中的指定方法、指定属性
1.调用指定方法
通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用Object invoke(Object obj, Object[] args)进行调用(参数1是需调用该方法的对象),并向方法中传递要设置的obj对象的参数信息。
Object invoke(Object obj, Object … args)
说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
2.调用指定属性
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
public Field getField(String name)
返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的Field。
在Field中:
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
注:在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。
public void setAccessible(true)访问私有属性时,让这个属性可见。
四、动态代理
动态代理作用:
动态代理就是为了不改变类源代码的基础上,为其增加功能
步骤:
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理
4.通过 Subject代理调用RealSubject实现类的方法
例子:
1.被代理类的接口
package 反射;
public interface TestI {
public void test1();
}
2.被代理类
package 反射;
public class Test implements TestI{
public void test1(){
System.out.println("test");
}
}
3.代理类,要求实现InvocationHandler的invoke方法
package 反射;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyDemo implements InvocationHandler{
Object target;//被代理的对象,实际方法的执行者
ProxyDemo(Object target)
{
this.target=target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result=method.invoke(target, args);
after();
return result;
}
//调用invoke方法后执行
private void after() {
System.out.println("调用invoke方法后执行");
}
//调用invoke方法前执行
private void before() {
System.out.println("调用invoke方法前执行");
}
}
4.主方法
package 反射;
import java.lang.reflect.Proxy;
/*
* 动态代理
*/
public class Main2 {
public static void main(String[] args) {
//创建被代理的对象
Test t=new Test();
//创建代理对象
ProxyDemo h=new ProxyDemo(t);
//参数1 :代理对象的类加载器 参数2: 被代理对象的接口 参数3:代理对象
TestI p=(TestI) Proxy.newProxyInstance(h.getClass().getClassLoader(),t.getClass().getInterfaces(), h);
p.test1();
}
}
运行结果