美文网首页
Java反射机制学习笔记

Java反射机制学习笔记

作者: 捉虫大师 | 来源:发表于2018-08-06 22:41 被阅读5次

    上一篇《java注解学习笔记》中最后说到了注解的实现主要依赖java的反射机制,那么这一篇主要介绍一下java的反射机制。

    什么是java反射机制

    java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

    具体的api

    主要的api都在java.lang.reflect.*这个包中(除了注解外)。

    各种获取注解、构造器、方法、属性的示例:

    // 获取所有注解
    Annotation[] allAnnotations = studentClass.getAnnotations();
    Annotation[] annotations = studentClass.getDeclaredAnnotations();
    // 获取特定的注解
    Annotation annotation = studentClass.getAnnotation(Deprecated.class);
    
    // 获取所有构造器
    Constructor[] allConstructors = studentClass.getConstructors();
    
    // 获取特定构造器
    Constructor allConstructor;
    try {
        allConstructor = studentClass.getConstructor(String.class, int.class, int.class);
    } catch (NoSuchMethodException e) {
        throw e;
    }
    
    // 获取所有构造器
    Constructor[] constructors = studentClass.getDeclaredConstructors();
    // 获取特定构造器
    Constructor constructor;
    try {
         constructor = studentClass.getDeclaredConstructor(String.class);
    } catch (NoSuchMethodException e) {
        throw e;
    }
    
    // 获取所有方法
    Method[] allMethods = studentClass.getMethods();
    Method allMethod;
    // 获取特定方法
    try {
        allMethod = studentClass.getMethod("answer");
    } catch (NoSuchMethodException e) {
        throw e;
    }
    
    // 获取所有本类定义的方法
    Method[] methods = studentClass.getDeclaredMethods();
    Method method;
    try {
        method = studentClass.getDeclaredMethod("answer");
    } catch (NoSuchMethodException e) {
        throw e;
    }
    
    // 获取所有字段
    Field[] allFields = studentClass.getFields();
    Field allField;
    try {
        allField = studentClass.getField("name");
    } catch (NoSuchFieldException e) {
        throw e;
    }
    
    // 获取本类定义的字段
    Field[] fields = studentClass.getDeclaredFields();
    Field field;
    try {
        field = studentClass.getDeclaredField("age");
    } catch (NoSuchFieldException e) {
        throw e;
    }
    

    通过反射调用方法:

    constructor.setAccessible(true);
    Object obj = constructor.newInstance("lkxiaolou");
    // 执行方法
    String str = (String) method.invoke(obj);
    

    总结一下:

    • 可以获取到的内容有:构造器、方法(定义的方法)、属性(定义的属性),静态的属性和方法都能拿到。
    • getDeclaredXXX(getDeclaredConstructor(s),getDeclaredMethod(s),getDeclaredField(s))是获取定义的构造器,方法,属性,定义是指写在那个类中的,继承的不算。相似地getXXX(getConstructor(s),getMethod(s),getField(s))是获取所有的构造器、方法、属性,包括继承的。
    • 通过第二点拿到的构造器、方法、属性包含了public到private的各种修饰。
    • 如果取到的构造器、方法、属性是不可访问的(如public,protected),在用Method.invoke访问这些时会抛出一个java.lang.IllegalAccessException异常,除非在访问前设置可见属性为true。

    反射的用途

    根据配置运行相对应的方法

    这个很好理解,可以通过配置的方法名来改变运行时执行的方法。

    动态代理

    动态代理是代理的一种,代理是一种设计模式,一般有动态代理与静态代理,动态代理主要依赖InvocationHandler和Proxy,我们这里给一个简单的例子:

    package reflect;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class MyInvocationHandler implements InvocationHandler {
    
        private Object targetObject;
    
        public MyInvocationHandler(Object targetObject) {
            this.targetObject = targetObject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理处理..." + method.getName());
            return method.invoke(targetObject, args);
        }
    }
    
    package reflect;
    public interface Person {
        public String answer();
    }
    
    package reflect;
    
    public class Student implements Person {
    
        public String name;
    
        public Integer age;
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        private Student(String name) {
            this.name = name;
        }
    
        @Override
        public String answer() {
            return "I am " + name + ", and " + age + " years old";
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    }
    
    Student student = new Student("lk", 18);
    MyInvocationHandler h = new MyInvocationHandler(student);
    Person person = (Person) Proxy.newProxyInstance(h.getClass().getClassLoader(), student.getClass().getInterfaces(), h);
    String s = person.answer();
    System.out.println(s);
    

    运行一下可以看到


    正常运行

    调试中出现一个小插曲
    我在调试的时候出现了这样的结果


    调试出现

    每次我鼠标放在person上,控制台就多一次输出,这是为啥,我把method打印出来,发现执行了toString方法,所以这里应该是IDE执行toString方法触发了动态代理

    绕过泛型检查

    严格来说这并不是一个有用的特性,就像我们可以用反射机制来强行执行private方法,只是提供一种思路,但不是很好。

    这是后加的一段,第二天发现在给private方法做单元测试的时候,以前我都是把private改成public,测完再改回去,现在发现可以用发射来做,这样就不用改代码啦。

    泛型检查是在编译期做的检查,如果要这么写:

    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add(3); // 编译不通过
    

    这样是通过不了编译的。但是我们可以这样做:

    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    Method listAddMethod = ArrayList.class.getMethod("add", Object.class);
    listAddMethod.invoke(list, 3);
    for (Object object : list) {
        System.out.println(object);
    }
    
    绕过泛型检查

    相关文章

      网友评论

          本文标题:Java反射机制学习笔记

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