先总结下,String类具有以下特性:
下文将详细说明这些特性。
本文基于JDK17说明。
String的不可变性指的是一旦创建了String对象,它的值就不能被修改。
这意味着在任何对String对象进行操作时,都会返回一个新的String对象,而原始对象的值保持不变。
这种特性有助于保护数据的一致性,并且在多线程环境下也更加安全。
下面是一个示例来说明String的不可变性:
public class ImmutableStringExample {
public static void main(String[] args) {
String original = "Hello";
String modified = original.concat(", World!");
System.out.println("Original string: " + original);
System.out.println("Modified string: " + modified);
}
}
输出结果为:
Original string: Hello
Modified string: Hello, World!
在这个例子中,虽然使用了 concat
方法对原始字符串进行了修改,但是原始字符串 original
的值并没有改变。相反,concat
方法返回了一个新的字符串对象,其中包含了修改后的值。
其中concat
函数的主要代码如下:
@ForceInline
static String simpleConcat(Object first, Object second) {
String s1 = stringOf(first);
String s2 = stringOf(second);
if (s1.isEmpty()) {
// 直接返回s2参数
return new String(s2);
}
if (s2.isEmpty()) {
// 直接返回s1参数
return new String(s1);
}
// start "mixing" in length and coder or arguments, order is not
// important
long indexCoder = mix(initialCoder(), s1);
indexCoder = mix(indexCoder, s2);
byte[] buf = newArray(indexCoder);
// prepend each argument in reverse order, since we prepending
// from the end of the byte array
indexCoder = prepend(indexCoder, buf, s2);
indexCoder = prepend(indexCoder, buf, s1);
// 返回新建的String对象
return newString(buf, indexCoder);
}
String的不可变性对于设计具有很多优点。
String的不可变性使得它在Java中成为一种简单、安全且高效的数据结构。
String 的不可变性是通过类的设计、内部实现和方法设计来保证的,这种不可变性使得 String 对象在多线程环境下更加安全,并且可以被方便地共享和重用。
String 的不可变性是通过以下几种方式来保证的:
byte[]
来存储字符串的值,而且这个字节数组是被声明为 final
的,即不可修改。一旦一个 String 对象被创建,它的值就会被存储在这个字节数组中,而且这个值是无法被修改的。也没对外提供get、set方法。concat()
、子串提取 substring()
、大小写转换 toUpperCase()
和 toLowerCase()
等方法,都会返回一个新的 String 对象,而不会修改原始字符串。如下是String对象的部分源码,可以看到value和对象都被final修饰。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
@Stable
private final byte[] value;
// ...
}
在Java中,String对象的传递是通过值传递(pass by value)进行的。
这意味着在将String对象传递给方法或赋值给另一个变量时,传递的是对象的副本而不是对象本身。
当你将一个String对象传递给方法时,实际上传递的是对象的引用的副本,而不是对象本身。这意味着方法内部的操作不会影响原始的String对象,因为它们操作的是副本。
下面是一个示例来说明String的值传递:
public class StringValuePassingExample {
public static void main(String[] args) {
String original = "Hello";
modifyString(original);
System.out.println("Original string after method call: " + original);
}
public static void modifyString(String str) {
str = str + ", World!";
System.out.println("Modified string inside method: " + str);
}
}
输出结果为:
Modified string inside method: Hello, World!
Original string after method call: Hello
在这个例子中,虽然在 modifyString
方法内部对 str
进行了修改,但原始的 original
字符串并没有受到影响。这是因为在方法调用时,传递的是 original
字符串的副本,而不是原始对象本身。
因此,在方法内部对 str
的任何修改都不会影响原始的 original
字符串。
StringTable是一种特殊的内存区域,用于存储字符串常量。
当创建字符串时,如果该字符串已经存在于StringTable中,则直接返回对该字符串的引用,而不会创建新的字符串对象;如果该字符串不在StringTable中,则会创建一个新的字符串对象,并将其添加到StringTable中。
如果字符串是动态创建的,比如通过new、concat、substring、toUpperCase动态创建的会放到堆内存中。
StringTable、字符串、堆的示意图如下所示:
StringTable的设计有几个主要原因:
import java.util.HashMap;
public class StringTableDemo {
public static void main(String[] args) {
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1 == str2);//false
String str3 = new String("abc");
System.out.println(str3 == str2);//false
String str4 = "a" + "b";
System.out.println(str4 == "ab");//true
String s1 = "a";
String s2 = "b";
String str6 = s1 + s2;
System.out.println(str6 == "ab");//false
String str7 = "abc".substring(0, 2);
System.out.println(str7 == "ab");//false
String str8 = "abc".toUpperCase();
System.out.println(str8 == "ABC");//false
String s5 = "a";
String s6 = "abc";
String s7 = s5 + "bc";
System.out.println(s6 == s7.intern());//true
}
}
通过以上的例子可以总结出以下规律:
""
引号创建的字符串都是常量,编译期就已经确定存储到StringPool中。intern()
方法可以向String Pool中动态添加对象。来自全栈程序员nine的探索与实践,持续迭代中。
欢迎关注和点赞~
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。