使用反射修改字符串的目的是什么?

Jro*_*rom 3 java string reflection immutability

我正在阅读一篇文章,说Java字符串不是完全不可变的.但是,在文章修改字符串的示例代码中,它调用string.toUpperCase().toCharArray(),它返回一个新字符串.那么,如果你调用toUpperCase(),那么通过更改字符串的过程是什么目的呢?这是代码:

public static void toUpperCase(String orig)
{
 try
 {
    Field stringValue = String.class.getDeclaredField("value");
    stringValue.setAccessible(true);
    stringValue.set(orig, orig.toUpperCase().toCharArray());
 }
 catch (Exception ex){}
}
Run Code Online (Sandbox Code Playgroud)

另外,我注意到string.toUpperCase()本身不起作用.它需要是string.toUpperCase().toCharArray().是否有一个原因?

cor*_*iKa 12

他在做什么:

他正在获取一些他知道正确长度的字符数组(例如String的大写版本)并将其作为String的后备数组.(value在String类中调用支持数组.)

为什么他这样做:

为了说明您可以将任何char数组放在那里.

为什么这很有用:

字符串是不可变的,这允许您规避不变性.当然,不建议这样做 - 永远.相反,如果他说"请注意,因为人们可能会对您的代码执行此操作,我不会感到惊讶.即使您认为您的东西是安全的,也可能不是!"

这种影响是广泛的.不可变变量不再是不可变的.最终变量不再是最终的.线程安全对象不再是线程安全的.您认为可以依赖的合同,您不能再这样做了.所有这些都是因为某个工程师在某个地方遇到了问题,他无法用正常手段修复,所以他深入研究解决问题.不要'那个家伙'.

您还将注意到现在如何更改该String的hashCode.所以,如果你从未计算过该String的hashCode,它仍然为0,所以你没事.另一方面,如果你已经计算了它,当你把它放在HashMap或HashSet中时,它将不会被检索.

考虑以下:

import java.util.*;
import java.lang.reflect.*;

class HashTest {        

    /** Results:
     C:\Documents and Settings\glowcoder\My Documents>java HashTest
        Orig hash: -804322678
        New value: STACKOVERFLOW
        Contains orig: true
        Contains copy: false
     */

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

        Set<String> set = new HashSet<String>();
        String str = "StackOverflow";
        System.out.println("Orig hash: " + str.hashCode());
        set.add(str);

        Field stringValue = String.class.getDeclaredField("value");
        stringValue.setAccessible(true);
        stringValue.set(str, str.toUpperCase().toCharArray()); // 

        System.out.println("New value: " + str);

        String copy = new String(str); // force a copy
        System.out.println("Contains orig: " + set.contains(str));
        System.out.println("Contains copy: " + set.contains(copy));
    }

}
Run Code Online (Sandbox Code Playgroud)

我敢打赌他这样做是为了警告不良行为,而不是表现出"酷"的伎俩.

编辑:我找到了你所指的文章,以及它所基于的文章.最初的文章指出:"这意味着如果另一个包中的类"摆弄"一个实习字符串,它会对你的程序造成严重破坏.这是好事吗?(你不需要回答;-)"我我认为这很清楚这是一个保护指南而不是如何编码的建议.

因此,如果你只用一条信息离开这个线程,那就是反射是危险的,不可靠的,而且不容小觑!

  • 但请注意,这不是安全问题,因为任何有权使用反射API的代码都已拥有城堡的密钥.几乎相当于需要管理员帐户的Windows下的"漏洞利用".虽然它应该提醒人们在允许外部代码运行时设置正确的安全策略(例如插件)! (2认同)

Boh*_*ian 6

不要在家尝试这个!

你正在颠覆String的不变性.有没有很好的理由这样做.


Edw*_*rzo 5

我想我不能补充已经提供的解释,所以也许我可以通过建议如何防止这种情况来增加讨论。

您可以通过使用安全管理器来防止有人以这些和其他意外方式篡改您的代码。

public static void main(String args[]){

    System.setSecurityManager(new SecurityManager());
    String jedi1 = "jedi";

    toUpperCase(jedi1);
    System.out.println(jedi1);
} 
Run Code Online (Sandbox Code Playgroud)

这将在 toUpperCase 方法中生成异常,前提是您没有将所有权限授予默认策略文件中的所有代码库。(在您当前的代码中,您的异常目前已被吞下)。