在平时的代码开发过程中必不可少的需要使用字符串,那必然是要用到String类型,一般来说创建String对象常用就这几种方式:
String s1 = "china"; String s2 = new String("hello"); String s3 = "ch" + "i" + "na"; String s4 = "love " + "china"; String s5 = "love china"; String s6 = "love " + s1;
它们之间有什么区别,你知道吗?如果不清楚,那现在我们一条一条的来解读。
String s1 = "china";
这条代码在编译成 .class 文件时,将会把 china 字符串直接编译进常量表中,这个可以通过 javap 来反编译 .class 文件来查看到。
这样当加载 .class 文件时,就已经生成了该String对象。以后要是再直接使用 china 的String 对象时,就会直接从常量表中拿到对应 china 字符串的 String 对象。
如果定义了 String t1 = “china”; 再进行 t1 == s1 比较时,返回的结果为 true ,表示为相同的内存地址引用。
String s2 = new String("hello");
这条语句,一样是从常量表中拿到 hello的String对象,再做为String构造方法的参数,再创建一个新的String对象。
看到没,虽然这里是用 hello 字符来创建一个新的 String 对象,但你要知道实际上在常量表中已经存在为 hello 字符的String对象。
说到这里顺便来看一下String这个类。
public final class String { private final char value[]; public String(String original) { this.value = original.value; this.hash = original.hash; } public String(char value[]) { this.value = Arrays.copyOf(value, value.length); } public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; } }
内部有一个只能初始化一次的 char 类型的 value[] ,这也就很明显示表示String对象的内容是初始化后不可变的。
这时定义一个 String t2 = “hello”; 再进行 t2 == s2 内存地址比较时,自然结果为 false 。
使用 t2.equals(s2) 比较时结果为 true,那是因为使用 equals 比较时,实际是对内部的 char 字符集合逐一比较,通过观察 String.equals 源码,便很清楚的了解比较过程。
String s4 = "love " + "china"; String s5 = "love china";
要了解这两行代码,需要比较着来看。String s4 = “love ” + “china”; 这行代码实际会被编译器优化为 String s4 = “love china”; 如果进行地址比较 s4 == s5 返回的结果必然是 true 的。
同样通过反编译就可以很楚清的知道,倒底发生了什么事情。这两个变量均引用了 #16 的那个String实例。
String s1 = "china"; String s5 = "love china"; String s6 = "love " + s1;
那么 s6 呢?如果拿 s6 == s5 进行比较,那结果会是 true 还是 false ?要是不清楚,那么继续来看一下反编译后的代码吧。
要是看不太懂的话,那就看下面还原成 java 源码的代码段吧。
String s1 = "china"; String s5 = "love china"; String s6 = (new StringBuilder("love ")).append(s1).toString();
可以很清楚的看到编译器对于String字符集的添加其实会转换为 StringBuilder 进行连接。那它也自然会产生新的String对象。
那既然java编译器会使用 StringBuilder 进行字符连接,那是不是就不需要手工创建StringBuilder 而直接使用 + 来完成字符串的连接呢?
要想知道这个答案我们还需要看一段代码来了解java字符连接的规则。
String s1 = "love "; for (int i = 0; i < 10; i++) { s1 = s1 + "china,"; } //---- 以下是 java 编译后的代码----- String s1 = "love "; for(int i = 0; i < 10; i++) s1 = (new StringBuilder(String.valueOf(s1))).append("china,").toString();
这下就该清楚了,java 会把每一行都创建一个StringBuilder实例,这样当有N多行字符相加时,对于性能损耗还是很大的。所以对于大量字符相加还是需要手工创建StringBuilder 进行添加,以获取最优的性能。