访问静态成员

静态成员的获取和普通的成员获取方法一致,但静态成员不需要在对象上执行(在反射中这样表述更合乎语法一些),所以不需要传入承载的对象,也就不需要获得类的对象。

package testReflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

//自定的一个类
class MyClass {
    private static double a1;// 静态成员变量

    // 主方法也是静态成员方法
    public static void main(String[] args) {
        System.out.println("静态方法执行了");
        System.out.println(Arrays.toString(args));
    }
}

// 在主类main方法中测试
public class Main {

    public static void main(String[] args) {
        try {
            // 加载类
            Class cls = Class.forName("testReflect.MyClass");

            // 从Class对象获取指定的成员方法
            Method mthd = cls.getDeclaredMethod("main", String[].class);
            // mthd.setAccessible(true);
            // 调用该成员方法,静态方法不需有载体对象因此传入null,还需传入参数并上转型到Object类型
            mthd.invoke(null, (Object) new String[] { "刘知昊", "大傻逼" });

            // 从Class对象获取指定的成员
            Field f1 = cls.getDeclaredField("a1");
            // 暴力反射,让这个私有静态成员变量可以越过权限检查
            f1.setAccessible(true);
            // 为成员变量设置值,无载体对象,还需传入值
            f1.set(null, 8.88);
            // 输出这个静态成员变量的值,于null无对象上(即表示静态成员变量)
            System.out.println("静态成员变量a1=" + f1.get(null));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

输出:

静态方法执行了
[刘知昊, 大傻逼]
静态成员变量a1=8.88

越过泛型检查

泛型是在编译期间起作用的,也就是在java类编译成class文件的过程中,会根据书写的泛型的具体类型,来对泛型类的对象的增删改查做类型检查。当编译完成,生成class文件之后,类型检查就不再起作用了,即做了泛型擦除使泛型中的实参不再生效。

反射通过class文件中的Class类的对象,复活这个类甚至对象,这个过程中该类或者该对象是没有经过编译的,而是从编译好的class文件中取出来的,所以用使用反射时也就避开了泛型检查

package testReflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

// 在主类main方法中测试
public class Main {
    private static ArrayList<String> al = new ArrayList<>();

    public static void main(String[] args) {
        // 获取其Class对象
        Class cls = al.getClass();

        try {
            // 获取其add方法,注意绕过了泛型的add参数完全是Object类型的
            Method mthd = cls.getDeclaredMethod("add", Object.class);
            // 添加一些其它类型的元素进去试试
            mthd.invoke(al, 100);
            mthd.invoke(al, true);
            mthd.invoke(al, 3.14);
            // 查看一下
            System.out.println(al.toString());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

输出:

[100, true, 3.14]