常量池:
静态常量池:指的是编译时常量池,我们不关注
字符串常量池:在java8之后放在了堆里
运行时常量池:放在了元空间里,元空间放在了主存里(不在堆里了,不占有jvm内存空间)

使用字符串常量池(也就是当我们用类似String a = “xiaoming”;)。每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性我们可以十分肯定常量池中一定不存在两个相同的字符串!

但当我们使用new创建字符串时首先查看池中是否有相同值的字符串,如果有,则拷贝一份到堆中,然后返回堆中的地址;如果池中没有,则在堆中创建一份,然后放池里一份(所以new一般是new两个对象),然后返回堆中的地址。

举个例子来理解String字符串:

String a = “xiaoming”;
 final String m = “xiao”;
 final String n = “ming”;
 String b = “xiao”;
 String c = “ming”;
 String d = “xiao” + “ming”;
 String e= b + c;
 String f = m+n;
 String g = new String(“xiaoming”);
 System.out.println((a == d));
 System.out.println((a == e));
 System.out.println((a == f));
 System.out.println((a == g));输出结果:
 true
 false
 true
 false

首先,==比的都是地址,第一种情况下,由于编译期会优化,优化后的 d = “xiaoming”,由于字符串常量池的原因,d会指向已经创建好的a,所以地址不变返回true。

第二种情况,JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,所以会新建一个变量,a和e的地址就不相同了,返回true。

第三种情况,被final修饰后的变量不可再赋值,所以会返回true。

第四种情况,最常见,直接新建一个变量在堆里,二者地址显然不同,返回false。

String、StringBuffer、StringBuilder的区别

(1)可变与不可变:String是不可变字符串对象,StringBuilder和StringBuffer是可变字符串对象(其内部的字符数组长度可变)。

(2)是否多线程安全:String中的对象是不可变的,也就可以理解为常量,显然线程安全。StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,只是StringBuffer 中的方法大都采用了synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是非线程安全的。

(3)String、StringBuilder、StringBuffer三者的执行效率:
StringBuilder > StringBuffer > String 当然这个是相对的,不一定在所有情况下都是这样。比如String str = “hello”+ "world"的效率就比 StringBuilder st = new StringBuilder().append(“hello”).append(“world”)要高。因此,这三个类是各有利弊,应当根据不同的情况来进行选择使用:
当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;
当字符串相加操作较多的情况下,建议使用StringBuilder,如果采用了多线程,则使用StringBuffer。