今天看到了一个Java string的相关问题,解决问题的过程当中就想把string 好好理顺了,总结在这里。数组
== 是判断两个变量是否指向同一个对象,equals()只判断两个字符串内容是否相同安全
public class Cons { public static void main(String[] args) throws InterruptedException { String s2 = new String("vv"); String s3 = "vv"; System.out.println(s2 == s3);//false System.out.println(s3.equals(s2));//true } }
String和StringBuilder:StringBuilder是可变的,也就是说用StringBuilder建立的字符串你能够随时改变它。
StringBuilder和StringBuffer:StringBuffer是同步的,它是线程安全(thread-safe)的,但效率要比StringBuilder差得多。函数
查看String的构造函数jdk源码:工具
public String(StringBuffer buffer) { synchronized(buffer) { this.value = Arrays.copyOf(buffer.getValue(), buffer.length()); } } public String(StringBuilder builder) { this.value = Arrays.copyOf(builder.getValue(), builder.length()); }
可见 为了使buffer的线程安全性在构造String时获得延续,加了同步块。ui
答案:1或2
new String("vv")在堆中建立了1个实例对象,而另1个就是放在常量池中的 "vv" 对象,固然这里的str自己 只是一个引用,放在栈里,用来指向堆中建立出来的对象。因此若是常量池已经有"vv" 对象,就只在堆中建立一个对象;若是尚未,就会放入常量池,而后再在堆中建立一个对象,怎么验证呢?this
public class Cons { public static void main(String[] args) throws InterruptedException { String s1 = new String("vv"); String s2 = "vv"; System.out.println(s1 == s2);//false } }
而后用命令行工具( 深刻理解Java虚拟机 一书中看的工具)spa
可见常量池中有一个String 类型的 对象 vv,并且new出来的对象不是指向常量池的那个对象,亦即新建立了一个命令行
注:jdk1.7 之后,虚拟机把存储Java对象的地方定义为堆,其它地方是不会有Java对象的实体的。故常量池再也不存储对象实例,而是存储的引用,实际对象仍是在堆中,因此有所不一样,下文再也不赘述。线程
答案:0或1
若是常量池已经有"vv" 对象,就直接返回引用,若是尚未,就会放入常量池,而后返回引用。code
public class Cons { public static void main(String[] args) throws InterruptedException { String s1 = "vv"; String s2 = "vv"; System.out.println(s1 == s2);//true } }
可见s1,s2指向同一个对象。
并且常量池也有 vv
答案:0或1
常量字符串是在编译的时候就被肯定的,"v"是常量,因此编译时肯定
这个代码编译后 与 String str = "vv"; 是同样的
public class Cons { public static void main(String[] args) throws InterruptedException { String s1 = "vv"; String s2 = "v"+"v"; System.out.println(s1 == s2);//true } }
可见 s1,s2 指向同一个对象
答案 视状况而定
“+”链接的两个字符串自己就是字面常量字符串时,若是池中存在这样链接后的字符串,则是不会从新建立对象,而是直接引用池中的字符串对象;若是“+”链接的两字符串中只要有一个是变量,是会产生新的字符串对象。
public class Cons { public static void main(String[] args) throws InterruptedException { String s1 = "ww"; String s2 = "vv"; String s3 = "vvww"; String s4 = "vv"+"ww"; String s5 = "vv"+s1; System.out.println(s3 == s4);//true System.out.println(s3 == s5);//false } }
可是若是变量是常量时,就不一样了
public class Cons { public static void main(String[] args) throws InterruptedException { final String s1 = "ww"; String s2 = "vv"; String s3 = "vvww"; String s4 = "vv"+"ww"; String s5 = "vv"+s1; String s6 = s2+s1; System.out.println(s3 == s4);//true System.out.println(s3 == s5);//true } }
但若是先定义final字符串,但未在定义处初始化,那么又不一样了,
public class Cons { public static void main(String[] args) throws InterruptedException { final String s1 ; String s2 = "vv"; String s3 = "vvww"; String s4 = "vv"+"ww"; s1 = "ww"; String s5 = "vv"+s1; String s6 = s2+s1; System.out.println(s3 == s4);//true System.out.println(s3 == s5);//false } }
由于s1是在运行过程肯定的,因此s5也只能运行时肯定;
总结起来, String str=s1+s2 建立几个变量,关键取决于 s1,s2 可否在编译期肯定
public class Cons { public static void main(String[] args) throws InterruptedException { String s1 = new String("vv"); String s2 = "v".concat("v"); String s4 = "v"+"v"; String s3 = "vv"; System.out.println(s2 == s3);//false System.out.println(s1 == s3);//false System.out.println(s4 == s3);//true } }
可见concat 产生的变量没有直接引用常量池的对象。
查看jdk8源码
public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true); }
果真是新建立了一个 String对象。
查看jdk源码,知道 String的内部实现就是一个 Char 数组, 说String 不可变,也是由于 这个数组就是一个final 类型的 变量。
参考
http://jiangzhengjun.iteye.co...
《深刻理解Java虚拟机》
欢迎访问个人我的主页 mageek(mageek.cn)