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

Posted

技术标签:

【中文标题】所有编译时常量都内联吗?【英文标题】:Are all compile-time constants inlined? 【发布时间】:2010-09-27 12:41: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";

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

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

【问题讨论】:

【参考方案1】:

您可以使用 String.intern() 来获得所需的效果,但应该注释您的代码,因为没有多少人知道这一点。即

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

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

作为替代方案,您总是可以这样做

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

但是这不会使用编译后的实习生字符串,它会在旧版本上创建一个新字符串。没什么大不了的,可能更容易阅读。无论哪种方式,由于这有点奇怪,您应该对代码进行注释以告知维护它的人您正在做什么。

编辑: 找到另一个提供 JLS 参考的 SO 链接,以获取更多信息。 When to use intern() on String literals

【讨论】:

代码aString.toString() 本身返回aString,而不是新的,因此对于这个问题,它与intern() 一样有效。很少使用的构造函数 new String(aString) 将创建一个新的 String 副本。 intern() 字符串字面量没用。他们已经“被拘留”了。见here。 @ceving - 在这种情况下它并不是没用的,因为它可以防止 configOption1 等被内联到其他类中。否则,如果某些其他类引用configOption1,然后您更改常量并且不重新编译其他类,则行为将不是您所期望的(或可能想要的)。这就是 OP 问题的重点。【参考方案2】:

不,恐怕它是 JLS 的一部分。在 Java Puzzlers 中简要地提到了这一点,但我手头没有副本。

我猜你可能会考虑在属性文件中定义这些常量,并让类定期加载它们。

参考:http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#5313

【讨论】:

是的。如果你用 JAD 反编译你的代码,你会发现所有的编译时常量都被内联了。我会投票支持属性文件或所描述的静态方法。【参考方案3】:

没有。不过,您可以将它们替换为静态方法调用,例如:

class ApplicationDefs 

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


当然,它并不漂亮,但可以满足您的要求。 :)

【讨论】:

此解决方案呈现的行为与我的回答中建议的行为相同。我没有看到将常量包装在方法中的意义。 @Yuval - 除了在您建议的答案中,变量可以由外部类更改。【参考方案4】:

实际上,如果您删除final 关键字,这些常量将不再是编译时常量,然后您的配置将按照您的意愿工作

但是,强烈建议如果这确实是您正在尝试执行的某种配置,您应该使用比某些类文件中的常量更易于管理的方式。

【讨论】:

实际上,如果去掉final关键字,常量就不再是常量,而是变量。【参考方案5】:

您可以通过将常量设为非编译时常量来禁止内联...

例如,null 不是编译时间常数。任何涉及非编译时常量的表达式都不是编译时常量,尽管 javac 可以在编译单元内进行常量折叠。

public static final String configOption1 = null!=null?"": "some option";

【讨论】:

【参考方案6】:

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

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

这就是无法关闭内联的原因。要么JVM不内联就没有问题,要么内联,JVM保证不内联。

我不确定当您静态导入此类时会发生什么。我认为(不确定)内​​联已执行,可能会导致您提到的麻烦。如果是这样的话,你基本上可以删除静态导入,你就可以了。

【讨论】:

我认为情况并非如此:JLS 指定编译时常量始终是内联的。此外,在 String 编译时常量的情况下,它们是 intern 并且所有使用都引用同一个实例。 很高兴知道 JLS 在哪里规定必须在编译时内联编译时常量。

以上是关于所有编译时常量都内联吗?的主要内容,如果未能解决你的问题,请参考以下文章

内联函数

何时可以/将在 C++ 中内联函数?可以强制内联行为吗?

是否有任何忽略默认内联函数的 C++ 标准的编译器? [复制]

编译时常量和变量

虚函数可以是内联函数吗?

iOS开发 CG_INLINE、static inline内联函数