JVM理论:(三/8)关于String、StringBuffer、StringBuilder、final

  以下是从网上整理出的资料,不同作者对某些具体描述有略微差别,有的具体描述可能会存在不准确的情况,但结论是一致的。

一、String

  equals比较值,==比较引用,我们主要关注引用的比较。

1、两种方式创建String的过程

String s1="abc";
String s2="abc";
String s3 = new String("abc");
System.out.println(s1==s2);
System.out.println(s1==s3);

输出结果:
true
false

  对于 String s1="abc"; 这种方式,如果常量池中已经存在字符串“abc”,会直接引用这个字符串,否则会创建一个“abc”字符串,所以对于s1、s2来说,引用指向的地址是一样的,返回true。相当于s1,s2 —>常量池中的字符串。(不确定的是,是不是栈中的引用直接指向常量池中的地址)

  对于 String s3 = new String("abc"); 这种方式,无论如何都会在堆中new一个String("abc")对象,当然如果常量池中没有"abc"字符串,也会先创建。相当于s3 —>堆中的对象 —>常量池中的字符串。

  s3指向的是堆中的对象,明显s1与s3指向的地址不同,所以返回false。  String s1="abc"; 在编译阶段就被确定, String s3 = new String("abc"); 在运行时才能确定。

2、String不可变

  java.lang.String类使用了final修饰,不能被继承。Java程序中的所有字面值,即双引号括起的字符串,如"abc",都是作为String类的实例实现的。String是常量,其对象一旦构造就不能再被改变。换句话说,String对象是不可变的,每一个看起来会修改String值的方法,实际上都是创造了一个全新的String对象,以包含修改后的字符串内容,而最初的String对象则丝毫未动。

String s = "abc";
s = "123";
System.out.println("s=" + s);

输出结果:
s=123

  这里看似变量s被修改了,实际上将s重新指向一个字符串“123”,而不是直接将原来的"abc"修改为"123"。

3、String的相加运算

String s1 = "abc";
String s2 = "ab" + "c";
System.out.println(s1==s2);

final String  s3 = "ab";
String s4 = s3 + "c";
System.out.println(s1==s4);

String s5 = "ab";
String s6 = s5 + "c";
System.out.println(s1==s6);

String s7 = new String("ab") + "c";
System.out.println(s1==s7);

输出结果:
true
true
false
false

  第1,2个结果是true是因为,编译器对那些在编译期就可以确定其值的常量的运算会进行优化,因为s2+号两边是字面量,所以s2在编译期就会被优化为 String s2 = "abc";

  对于第3、4个结果,运算中存在变量,虚拟机对+的运算就会被解析为StringBuilder(或 StringBuffer)类及其 append 方法实现的,再通过toString 方法转换为字符串,所以s6相当于 s6=new StringBuffer(s5).append("c").toString(),而s6重新指向一个新的StringBuffer对象。

//方式一
String s = "s";
for (int i = 0; i < 20; i++) {
    s += i;
}
//方式二
StringBuffer sb = new StringBuffer("s");
for (int i = 0; i < 20; i++) {
    sb.append(i);
}

  结合我们平时的应用,如果用String+的方式,每循环一次,就会重新new一个StringBuffer对象,无疑造成了很多不必要的开销,而方式二会更高效一点。

二、String,StringBuilder,StringBuffer三者的区别

  运行速度快慢为:StringBuilder > StringBuffer > String  

String与StringBuilder、StringBuffer的区别:

  String为字符串常量,即String对象一旦创建之后该对象是不可更改的,前文中对于存在变量的String+操作,实质是new StringBuffer和.toString()操作。而StringBuilder和StringBuffer都可以直接对原对象追加、插入和删除。

StringBuilder与StringBuffer的区别:

  StringBuilder是线程不安全的。而StringBuffer是线程安全的 ,StringBuffer中很多方法可以带有synchronized关键字。

 综上,

  String:适用于少量的字符串操作的情况

  StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况

  StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况

三、final关键字

1)被final修饰的类不可以被继承
2)被final修饰的方法不可以被重写,JVM会尝试为之寻求内联。
3)被final修饰的变量不可以被改变,在编译阶段会存入调用类的常量池中。
  
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
被final修饰的变量不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的。
 
final标记的成员变量必须在声明的同时赋值,如果在声明的时候没有赋值,那么只有一次赋值的机会,而且只能在构造方法中显式赋值,然后才能使用
final标记的局部变量可以只声明不赋值,也只能进行一次赋值


 

 

参考链接:  

https://www.cnblogs.com/baihehanqiu/p/6938850.html

http://www.cnblogs.com/avivahe/p/5747127.html

https://segmentfault.com/a/1190000009888357

https://blog.csdn.net/wang854837173/article/details/52439123

https://www.cnblogs.com/qiaoyanlin/p/6877628.html

https://www.cnblogs.com/tianyaxue/p/3145079.html

https://blog.csdn.net/sugar_rainbow/article/details/68150249

http://www.cnblogs.com/roy-mustango/p/4240302.html

https://www.cnblogs.com/hithlb/p/4872373.html

 

String类型相加

https://blog.csdn.net/changjinglubeipan/article/details/78614969

https://www.zhihu.com/question/35014775

https://baijiahao.baidu.com/s?id=1576306750248354576&wfr=spider&for=pc

 

关于string的面试题

https://blog.csdn.net/uotail/article/details/71244606

https://blog.csdn.net/nwpu_geeker/article/details/78701343

 

StringBuffer

https://www.cnblogs.com/su-feng/p/6659064.html

 

final

https://www.jianshu.com/p/17becea7942d

https://www.cnblogs.com/dolphin0520/p/3736238.html

https://www.cnblogs.com/xh0102/p/5729381.html

posted @ 2018-07-29 14:30  湮天霸神666  阅读(209)  评论(0编辑  收藏  举报