java.lang.String真是不可变的吗?在java11中,反射能修改值
反射修改string,导致string内容改变。
示例在java11版本下测试:
package com.example.demo;
import java.lang.reflect.Field;
import java.util.Arrays;
/**
* @author 认知科技技术团队
* 微信公众号:认知科技技术团队
*/
public class Demo {
public static void main(String[] args) throws ReflectiveOperationException {
String a = "崔认知";
String b = "崔认知";
System.out.println("反射更改String前");
System.out.println(a);
System.out.println(b);
System.out.println(a.hashCode());
System.out.println(b.hashCode());
/////////////////////////////////////////
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
byte[] byteValue = (byte[]) value.get(a);
Arrays.fill(byteValue, (byte)0);
/////////////////////////////////////////
System.out.println("反射更改String后");
System.out.println(a);
System.out.println(b);
System.out.println(a.hashCode());
System.out.println(b.hashCode());
}
}
反射修改的关键是:获取内部的
private final byte[] value;
byte数组,对此数组中值修改。
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
byte[] byteValue = (byte[]) value.get(a);
Arrays.fill(byteValue, (byte)0);
结果:
字符串内容被修改,修改a,但是b也被修改了,这和jvm中的String Pool有关系,可以参考
Caching the String literals and reusing them saves a lot of heap space because different String variables refer to the same object in the String pool. String intern pool serves exactly this purpose. https://www.baeldung.com/java-string-immutable#1-introduce-tostring-pool
但是hashCode 值没变。
hashCode 值没变,是因为字符串内部对此值做了缓存:
/** Cache the hash code for the string */
private int hash; // Default to 0
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
hash = h = isLatin1() ? StringLatin1.hashCode(value)
: StringUTF16.hashCode(value);
}
return h;
}
虽然我们修改成功了,但是java也给我们打印了警告??日志,
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.example.demo.Demo (file:/Users/cuirenzhi/gitlab/demo1/target/classes/) to field java.lang.String.value
WARNING: Please consider reporting this to the maintainers of com.example.demo.Demo
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
我们这种反射修改字符串内部的数据是不合法的,反射底层会有校验。
java.lang.String真是不可变的吗?在java17中,反射就不能修改值
java17下运行的结果:
??java17中反射就不能修改值,java17中,不再已警告日志输出,而是直接异常输出控制台,再次抛出异常,我们的代码不能运行了。
反射修改值做了很多限制:Module及Module导出权限、修改的值的权限(
PUBLIC、PRIVATE等权限)做了很多校验。
小结
java.lang.String,反射修改内部的
private final byte[] value
值,在java11和java17版本中有不同的行为结果: