字符串String str = new String("my string")真的比较低效吗?

6
我经常看到人们说String str = new String("my string")不如写String str = "my string"高效,因为前者会创建一个静态的"my string"对象,然后再new一个String对象从静态对象中复制过来。
然而,考虑到这里使用的语言非常简单和明确,我几乎无法想象Java优化器不会尝试简单地将前者转换为后者。为什么它真的会选择以更费力的方式执行呢?如果Java进行了优化,可能会有什么负面影响吗?

2
你学过这个吗?https://dev59.com/6XA75IYBdhLWcg3w4tR7 - Youcef LAIDANI
3
将 interned strings 描述为 "静态" 实际上并不准确,因为它们不属于任何特定的类。无论如何,对象不是 "静态的" 或者 "实例级别的",只有对它们的引用才是。 - Lew Bloch
2
这确实很简单明了:根据语言规范,调用构造函数必须创建一个新对象。你提出的优化将违反该规则。 - shmosel
4个回答

9
然而,由于这里的语言如此简单明了,我几乎无法想象Java优化器不会尽最大努力将前者简单地转换为后者。
首先,你不应该编写代码以执行缓慢的操作,只因为你认为编译器会用更快的方法来替换它。
其次,编译器通常无法进行优化,因为“new”保证创建一个新对象。如果你这样做:
String str = new String("my string");

如果这样做,那么可以保证str != "my string"。只有当Java能够证明String对象的身份对程序的执行无关时,它才能优化掉new


2
每次评估[类实例创建]表达式时,都会创建一个新的对象。 - shmosel

6
请查看生成的字节码,用于String s1 = "1"
LDC "1"
ASTORE 1

And for String s2 = new String("2"):

NEW java/lang/String
DUP
LDC "2"
INVOKESPECIAL java/lang/String.<init> (Ljava/lang/String;)V
ASTORE 2

后面的例子是多余而且更加复杂。在这种情况下,编译器不会做任何优化。它保证一个新的String类实例将被创建。
使用String str =“my string”,JVM 可以重用String实例而不是创建新的实例。考虑以下示例:
String s1 = "1";
String s2 = "1";
System.out.println(s1 == s2); // true  => same reference, s1 & s2 point to the same object

String s3 = "1";
String s4 = new String("1");
System.out.println(s3 == s4); // false => s3 & s4 point to different objects

这并没有回答为什么它不能被优化的问题。 - shmosel

2
你应该尽可能编写最简单、最清晰的代码。在90%以上的情况下,开发人员的效率比处理器效率更重要。因此,即使JIT可以优化它,但你会让开发人员承担解释为什么这段代码比必要复杂的负担,而最难找到的答案是;没有任何好的理由。

为什么它真的会选择用更费力的方式呢?

当人们从其他语言(如C++)迁移时,他们会带上他们的习惯用法。例如,在C++中,你需要编写new string("some text"),但在Java中这是不必要的冗余。

如果Java进行了优化,会有什么负面影响吗?

Java可以通过逃逸分析进行优化。如果字符串在内联后不会逃逸当前方法,则new String可以放置在堆栈上并被消除,因为它是多余的。但这已经不是重点了。你已经让维护代码的人生活更加艰难,而JIT也无法帮助你解决这个问题。

0

这不是性能问题,只是以一种不必要的复杂方式执行某些操作。除非您正在使用interned Strings来进行==比较,否则代码也将以相同的方式工作。

Java不会优化任何内容,因为没有什么可以优化的。如果您使用字符串字面量,则相同的字符串将被缩减为字符串池中的同一实例,因此

String foo = "foo";
String bar = "foo";
System.out.println(foo == bar); // True, the same object

然而,使用显式构造函数

String foo = "foo";
String bar = new String("foo");
System.out.println(foo == bar); // False, bar is explicitly created as a new object with the constructor

然而,由于您仍应该对所有对象比较使用equals(),因此在运行代码时不会看到任何差异。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接