字符串连接运算符()
+符号可以表示 Java 中的三个不同运算符:
- 如果+之前没有操作数,则它是一元的 Plus 运算符。
- 如果有两个操作数,它们都是数字。那么它是二元加法运算符。
- 如果有两个操作数,并且其中至少有一个是 String,那么它就是二元连接运算符。
在简单的情况下,Concatenation 运算符连接两个字符串以提供第三个字符串。例如:
String s1 = "a String";
String s2 = "This is " + s1;    // s2 contains "This is a String"
当两个操作数中的一个不是字符串时,它将转换为 String,如下所示:
- 
一个操作数,其类型是基本类型被转换为如果通过调用 toString()在装箱值。
- 
通过调用操作数的 toString()方法转换其类型为引用类型的操作数。如果操作数是null,或者如果toString()方法返回null,则使用字符串文字null。
例如:
int one = 1;
String s3 = "One is "  + one;         // s3 contains "One is 1"
String s4 = null + " is null";        // s4 contains "null is null"
String s5 = "{1} is " + new int[]{1}; // s5 contains something like
                                      // "{} is [I@xxxxxxxx"
s5 示例的解释是数组类型的 toString() 方法继承自 java.lang.Object,行为是生成一个由类型名称和对象的标识哈希码组成的字符串。
指定 Concatenation 运算符以创建新的 String 对象,除非表达式是常量表达式。在后一种情况下,表达式在编译类型时计算,其运行时值等同于字符串文字。这意味着在拆分长字符串文字时没有运行时开销,如下所示:
String typing = "The quick brown fox " +
                "jumped over the " +
                "lazy dog";           // constant expression
优化和效率
如上所述,除常量表达式外,每个字符串连接表达式都会创建一个新的 String 对象。考虑以下代码:
public String stars(int count) {
    String res = "";
    for (int i = 0; i < count; i++) {
        res = res + "*";
    }
    return res;
}
在上面的方法中,循环的每次迭代将创建一个比前一次迭代长一个字符的新 String。每个连接都复制操作数字符串中的所有字符以形成新的 String。因此,stars(N) 将:
- 创造 N新的String对象,扔掉除了最后一个之外的所有对象,
- 复制 N * (N + 1) / 2个字符,和
- 生成 O(N^2)字节的垃圾。
这对于大型的 tihuan 来说非常昂贵 23。实际上,任何在循环中连接字符串的代码都容易出现这个问题。写这个的更好方法如下:
public String stars(int count) {
    // Create a string builder with capacity 'count' 
    StringBuilder sb = new StringBuilder(count);
    for (int i = 0; i < count; i++) {
        sb.append("*");
    }
    return sb.toString();
}
理想情况下,你应该设置 StringBuilder 的容量,但如果这不可行,则类将自动增大构建器用于保存字符的后备数组。 (注意:实现会以指数方式扩展后备数组。这种策略可以将字符数量复制到 O(N) 而不是 O(N^2)。)
有些人将此模式应用于所有字符串连接。但是,这是不必要的,因为 JLS 允许 Java 编译器优化单个表达式中的字符串连接。例如:
String s1 = ...;
String s2 = ...;    
String test = "Hello " + s1 + ". Welcome to " + s2 + "\n";
将典型的字节码编译器是这样进行优化;
StringBuilder tmp = new StringBuilder();
tmp.append("Hello ")
tmp.append(s1 == null ? "null" + s1);
tmp.append("Welcome to ");
tmp.append(s2 == null ? "null" + s2);
tmp.append("\n");
String test = tmp.toString();
(JIT 编译器可以进一步优化,如果它可以推断出 s1 或 s2 不能是这样的话。)但是请注意,这种优化只允许在一个表达式中使用。
简而言之,如果你担心字符串连接的效率:
- 如果你在循环(或类似)中重复连接,请进行手动优化。
- 不要手动优化单个连接表达式。