写在前面
Hello大家好, 我是【麟-小白】,一位软件工程专业的学生,喜好计算机知识。希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!!
如果小哥哥小姐姐们对我的文章感兴趣,请不要吝啬你们的小手,多多点赞加关注呀!❤❤❤ 爱你们!!!
目录
写在前面
1. 获取运行时类的完整结构
1.1 通过反射获取运行时类的完整结构
1.2 代码演示
2. 调用运行时类的指定结构
2.1 调用运行时类的指定结构
2.2 关于setAccessible方法的使用
2.3 代码演示
3. 反射的应用:动态代理
3.1 代理设计模式的原理
3.2 Java动态代理相关API
3.3 动态代理步骤
3.4 动态代理与AOP(Aspect Orient Programming)
结语
【往期回顾】
一文带你深入理解【Java基础】· Java反射机制(上)
一文带你深入理解【Java基础】· 网络编程(下)
一文带你深入理解【Java基础】· 网络编程(上)
一文带你深入理解【Java基础】· IO流(下)
一文带你深入理解【Java基础】· IO流(中)
一文带你深入理解【Java基础】· IO流(上)
一文带你深入理解【Java基础】· 泛型
1.1 通过反射获取运行时类的完整结构
Field、Method、Constructor、Superclass、Interface、Annotation使用反射可以取得:
- 实现的全部接口
- 所继承的父类
- 全部的构造器
- 全部的方法
- 全部的Field
- 实现的全部接口
- public Class>[] getInterfaces()
- 确定此对象所表示的类或接口实现的接口。
- 所继承的父类
- public Class Super T> getSuperclass()
- 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的Class。
小 结: 1.在实际的操作中,取得类的信息的操作代码,并不会经常开发。 2.一定要熟悉java.lang.reflect包的作用,反射机制。 3.如何取得属性、方法、构造器的名称,修饰符等。
- 全部的构造器
- public Constructor
[] getConstructors() - 返回此 Class 对象所表示的类的所有public构造方法。
- public Constructor
[] getDeclaredConstructors() - 返回此 Class 对象表示的类声明的所有构造方法。
- Constructor类中:
- 取得修饰符: public int getModifiers();
- 取得方法名称: public String getName();
- 取得参数的类型:public Class>[] getParameterTypes();
- 全部的方法
- public Method[] getDeclaredMethods()
- 返回此Class对象所表示的类或接口的全部方法
- public Method[] getMethods()
- 返回此Class对象所表示的类或接口的public的方法
- Method类中:
- public Class> getReturnType()取得全部的返回值
- public Class>[] getParameterTypes()取得全部的参数
- public int getModifiers()取得修饰符
- public Class>[] getExceptionTypes()取得异常信息
- 全部的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的名称。
- Annotation相关
- get Annotation(Class
annotationClass) - getDeclaredAnnotations()
- 泛型相关
- 获取父类泛型类型:Type getGenericSuperclass()
- 泛型类型:ParameterizedType
- 获取实际的泛型类型参数数组:getActualTypeArguments()
- 类所在的包
- Package getPackage()
1.2 代码演示
public class FieldTest {@Testpublic void test1() {Class clazz = Person.class;//获取属性结构//getFields():获取当前运行时类及其父类中声明为public访问权限的属性Field[] fields = clazz.getFields();for (Field f : fields) {System.out.println(f);}System.out.println();//getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)Field[] declaredFields = clazz.getDeclaredFields();for (Field f : declaredFields) {System.out.println(f);}}/** 权限修饰符 数据类型 变量名 */@Testpublic void test2() {Class clazz = Person.class;Field[] declaredFields = clazz.getDeclaredFields();for (Field f : declaredFields) {//1.权限修饰符int modifier = f.getModifiers();System.out.print(Modifier.toString(modifier) + "\t");//2.数据类型Class type = f.getType();System.out.print(type.getName() + "\t");//3.变量名String fName = f.getName();System.out.print(fName);System.out.println();}} }
2.1 调用运行时类的指定结构
- 调用指定方法
- 通过反射,调用类中的方法,通过Method类完成。步骤:
- 通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
- 之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
Object invoke(Object obj, Object … args) 说明:
- Object 对应原方法的返回值,若原方法无返回值,此时返回null
- 若原方法若为静态方法,此时形参Object obj可为null
- 若原方法形参列表为空,则Object[] args为null
- 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
- 调用指定属性
- 在反射机制中,可以直接通过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的属性内容
2.2 关于setAccessible方法的使用
- Method和Field、Constructor对象都有setAccessible()方法。
- setAccessible启动和禁用访问安全检查的开关。
- 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
- 使得原本无法访问的私有成员也可以访问
- 参数值为false则指示反射的对象应该实施Java语言访问检查。
2.3 代码演示
public class MethodTest {@Testpublic void test1() {Class clazz = Person.class;//getMethods():获取当前运行时类及其所有父类中声明为public权限的方法Method[] methods = clazz.getMethods();for (Method m : methods) {System.out.println(m);}System.out.println();//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)Method[] declaredMethods = clazz.getDeclaredMethods();for (Method m : declaredMethods) {System.out.println(m);}}/**@Xxxx权限修饰符 返回值类型 方法名(参数类型1 形参名1,...) throws XxxException{}*/@Testpublic void test2() {Class clazz = Person.class;Method[] declaredMethods = clazz.getDeclaredMethods();for (Method m : declaredMethods) {//1.获取方法声明的注解Annotation[] annos = m.getAnnotations();for (Annotation a : annos) {System.out.println(a);}//2.权限修饰符System.out.print(Modifier.toString(m.getModifiers()) + "\t");//3.返回值类型System.out.print(m.getReturnType().getName() + "\t");//4.方法名System.out.print(m.getName());System.out.print("(");//5.形参列表Class[] parameterTypes = m.getParameterTypes();if (!(parameterTypes == null && parameterTypes.length == 0)) {for (int i = 0; i < parameterTypes.length; i++) {if (i == parameterTypes.length - 1) {System.out.print(parameterTypes[i].getName() + " args_" + i);break;}System.out.print(parameterTypes[i].getName() + " args_" + i + ",");}}System.out.print(")");//6.抛出的异常Class[] exceptionTypes = m.getExceptionTypes();if (exceptionTypes.length > 0) {System.out.print("throws ");for (int i = 0; i < exceptionTypes.length; i++) {if (i == exceptionTypes.length - 1) {System.out.print(exceptionTypes[i].getName());break;}System.out.print(exceptionTypes[i].getName() + ",");}}System.out.println();}} }
3.1 代理设计模式的原理
- 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
- 之前为大家讲解过代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
- 动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
- 动态代理使用场合:
- 调试
- 远程方法调用
动态代理相比于静态代理的优点:
- 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。
3.2 Java动态代理相关API
- Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
- 提供用于创建动态代理类和动态代理对象的静态方法
3.3 动态代理步骤
1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。![]()
2.创建被代理的类以及接口
3.通过Proxy的静态方法
![]()
4.通过 Subject代理调用RealSubject实现类的方法
3.4 动态代理与AOP(Aspect Orient Programming)
前面介绍的Proxy和InvocationHandler,很难看出这种动态代理的优势,下面介绍一种更实用的动态代理机制![]()
改进后的说明:
- 代码段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对象,作为targetDog target = new HuntingDog();// 以指定的target来创建动态代理Dog dog = (Dog)MyProxyFactory.getProxy(target);dog.info();dog.run();} }
- 使用Proxy生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理
- 这种动态代理在AOP中被称为AOP代理,AOP代理可代替目标对象,AOP代理包含了目标对象的全部方法。但AOP代理中的方法与目标对象的方法存在差异:
- AOP代理里的方法可以在执行目标方法之前、之后插入一些通用处理
结语
本人会持续更新文章的哦!希望大家一键三连,你们的鼓励就是作者不断更新的动力