java字面量 这个Java代码片断如何工作?(字符串池和反射)




java字面量 (6)

这个问题在这里已经有了答案:

与反射结合的Java字符串池可以在Java中产生一些难以想象的结果:

import java.lang.reflect.Field;

class MessingWithString {
    public static void main (String[] args) {
        String str = "Mario";
        toLuigi(str);
        System.out.println(str + " " + "Mario");
    }

    public static void toLuigi(String original) {
        try {
            Field stringValue = String.class.getDeclaredField("value");
            stringValue.setAccessible(true);
            stringValue.set(original, "Luigi".toCharArray());
        } catch (Exception ex) {
            // Ignore exceptions
        }
    }
}

以上代码将打印:

"Luigi Luigi" 

马里奥怎么了?


马里奥怎么了?

基本上你改变了它。 是的,通过反射,你可以违反字符串的不变性......并且由于字符串实习,这意味着任何对“马里奥”的使用(除了在编译时已经解决的更大的字符串常量表达式之外)将会结束在其余的节目中作为“Luigi”。

这种事情是为什么反射需要安全权限...

请注意,由于+的左关联性,表达式str + " " + "Mario"不执行任何编译时级联。 这是有效的(str + " ") + "Mario" ,这就是为什么你仍然看到Luigi Luigi 。 如果您将代码更改为:

System.out.println(str + (" " + "Mario"));

...然后你会看到Luigi Mario因为编译器会将" Mario"实现为与"Mario"不同的字符串。


您只是将字符串常量池 Mario更改为由多个String引用的Luigi ,因此每个参考文字 Mario现在都是Luigi

Field stringValue = String.class.getDeclaredField("value");

您已从类String提取char[]命名value字段

stringValue.setAccessible(true);

使其可访问。

stringValue.set(original, "Luigi".toCharArray());

您将original String字段更改为Luigi 。 但原来是MarioString 文字和文字属于String池,都是被禁止的 。 这意味着所有具有相同内容的文字都指向相同的内存地址。

String a = "Mario";//Created in String pool
String b = "Mario";//Refers to the same Mario of String pool
a == b//TRUE
//You changed 'a' to Luigi and 'b' don't know that
//'a' has been internally changed and 
//'b' still refers to the same address.

基本上,你已经改变了所有引用字段中反映String池的Mario。 如果你创建String Object (即new String("Mario") )而不是文字你不会面对这种行为,因为你会有两个不同的Mario s。


它被设置为路易吉。 Java中的字符串是不可改变的; 因此,编译器可以将所有提到的"Mario"解释为对同一个String常量池项(大致为“内存位置”)的引用。 你用反射来改变那个项目; 所以你的代码中的所有"Mario"现在好像你写了"Luigi"


其他答案充分解释了正在发生的事情。 我只是想补充一点,只有在没有安装安全管理器的情况下才能使用。 当从命令行默认运行代码时,没有,并且你可以做这样的事情。 但是,在可信代码与不可信代码混合的环境中(例如生产环境中的应用程序服务器或浏览器中的小应用程序沙箱),通常会有安全管理器存在,并且您不会被允许使用这些恶意软件,所以这看起来不像一个可怕的安全漏洞。


为了更多地解释现有的答案,让我们来看看你生成的字节码(这里只有main()方法)。

现在,对该位置的内容进行的任何更改都会影响到这两个引用 (以及您也提供的任何其他内容)。


字符串文字存储在字符串池中,并使用它们的规范值。 两个"Mario"文字不仅仅是具有相同值的字符串,它们是同一个对象 。 操纵其中的一个(使用反射)将修改它们的“两个”,因为它们只是对同一对象的两个引用。





string-pool