一、前言
在上一节,我们认识了一下String 类和基本用法,在这一节,我们要更加深入的认识和学习一下String 类的知识和用法。
二、String 类
1.关于String 的不可变性
String是一种不可变对象. 字符串中的内容是不可改变的。字符串不可被修改,这是因为字符串在设计之初就已经规定了内容只要创建好就无法被修改。原因是在代码中我们可以看到,字符串存放在value 数组中,同时被private修饰,因此字符串是不可被修改的。同时我们还可以看到,String类被final 修饰,说明该类是不可被继承的。
因此,所有涉及到修改字符串内容的操作都是要创建一个新对象,改变的也是新对象。有一个误区,有人认为字符串不可变是因为存放字符串的 value 数组被 final 修饰,所以没法改变,这是错误的。字符串无法改变不是因为 String 类或者其内部数组被 final 修饰,final 修饰类表明该类无法被继承,final 修饰引用类型表明无法引用其他对象,但可以修改其引用内容。
那么问题就来了,如何修改字符串的内容呢?
2.字符串的修改
首先要避免直接对 String 类型的对象进行修改,因为String 类本身不能修改,每次修改都要创建一个新的对象,效率非常低下。
public static void main(String[] args) {
String s = "hello";
s += " world";
System.out.println(s); // 输出:hello world
}
这种方法虽然确实修改了内容,但他在运行时创建了很多临时变量,效率非常低下,因此非常不建议使用。我们通过他的字节码文件,进入底层代码查看他的汇编,发现这个代码除了创建了“hello”和“abc”的对象,还创建了很多其他的对象,比如 StringBuilder 和 StringBuffer。
我们根据他的汇编语言重新写一下这个代码,如下:
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);
}
最终运行结果和上面的代码是一样的。那这两段代码的区别在哪里呢,我们再通过一段代码看出来:
public static void main(String[] args) {
long start = System.currentTimeMillis();
String s = "";
for(int i = 0; i < 10000; ++i){
s += i;
}
long end = System.currentTimeMillis();
System.out.println("String:"+(end - start));
start = System.currentTimeMillis();
StringBuffer sbf = new StringBuffer("");
for(int i = 0; i < 10000; ++i){
sbf.append(i);
}
end = System.currentTimeMillis();
System.out.println("StringBuilder:"+(end - start));
start = System.currentTimeMillis();
StringBuilder sbd = new StringBuilder();
for(int i = 0; i < 10000; ++i){
sbd.append(i);
}
end = System.currentTimeMillis();
System.out.println("StringBuffer:"+(end - start));
}
最终运行结果:
上述代码是观察String,StringBuilder和StringBuffer 的运行速度,单位是毫秒,我们能够直观看到String 的运行速度是最慢的,StringBuilder和StringBuffer 的运行速度基本可以画等号,是相当快的。所以我们在修改 String字符串时,尽量避免对他的直接修改,选择使用StringBuilder和StringBuffer 的修改方式。而他们之所以速度快原因就在 append 。
当我们按住 Ctrl ,进入StringBuilder 的底层代码时,我们会发现:
StringBuilder 返回的是 this , 因此当我们不断 append 时,他返回的只是一个对象;而 String 的循环则会不断创建对象销毁对象。这就是 StringBuffer 和 StringBuilder 的优点。那么 StringBuffer 和StringBuilder 的区别是什么呢?
3.StringBuffer 和 StringBuilder
同样我们再进入 StringBuffer 的底层代码,会发现和StringBuilder 有点区别:
我们再重新看 StringBuilder 的代码:
我们会发现,他们俩的最大区别就是 StringBuffer 多了一个关键字 synchronized , 这个关键字的作用是保证线程安全。我们可以将它理解为一把锁,把一个一个的线程理解为需要上厕所的人,当一个线程执行时,会自动上锁,等到这个线程执行完毕时,下一个线程才可以执行。所以 StringBuffer 相比来说更加安全。但当执行多线程时,不断地上锁释放锁会出现资源损耗和浪费的情况,同时运行效率也很低下。
String 和 StringBuilder 不能直接转换,想要转换需要遵循以下原则:
1.String变为StringBuilder:利用 StringBuilder 的构造方法或 append() 方法
2.StringBuilder 变为 String:利用toString() 方法
总结
String , StringBuilder 和 StringBuffer的区别
1.String 的内容不可修改,StringBuilder 和 StringBuffer 的内容可以修改。
2.StringBuilder 和StringBuffer 功能大致相同
3.StringBuffer 采用同步处理,属于线程安全操作;而 StringBuilder 未采用同步处理,属于线程不安全操作