1. 字符串有六种基本的创建(出生)方式
使用 char[] 数组配合 new 来创建 String s = new String(new char[]{'a', 'b', 'c'}); 使用 byte[] 数组配合 new 来创建 String s = new String(new byte[]{97, 98, 99}); 使用 int[] 数组配合 new 来创建 String s = new String(new int[]{0x1F602}, 0, 1); 使用 已有字符串配合 new 来创建 String s1 = new String(new char[]{'a', 'b', 'c'}); String s2 = new String(s1); //源码 public String(String original) { this.value = original.value; this.hash = original.hash; }使用字面量创建(不使用 new ) String s = "abc"; 说明:其中“abc”编译后会被加载到方法区中的运行时常量池中,执行到了代码区中的idc #2后会利用常量池中的"abc"在堆内存中创建一个字符串对象,然后栈帧中的局部变量s会引用的对象
合二为一,使用 + 运算符来拼接创建 String s = "a" + "b";
前面我们讲解了 String 的六种创建方式,除了字面量方式创建的字符串是家养的以外,其它方法创建的字符串都是野生的。什么意思呢?
字面量方式创建的字符串,会放入 StringTable 中,StringTable 管理的字符串,才具有不重复的特性,这种就像是家养的而 char[],byte[],int[],String,以及 + 方式本质上都是使用 new 来创建,它们都是在堆中创建新的字符串对象,不会考虑字符串重不重复,这种就像是野生的,野生字符串的缺点就是如果存在大量值相同的字符串,对内存占用非常严重说明:也就是说我们可以把通过字面量方式创建的字符串看作是家养的,可以在StringTable中统一管理,而其他方式创建的字符串是野生的,即不受StringTable的管理。
如何保证家养的字符串对象不重复呢? JDK 使用了 StringTable 来解决,StringTable 是采用 c++ 代码编写的,数据结构上就是一个 hash 表,字符串对象就充当 hash 表中的 key,key 的不重复性,是 hash 表的基本特性 下面的代码s1和s2引用的都是StringTable中的同一个对象,他们都是家养的
String s1 = "abc"; //家养 String s2 = "abc"; //家养public native String intern(); 它会尝试将调用者放入 StringTable
如果 StringTable 中已有例子:
String x = new String(new char[]{'a', 'b', 'c'}); // 野生的 String y = "abc"; // 将 "abc" 加入 StringTable String z = x.intern(); // 已有,返回 StringTable 中 "abc",即 y System.out.println(y == z); //true System.out.println(x == z); //false说明:String z = x.intern()尝试将野生的x转化为家养的,即存到StringTable中,但是StringTable中已经有亲儿子y了,所以会转化失败,x还是野生的,返回StringTable中已经存在的y,即 y == z,但是 x != z
如果 StringTable 中没有(1.7 以上 JDK 的做法) 例子: String x = new String(new char[]{'a', 'b', 'c'}); // 野生的 String z = x.intern(); // 野生的 x 加入 StringTable,StringTable 中有了 "abc" String y = "abc"; // 已有,不会产生新的对象,用的是 StringTable 中 "abc" System.out.println(x == z); //true System.out.println(y == z); //true说明:JDK1.7以上的做法是String z = x.intern();野生的x尝试转化为家养的,发现StringTable中没有 "abc"对象,所以会顺利变为家养的,同时返回StringTable中的 “abc” 对象 x,即 x 和 z 引用了同一对象,x == z。 接下来String y = "abc";会到StringTable中看有没有"abc"对象了,发现有了,所以y引用的仍然是StringTable中的"abc"对象,所以y == z。
如果 StringTable 中没有(1.6 JDK 的做法) String x = new String(new char[]{'a', 'b', 'c'}); // 野生的 String z = x.intern(); // 野生的 x 被复制后加入 StringTable,StringTable 中有了 "abc" String y = "abc"; // 已有,不会产生新的对象,用的是 StringTable 中 "abc" System.out.println(x == z); //false System.out.println(y == z); //true说明:JDK1.6的做法是String z = x.intern()发现StringTable中没有"abc"对象,把野生的x拷贝一份,将拷贝的那份加入到StringTable中,x本身还是野生的,返回StringTable中的"abc"对象,所以 x != z。 String y = "abc";StringTable中已经有"abc"对象了,y引用该对象,所以 y == z。