String为什么是不可变的?
String为什么是不可变的?
本文将深入探讨Java中String类为什么是不可变的,通过代码案例、String类的内部数据结构以及相关方法的使用来详细说明这一特性。
一、代码案例以演示String不可变的特性
1、final关键字
final可以用于修饰类、成员变量、成员方法
- 当final修饰类时,表示这个类是一个“最终类”,也就是说,这个类不能被继承
下图中的Animal类就是一个被final修饰的类,所以在Dog类继承(extends)Animal类时,发生了报错。
- final修饰成员变量时,该成员变量不能被改变或指向其他对象。
如果该成员变量是基本数据类型,那么数值被指定后将不能被改变,否则引发报错
如果该成员变量是引用类型,那么对该成员变量初始化后,不能改变指向,即不能指向其他对象
- final修饰方法时,该方法不能被重写
Animal类是Dog类的父类,在Dog类中重写了feed方法,发生了报错,原因就是Animal类中的此方法用了final进行修饰
由此,我们来看看String类的源码:
String类在定义时,用了final关键字修饰,这就是一个最终类,使得String类不能被继承,避免了类的核心逻辑被修改
在定义并创建String类型的字符串时,在计算机中是将其转换成了char数组进行存放的,而源码中用于保存数据的数组value在声明时用final进行修饰,这就导致了String的指向不可变。
2、代码案例
案例一:通过赋值操作演示
public class Demo {
public static void main(String[] args) {
String oldWord = "Hello";
System.out.println("原字符串: " + oldWord);
// 尝试修改字符串,实际上是创建了一个新的 String 对象
String newWord = oldWord + " World";
System.out.println("原字符串内容: " + oldWord);
System.out.println("修改后的字符串: " + newWord);
System.out.println("原字符串是否改变: " + (oldWord == newWord));
}
}
通过比较
original
和
modified
的引用是否相等(
original == modified
),可以发现它们是不同的对象。最后输出
original
的内容,发现其值并没有改变
案例二: 通过方法调用演示
public class Demo {
public static void main(String[] args) {
String str = "abc";
System.out.println("调用方法前的字符串: " + str);
// 调用 replace 方法,该方法会返回一个新的 String 对象
String newStr = str.replace('a', 'x');
System.out.println("调用方法后返回的新字符串: " + newStr);
System.out.println("原始字符串是否改变: " + (str == newStr));
System.out.println("原始字符串内容: " + str);
}
}
比较
str
和
newStr
的引用,发现它们不相等,说明是不同的对象。最后输出
str
的内容,其值保持不变。
二、String类的内部数据结构及特点
1、String的内部数据结构
String的构造方法有:(其中三种)
其中,value是String类中存放数据的char数组,由此得出,String的内部数据结构是char数组,将String中的每一个字符依次存放到char数组中。
2、String类数据结构的特点
- 不可变性
- 底层实现基于字符数组
- 比较操作只能使用equal关键字
- 字符串池机制
三、String的某些方法,对于内容的修改会产生新的字符串对象
1、substring()
字符串截取,产生一个新的字符串,需要创建新对象去接收生成的字符串
public class Demo {
public static void main(String[] args) {
String a = "文学是人类精神世界的璀璨星辰,照亮我们漫漫人生旅途。";
String sub1 = a.substring(6);
String sub2 = a.substring(0, 6);
System.out.println(a);
System.out.println(sub1);
System.out.println(sub2);
}
}
当传入一个参数时,则从指定下标(包括此下标)开始截取到整个字符串的末尾;当传入两个参数时,则从指定下标begin(包括)截取到指定下标end(不包括),常记:包前不包后。
2、toLowerCase()和toUpperCase()
大小写转换,将原字符串全转为大写/小写。均产生新的字符串
public class Demo {
public static void main(String[] args) {
String a = "abCdEFGhijK";
String x = a.toLowerCase();
String y = a.toUpperCase();
System.out.println("原字符串:"+a);
System.out.println("转小写后:"+x);
System.out.println("转大写后:"+y);
}
}
toLowerCase():将原字符串全转为小写;toUpperCase():将原字符串全转为大写。
3、replace()和replaceAll()
替换,将原字符串中指定内容替换成指定内容。均产生新的字符串
public class Demo {
public static void main(String[] args) {
//replace
String a = "#北京#上海#西安#成都#昆明";
String replace = a.replace("#", "");
System.out.println("原字符串:"+a);
System.out.println("替换后:"+replace);
//replaceAll
String b = "1咸阳2宝鸡3渭南4榆林5延安";
System.out.println("原字符串:"+b);
String replaceAll = b.replaceAll("\\d", "");
System.out.println("替换后:"+replaceAll);
}
}
replace():将原字符串中指定内容替换成指定内容;replaceAll()将将原字符串中指定内容(用正则表达式)替换成指定内容。
4、trim()
去除首尾空格,产生一个新的字符串
public class Demo {
public static void main(String[] args) {
//trim
String x = " 在字里行间汲取力量 ";
String y = x.trim();
System.out.println("未处理的字符串:"+x);
System.out.println("未处理的字符串长度:"+x.length());
System.out.println("处理后的字符串:"+y);
System.out.println("处理后的字符串:"+y.length());
}
}
以上都是个人见解与总结,如果错误,敬请指正;如有疑问,欢迎交流。