Java 中的 反射

2021-12-24  本文已影响0人  MikeShine

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. 获取类的构造方法

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

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

上一篇下一篇

猜你喜欢

热点阅读