美文网首页
Java 中的 反射

Java 中的 反射

作者: MikeShine | 来源:发表于2021-12-24 14:19 被阅读0次

    1. 写在前面

    “反射” 这个概念一直不了解,不过其实也不是什么高深的东西。本着代码能有什么高深的呢这样的出发点,我们来看一下所谓的reflection 是什么东西。


    2. 反射是干什么的

    Reflection 是指 通过 Class 实例 来获取 Class 的相关信息


    3. Class

    要明白反射,我们首先要知道,java 中所有的类都是 Class

    java 代码执行过程 类加载过程

    jvm 在运行时遇到一个类,会将其读入内存,动态加载。而不是一次性加载所有类。
    而 jvm 加载一个类的时候,就会创建一个 Class,就是一个 类名为 Classclass实例。

    Class class = new Class(String);
    

    而每一个类的实例都包含其所有信息。

    获取一个 Class 实例

    String s = "test";
    Class cls = s.getClass();
    
    Class cls = String.class();
    

    看下面一段代码

    // reflection 
    public class Main {
        public static void main(String[] args) {
            printClassInfo("".getClass());
            printClassInfo(Runnable.class);
            printClassInfo(java.time.Month.class);
            printClassInfo(String[].class);
            printClassInfo(int.class);
        }
    
        static void printClassInfo(Class cls) {
            System.out.println("Class name: " + cls.getName());
            System.out.println("Simple name: " + cls.getSimpleName());
            if (cls.getPackage() != null) {
                System.out.println("Package name: " + cls.getPackage().getName());
            }
            System.out.println("is interface: " + cls.isInterface());
            System.out.println("is enum: " + cls.isEnum());
            System.out.println("is array: " + cls.isArray());
            System.out.println("is primitive: " + cls.isPrimitive());
        }
    }
    
    

    可以看到 ,JVM 给 int 也创建了 class 实例。

    class 实例可以直接用来比较

    Integer n = new Integer(123);
    
    boolean b1 = n instanceof Integer; // true,因为n是Integer类型
    boolean b2 = n instanceof Number; // true,因为n是Number类型的子类
    
    boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
    boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class
    

    instanceof 会匹配类型和子类


    4. 获取类的字段

    这里提供了两种方法用来获取字段:
    getField() 获取包括父类的public字段
    getDeclaredField() 获取当前类的所有字段

    public class Main {
        public static void main(String[] args) throws Exception {
            Class stdClass = Student.class;
            // 获取public字段"score":
            System.out.println(stdClass.getField("score"));
            // 获取继承的public字段"name":
            System.out.println(stdClass.getField("name"));
            // 获取所有字段"grade": 这里当然包括了 private & public
            System.out.println(stdClass.getDeclaredField("grade"));
        }
    }
    
    class Student extends Person {
        public int score;
        private int grade;
    }
    
    class Person {
        public String name;
    }
    

    获取类的字段 的 值

    看下面的代码,也可以获取类的字段的值

     public class Main {
    
        public static void main(String[] args) throws Exception {
            Object p = new Person("Xiao Ming");
            Class c = p.getClass();
            Field f = c.getDeclaredField("name");
            Object value = f.get(p);
            System.out.println(value); // "Xiao Ming"
        }
    }
    

    修改类的字段的值

      public static void main(String[] args) throws Exception {
            Person p = new Person("Xiao Ming");
            System.out.println(p.getName()); // "Xiao Ming"
            Class c = p.getClass();
            Field f = c.getDeclaredField("name");
            f.setAccessible(true);
            f.set(p, "Xiao Hong");
            System.out.println(p.getName()); // "Xiao Hong"
        }
    }
    

    这里如果要修改 private 的字段的值,需要先 setAccessible()

    小结

    通过Class实例的方法可以获取Field实例:getField()getFields()getDeclaredField()getDeclaredFields()

    通过Field实例可以获取字段信息:getName()getType()getModifiers()

    通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。

    通过反射读写字段是一种非常规方法,它会破坏对象的封装。


    5. 获取类的方法

    基本上和获取类的字段是一样的

    通过Class实例的方法可以获取Method实例:getMethod()getMethods()getDeclaredMethod()getDeclaredMethods()

    通过Method实例可以获取方法信息:getName()getReturnType()getParameterTypes()getModifiers()

    通过Method实例可以调用某个对象的方法:Object invoke(Object instance, Object... parameters)

    通过设置setAccessible(true)来访问非public方法;


    6. 获取类的构造方法

    • getConstructor(Class...):获取某个publicConstructor
    • getDeclaredConstructor(Class...):获取某个Constructor
    • getConstructors(Class...)
    • getDeclaredConstructors(Class...)

    调用非publicConstructor时,必须首先通过setAccessible(true)设置允许访问。setAccessible(true)可能会失败

    这里获取的构造方法都是自己的,不会是父类

    相关文章

      网友评论

          本文标题:Java 中的 反射

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