5. 获取运行类的完整实例
5.1 通过反射获取运行时类的完整结构
Fileds、Menthod、Constructor、Superclass、Interface、Annotation
实现的全部接口
所继承的父类
全部的构造器
全部的方法
全部的Field
使用反射可以取得:
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的方法
Method类中:
public Class<?> getReturnType()取得全部的返回值
public Class<?>[] getParameterTypes()取得全部的参数
public int getModifiers()取得修饰符
public Class<?>[] getExceptionTypes()取得异常信息
5.全部的Field
public Field[] getFields()
返回此Class对象所表示的类或接口的public的Field。
public Field[] getDeclaredFields() 返回此Class对象所表示的类或接口的全部Field。
Field方法中:
public int getModifiers() 以整数形式返回此Field的修饰符
public Class<?> getType() 得到Field的属性类型
public String getName() 返回Field的名称。
6.Annotation相关
get Annotation(Class annotationClass)
getDeclaredAnnotations()
7.泛型相关
获取父类泛型类型:Type getGenericSuperclass()
泛型类型:ParameterizedType
获取实际的泛型类型参数数组:getActualTypeArguments()
8.类所在的包
Package getPackage()
6. 调用运行时类的的指定结构
6.1 调用指定方法
通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的**getMethod(String name,Class…parameterTypes)**方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用**Object invoke(Object obj, Object[] args)**进行调用,并向方法中传递要设置的obj对象的参数信息。
Object invoke(Object obj, Object … args)
说明:
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用
方法对象的setAccessible(true)方法,将可访问private的方法。
6.2 调用指定属性
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。
public Field getField(String name) 返回此Class对象表示的类或接口的指定的public的Field。
public Field getDeclaredField(String name)返回此Class对象表示的类或接口的
指定的Field。
在Filed方法中 :
public Object get(Object obj) 取得指定对象obj上此Field的属性内容
public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
6.3 关于setAccessible方法的使用
Method和Filed、Construtor对象都有setAccessible()方法。
setAccessible启动和禁用访问安全检查的开关。
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
使得原本无法访问的私有成员也可以访问
参数值为false则指示反射的对象应该实施Java语言访问检查
7. 反射的应用 : 动态代理
7.2 初始代理模式
代理设计模式的原理:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
静态代理,特征是代理类和目标
对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
动态代理使用场合:
调试
远程方法调用
动态代理相比于静态代理的优点:
抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
7.2 Java动态代理相关的API
Proxy : 专门完成代理的操作类,是所有动态代理的父类。通过此类可以为一个或多个接口动态生成实现类。
提供用于创建动态代理类和动态代理的静态方法。
7.3 动态代理的相关步骤
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。
2.创建被代理的类以及接口
3.通过Proxy的静态方法
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个Subject接口代理。
RealSubject target = new RealSubject(); // Create a proxy to wrap the original implementation DebugProxy proxy = new DebugProxy(target); // Get a reference to the proxy through the Subject interface Subject sub = (Subject) Proxy.newProxyInstance( Subject.class.getClassLoader(),new Class[] { Subject.class }, proxy);
4.通过 Subject代理调用RealSubject实现类的方法
String info = sub.say(“Peter", 24); System.out.println(info);
7.4 动态代理与AOP(Aspect Orient Progranmming
改进后的说明:
代码段1、代码段2、代码段3和深色代码段分离开了,但代码段1、2、3又和一个特定的方法A耦合了!最理想的效果是:代块1、2、3既可以执行方法A,又无须在程序中以硬编码的方式直接调用深色代码的方法
代码示例 :
public interface Dog{ void info(); void run(); } public class HuntingDog implements Dog{ public void info(){ System.out.println("我是一只猎狗"); } public void run(){ System.out.println("我奔跑迅速"); } }
public class DogUtil{ public void method1(){ System.out.println("=====模拟通用方法一====="); } public void method2(){ System.out.println("=====模拟通用方法二====="); } }
public class MyInvocationHandler implements InvocationHandler{ // 需要被代理的对象 private Object target; public void setTarget(Object target){ this.target = target;} // 执行动态代理对象的所有方法时,都会被替换成执行如下的invoke方法 public Object invoke(Object proxy, Method method, Object[] args) throws Exception{ DogUtil du = new DogUtil(); // 执行DogUtil对象中的method1。 du.method1(); // 以target作为主调来执行method方法 Object result = method.invoke(target , args); // 执行DogUtil对象中的method2。 du.method2(); return result;}}
public class MyProxyFactory{ // 为指定target生成动态代理对象 public static Object getProxy(Object target) throws Exception{ // 创建一个MyInvokationHandler对象 MyInvokationHandler handler = new MyInvokationHandler(); // 为MyInvokationHandler设置target对象 handler.setTarget(target); // 创建、并返回一个动态代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader() , target.getClass().getInterfaces() , handler); } }
public class Test{ public static void main(String[] args) throws Exception{ // 创建一个原始的HuntingDog对象,作为target Dog target = new HuntingDog(); // 以指定的target来创建动态代理 Dog dog = (Dog)MyProxyFactory.getProxy(target); dog.info(); dog.run(); } }
8.反射的基本应用
8.1 获取类型的详细信息
可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)
示例代码获取常规信息:
package com.atguigu.reflect; package com.atguigu.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class TestClassInfo { public static void main(String[] args) throws Exception { //1、先得到某个类型的Class对象 Class clazz = String.class; //比喻clazz好比是镜子中的影子 //2、获取类信息 //(1)获取包对象,即所有java的包,都是Package的对象 Package pkg = clazz.getPackage(); System.out.println("包名:" + pkg.getName()); //(2)获取修饰符 //其实修饰符是Modifier,里面有很多常量值 /* * 0x是十六进制 * PUBLIC = 0x00000001; 1 1 * PRIVATE = 0x00000002; 2 10 * PROTECTED = 0x00000004; 4 100 * STATIC = 0x00000008; 8 1000 * FINAL = 0x00000010; 16 10000 * ... * * 设计的理念,就是用二进制的某一位是1,来代表一种修饰符,整个二进制中只有一位是1,其余都是0 * * mod = 17 0x00000011 * if ((mod & PUBLIC) != 0) 说明修饰符中有public * if ((mod & FINAL) != 0) 说明修饰符中有final */ int mod = clazz.getModifiers(); System.out.println("类的修饰符有:" + Modifier.toString(mod)); //(3)类型名 String name = clazz.getName(); System.out.println("类名:" + name); //(4)父类,父类也有父类对应的Class对象 Class superclass = clazz.getSuperclass(); System.out.println("父类:" + superclass); //(5)父接口们 System.out.println("父接口们:"); Class[] interfaces = clazz.getInterfaces(); for (Class iter : interfaces) { System.out.println(iter); } //(6)类的属性, 你声明的一个属性,它是Field的对象 /* Field clazz.getField(name) 根据属性名获取一个属性对象,但是只能得到公共的 Field[] clazz.getFields(); 获取所有公共的属性 Field clazz.getDeclaredField(name) 根据属性名获取一个属性对象,可以获取已声明的 Field[] clazz.getDeclaredFields() 获取所有已声明的属性 */ // Field valueField = clazz.getDeclaredField("value"); // System.out.println("valueField = " +valueField); System.out.println("------------------------------"); System.out.println("成员如下:"); System.out.println("属性有:"); Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { //修饰符、数据类型、属性名 int modifiers = field.getModifiers(); System.out.println("属性的修饰符:" + Modifier.toString(modifiers)); String name2 = field.getName(); System.out.println("属性名:" + name2); Class<?> type = field.getType(); System.out.println("属性的数据类型:" + type); } System.out.println("-------------------------"); //(7)构造器们 System.out.println("构造器列表:"); Constructor[] constructors = clazz.getDeclaredConstructors(); for (int i=0; i<constructors.length; i++) { Constructor constructor = constructors[i]; System.out.println("第" + (i+1) +"个构造器:"); //修饰符、构造器名称、构造器形参列表 、抛出异常列表 int modifiers = constructor.getModifiers(); System.out.println("构造器的修饰符:" + Modifier.toString(modifiers)); String name2 = constructor.getName(); System.out.println("构造器名:" + name2); //形参列表 System.out.println("形参列表:"); Class[] parameterTypes = constructor.getParameterTypes(); for (Class parameterType : parameterTypes) { System.out.println(parameterType); } //异常列表 System.out.println("异常列表:"); Class<?>[] exceptionTypes = constructor.getExceptionTypes(); for (Class<?> exceptionType : exceptionTypes) { System.out.println(exceptionType); } System.out.println(); } System.out.println("---------------------------------"); //(8)方法们 System.out.println("方法列表:"); Method[] declaredMethods = clazz.getDeclaredMethods(); for (int i=0; i<declaredMethods.length; i++) { Method method = declaredMethods[i]; System.out.println("第" + (i+1) +"个方法:"); //修饰符、返回值类型、方法名、形参列表 、异常列表 int modifiers = method.getModifiers(); System.out.println("方法的修饰符:" + Modifier.toString(modifiers)); Class<?> returnType = method.getReturnType(); System.out.println("返回值类型:" + returnType); String name2 = method.getName(); System.out.println("方法名:" + name2); //形参列表 System.out.println("形参列表:"); Class[] parameterTypes = method.getParameterTypes(); for (Class parameterType : parameterTypes) { System.out.println(parameterType); } //异常列表 System.out.println("异常列表:"); Class<?>[] exceptionTypes = method.getExceptionTypes(); for (Class<?> exceptionType : exceptionTypes) { System.out.println(exceptionType); } System.out.println(); } } }
8.2 创建任意引用类型的对象
两种方式:
1、直接通过Class对象来实例化(要求必须有公共的无参构造)
2、通过获取构造器对象来进行实例化
方式一的步骤:
(1)获取该类型的Class对象(2)创建对象
方式二的步骤:
(1)获取该类型的Class对象(2)获取构造器对象(3)创建对象
如果构造器的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)
示例代码:
package com.atguigu.reflect; import org.junit.Test; import java.lang.reflect.Constructor; public class TestCreateObject { @Test public void test1() throws Exception{ // AtGuiguClass obj = new AtGuiguClass();//编译期间无法创建 Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguClass"); //clazz代表com.atguigu.ext.demo.AtGuiguClass类型 //clazz.newInstance()创建的就是AtGuiguClass的对象 Object obj = clazz.newInstance(); System.out.println(obj); } @Test public void test2()throws Exception{ Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo"); //java.lang.InstantiationException: com.atguigu.ext.demo.AtGuiguDemo //Caused by: java.lang.NoSuchMethodException: com.atguigu.ext.demo.AtGuiguDemo.<init>() //即说明AtGuiguDemo没有无参构造,就没有无参实例初始化方法<init> Object stu = clazz.newInstance(); System.out.println(stu); } @Test public void test3()throws Exception{ //(1)获取Class对象 Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguDemo"); /* * 获取AtGuiguDemo类型中的有参构造 * 如果构造器有多个,我们通常是根据形参【类型】列表来获取指定的一个构造器的 * 例如:public AtGuiguDemo(String title, int num) */ //(2)获取构造器对象 Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,int.class); //(3)创建实例对象 // T newInstance(Object... initargs) 这个Object...是在创建对象时,给有参构造的实参列表 Object obj = constructor.newInstance("尚硅谷",2022); System.out.println(obj); } }
8.3 操作任意类型的属性
(1)获取该类型的Class对象
Class clazz = Class.forName(“包.类名”);
(2)获取属性对象
Field field = clazz.getDeclaredField(“属性名”);
(3)如果属性的权限修饰符不是public,那么需要设置属性可访问
field.setAccessible(true);
(4)创建实例对象:如果操作的是非静态属性,需要创建实例对象
Object obj = clazz.newInstance(); //有公共的无参构造
Object obj = 构造器对象.newInstance(实参…);//通过特定构造器对象创建实例对象
(4)设置属性值
field.set(obj,“属性值”);
如果操作静态变量,那么实例对象可以省略,用null表示
(5)获取属性值
Object value = field.get(obj);
如果操作静态变量,那么实例对象可以省略,用null表示
示例代码:
package com.atguigu.reflect; public class Student { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
package com.atguigu.reflect; import java.lang.reflect.Field; public class TestField { public static void main(String[] args)throws Exception { //1、获取Student的Class对象 Class clazz = Class.forName("com.atguigu.reflect.Student"); //2、获取属性对象,例如:id属性 Field idField = clazz.getDeclaredField("id"); //3、如果id是私有的等在当前类中不可访问access的,我们需要做如下操作 idField.setAccessible(true); //4、创建实例对象,即,创建Student对象 Object stu = clazz.newInstance(); //5、获取属性值 /* * 以前:int 变量= 学生对象.getId() * 现在:Object id属性对象.get(学生对象) */ Object value = idField.get(stu); System.out.println("id = "+ value); //6、设置属性值 /* * 以前:学生对象.setId(值) * 现在:id属性对象.set(学生对象,值) */ idField.set(stu, 2); value = idField.get(stu); System.out.println("id = "+ value); } }
8.4 调用任意类型的方法
(1)获取该类型的Class对象
Class clazz = Class.forName(“包.类名”);
(2)获取方法对象
Method method = clazz.getDeclaredMethod(“方法名”,方法的形参类型列表);
(3)创建实例对象
Object obj = clazz.newInstance();
(4)调用方法
Object result = method.invoke(obj, 方法的实参值列表);
如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)
如果方法是静态方法,实例对象也可以省略,用null代替
示例代码:
package com.atguigu.reflect; import org.junit.Test; import java.lang.reflect.Method; public class TestMethod { @Test public void test()throws Exception { // 1、获取Student的Class对象 Class<?> clazz = Class.forName("com.atguigu.reflect.Student"); //2、获取方法对象 /* * 在一个类中,唯一定位到一个方法,需要:(1)方法名(2)形参列表,因为方法可能重载 * * 例如:void setName(String name) */ Method setNameMethod = clazz.getDeclaredMethod("setName", String.class); //3、创建实例对象 Object stu = clazz.newInstance(); //4、调用方法 /* * 以前:学生对象.setName(值) * 现在:方法对象.invoke(学生对象,值) */ Object setNameMethodReturnValue = setNameMethod.invoke(stu, "张三"); System.out.println("stu = " + stu); //setName方法返回值类型void,没有返回值,所以setNameMethodReturnValue为null System.out.println("setNameMethodReturnValue = " + setNameMethodReturnValue); Method getNameMethod = clazz.getDeclaredMethod("getName"); Object getNameMethodReturnValue = getNameMethod.invoke(stu); //getName方法返回值类型String,有返回值,getNameMethod.invoke的返回值就是getName方法的返回值 System.out.println("getNameMethodReturnValue = " + getNameMethodReturnValue);//张三 } @Test public void test02()throws Exception{ Class<?> clazz = Class.forName("com.atguigu.ext.demo.AtGuiguClass"); Method printInfoMethod = clazz.getMethod("printInfo", String.class); //printInfo方法是静态方法 printInfoMethod.invoke(null,"尚硅谷"); } }