Java中字符串连接概述

在Java中,字符串连接是一项常见的操作。本教程将介绍几种字符串连接的方法,重点讲解如何使用concat()方法和"+"运算符。最后,我们将讨论根据需求选择合适的方法。

2. 字符串连接方法

通常,Java中有多种方式来连接两个或多个字符串。让我们通过一些示例来看看每种方法的描述。

2.1 使用"+"运算符

在Java中,最常见的字符串连接方法之一是使用"+"运算符。

"+"运算符提供了比其他方法更灵活的字符串连接功能。首先,它不会对null值抛出异常。其次,它会将null转换为其字符串表示形式。此外,我们还可以用它来连接超过两个字符串。

下面是一个代码示例:

@Test
void whenUsingPlusOperatorANull_thenAssertEquals() {
    String stringOne = "Hello ";
    String stringTwo = null;
    assertEquals("Hello null", stringOne + stringTwo);
}

在内部,编译器会将"+"运算符转换为StringBuilder(或StringBuffer)类及其append()方法。

由于"+"运算符会无声地将参数转换为String(通过对象的toString()方法),**这样可以避免NullPointerException**。然而,我们需要确保最终的字符串结果中包含"null"时对我们来说是适用的。

2.2 使用concat()方法

String类中的concat()方法会在当前字符串末尾添加指定的字符串,并返回新的组合字符串。由于String类是不可变的,原始字符串不会被改变。

让我们测试一下这个行为:

@Test
void whenUsingConcat_thenAssertEquals() {
    String stringOne = "Hello";
    String stringTwo = " World";
    assertEquals("Hello World", stringOne.concat(stringTwo));
}

在上例中,stringOne变量是基础字符串。使用concat()方法,stringTwo会被附加到stringOne的末尾。由于concat()操作是不可变的,所以我们需要显式地赋值。下个例子说明了这一点:

@Test
void whenUsingConcatWithOutAssignment_thenAssertNotEquals() {
    String stringOne = "Hello";
    String stringTwo = " World";
    stringOne.concat(stringTwo);
    assertNotEquals("Hello World", stringOne); // we get only Hello
}

为了获取最终连接的字符串,我们需要将concat()的结果赋给一个变量:

stringOne = stringOne.concat(stringTwo);
assertEquals("Hello World", stringOne);

concat()方法的另一个优点是可以连接多个String对象,而且还可以添加空格和特殊字符:

@Test
void whenUsingConcatToMultipleStringConcatenation_thenAssertEquals() {
    String stringOne = "Hello";
    String stringTwo = "World";
    String stringThree = ", in Jav";
    stringOne = stringOne.concat(" ").concat(stringTwo).concat(stringThree).concat("@");
    assertEquals("Hello World, in Jav@", stringOne);
}

至于null值,当前字符串和要附加的字符串都不能为null。否则,concat()方法会**抛出NullPointerException**:

@Test
void whenUsingConcatAppendANull_thenAssertEquals() {
    String stringOne = "Hello";
    String stringTwo = null;
    assertThrows(NullPointerException.class, () -> stringOne.concat(stringTwo));
}

2.3 StringBuilder

首先,我们有StringBuilder类。它提供了append()方法进行字符串连接操作。以下是如何使用的例子:

@Test
void whenUsingStringBuilder_thenAssertEquals() {
    StringBuilder builderOne = new StringBuilder("Hello");
    StringBuilder builderTwo = new StringBuilder(" World");
    StringBuilder builder = builderOne.append(builderTwo);
    assertEquals("Hello World", builder.toString());
}

另一方面,StringBuffer类提供了类似的连接功能,但与非线程安全的StringBuilder不同,StringBuffer是线程安全的。然而,它的性能略逊于StringBuilder。它也像StringBuilder一样具有append()方法。

2.4 Stringformat()方法

另一种进行字符串连接的方式是使用String类的format()方法。通过使用格式化占位符(如%s),我们可以根据它们的字符串值或对象来连接多个字符串:

@Test
void whenUsingStringFormat_thenAssertEquals() {
    String stringOne = "Hello";
    String stringTwo = " World";
    assertEquals("Hello World", String.format("%s%s", stringOne, stringTwo));
}

2.5 Java 8及更高版本的连接方法

从Java 8开始,String类的join()方法可用于字符串连接。在这种情况下,方法的第一个参数是用于分隔要连接的字符串的分隔符:

@Test
void whenUsingStringJoin_thenAssertEquals() {
    String stringOne = "Hello";
    String stringTwo = " World";
    assertEquals("Hello World", String.join("", stringOne, stringTwo));
}

Java 8新增了StringJoiner类,它使用分隔符、前缀和后缀来连接字符串。以下是其使用的示例代码:

@Test
void whenUsingStringJoiner_thenAssertEquals() {
    StringJoiner joiner = new StringJoiner(", ");
    joiner.add("Hello");
    joiner.add("World");
    assertEquals("Hello, World", joiner.toString());
}

随着Java 8中流API(Stream API)的引入,我们有了Collectors类,其中的joining()方法。这个方法的工作原理类似于String类的join()方法,适用于集合。以下示例展示了其工作方式:

@Test
void whenUsingCollectors_thenAssertEquals() {
    List<String> words = Arrays.asList("Hello", "World");
    String collect = words.stream().collect(Collectors.joining(", "));
    assertEquals("Hello, World", collect);
}

3. 选择方法

最后,如果我们要在concat()方法和"+"运算符之间做出选择,我们需要考虑一些方面。

首先,concat()方法只接受字符串。而"+"运算符可以接受任何类型并将其转换为字符串。另一方面,concat()方法在遇到null值时会抛出NullPointerException,而"+"运算符则不会。

此外,它们之间存在性能差异concat()方法的性能优于"+"运算符。后者总是创建一个新的字符串,不论字符串长度如何。另外,我们需要考虑的是,当要附加的字符串长度大于0时,concat()方法才会创建新字符串;否则,它将返回相同的对象。

4. 总结

在这篇文章中,我们快速概述了Java中的字符串连接。详细介绍了如何使用concat()方法和"+"运算符进行字符串连接,并进行了两者之间的比较分析,以确定在不同上下文中的选择。

本文中所有使用的代码片段可在GitHub上找到。