为什么下面的课程输出不正常?

时间:2016-07-04 09:10:06

标签: java

以下课程的输出让我感到震惊,但我不明白它会如何发生。

public class SampleTest {

    public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {

        String someString = "IMMUTABLE";
        Field field = Class.forName("java.lang.String").getDeclaredField("value");
        field.setAccessible(true);
        char[] value = (char []) field.get(someString);
        String anotherString = "NOTREALLY";
        for (int i=0; i<value.length; i++){
            char c = anotherString.toCharArray()[i];
            value[i]=c;
        }

        System.out.println(someString); // prints NOTREALLY
        System.out.println("IMMUTABLE"); // Why it prints NOTREALLY here..!!!

    }

}

1 个答案:

答案 0 :(得分:0)

TL; DR:不要这样做。 该代码是(ab)使用反射来违反规范。根据JLS,String values cannot change。该代码通过访问其未记录的私有内部结构来更改字符串的值。不好的主意。

发生了什么:

类中的等价字符串文字全部组合在一起,因此在运行时,它们都引用内存中相同的字符串对象;他们被写入班级的“恒定池”。因此,该类中出现的所有"IMMUTABLE"实际上都引用了String的同一个实例。这意味着实际上是最后一行

System.out.println("IMMUTABLE");

......相当于

System.out.println(someString);

...因为一旦加载了类,文字和变量就会引用同一个对象。

由于代码(ab)使用反射来覆盖字符串对象的私有未记录值,因此您自然会在使用该对象的所有位置看到更新状态。

如果您将System.out.println("IMMUTABLE");行移到单独的类中并确保在修改代码 之前已加载该类,那么也会发生这种情况someString。这是因为当加载一个类时,其常量池中的字符串是interned,因此不同类中的等效字符串常量最终会引用相同的String对象。这不是你想要依赖的东西,尤其是因为字符串可以(这些天)从实习池中被淘汰出来,但在一个简单的例子中你可能会观察它。

例如,假设您有这个单独的类:

public class Separate {
    public static void show() {
        System.out.println("IMMUTABLE");
    }
}

您修改了代码以使用它,但只有在修改someString之后

public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException {
    String someString = "IMMUTABLE";
    // ...

    System.out.println(someString); // prints NOTREALLY
    Separate.show();
}

你会得到

NOTREALLY
IMMUTABLE

...因为在加载Separate时,您已经修改了实习池中字符串的版本,因此当我们加载Separate时,它不匹配并最终引用不同的String对象。

但是,如果您确保在之前加载了 ,则修改它:

Separate

你得到了一点乐趣:

IMMUTABLE
NOTREALLY
NOTREALLY

...因为加载public static void main(String[] args) throws SecurityException, NoSuchFieldException, ClassNotFoundException, IllegalArgumentException, IllegalAccessException { Separate.show(); // <================ Note String someString = "IMMUTABLE"; // ... System.out.println(someString); // prints NOTREALLY Separate.show(); } 时,其Separate等同于实习池中的"IMMUTABLE",因此Separate使用String实例 - 然后,main中的代码会修改。