所有编译时常量都会被内联吗?

25

假设我有一个类像这样:

class ApplicationDefs{
public static final String configOption1 = "some option";
public static final String configOption2 = "some other option";
public static final String configOption3 = "yet another option";
}

我的应用程序中的许多其他类都使用这些选项。现在,我想单独更改其中一个选项并部署编译后的类。 但是,如果这些字段在消费者类中被内联,那么这就变得不可能了,对吗?

是否有任何选项可以禁用编译时常量的内联?

6个回答

29

你可以使用 String.intern() 来实现所需的效果,但应该注释你的代码,因为并不是很多人都知道这个方法。例如:

public static final String configOption1 = "some option".intern();

这将防止编译时的内联。由于它引用编译器会放置在perm中的完全相同的字符串,因此您不会创建任何额外的内容。

作为替代方案,您总是可以执行

public static final String configOption1 = "some option".toString();

这可能更容易阅读。无论如何,由于这有点奇怪,您应该对代码进行注释,以通知那些维护它的人您正在做什么。

编辑: 发现另一个SO链接,提供了有关此更多信息的JLS参考。 何时在字符串文字上使用intern()


5
aString.toString() 返回 aString 本身,而不是一个新的字符串,因此对于这个问题来说,它与 intern() 一样有效。如果需要创建一个全新的字符串副本,可以使用很少使用的构造函数 new String(aString) - Christian Semrau
2
对于字符串字面量使用intern()是无用的。它们已经被“interned”了。请参见这里 - ceving
2
@ceving - 在这种情况下,它并不是无用的,因为它可以防止类似configOption1等内容被内联到其他类中。否则,如果某个其他类引用了configOption1,而你又更改了常量并没有重新编译其他类,则其行为将不会按照你期望的(或可能想要的)方式发生。这就是OP问题的全部意义所在。 - Ted Hopp

10

没错。如果你使用JAD反编译你的代码,你会发现所有编译时常量都已经被内联了。我会投票支持属性文件或所描述的静态方法。 - Dave Ray

7

实际上,如果你移除final关键字,常量就不再是编译时常量,然后你的配置 将按照你的要求工作

然而,强烈建议如果这确实是你想要做的某种配置,你应该采用比在某个类文件中使用常量更可管理的方式。


9
如果删除最终关键字,那些常量就不再是常量了,它们会变成变量。 - whiskeysierra

7
不需要。但是你可以用静态方法调用来替换它们,例如:
class ApplicationDefs {

    public static String configOption1() { return "some option"; }

}

虽然不太美观,但它可以满足你的需求。 :)


这个解决方案的行为与我的答案中建议的行为完全相同。我不明白用方法包装常量的意义。 - Yuval Adam
6
除了你提出的答案中,变量可以被外部类改变。 - Greg

6
您可以通过使您的常量不是编译时常量来抑制内联...
例如,null 不是编译时常量。任何涉及非编译时常量的表达式都不是编译时常量,尽管 javac 可能在编译单元内执行常量折叠。
public static final String configOption1 = null!=null?"": "some option";

-6

这里没有说明这些值应该内联。你只是声明了一些publicstatic成员。那些其他类正在使用这些成员的值。没有要求内联。即使是final关键字。

但出于性能原因,一些JVM可能会在这些其他类中内联这些值。这是一种优化。没有任何优化应该改变程序的行为。因此,如果更改这些成员的定义,则JVM应该取消内联先前的值。

这就是为什么没有办法关闭内联的原因。如果JVM不进行内联并且没有问题,或者如果它被内联,则JVM保证取消内联。

我不确定静态导入此类时会发生什么。我认为(不确定)将执行内联操作,并可能导致您提到的问题。如果是这种情况,您可以基本上删除静态导入,然后就可以了。


我不认为是这样的:JLS规定编译时常量始终会被内联。此外,在String编译时常量的情况下,它们会被池化并且所有使用都引用同一个实例。 - GaryF
很好知道JLS规定编译时常量必须在编译时内联。 - Pierre
1
十四年的时间相当长,但如果你仍然想知道JLS是否已经规定了内联行为,答案已经得到了回答,例如在Does the JLS require inlining of final String constants?中。 - Holger

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