个人主页:Ice_Sugar_7
所属专栏:快来卷Java啦
欢迎点赞收藏加关注哦!
String类在设计时就规定字符串中的内容不可改变,这点我们可以从源码中看出来:
要修改字符串,我们就得取到字符数组中的元素,而它用private修饰,并且String类也没提供get方法,所以根本拿不到,也就没法修改咯
注意:
不可修改和被final修饰无关,因为被final修饰的数组仍然可以修改其中的元素,只不过是不能修改value本身的值,即不能引用其它字符数组
由此我们可以作一个总结:
final修饰类表明该类不能被继承
final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的
public static void main(String[] args) {
final int a = 0;
a = 10; //报错,此时a为常量,不可修改
final String[] array = {"hello","world"};
array[1] = "China";
for (String str:array) {
System.out.println(str);
}
}
因为字符数组value的内容不能被修改,所以:所有涉及到可能修改字符串内容的操作,都是先创建一个新对象
,改变的是新对象
所以要修改字符串的话不要采用
下面的写法,因为会产生临时对象,导致效率低下
public static void main(String[] args) {
String str = "hello";
str += "abc";
System.out.println(str); // 输出:hello world
}
为什么会产生临时对象呢?我们可以看下汇编代码:
里面有几行都出现了StringBuilder,而且调用它的构造方法和append方法,说明创建了一个StringBuilder类的对象
上面的汇编代码你可能看不懂,它等价于下面的代码:
public static void main(String[] args) {
String str = "hello";
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(str);
stringBuilder.append("abc");
str = stringBuilder.toString();
System.out.println(str);
}
这个过程就是:首先创建StringBuilder类的对象,然后向这个对象添加str和"abc",再调用toString得到新字符串(helloabc),赋给str,让str指向helloabc
(StringBuilder对象调用toString()时,它会返回一个表示StringBuilder对象当前内容的字符串)
因为这个过程实例化了对象,所以会有时间和内存上的消耗,每和一个字符串相加就会创建一个对象,加的次数越多,消耗就越大
所以尽量不要直接修改String,如果要修改建议使用StringBuffer
或者StringBuilder
为了方便字符串的修改,Java提供了StringBuilder和StringBuffer类,这两个类大部分功能是相同的
StringBuilder的append方法返回的this引用,也就是说不管调用多少次append,它都是在原字符串的基础上进行修改,不用new额外的对象
所以,如果遇到需要频繁修改字符串的情况,那就考虑用StringBuilder或StringBuffer吧
其他涉及到字符串修改的方法也是返回this指针,比如deleteCharAt
接下来说说StringBuilder和StringBuffer的区别
以append方法为例
我们发现,相比于StringBuilder,StringBuffer的append方法多了一个关键字synchronized
只能有一个线程
访问该方法或代码块的内容,其他线程必须等待当前线程执行完毕后才能访问。这样可以避免多个线程同时访问共享资源时出现的数据竞争和并发访问的问题StringBuffer采用同步处理,属于线程安全操作;而StringBuilder未采用同步处理,属于线程不安全操作。
因此在单线程环境下,StringBuilder的性能会比StringBuffer更好;
但是在多线程环境下,如果有多个线程同时访问一个StringBuilder对象,可能会出现竞态条件
,导致数据错误。
String、StringBuffer、StringBuilder的区别: