String顶多作为一种数据类型,但是是最常用的一个类。
String是底层数据结构是用final修饰的字符数组,具体的源码如下: value被声明了final不可变,所以String是线程安全的。
private final char value[];
JVM对String做了大量的优化,例如用字符常量池:例如创建相同的字符串时,会先从常量池中获取,或者满足大量的字符操作时就以字符对象的身份去处理。同时在存储上:如果是字符串常量形式的声明首先会查看常量池中是否存在这个常量,如果存在就不会创建新的对象,否则在在常量池中创建该字符串并创建引用,此后不论以此种方式创建多少个相同的字符串都是指向这一个地址的引用,而不再开辟新的地址空间放入相同的数据;但是字符串对象每次new都会在堆区形成一个新的内存区域并填充相应的字符串,不论堆区是否已经存在该字符串!
下面对于常见的字符比较做以说明:
第一种:
String a = new String("helloJava");
String b = "helloJava";
结论:a !=b 一个在堆区,一个在常量池,是不同的两个对象。
第二种:
String b = "helloJava";
String c = "hello" + "Java";
结论:b = c JVM在编译期间会自动把字符串常量相加操作计算完毕后再执行比较,所以计算完成后其实c=“helloJava”,因为字符串存在这样的常量,所以直接引用即可,使用的是同一内存地址的数据。
第三种:
String b = "helloJava";
String d1 = "hello";
String d2 = "Java";
String d = d1 + d2;
String e = d1 + new String("Java");
String f = "hello" + new String("Java");
结论:b!=d != e !=f 字符串变量的连接动作,在编译阶段会被转化成StringBuilder的append操作,变量d最终指向Java堆上新建String对象,变量b指向常量池的”helloJava”字符串,所以 b != d,其实这也就是文章开头讲String不可变的机制,为什么JVM要这样处理?
这是因为d1和d2在编译阶段最终指向的对象是不可知的,所以不能当做常量来看待。但是如果添加final关键字的话,那么就是相等的了,因为final关键字强制性的使d1和d2无法再变更,所以可以当做常量看待!其他不等的情况同理
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,具体源码如下所示:
char[] value;
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder并不是线程安全的。
它们都用公共的方法例如:如expandCapacity、append、insert、indexOf等公共方法。
最后,如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
String:适用于少量的字符串操作的情况
StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
大早上的总结面试题,冲冲冲