我们都知道,在Java中,String是不可变的。但是,通过使用反射,可以获取Field并设置访问级别来更改它。(我知道这是不建议的,我没有计划这样做,这个问题是纯理论性质的)。
我的问题:假设我知道自己在做什么(并根据需要修改所有字段),程序会正确运行吗?或者JVM进行了一些依赖于String不可变的优化,那么我会遭受性能损失吗?如果确实会有性能损失,它所做的假设是什么?程序中会出现什么问题?
注:String只是一个例子,我实际上对一个通用答案感兴趣,除了这个例子以外。
谢谢!注:String只是一个例子,我实际上对一个通用答案感兴趣,除了这个例子以外。
谢谢!public static void main(String args[]) throws Exception {
String s1 = "Hello"; // I want to edit it
String s2 = "Hello"; // It may be anywhere and must not be edited
Field f = String.class.getDeclaredField("value");
f.setAccessible(true);
f.set(s1, "Doesn't say hello".toCharArray());
System.out.println(s2);
}
输出:
Doesn't say hello
function("Bob")
这样的函数;在这种情况下,字符串"Bob"
将在整个应用程序中被更改(这也适用于声明为final
的字符串常量)。
- 你修改了一个用于子字符串操作或作为子字符串操作结果的字符串。在Java中,对字符串进行子字符串操作实际上使用与源字符串相同的基础字符数组,这意味着对源字符串的修改将影响到子字符串(反之亦然)。
- 你修改了一个恰好被用作地图键的字符串。它将不再等于其原始值,因此查找将失败。
我知道这个问题是关于Java的,但我之前写了一篇博客文章,展示了如果在.NET中改变一个字符串会导致程序表现得多么疯狂。这些情况非常相似。
String
实例,将会打破运行时和语言级别的假设,并导致未定义的行为。如果您正在寻找除字符串驻留之外的其他JVM特定示例,我无法立即想到任何示例。但我没有编写JVM。 - Dan Taointern()
的内容都指向同一个字符串对象。如果您开始操纵一个驻留的字符串字面量的内容,您可能会看到所有使用相同基础对象的其他字面量上发生完全相同的改变。除了字符串外,我不知道有什么性能增强措施可以使对象成为不可变的(事实上,我认为目前JVM甚至无法确定一个对象是否不可变,除了反射攻击之外)。但这可能会影响checker-framework等试图静态分析代码以确保其不可变的工具。
char []
数组的内容。 - Dan TaohashCode()
的结果将会不同。任何尝试从基于哈希的集合中恢复字符串的操作可能会失败。 - rossumString
实例会缓存自己的哈希码,因此,例如,使用相同的字符串对象调用containsKey
将实际成功;奇怪的是,使用完全相同但分离的实例调用将失败。 - Dan TaoSystem.out.print("Initial: "); System.out.println(addr);
editIntStr("ADDR_PLACEH", "192.168.1.1");
System.out.print("From var: "); System.out.println(addr);//
System.out.print("Hardcoded: "); System.out.println("ADDR_PLACEH");
System.out.print("Substring: "); System.out.println("ADDR_PLACE" + "H".substring(0));
System.out.print("Equals test: "); System.out.println("ADDR_PLACEH".equals("192.168.1.1"));
System.out.print("Equals test with substring: "); System.out.println(("ADDR_PLACE" + "H".substring(0)).equals("192.168.1.1"));
输出:
Initial: ADDR_PLACEH
From var: 192.168.1.1
Hardcoded: 192.168.1.1
Substring: ADDR_PLACEH
Equals test: true
Equals test with substring: false
public static void main(String args[]){
String a = "test213";
String s = new String("test213");
try {
System.out.println(s);
System.out.println(a);
char[] value = (char[])getFieldValue(s, "value");
value[1] = 'a';
System.out.println(s);
System.out.println(a);
} catch (Exception e) {
e.printStackTrace();
}
}
static Object getFieldValue(String s,String fieldName) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
Object chars = null;
Field innerCharArray = String.class.getDeclaredField(fieldName);
innerCharArray.setAccessible(true);
chars = innerCharArray.get(s);
return chars;
}
根据所有答案所述,改变S的值将会改变a的字面量。