Java反射片段输出

9
我刚刚在研究Java反射API时遇到了以下代码片段。
public class Main {
    public static void main(String[] args) throws IllegalAccessException, NoSuchFieldException{
            Field value=Integer.class.getDeclaredField("value");
            value.setAccessible(true);
            value.set(42, 43);

            System.out.printf("six times seven %d%n",6*7);
            System.out.printf("six times seven %d%n",42);
            System.out.println(42);
        }
    }

输出:

six times seven 43
six times seven 43
42

我阅读了set方法的文档,该方法说明它为给定对象设置字段的值。但是,我无法理解代码的输出,因为在所有情况下都应该打印42。

请问有人能够解释一下代码中发生了什么吗?


1
http://www.dzone.com/snippets/reflection-integer-destroyer - zw324
1个回答

4
        System.out.println(42);

调用的是println(int)而不是println(Object)。这样就避免了装箱,使得代码运行更快,并且在1.5版本之前也适用。

在其他情况下,你通过Integer.valueOf(int)进行装箱操作。这个方法被定义为对于-128到127之间(包括)的值始终返回完全相同的Integer对象(对于其他值可能会有不同的行为)。因此,无论在程序中的哪里装箱了42,你都将得到相同的对象,当你设置该对象中的value时,无论通过哪个引用读取,其值都会改变。

如果你想在代码中显式地进行装箱操作,那么代码应该如下所示:

        value.set(Integer.valueOf(42), 43);

        System.out.printf("six times seven %d%n",Integer.valueOf(6*7));
        System.out.printf("six times seven %d%n",Integer.valueOf(42));
        System.out.println(42);

我们知道,Integer.valueOf()方法对于参数为42时会返回同一个对象,因此代码实际上是:

        Integer obj42 = Integer.valueOf(42);

        value.set(Integer.valueOf(obj42, 43);

        System.out.printf("six times seven %d%n", obj42);
        System.out.printf("six times seven %d%n", obj42);
        System.out.println(42);

这有点难以理解。您能否给我一些参考资料,让我找到更多详细信息? - ATR
@ankur.trapasiya 关于装箱拆箱吗?细节在Integer.valueOf的API文档和JLS中。 - Tom Hawtin - tackline
在这里,您可以找到有关装箱和缓存整数的有趣问题。由于printf(String format,Object ... args)要求在format中使用对象作为参数,因此Java将自动将int装箱为Integer,但是由于您更改了表示42的缓存Integer的值为43,因此将打印此值。 println(int)不会出现此问题,因为它不需要装箱。您还可以尝试System.out.printf(“六乘七%d%n”,new Integer(6 * 7));以创建新的值为42的整数,该整数将不被缓存。 - Pshemo
谢谢,我明白这个问题了。将值43设置为42会缓存该值,并且如你所说,对于-128到127之间的值也会发生这种情况。因此,任何自动装箱都将引用相同的对象。 - ATR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接