杰瑞科技汇

Java string构造函数有哪些常见用法?

String 类在 Java 中非常特殊,它被 final 修饰,意味着它不能被继承。String 对象是不可变的(immutable),这是 Java 设计的核心原则之一,带来了诸多好处,如线程安全、可以被哈希缓存、常量池优化等。

Java string构造函数有哪些常见用法?-图1
(图片来源网络,侵删)

String 提供了非常多的构造函数,以便能从各种数据源(如字符数组、字节数组、其他字符串等)创建字符串对象。

下面我将按照不同的来源分类,详细解释每个构造函数,并提供代码示例。


直接使用字符串字面量(最常见的方式)

这是创建 String 对象最常用、最简单的方式。

String str1 = "Hello, World!";
String str2 = "Java";

内部机制: 当你使用这种方式时,JVM 会首先在字符串常量池 中查找是否存在相同内容的字符串。

Java string构造函数有哪些常见用法?-图2
(图片来源网络,侵删)
  • 如果存在,则直接返回常量池中该字符串的引用。
  • 如果不存在,则在常量池中创建一个新的字符串对象,并返回其引用。

这种方式的好处是利用了常量池,可以避免重复创建相同的字符串,节省内存。


基于 char 数组构造

你可以从一个字符数组创建一个 String 对象。

public String(char[] value)

使用字符数组的全部内容来构造字符串。

char[] chars = {'J', 'a', 'v', 'a', ' ', 'i', 's', ' ', 'f', 'u', 'n'};
String str = new String(chars);
System.out.println(str); // 输出: Java is fun

public String(char[] value, int offset, int count)

从字符数组的指定 offset 位置开始,取 count 个字符来构造字符串。

char[] chars = {'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd'};
// 从索引 6 开始,取 5 个字符
String str = new String(chars, 6, 5);
System.out.println(str); // 输出: World

基于 byte 数组构造

这在处理网络传输或文件 I/O 时非常有用,因为底层数据流通常是字节数组。byte 数组会被转换为 char 数组,然后再构造 String,转换时会使用平台的默认字符集

public String(byte[] bytes)

使用字节数组的全部内容,并使用 JVM 默认的字符集(如 UTF-8)来解码。

byte[] bytes = {72, 101, 108, 108, 111}; // "Hello" 的 UTF-8 编码
String str = new String(bytes);
System.out.println(str); // 输出: Hello

public String(byte[] bytes, int offset, int length)

从字节数组的指定 offset 位置开始,取 length 个字节来构造字符串。

byte[] bytes = {72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100};
// 从索引 6 开始,取 5 个字节
String str = new String(bytes, 6, 5);
System.out.println(str); // 输出: World

public String(byte[] bytes, String charsetName)

使用指定的字符集(如 "UTF-8", "ISO-8859-1")来解码字节数组。这是更推荐的方式,因为它避免了因不同平台默认字符集不同而导致的问题。

byte[] bytes = {(byte) 0xE4, (byte) 0xBD, (byte) 0xA0, (byte) 0xE5, (byte) 0xA5, (byte) 0xBD}; // "你好" 的 UTF-8 编码
// 使用 UTF-8 字符集解码
String str = new String(bytes, "UTF-8");
System.out.println(str); // 输出: 你好

基于 StringBufferStringBuilder 构造

StringBufferStringBuilder 都是可变的字符串序列,你可以将它们转换成不可变的 String 对象。

public String(StringBuffer buffer)

StringBuffer sb = new StringBuffer("Hello");
sb.append(", Java!");
String str = new String(sb);
System.out.println(str); // 输出: Hello, Java!

public String(StringBuilder builder)

StringBuilder sb = new StringBuilder("Start");
sb.append(" End");
String str = new String(sb);
System.out.println(str); // 输出: Start End

其他重要的构造函数

public String(String original)

复制一个现有的 String 对象,创建一个新的 String 实例,虽然 String 是不可变的,这个构造函数在特定场景下(如反射、泛型等)或为了代码清晰度而使用。

String original = "Original";
String copy = new String(original); // 创建了一个新的 String 对象
System.out.println(copy); // 输出: Original

注意:这里的 new 关键字确实创建了一个新的对象,即使内容相同,它与直接赋值 String copy = original; 不同,后者只是将引用指向同一个对象。

public String(char[] value, int codePointOffset, int count)

这是一个比较特殊的构造函数,它处理的是 Unicode 代码点,而不是 char,在 Java 中,一个 char 是 16 位的,但有些 Unicode 字符(如某些 Emoji)需要两个 char(一个代理对,surrogate pair)来表示,这个构造函数可以正确处理这种情况,避免截断字符。

// 一个需要代理对的 Emoji 字符,由两个 char 组成
char[] emoji = {'\uD83D', '\uDE00'}; // 对应 😀
// 如果用 char[] 构造,offset=1, count=1,会得到一个无效的字符
// String wrong = new String(emoji, 1, 1); // 可能会得到乱码
// 使用 codePointOffset 可以正确处理
int codePoint = Character.toCodePoint(emoji[0], emoji[1]);
// 这个构造函数比较复杂,通常我们直接用 String.valueOf(char[]) 即可
String correct = new String(emoji, 0, 2);
System.out.println(correct); // 输出: 😀

重要概念辨析:new String() vs. 字符串字面量

这是面试中非常经典的问题。

// 情况一:使用字符串字面量
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true,因为 s1 和 s2 指向常量池中同一个对象
// 情况二:使用 new 关键字
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s3 == s4); // false,因为 new 总是在堆上创建新对象,s3 和 s4 是两个不同的对象
// 情况三:混合使用
String s5 = "hello";
String s6 = new String("hello");
System.out.println(s5 == s6); // false,s5 指向常量池,s6 指向堆中的新对象
// 情况四:intern() 方法
String s7 = new String("hello").intern(); // intern() 会返回常量池中已存在的或新创建的字符串引用
String s8 = "hello";
System.out.println(s7 == s8); // true,s7 现在也指向了常量池中的 "hello"
  • 比较的是对象的内存地址(引用)
  • equals() 比较的是。
  • 使用 new String() 总是在堆上创建一个新的 String 对象,而不管内容是否已经存在于常量池中。
  • 使用字符串字面量会优先使用常量池,是创建字符串更高效的方式。

最佳实践

  1. 优先使用字符串字面量:除非有特殊需求,否则 String str = "abc"; 总是比 String str = new String("abc"); 更好。
  2. 明确指定字符集:在使用 byte[] 构造 String 时,始终显式指定字符集(如 StandardCharsets.UTF_8),而不是依赖平台默认值,以避免潜在的乱码问题。
  3. 不要不必要的构造:不要为了创建一个副本而使用 new String(existingString),这除了浪费内存外没有任何好处,由于 String 的不可变性,你完全可以安全地共享同一个引用。
分享:
扫描分享到社交APP
上一篇
下一篇