美文网首页
Java系列7 反射

Java系列7 反射

作者: 莫小归 | 来源:发表于2019-03-15 22:11 被阅读0次

参考:
反射例程:http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html
反射:https://www.jianshu.com/p/f67182a482eb
https://www.jianshu.com/p/779b3e27b26d
反射和动态代理:https://www.jianshu.com/p/27b255a6f479
https://www.cnblogs.com/hanganglin/p/4485999.html

一.概述

1.反射定义
  • 运行状态
    1)对于任意一个,都能够知道这个类中的所有方法和属性
    2)对于任意一个对象,都能够调用它的任意一个方法和属性
  • 这种动态获取类的信息以及动态调用对象方法的功能称为Java语言的反射机制
2.反射的优点和缺点
  • 静态编译和动态编译的概念
    1)静态编译:在编译确定类型或绑定对象
    2)动态编译:在运行确定类型或绑定对象。动态编译最大限度发挥了Java的灵活性,体现多态的应用,降低类之间的耦合性
  • 优点:可以实现动态创建对象和编译,比较灵活
    场景:大型软件难以一次性设计得很完美,如果使用静态编译,需要卸载原有程序,将整个程序重新编译,而采用反射机制可避免重新安装程序,在程序运行时动态编译更新的类和对象即可
  • 缺点:反射是一种解释操作,对性能有影响
3.反射的应用场景
  • 逆向代码,如反编译
  • 与注解向结合的框架,如Retrofit
  • 单纯的反射机制应用框架,如EventBus
  • 动态生成类的框架,如Gson

二.反射的使用

1.Class类和类类型(class type)
  • 类是java.lang.Class类的实例对象
  • Class所有类的类
  • 类类型类的类型,描述一个类是什么,有哪些方法和属性
  • 反射通过类类型调用一个类的属性和方法
  • 获取一个类的类类型方法有:
    1)通过类的静态成员变量class: Class c1 = Demo.class;
    2)通过类的对象的getClass()方法Class c2 = demo1.getClass();
    3)Class类的静态方法forName()Class c3 = Class.forName("com.dengxin.reflection.Demo");
2.通过反射查看类信息
  • 获取类类型
//第一种方式 通过Class类的静态方法——forName()来实现
Class<?> class1 = Class.forName("com.dengxin.reflection.Demo");
//第二种方式 通过类的class属性
Class<?> class1 = Demo.class;
//第三种方式 通过对象getClass方法
Demo demo = new Demo();
Class<?> class1 = demo.getClass();
  • 通过类类型获取class对象的成员变量
Field[] allFields = class1.getDeclaredFields();      //获取class对象的所有属性
Field[] publicFields = class1.getFields();                //获取class对象的public属性
Field ageField = class1.getDeclaredField("age");          //获取class指定属性
Field desField = class1.getField("des");                  //获取class指定的public属性
  • 通过类类型获取class对象的方法
//获取class对象的所有声明方法
Method[] methods = class1.getDeclaredMethods();
//获取class对象的所有public方法 包括父类的方法
Method[] allMethods = class1.getMethods();
//返回次Class对象对应类的、带指定形参列表的public方法
Method method = class1.getMethod("info", String.class);
//返回次Class对象对应类的、带指定形参列表的方法
Method declaredMethod = class1.getDeclaredMethod("info", String.class);
  • 通过类类型获取class对象的构造方法
//获取class对象的所有声明构造函数
Constructor<?>[] allConstructors = class1.getDeclaredConstructors();
//获取class对象public构造函数
Constructor<?>[] publicConstructors = class1.getConstructors();
//获取指定声明构造函数
Constructor<?> constructor = class1.getDeclaredConstructor(String.class);
//获取指定声明的public构造函数
Constructor publicConstructor = class1.getConstructor(String.class);
  • 通过类类型获取class对象的信息
oolean isPrimitive = class1.isPrimitive();                      //判断是否是基础类型
boolean isArray = class1.isArray();                              //判断是否是数组
boolean isAnnotation = class1.isAnnotation();             //判断是否是注解类
boolean isInterface = class1.isInterface();                   //判断是否是接口类
boolean isEnum = class1.isEnum();                            //判断是否是枚举类
boolean isAnonymousClass = class1.isAnonymousClass();          //判断是否是匿名内部类
boolean isAnnotationPresent = class1.isAnnotationPresent(Deprecated.class);        //判断是否被某个注解类修饰
String className = class1.getName();                      //获取class名字 包含包名路径
Package aPackage = class1.getPackage();               //获取class的包信息
String simpleName = class1.getSimpleName();         //获取class类名
int modifiers = class1.getModifiers();                          //获取class访问权限
Class<?>[] declaredClasses = class1.getDeclaredClasses();                  //内部类
Class<?> declaringClass = class1.getDeclaringClass();                          //外部类
  • 其他方法
Annotation[] annotations = (Annotation[]) class1.getAnnotations();    //获取class对象的所有注解
Annotation annotation = (Annotation) class1.getAnnotation(Deprecated.class);    //获取class对象指定注解
Type genericSuperclass = class1.getGenericSuperclass();               //获取class对象的直接超类的 Type
Type[] interfaceTypes = class1.getGenericInterfaces();                //获取class对象的所有接口的type集合
3.通过反射生成并操作对象
  • 生成类的实例对象,两种方法
    1)使用Class对象的newInstance()方法创建该Class对象对应类的实例。此方法要求该Class对象的对应类有默认构造器
    2)使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法创建Class对象对应类的实例。通过该方法可选择使用指定的构造器创建实例
//第一种方式 Class对象调用newInstance()方法生成
Object obj = class1.newInstance();
//第二种方式 对象获得对应的Constructor对象,再通过该Constructor对象的newInstance()方法生成
Constructor<?> constructor = class1.getDeclaredConstructor(String.class);
obj = constructor.newInstance("hello");
  • 调用类的方法
    1)先通过Class对象的getMethods()方法或者getMethod()方法返回Method数组或对象
    2)调用Method对象的*Object invoke(Object obj, Object... args)*方法,第一个参数为调用该方法的实例对象,第二个参数为该方法的参数
    3)通过Method的invoke()方法调用对象方法时,要求必须有方法权限。可通过调用Method的setAccessible(true)取消调用方法的访问权限检查
// 生成新的对象:用newInstance()方法
 Object obj = class1.newInstance();
//首先需要获得与该方法对应的Method对象
Method method = class1.getDeclaredMethod("setAge", int.class);
//调用指定的函数并传递参数
method.invoke(obj, 28);

三.反射与动态代理

1.代理概述
  • 定义
    给某个对象提供一个代理对象,并由代理对象控制对原对象的访问。即客户不直接操控原对象,而是通过代理间接地操控原对象
  • 场景
    1)因为安全需要屏蔽客户端直接访问真实对象
    2)在远程调用中使用代理类处理远程方法调用的技术细节
    3)为了提升性能,封装真实对象,达到延迟加载的目的
  • 代理模式的参与者
    1)主题接口Subject是原对象和代理对象都共同实现的接口,即代理类所实现的行为接口,Request()是委托对象和代理对象共同拥有的方法
    2)目标对象RealSubject是原对象,被Proxy所代理
    3)代理对象Proxy是代理对象,用来封装原对象
    4)客户端:通过主题接口和代理对象完成业务,不直接接触目标对象
代理模式中的角色
  • 代理模式的实现思路
    1)代理对象和目标对象均实现同一行为接口
    2)代理对象和目标对象分别实现行为接口的方法逻辑
    3)在代理类的构造函数中实例化一个目标对象,并通过这个对象调用目标对象的行为接口
    4)客户端只能通过代理类实现的目标对象,间接调用目标对象的行为接口
  • 分类
    静态代理:代理类在编译时实现,即Java编译完成后代理类是一个实际的class文件
    动态代理:代理类在运行时生成,即Java在运行时动态生成类字节码并加载到JVM中
  • 静态代理的简单实现
public class ProxyDemo {
    public static void main(String args[]){
        RealSubject subject = new RealSubject();
        Proxy p = new Proxy(subject);
        p.request();
    }
}

//主题接口
interface Subject{
    void request();
}

//目标对象
class RealSubject implements Subject{
    public void request(){
        System.out.println("request");
    }
}

//代理对象
class Proxy implements Subject{
    private Subject subject;
    public Proxy(Subject subject){
        this.subject = subject;
    }
    public void request(){
        System.out.println("PreProcess");
        subject.request();
        System.out.println("PostProcess");
    }
}
2.动态代理
  • 优点
    1)不需要写一个与目标对象RealSubject形式完全一样的封装类,代理对象不必实现公共接口Subject的每个方法定义,从而减少接口变动引起的代码修改量
    2)动态代理生成的方法甚至可以在运行时制定代理类的执行逻辑,从而大大提升系统灵活性

  • 动态代理的分类
    1)JDK动态代理:包含Proxy类和InvocationHandler接口,只能代理实现接口的类
    2)Cglib动态代理:针对类实现代理,对指定的目标类生成一个子类,并通过覆盖其中方法实现增强,采用继承实现,无法代理被final修饰的类,使用Cglib需下载cglib.jar

  • JDK动态代理相关类
    1)java.lang.reflect.Proxy类
    提供用户创建动态代理类和代理对象的静态方法,是所有动态代理类的父类
    2)java.lang.reflect.InvocationHandler接口
    代理对象通过实现InvocationHandler接口的invoke()方法调用目标对象方法

  • Proxy创建动态代理对象的两种方法

        //方法1:通过动态代理类的构造方法创造
        //创建一个InvocationHandler对象
        InvocationHandler handler = new MyInvocationHandler(.args..);
        //使用Proxy生成一个动态代理类
        Class proxyClass = Proxy.getProxyClass(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
        //获取proxyClass类中一个带InvocationHandler参数的构造器
        Constructor constructor = proxyClass.getConstructor(InvocationHandler.class);
        //调用constructor的newInstance方法来创建动态代理实例
        RealSubject real = (RealSubject)constructor.newInstance(handler);
        //方法2:通过Proxy类的newProxyInstance()静态方法直接创造
        //创建一个InvocationHandler对象
        InvocationHandler handler = new MyInvocationHandler(.args..);
        //使用Proxy直接生成一个动态代理对象
        RealSubject real =Proxy.newProxyInstance(RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);
  • 其他常用方法
    1)InvocationHandler getInvocationHandler(Object proxy) Proxy类方法,获取代理对象对应的调用处理器对象
    2)Class getProxyClass(ClassLoader loader,Class[] interfaces) Proxy类方法,根据类加载器和实现的接口获得代理类
    3)invoke(Object proxy,Method method,Object[] args) InvocationHandler接口方法,调用代理对象绑定的真实对象的方法。 参数说明:① Object proxy:指被代理的对象;② Method method:要调用的方法;③ Object[] args:方法调用时所需要的参数;
  • JDK动态代理的简单实现
public class DynamicProxyDemo {
    public static void main(String[] args) {
        //1.创建目标对象
        RealSubject realSubject = new RealSubject();    
        //2.创建调用处理器对象
        ProxyHandler handler = new ProxyHandler(realSubject);    
       //3.动态生成代理对象
        Subject proxySubject = (Subject)Proxy.newProxyInstance(
                RealSubject.class.getClassLoader(),RealSubject.class.getInterfaces(), handler);   
        //4.通过代理对象调用方法   
        proxySubject.request();    
    }
}

/**
 * 主题接口
 */
interface Subject{
    void request();
}

/**
 * 目标对象类
 */
class RealSubject implements Subject{
    public void request(){
        System.out.println("====RealSubject Request====");
    }
}
/**
 * 代理类的调用处理器
 */
class ProxyHandler implements InvocationHandler{
    private Subject subject;
    public ProxyHandler(Subject subject){
        this.subject = subject;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        //定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
        System.out.println("====before====");
       //调用RealSubject中的方法
        Object result = method.invoke(subject, args);
        System.out.println("====after====");
        return result;
    }
}

月度银墙,不辨花丛哪瓣香

相关文章

  • Java系列7 反射

    参考:反射例程:http://www.cnblogs.com/rollenholt/archive/2011/09...

  • 【阿里P8大牛教你Android入门之路(java篇)】Java

    【阿里P8大牛教你Android入门之路(java篇)】Java集合——Java反射1(系列篇7)链接:https...

  • 系列(7)谈谈 Java 反射机制

    什么是反射? 当程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。我们认为 Java 并不是动态语言,...

  • Java系列 - 反射

    一、为什么需要反射 反射是体现java语言动态性最重要的特征,举几个例子: 1、服务器框架的动态配置能力 - 用反...

  • Java基础:注解

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 1. 概述 注解...

  • Java反射机制详解(一)

    接下来我们将介绍Java反射机制的一系列的知识。本篇文章主要针对Java反射机制的介绍以及反射API的使用知识。 ...

  • Java基础:动态代理

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 概述 在运行时,...

  • Java基础:类加载器

    系列阅读 Java基础:类加载器 Java基础:反射 Java基础:注解 Java基础:动态代理 1. 什么是类加...

  • java系列23:反射

    反射:使用类的字节码所做的一系列操作。 源代码编译过后的字节码文件(.class文件),在java中也被视为对象,...

  • Java进阶系列——反射

    学习笔记,基础知识。开头文字很长,不要着急,干货会有的,先弄清楚概念,理解与使用起来就会事半功倍,不会纠结什么时候...

网友评论

      本文标题:Java系列7 反射

      本文链接:https://www.haomeiwen.com/subject/thqdmqtx.html