为何Java String类保留无参及String参数构造方法?
1. 会不会造成内存浪费?
先看该构造方法的实现:
@IntrinsicCandidate public String(String original) { this.value = original.value; this.coder = original.coder; this.hash = original.hash; }
这个构造方法并没有复制原字符串的字符数组,只是引用了原对象的value、coder、hash字段,堆中新建的String对象只是一个"空壳",真正的字符数据是和原对象共享的。
如果用new String("字面量")的方式调用,确实会产生两个String对象:常量池里的字面量对象,以及堆里的新对象。但内存浪费程度很小——只是多了一个String对象的内存开销(几个字段,约几十字节),而非复制整个字符数组。不过这种场景下,堆里的新对象完全多余,直接使用常量池的对象即可。
2. 为什么注释说没必要使用这个构造方法?
源码注释提到:
Initializes a newly created {@code String} object so that it represents
the same sequence of characters as the argument; in other words, the
newly created string is a copy of the argument string. Unless an
explicit copy of {@code original} is needed, use of this constructor is
unnecessary since Strings are immutable.
核心原因是String是不可变的:一旦创建,String的内容就无法修改。既然原字符串和新字符串内容完全一致且都不可变,直接使用原对象就足够,新建对象没有实际意义,只会额外占用少量内存。
3. 为什么要保留这些"不必要"的构造方法?
主要有两个原因:
- 历史向后兼容:这些构造方法从Java早期版本(JDK 1.0)就存在,大量老代码可能依赖它们。如果移除,会导致老项目编译失败或运行报错,破坏兼容性。
- 极端场景的遗留需求:在早期JDK版本中,String的
value字符数组并非private final,存在被外部修改的风险,此时String(String)构造方法可用来复制数组,保证新对象的独立性。虽然现在value已经是不可变的,但为了兼容历史场景,仍保留该方法。
至于无参构造String(),它会创建一个空String对象,同样可以用常量池的""代替,保留它也是出于向后兼容的考虑。
内容的提问来源于stack exchange,提问作者kuruthi

