目录

Reflection(反射)

反射(Reflection)用于操作字节码文件,可以调用任意类中所有成员变量、方法,或是获取定义的信息。

java.lang.Class 类

Class 类代表一个类的类,可以通过 Class 获取当前运行对象中任意成员变量和方法。

所有的操作类之前需要获取 Class 实例,有以下 3 种获取方法:

  • Object.getClass(),通过对象继承 Object 类中的 getclass() 获取 Class 类实例。
  • className.class,通过基本/引用数据类型的 .class 属性获取。例如 int.Class、String.Class。
  • Class.forName("类全限定名"),通过 Class 类静态方法 forName 获取指定类。它的特点是触发类加载,类里面的静态代码块/静态属性/静态方法优先加载,而静态代码块会被执行。

要想获取关于父类信息可以使用下面方法:

  • getSuperclass(),返回父类 Class 类型实例,可以通过 getName() 获得父类名称
  • getInterfaces(),返回 Class 数组,通过 getName() 可以获得接口名称。

创建 subClass.java:

package reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

class superClass {
    protected boolean status = false;
    public final int NUM = 1;
    public static final boolean TESTZ = true;
    public int count = 12;
    private int testPrivatee = 1;

    public static void superEchoStr(String str1, String str2) {
        System.out.println("superClass.superEchoStr 静态方法,参数 st1=" + str1 + ",参数 st2=" + str2);
    }

    private String superReturnStr() { return "superClass.superReturnStr 实例方法"; }
}

public class subClass extends superClass {
    private String tmpStr = "subClass.tmpStr";
    public String tmpStr2 = "subClass.tmpStr2";
    public static final String tmpStr3 = "subClass.tmpStr3";
    protected static final String tmpStr4 = "subClass.tmpStr4";
    String tmpStr5 = "subClass.tmpStr5";

    public static void subEchoStr() { System.out.println("subClass.subEchoStr 静态方法"); }

    public String subReturnStr(int num1, String str2) { return "subClass.subReturnStr 实例方法"; }

    private String subReturnStr2() { return "subClass.subReturnStr2 实例方法"; }

    public static void main(String[] args) throws Exception {
        subClass subclass = new subClass();
        superClass superclass = new superClass();
        System.out.println("----------1.获取 Class 类----------");
        Class clazz = subclass.getClass();
        Class clazz1 = subClass.class;
        Class clazz2 = Class.forName("reflection.subClass");
        Class clazz3 = superClass.class;
        System.out.println(clazz == clazz1 && clazz == clazz2); // 比较结果都是 true
    }
}

通过 getClass().classClass.forName("") 这三种方法获取,最终得到的都是同一个对象。

运行结果是 true

----------1.获取 Class 类----------
true

java.lang.Class 类里面有以下常用方法:

  • getName(),返回 String 类型类的全限定名。
  • getSimpleName(),返回 String 类型的类名。

通过 clazz 来获取类的信息:

System.out.println("获取类名:" + clazz.getName() + ", " + clazz.getSimpleName());
System.out.println("获取父类名称:" + clazz.getSuperclass().getName() + ", "+ clazz.getSuperclass().getSimpleName());
System.out.println("获取类中实现的接口:");
for (Class i : Class.forName("java.lang.String").getInterfaces()) {
    System.out.println("\t" + i.getName());
}

运行输出得到类相关信息:

----------1.获取 Class 类----------
true
获取类名:reflection.subClass, subClass
获取父类名称:reflection.superClass, superClass
获取类中实现的接口:
    java.io.Serializable
    java.lang.Comparable
    java.lang.CharSequence

java.lang.reflect.Field 类

Class 类有几个方法用于获取成员变量,最终返回 Field 实例,通过此实例来操作成员变量:

  • getField("VariableName"),获取当前类或父类指定成员变量。通过 getField() 获取 Field 实例要注意,先确定类实例里有,不然会抛 NoSuchFieldException 异常。
  • getFields(),获取当前类和父类所有 public 成员变量,返回一个 Field 类型数组。
  • getDeclaredField("VariableName"),获取当前类自己声明的某个成员变量,非 public 权限也可以。不能获取继承自父类成员变量。
  • getDeclaredFields(),获取当前类所有自己声明的成员变量返回 Field 类型数组。不能获取继承自父类成员变量。

对成员变量获取并访问:

System.out.println("----------2.获取 Field 类----------");
Field field1 = clazz.getField("NUM");
Field field2 = clazz.getField("count");
Field field3 = clazz.getField("TESTZ");
Field field4 = clazz3.getDeclaredField("testPrivatee");

// 操作 Field 类
System.out.println("获取指定 public 成员变量:\n\t" + field1 + "\n\t" + field2 + "\n\t" + field3);
System.out.println("获取指定成员变量(任意访问权限):\n\t" + field4);
System.out.println("获取直接父类和当前类所有 public 成员变量:");
for (Field f : clazz.getFields()) {
    System.out.println("\t" + f);
}
System.out.println("获取当前类所有成员变量(任意访问权限):");
for (Field f : clazz.getDeclaredFields()) {
    System.out.println("\t" + f);
}
System.out.println();

运行输出:

----------2.获取 Field 类----------
获取指定 public 成员变量:
    public final int reflection.superClass.NUM
    public int reflection.superClass.count
    public static final boolean reflection.superClass.TESTZ
获取指定成员变量(任意访问权限):
    private int reflection.superClass.testPrivatee
获取直接父类和当前类所有 public 成员变量:
    public java.lang.String reflection.subClass.tmpStr2
    public static final java.lang.String reflection.subClass.tmpStr3
    public final int reflection.superClass.NUM
    public static final boolean reflection.superClass.TESTZ
    public int reflection.superClass.count
获取当前类所有成员变量(任意访问权限):
    private java.lang.String reflection.subClass.tmpStr
    public java.lang.String reflection.subClass.tmpStr2
    public static final java.lang.String reflection.subClass.tmpStr3
    protected static final java.lang.String reflection.subClass.tmpStr4
    java.lang.String reflection.subClass.tmpStr5

java.lang.reflect.Field 类里常用方法:

  • getModifiers(),获取修饰符,返回 int 数值,此数值需要使用 java.lang.reflect.Modifier.toString() 转为实际 String 字符。
  • getType(),返回指定成员变量类型,以 String 字符串呈现。
  • getName(),返回指定成员变量名称,以 String 字符串呈现。
  • get(Object value),传入的一个对象,用于获取指定成员值,最终返回 Object 类型变量值。
  • set(Object obj, Object value),设置对象成员变量值,需要传入对象。

获取 Field 常用信息:

System.out.println("获取实例变量 subClass.NUM 修饰符、数据类型和成员变量名称:" +
        Modifier.toString(field1.getModifiers()) +
        field1.getType() + " " +
        field1.getName());
System.out.println("获取实例变量 subClass.NUM 值:" + field1.get(subclass));
System.out.println("获取实例变量 superClass.NUM 值:" + field1.get(superclass));
System.out.println("获取类变量 subClass.TESTZ 值:" + field3.get(null));
System.out.println("获取当前类私有实例变量 subClass.tmpStr 值:" + clazz.getDeclaredField("tmpStr").get(subclass)); // 操作当前类 private 成员变量则不需要设置访问权限为 true,这是 private 权限规则使然。
field4.setAccessible(true); // 不是操作当前类私有变量则需要设置为 ture,后续操作(get/set)才不会抛 IllegalAccessException 异常。
System.out.println("获取其他类私有实例变量 superClass.testPrivatee 值:" + field4.get(superclass));
System.out.println();
System.out.println("实例变量 superClass.testPrivatee 修改前:" + field4.get(subclass));
field4.set(subclass, 111);
System.out.println("实例变量 superClass.testPrivatee 修改后:" + field4.get(subclass));
System.out.println("实例变量 subclass.count 修改前:" + field2.get(subclass));
field2.set(subclass, 122);
System.out.println("实例变量 subclass.count 修改后:" + subclass.count);

运行输出:

获取实例变量 subClass.NUM 修饰符、数据类型和成员变量名称:public finalint NUM
获取实例变量 subClass.NUM 值:1
获取实例变量 superClass.NUM 值:1
获取类变量 subClass.TESTZ 值:true
获取当前类私有实例变量 subClass.tmpStr 值:subClass.tmpStr
获取其他类私有实例变量 superClass.testPrivatee 值:1

实例变量 superClass.testPrivatee 修改前:1
实例变量 superClass.testPrivatee 修改后:111
实例变量 subclass.count 修改前:12
实例变量 subclass.count 修改后:122

java.lang.reflect.Method 类

Method 实例通过 Class 类以下方法获取:

  • getMethod(String name, Class<?>... parameterTypes),获取当前类或直接父类 publilc 方法。参数可以填写多个。
  • getMethods(),获取当前类和直接父类所有 publilc 成员方法,返 Method 类型数组。
  • getDeclaredMethod(String name, Class<?>... parameterTypes),获取当前类指定成员方法。传入 String 类型方法名,parameterTypes 是可变长度参数,传 Class 类型数据,可以传多个,例如:String.class,int.class,Date.class。
  • getDeclaredMethods(),获取当前类所有成员方法。

java.lang.reflect.Method 类里常用方法:

  • getModifiers(),获取修饰符,返回 int 数值,此数值需要使用 java.lang.reflect.Modifier.toString() 转为实际 String 字符。
  • getReturnType(),获取返回值类型,实际返回 Class 实例,需要通过 getName()getSimpleName() 获取 String 字符串。
  • getName(),获取方法名称,返回 String 类型。
  • getParameterTypes(),返回 Class 实例数组,通过 getName()getSimpleName() 获取 String 字符串。
  • getParameters(),获取形参名称,实际返回 Parameter 数组通过 getName() 获取形参名称。使用时需要开启 -parameters 编译选项,打开 IDEA菜单 File -> Settings(Ctrl+Alt+S) -> Build, Execution, Deployment -> Compiler -> Java Compiler 找到 Javac Options 的 Additional command line parameters 输入框添加即可,如果还是不生效重新 ReBuilde Project 。具体原因参考通过反射获取方法的参数名称(JDK8以上支持)一文。
  • invoke(Object obj, Object... args),第一个参数是在哪个调用对象上调方法,第二个参数是变长参数,可以传参 0-N 个实参。最终返回个 Object 类型数据,可以根据实际类型进行强制类型转换。
System.out.println("----------3.获取 Method 类----------");
System.out.print("获取当前类/父类指定方法 superEchoStr 并调用:");
Method superReturnStrMethod = clazz3.getMethod("superEchoStr", String.class, String.class);
superReturnStrMethod.invoke(superclass, "1", "2");
System.out.println("获取当前类和父类所有 public 方法:");
for (Method m : clazz.getMethods()) {
    System.out.println("\t" + m);
}
System.out.print("获取当前类指定方法 subReturnStr2 并执行(任意访问权限):");
Method subReturnStr2Method = clazz.getDeclaredMethod("subReturnStr2");
System.out.println(subReturnStr2Method.invoke(subclass));
System.out.println("获取当前类所有方法(任意访问权限)并输出修饰符、数据类型、标识符、参数类型和参数标识符:");
for (Method m : clazz.getDeclaredMethods()) {
    System.out.print("\t" + Modifier.toString(m.getModifiers()) + " " +
            m.getReturnType().getSimpleName() + " " +
            m.getName() + " ");
    Class[] parmType = m.getParameterTypes();
    Parameter[] parmName = m.getParameters();
    for (int i = 0; i < parmType.length; i++) {
        System.out.print(parmType[i].getSimpleName() + " ");
        System.out.print(parmName[i].getName() + ", ");
    }
    System.out.println();
}

运行输出:

----------3.获取 Method 类----------
获取当前类/父类指定方法 superEchoStr 并调用:superClass.superEchoStr 静态方法,参数 st1=1,参数 st2=2
获取当前类和父类所有 public 方法:
    public static void reflection.subClass.main(java.lang.String[]) throws java.lang.Exception
    public java.lang.String reflection.subClass.subReturnStr(int,java.lang.String)
    public static void reflection.subClass.subEchoStr()
    public static void reflection.superClass.superEchoStr(java.lang.String,java.lang.String)
    public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    public final void java.lang.Object.wait() throws java.lang.InterruptedException
    public boolean java.lang.Object.equals(java.lang.Object)
    public java.lang.String java.lang.Object.toString()
    public native int java.lang.Object.hashCode()
    public final native java.lang.Class java.lang.Object.getClass()
    public final native void java.lang.Object.notify()
    public final native void java.lang.Object.notifyAll()
获取当前类指定方法 subReturnStr2 并执行(任意访问权限):subClass.subReturnStr2 实例方法
获取当前类所有方法(任意访问权限)并输出修饰符、数据类型、标识符、参数类型和参数标识符:
    public static void main String[] args, 
    public String subReturnStr int num1, String str2, 
    private String subReturnStr2 
    public static void subEchoStr 

java.lang.reflect.Constructor 类

通过 Class 类以下方法获取 Constructor 实例:

  • getDeclaredConstructor(Class<?>... parameterTypes),传一个变长参数,类型是 Class。不传就成了获取无参构造方法 Constructor 实例。
  • getDeclaredConstructors(),返回 Constructor 类型数组,数组内里面是 Constructor 实例。

java.lang.reflect.Constructor 有以下方法:

  • newInstance(Object ... initargs),传入实际要初始化的参数,相当于构造方法传参。
// 获取 Class 类实例
Class clazz = Class.forName('xx.xx.xx')

// 获取无参构造方法并调用1
Constructor constructorMethod1 = clazz.getDeclaredConstructor(); 
Object obj1 = constructorMethod1.newInstance();

// 获取无参构造方法并调用2
Object obj2 = clazz.newInstance(); // 通过 Class 直接调用实例化方法,在 Java 8 还可以使用,Java 9 被废弃。

// 获取有参构造方法并调用1
Constructor constructorMethod2 = clazz.getDeclaredConstructor(int.class, String.class); 
Object obj3 = constructorMethod2.newInstance(1, "str test");

反射总结

不管操作 Field、Method 还是 Constructor 都需要通过 Class 实例,接着通过 Class 实例 getXXX()getXXXs() 方法获取 Field、Method、Constructor 对应实例。获取到各实例后通过各自类中实现方法,它们都能够获得修饰符、返回类型、标识符、形参类型、形参名字内容,甚至执行调用或重新赋值操作。

反射补充

类加载机制 loadClass()(未完成)

动态代理(未完成)

最近更新:

发布时间:

讨论讨论

Asia/Shanghai