为什么 String 类不可变
- final修饰符:
String
类被声明为final
,这意味着它不能被继承。因此,无法创建String
的子类来修改其行为。 - 私有字符数组(char[]):
String
类内部使用私有的字符数组来存储字符串的内容。这个字符数组是final
的,即它的引用不能被修改。一旦字符串被创建,它的内容就不能被更改。 - 不提供可变方法:
String
类没有提供用于修改字符串的方法。例如,没有类似于setCharAt(int index, char ch)
的方法,而是提供了返回新字符串的方法,比如substring()
、concat()
等。
有没有办法直接修改 String 对象的值而不是 重新创建一个字符串对象
其实是有的,Java 提供了反射机制是可以获取到私有的字段并且设置其字段值的。
关于Java反射的介绍
直接修改字符串的值,会重新创建一个字符串对象
public void test01() throws Exception {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] chars = null;
String str = "hhhhh";
chars = (char[]) value.get(str);
// identityHashCode() 基于对象地址返回哈希码
System.out.println("字符串地址:" + System.identityHashCode(str) +
" 值:" + str +
" value地址:" + System.identityHashCode(chars));
str = "hello world";
chars = (char[]) value.get(str);
System.out.println("字符串地址:" + System.identityHashCode(str) +
" 值:" + str +
" value地址:" + System.identityHashCode(chars));
}
// 输出
字符串地址:721748895 值:hhhhh value地址:1642534850
字符串地址:1724731843 值:hello world value地址:1305193908
使用反射直接修改字符串数组的值
public void test02() throws Exception {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] chars = null;
String str = "hello world";
chars = (char[]) value.get(str);
System.out.println("字符串地址:" + System.identityHashCode(str) +
" 值:" + str +
" value地址:" + System.identityHashCode(chars));
for (int i = 0; i < chars.length; i++) {
chars[i] = 'a';
}
chars = (char[]) value.get(str);
System.out.println("字符串地址:" + System.identityHashCode(str) +
" 值:" + str +
" value地址:" + System.identityHashCode(chars));
}
// 输出
字符串地址:721748895 值:hello world value地址:1642534850
字符串地址:721748895 值:aaaaaaaaaaa value地址:1642534850
使用反射修改字段的值
public void test03() throws Exception {
Field value = String.class.getDeclaredField("value");
value.setAccessible(true);
char[] chars = null;
String str = "hello world";
chars = (char[]) value.get(str);
System.out.println("字符串地址:" + System.identityHashCode(str) +
" 值:" + str +
" value地址:" + System.identityHashCode(chars));
char[] tmp = {'1','2','3'};
value.set(str,tmp);
chars = (char[]) value.get(str);
System.out.println("字符串地址:" + System.identityHashCode(str) +
" 值:" + str +
" value地址:" + System.identityHashCode(chars));
}
// 输出
字符串地址:721748895 值:hello world value地址:1642534850
字符串地址:721748895 值:123 value地址:1724731843
当然这种直接修改char[]
数组,绕过了String
类安全机制的操作是不推荐的,因为它可能导致程序在运行时产生不可预测的行为,并且可能破坏其他代码对字符串不可变性的依赖。
使用反射修改违背了 Java 语言中字符串不可变性的设计原则。在实际开发中,最好遵循这个设计原则,以确保代码的可靠性和可维护性。如果需要可变的字符串,建议使用StringBuilder
或StringBuffer
类。