关于Java 反射中设置实例域值的相关探讨

在反射中,想要为一个实例域设置值,需要调用相关的set方法
但在The Java Tutorials中的教程我们知道反射时可能会因为不能自动拆箱装箱导致IllegalArgumentException。

public class A {

    public static void main(String[] args) {
        try {
            B b = new B();
            Field f = B.class.getDeclaredField("i");
            f.setAccessible(true);
            f.setInt(b,30);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }

    }
}

class B {
    private Integer i;
}
// 输出
Exception in thread "main" java.lang.IllegalArgumentException

在这里插入图片描述
发现异常是在

	at sun.reflect.UnsafeObjectFieldAccessorImpl.setInt(UnsafeObjectFieldAccessorImpl.java:114)

我们跟踪以一下f.set()方法的运行过程
在这里插入图片描述
接着进入方法getFieldAccessor()
在这里插入图片描述
接着进入方法acquireFieldAccessor()
在这里插入图片描述
然后回到set()方法
在这里插入图片描述

再进入setInt()方法
在这里插入图片描述
发现在这个文件中,只有set()方法有可能不抛出异常,因为这是UnsafObjectField,Interger实例域实际上是一个Object对象,所以f.setInt()方法中getFieldAccessor()返回了一个UnsafeObjectFieldAccessorImpl对象。Java没有为自定义类和预定义类定义相应的类型,所有引用类型(除了基本类型的类型)都会返回UnsafeObjectField,但是每个基本类型都有对应的UnsafeLong,UnsafeBoolea, UnsafeInteger…

在这里插入图片描述
所以当想要设置的域不是基本类型时,一律用set()方法,即使该实例域的类型时基本类型的包装类。
下面将想要修稿的实例域的类型改为基本类型,再次跟踪一遍。
先调用setInt()方法
在这里插入图片描述
发现返回的就是UnsafeIntegerField
再次进入setInt()方法
在这里插入图片描述
发现再UnsafeIntegerFieldAccessorImpl中setINt就不再抛出异常SetIllegalArgumentException。

进入putInt()方法,是个native方法,没有找到源码,搜索后,在Open JDK 中发现

 public native  void putInt(Object o,
    long offset,
    int x)

Stores a value into a given Java variable.
The first two parameters are interpreted exactly as with
#getInt(Object, long) to refer to a specific
Java variable (field or array element). The given value
is stored into that variable.
The variable must be of the same type as the method
parameter x.

下面在对基本类型域(以int为例)调用set()方法,看看会出现什么结果。
还是返回的UnsafeIntegerField

进入set()方法:
在这里插入图片描述
发现UnsafeIntegerFieldAccesorImpl类中的set()方法除了抛出异常,同样可以设置值。
进入unsafe.putInt()方法后,发现跳转到了Integer类中的intValue()方法
在这里插入图片描述
说明java编译器多运行了一个方法进行了类似于拆箱的操作。
所以,对基本类型的域也可以调用set()方法,但是比直接调用setInt()的性能更差。

综上所述:

  • 当想要设置的域不是基本类型时,一律用set()方法,即使该实例域的类型时基本类型的包装类。
  • 对基本类型的域既可以调用setInt()方法也可以调用set()方法,但是调用set()方法的性能更差。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值