本文共 3723 字,大约阅读时间需要 12 分钟。
在Java编程中,String类的行为和特性是开发者需要深入理解的核心概念之一。本文将从String的不可变性、StringBuilder的可变性以及字符串操作的实现机制等方面,探讨这些概念的细节及其在实际开发中的应用。
String类在Java中是一个final类,这意味着它不能被继承。这一点非常重要,因为它决定了String对象的行为特性——不可变。
通过查看String类的源码,可以发现String对象内部存储了一个char[]数组,用来保存字符串的每个字符。由于这个数组是私有的,并且String类被final修饰,任何修改操作都无法改变原有的String对象。
许多常见的String方法(如substring、replace等)在实现时,都会返回一个新的String对象,而不会修改原有的String对象。例如:
substring方法会根据给定的起始和结束索引创建一个新数组,并返回一个新的String对象。replace方法则会创建一个新的字符数组,替换掉指定的字符。通过以下代码可以验证String对象的不可变性:
public void stringTest3() { String a = "nihaoshije"; String b = a.substring(0, 4); System.out.println(b); // niha System.out.println(a); // nihaoshije} 输出结果表明,原字符串a并未被修改,而是返回了一个新的String对象b。
StringBuilder类是一个可变字符串容器,它允许对字符串进行高效的修改操作。与String类不同,StringBuilder的字符是可以被修改的。
StringBuilder类通过append方法来添加字符或String对象。该方法返回的是当前StringBuilder对象本身,表明String对象被修改。例如:
public StringBuilder append(String str) { super.append(str); return this;} 实际上,append方法会调用父类的append方法,处理字符串追加操作。
StringBuilder内部使用一个char[]数组存储字符。通过反编译可以看到,append方法会先检查数组的容量是否足够,如果不够则扩容。然后调用String类的getChars方法,将字符复制到数组中。
以下代码可以直观地观察StringBuilder的可变性:
public void stringBuilderTest() { StringBuilder stringBuilder = new StringBuilder("hello"); System.out.println(stringBuilder); // 输出: hello stringBuilder.append("world"); System.out.println(stringBuilder); // 输出: helloworld} 可以看到,StringBuilder对象在调用append方法后,其内容确实发生了变化。
在Java中,字符串相加操作(如"hello" + "world")看似简单,却隐藏着深层的实现细节。
String类的+运算符是Java中唯一的重载运算符之一。当两个String对象相加时,Java会调用StringBuilder的append方法来高效地构建结果字符串。
通过反编译可以发现,"hello" + "world"在编译期间会被优化为一个新的String对象。这个优化过程意味着,结果字符串会直接引用池中的常量。
String str = "hello" + "world"。这种方式在编译阶段优化,效率较高。String b = "hello"; String c = "world"; String d = b + c。这种方式在运行时通过StringBuilder实现,效率相对较低。public void stringTest4() { String a1 = "helloworld"; String a = "hello" + "world"; String b = "hello"; String c = "world"; String d = b + c; System.out.println(a); // helloworld System.out.println(d); // helloworld System.out.println(a == d); // false System.out.println(a1 == a); // true System.out.println(a1 == d); // false} 输出结果表明,直接相加的字符串和引用相加的字符串在内存中是不同的对象。
String a = "hello";String b = "hello";String a1 = new String("hello");String a2 = new String("hello");StringBuilder c = new StringBuilder("hello");StringBuilder d = new StringBuilder("hello");System.out.println(a == b); // trueSystem.out.println(a1 == a2); // trueSystem.out.println(c == d); // falseSystem.out.println(a1.equals(a2)); // trueSystem.out.println(a.equals(c)); // falseSystem.out.println(c.equals(d)); // false 答案: true, false, false, true, false, false
String a = "hellonihao";String b = "hello" + "nihao";System.out.println(a == b); // true
答案: true
原因:在编译阶段,"hello" + "nihao" 会被优化为 "hellonihao",因此 a 和 b 指向同一个常量池中的字符串。String a = "nihaoshijie";String b = "nihao";String c = "shijie";String d = b + c;System.out.println(a == d); // false
答案: false
原因:字符串相加不会在编译阶段优化,因此 d 是一个新的字符串对象。String a = "hello";String b = new String("hello");String c = a.intern();System.out.println(a == b); // falseSystem.out.println(c == a); // true 答案: false, true
原因:intern方法会检查常量池中是否存在等于此字符串的实例。如果存在,则返回该实例;否则,将字符串添加到池中并返回新实例。转载地址:http://fuuy.baihongyu.com/