Java反射

前言

Java的类在被类加载器加载完成后会在JVM堆中的方法区(jdk 8 改名为元空间)中生成一个对应类的class 对象,这个class对象中几乎包含了这个类的所有信息。我们通过反射获取到这个类,来对类进行分析或者实例化等操作。

Class 类中包含的信息

class类中包含的信息很多,这里列出部分。主要有字段、方法、构造器方法、注释、包名、类加载器、类实现的接口等。详细部分可以查看API手册


下面我们定义一个Person类尝试获取这个类的class对象并对其进行操作

@SimpleAnnotation
public class Person {
    private String name;
    private String sex;
    private int id;
    public Person(){}
    @SimpleAnnotation
    private void show(Integer id){
        System.out.println("执行show()方法");
    }
    private Person(String name, String sex, int id) {
        this.name = name;
        this.sex = sex;
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", id=" + id +
                '}';
    }
}

获取Person类的Class 对象

获取一个类的对象有四种方法,下面通过代码展示

/**
         * 获取类的三种方式
         * Class<?> person  = Class.forName("entity.Person");
         * Class<? extends Person> person = people.getClass();
         * Class<Person> person = Person.class;
         * 第四种方式:通过类加载器实现
         */

操作Person类的Class对象

通过类对应的Class实例对应的对象我们可以得到很多类相关的信息。这里有一处很有意思,在获取类的构造器方法时,传入Intger.class失败 ,int.class成功。换句话说int.class 和Integer.class对应了不同类对象。

public class ClazzInstance {
    public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Person people = new Person();
        Class<Person> person = Person.class;
        // 获取Filed
        Field[] declaredFields = person.getDeclaredFields();//获取类中声明的所有字段
        Field[] fields = person.getFields();//获取公有字段
        Field field = person.getDeclaredField("name");//获取指定名称的字段,参数为String
        /*System.out.println(field);
        System.out.println(Arrays.toString(declaredFields));
        System.out.println(Arrays.toString(fields));*/

        // 获取构造器方法
        Constructor<?>[] declaredConstructors = person.getDeclaredConstructors();// 获取类中所有的构造器方法
        Constructor<?>[] constructors = person.getConstructors();
        // 获取指定的构造器方法,参数为构造器方法参数的class对象
        Constructor<Person> declaredConstructor = person.getDeclaredConstructor(String.class,String.class,int.class);
        /*System.out.println(Arrays.toString(declaredConstructors));
        System.out.println(Arrays.toString(constructors));
        System.out.println(declaredConstructor);*/

        // 获取Method
        Method[] declaredMethods = person.getDeclaredMethods();
        Method[] methods = person.getMethods();
        // 相较构造器方法多了一个方法名(谁让构造器方法名固定呢)
        Method showMethod = person.getDeclaredMethod("show", Integer.class);
        /*System.out.println(Arrays.toString(declaredMethods));
        System.out.println(Arrays.toString(methods));
        System.out.println(showMethod);*/

        // 获取类上的注解
        Annotation[] annotations = person.getAnnotations();//返回此元素上存在的注释
        Annotation[] declaredAnnotations = person.getDeclaredAnnotations();//返回直接出现在此元素上的注释
        SimpleAnnotation declaredAnnotation = person.getDeclaredAnnotation(SimpleAnnotation.class);
        System.out.println(Arrays.toString(declaredAnnotations));
        System.out.println(declaredAnnotation);

        // 获取方法上的注解
        Method show = person.getDeclaredMethod("show", Integer.class);
        show.getDeclaredAnnotations();
        System.out.println(Arrays.toString(show.getDeclaredAnnotations()));

        // 获取类所属包名和模块名
        /*System.out.println(person.getPackageName());
        System.out.println(person.getModule());*/

        /*修改类中的字段、执行方法、利用构造器方法返回构建实例*/
        field.setAccessible(true);
        // 修改的实体对象,修改的名称
        field.set(people,"zhangsan");
//        System.out.println(people);
        // 执行方法
        showMethod.setAccessible(true);
        // 在具有指定参数的指定对象上调用此方法对象表示的基础方法 obj 和 此方法的参数列表
        showMethod.invoke(people,1);
        declaredConstructor.setAccessible(true);
        Person firstPerson = declaredConstructor.newInstance("lisi", "男", 101);
        System.out.println(firstPerson);
    }
}

针对代码中涉及到的Filed、Method、Constructor的时,我们在操作这些类的时候需要判断是否是可接近的(方法是否私有的),通过setAceesible(true)设置可接近。下面我们来看一下这三个类中的部分方法。

(1)Field

 public Object get(Object obj) 取得指定对象obj上此Field的属性内容
 public void set(Object obj,Object value) 设置指定对象obj上此Field的属性内容
 public int getModifiers() 以整数形式返回此Field的修饰符
 public Class<?> getType() 得到Field的属性类型
 public String getName() 返回Field的名称。

(2)Mehtod

Object invoke(Object obj, Object … args)执行目标方法
 public Class<?> getReturnType()取得全部的返回值  public Class<?>[] getParameterTypes()取得全部的参数
 public int getModifiers()取得修饰符
 public Class<?>[] getExceptionTypes()取得异常信息

(3)Constructor

public T newInstance(Object … initargs)。返回创建的实例
 取得修饰符: public int getModifiers();
 取得方法名称: public String getName();
 取得参数的类型:public Class<?>[] getParameterTypes();

反射的应用

这里我们试着利用反射完成动态代理。(省略接口和接口实现类的代码)

方法一:实现接口完成动态代理
   /*
    要想实现动态代理,需要解决的问题?
    问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
    问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
     */
public class DynamicProxy {
    public static void main(String[] args) {
        Factory phoneFactory = new PhoneFactory();
        Factory proxyInstance = (Factory) DynamicProxyFactory.getProxyInstance(phoneFactory);
        proxyInstance.createProduct();
    }
}

class DynamicProxyFactory {
    public static Object getProxyInstance(Object obj) {
        MyInvocationHandler handler = new MyInvocationHandler();
        handler.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
    }
}

class MyInvocationHandler implements InvocationHandler {
    private Object obj;

    public void bind(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // proxy代理类 method需要执行的方法 args 执行方法的参数。
        System.out.println("动态代理执行前的一些的工作");
        // 执行的是被代理类的方法,不是代理类的方法。
        Object returnValue = method.invoke(obj, args);
        System.out.println("动态代理执行后的一些工作");
        return returnValue;
    }
}
  1. Proxy类: static Object newProxyInstance​(ClassLoader loader, 类<?>[] interfaces, InvocationHandler h) 返回指定接口的代理实例,该接口将方法调用分派给指定的调用处理程序。
  2. InvocationHanler接口:Object invoke​(Object proxy, 方法 method, Object[] args) 处理代理实例上的方法调用并返回结果。返回值Object对应方法的返回值。
  3. 代理类执行方法的流程:待了解
方法二:继承目标类完成动态代理

未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值