Java String字符串:创建、拼接与比较,常见问题解决

一、什么是String?

在Java中,String是用于表示文本数据的类,它是不可变的(一旦创建,内容就无法修改)。正因为不可变,String在Java中被广泛用于存储和处理文本,比如用户输入、文件名、URL等。

二、String的创建方式

Java中创建String对象主要有两种方式,它们的内存行为不同,初学者容易混淆,需要重点区分。

1. 直接赋值(推荐)
String s1 = "hello";
String s2 = "hello";
  • 原理:Java会先在字符串常量池(一种特殊的内存区域,用于复用相同的字符串常量)中查找是否存在"hello"
  • 如果存在,s1s2直接指向常量池中的同一个"hello"对象。
  • 如果不存在,会在常量池中创建新的"hello",并让s1/s2指向它。
  • 特点:相同常量会被复用,节省内存。例如,s1s2虽然是两个变量,但它们的引用相同(s1 == s2true)。
2. new关键字创建
String s3 = new String("hello");
String s4 = new String("hello");
  • 原理:无论常量池是否有"hello",都会在堆内存中创建一个新的String对象,内容为"hello",且新对象的引用与常量池无关。
  • 特点:即使内容相同,s3s4的引用也不同(s3 == s4false)。因为new会强制新建堆对象,而直接赋值优先复用常量池。
对比直接赋值与new创建
方式 引用是否相同(相同内容) 内存位置 是否复用常量池
直接赋值 true(常量池复用) 常量池
new创建 false(堆中新建) 堆内存 否(但内容相同的常量会先放入常量池)

三、String的拼接操作

拼接字符串是常用操作,Java提供了多种方式,需注意不同方式的性能和结果差异。

1. 使用+号拼接(最直观)
String a = "a";
String b = "b";
String c = a + b; // 结果:"ab"
  • 原理+号在编译时会被编译器优化。
  • 如果拼接的是全常量(如"a" + "b"),会直接在常量池生成"ab"
  • 如果拼接中包含变量(如a + ba/b是变量),会在堆中创建新的String对象。
  • 注意:频繁用+号拼接(尤其是循环中)会创建大量临时对象,效率极低!
2. 使用concat()方法
String d = "a".concat("b"); // 结果:"ab"
  • 原理concat()String类的方法,返回拼接后的新字符串,原字符串不变(因为String不可变)。
  • 示例
  String e = "hello";
  String f = e.concat("world"); // f是"helloworld",e仍为"hello"
3. 使用StringBuilder/StringBuffer(高效拼接)

String不可变,每次拼接都会新建对象;而StringBuilder/StringBuffer可变的字符序列,拼接时直接修改内部数组,效率更高。
- 区别
- StringBuilder:非线程安全,单线程下效率更高(默认推荐)。
- StringBuffer:线程安全(方法加了synchronized),多线程场景使用。

示例

StringBuilder sb = new StringBuilder();
sb.append("a"); // 拼接单个字符
sb.append("b"); // 拼接字符串
String result = sb.toString(); // 转换为String(最终结果)

四、String的比较操作

比较字符串时,==equals()的作用完全不同,初学者常因混淆两者导致逻辑错误。

1. ==:比较对象引用(是否指向同一个对象)
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");

System.out.println(s1 == s2); // true(指向常量池同一个对象)
System.out.println(s1 == s3); // false(s3是堆中新建对象,引用不同)
2. equals():比较字符串内容(忽略引用)
String s1 = "hello";
String s3 = new String("hello");

System.out.println(s1.equals(s3)); // true(内容相同)
  • 注意
  • equals()只能用于非null字符串,否则会抛出NullPointerException(如null.equals("a"))。
  • 避免用==代替equals()比较内容!
3. 正确比较空字符串

判断字符串是否为空时,推荐用isEmpty()length() == 0,而非== ""== null

String s = "";
System.out.println(s.isEmpty()); // true(空字符串,但非null)
System.out.println(s.length() == 0); // true
System.out.println(s == ""); // true(内容相同,但不推荐)

五、常见问题与解决方法

1. 混淆==equals()

错误场景

String a = new String("hello");
String b = new String("hello");
if (a == b) { // 错误!==比较引用,a和b是不同对象
    System.out.println("a和b相同");
} else {
    System.out.println("a和b内容相同但不是同一个对象");
}

解决:用equals()比较内容:

if (a.equals(b)) { // 正确!比较内容
    System.out.println("a和b内容相同");
}
2. 循环中频繁用+号拼接字符串

错误场景

String result = "";
for (int i = 0; i < 1000; i++) {
    result = result + i; // 每次循环新建String对象,效率极低!
}

解决:用StringBuilder替代+号:

StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i); // 直接修改内部数组,无额外对象创建
}
String result = sb.toString(); // 最终转换为String
3. 空字符串判断用== ""

错误场景

String s = null;
if (s == "") { // 错误!null不能用== ""判断
    System.out.println("空字符串");
}

解决:先判空,再比较内容:

if (s != null && s.isEmpty()) { // 推荐写法
    System.out.println("空字符串");
}

六、总结

  1. 不可变性String一旦创建无法修改,任何拼接/修改操作都会生成新对象。
  2. 创建方式:直接赋值优先复用常量池,new关键字强制新建堆对象。
  3. 拼接选择:少量拼接用+concat(),大量拼接(如循环)用StringBuilder
  4. 比较规则==比较引用,equals()比较内容,避免混淆;空字符串用isEmpty()length() == 0,并先判null

掌握以上内容,就能避免大部分String相关的常见错误,写出高效清晰的Java代码。

小夜