Java中反射实践记录一下

前言

  • Java中的反射可以帮助程序做很多是事,对于运行状态下的类能获取其所有属性和方法。
  • 一个类中的公共和私有的变量和方法、构造方法等信息都能通过反射进行获取

Class类:getDeclaredField把类的所有属性反射出来(含共有、私有、静态、final修饰的)。 getDeclaredClasses把类的所有内部类对象反射出来。 getDeclaredMethod把类的所有方法反射出来。 setAccessible(true)关闭java语言访问检查,如果想要获取私有成员需要设置为true,不安全但能提升反射效率(慎用)

通过反射获取Integer类私有属性

  • 可以对final属性进行赋值操作
  • 这里获取Integer类对象内value属性,并对其进行赋值,最后其执行结果就是固定为我们设定的值
public static void main (String[] args) throws Exception {
    Class<Integer> aClass = Integer.class;
    Field cache = aClass.getDeclaredField("value");
    cache.setAccessible(true);
    Integer a = 10, b = 10;
    cache.set(a, 16);
    System.out.println(a);
    cache.set(b, 12);
    System.out.println(b);
}
------------------
执行结果
16
12
复制代码

离职小技巧

  • 通过静态代码块把Integer内的缓存值给固定写死,看下面执行的结果出乎意料。
static {
    try {
        final Class<?>[] declaredClasses = Integer.class.getDeclaredClasses();
        Class<?> integerClass = declaredClasses[0];
        Field cache = integerClass.getDeclaredField("cache");
        cache.setAccessible(true);
        Integer[] integer = (Integer[]) cache.get(integerClass);
        for (int i = 0; i < integer.length; i++) {
            integer[i] = 3;
        }
    } catch (NoSuchFieldException | IllegalAccessException e) {
    }
}

public static void main (String[] args) throws Exception {
    // new Integer()相当于在堆内存中重新开辟了一个空间,并没有使用其内部的缓存对象
    // 而Integer.valueof()其内部使用缓存对象
    System.out.println(new Integer(2));
    System.out.println(Integer.valueOf(2));
    // 自动拆箱,相当于Integer.valueof()
    Integer a = 0;
    System.out.println(a);
    Integer c = 127;
    System.out.println(c);
    Integer d = 128;
    System.out.println(d);
    Integer e = 129;
    System.out.println(e);
}
--------------
执行结果
2
3
3
3
128
129
复制代码

通过反射获取String私有属性

  • 在JDK8内字符串是以char数组形式保存其字符内容,那通过反射获取value属性后对其进行某些不可描述操作,其执行结果如下
public static void main (String[] args) throws Exception {
    String a = "a";
    Class<?> stringClass = String.class;
    Field field = stringClass.getDeclaredField("value");
    field.setAccessible(true);
    char[] chars = (char[]) field.get(a);
    chars[0] = '郑';
    System.out.println(a);
}
--------------
执行打印结果
郑
复制代码

通过反射获取类的私有属性

  • 前期类准备
/**
 * @Author: ZRH
 * @Date: 2021/11/19 15:09
 */
@Data
public class TestModel {

    private Integer id;
    private String name;

    public TestModel () {
    }

    public TestModel (String name, Integer id) {
        this.name = name;
        this.id = id;
    }

    private String sendFun (String name) {
        return "OK - " + name;
    }
}
复制代码
public static void main (String[] args) throws Exception {
    TestModel model = new TestModel();
    Class<?> aClass = TestModel.class;
    Field nameField = aClass.getDeclaredField("name");
    nameField.setAccessible(true);
    nameField.set(model, "zrh");
    Field idField = aClass.getDeclaredField("id");
    idField.setAccessible(true);
    idField.set(model, 10000);
    System.out.println(model.toString());
}
---------------
执行结果
TestModel(id=10000, name=zrh)
复制代码

通过反射获取类的构造方法

public static void main (String[] args) throws Exception {

    // 获取当前类对象
    Class aClass = TestModel.class;
    // 获取当前类指定的构造方法
    Constructor constructor = aClass.getConstructor(String.class, Integer.class);
    // 实例化构造方法,获取类实例对象
    TestModel model = (TestModel) constructor.newInstance("222", 3);
    System.out.println(model);
    // 通过当前类默认无参的构造方法,获取实例对象
    model = (TestModel) aClass.newInstance();
    model.setId(2);
    model.setName("111");
    System.out.println(model);
}
----------------------
执行结果
TestModel(id=3, name=222)
TestModel(id=2, name=111)
复制代码

通过反射获取LinkedBlockingQueue的capacity私有属性

  • 之前的一篇《线程池调优之动态参数配置》文章中描述了如果想要动态配置线程池的队列大小,需要对其进行复制重写,并把其capacity属性的setCapacity操作放开。
  • 其实还可以通过反射获取capacity属性后进行重新赋值,看下面示例:
public static void main (String[] args) throws Exception {
    final LinkedBlockingQueue<Object> queue = new LinkedBlockingQueue<>(3);
    for (int i = 0; i < 5; i++) {
        queue.offer(i);
    }
    System.out.println("before size = " + queue.size());
    final Class<LinkedBlockingQueue> queueClass = LinkedBlockingQueue.class;
    final Field field = queueClass.getDeclaredField("capacity");
    field.setAccessible(true);
    field.set(queue, 5);
    for (int i = 0; i < 8; i++) {
        queue.offer(i);
    }
    System.out.println("after size = " + queue.size());
}
---------------
执行结果
before size = 3
after size = 5
复制代码
  • 当前队列初始化大小只有3个,然后通过反射并对其capacity重新赋值为5后,往队列重新插入元素,最后其大小为5个。

通过反射获取类的私有方法

  • 先获取当前类对象,然后getDeclaredMethod获取其方法对象,最后进行调用
public static void main (String[] args) throws Exception {
    // 获取类实例对象
    final TestModel model = new TestModel();
    // 获取实例的类对象
    final Class<?> aClass1 = model.getClass();
    // 获取类的方法对象
    final Method sendFun = aClass1.getDeclaredMethod("sendFun", String.class);
    // 私有方法需要禁止java语言访问检查
    sendFun.setAccessible(true);
    // 手动调用,可以解耦,也类似于方法增强
    final Object zrh = sendFun.invoke(model, "zrh");
    System.out.println(zrh);
}
---------------
执行结果
OK - zrh
复制代码

最后

  • 虚心学习,共同进步 -_-

猜你喜欢

转载自juejin.im/post/7037424005032181773