如果可以在编译时确定字符串常量,Java 是否保证内联字符串常量

发布于 2024-08-04 14:01:27 字数 527 浏览 6 评论 0原文

考虑这种情况:

public Class1 {
   public static final String ONE = "ABC";
   public static final String TWO = "DEF";
}

public Class2 {

  public void someMethod() {
    System.out.println(Class1.ONE + Class1.TWO);
  }
}

通常您会期望编译器内联 ONE 和 TWO 常量。然而,这种行为有保证吗?您是否可以在类路径中部署没有 Class1 的运行时 Class2,并期望它无论编译器如何都能工作,或者这是一个可选的编译器优化?

编辑:到底为什么要这样做?好吧,我有一个在应用程序的两端(通过 RMI 的客户端和服务器)之间共享的常量,在这种特殊情况下,将该常量放在只能位于该划分一侧的类上会非常方便(因为它在逻辑上是拥有该常量值的那个),而不是仅仅因为它需要由代码双方共享而将其放在任意常量类中。在编译时,它是一组源文件,但在构建时,它是按包划分的。

Consider this case:

public Class1 {
   public static final String ONE = "ABC";
   public static final String TWO = "DEF";
}

public Class2 {

  public void someMethod() {
    System.out.println(Class1.ONE + Class1.TWO);
  }
}

Typically you would expect the compiler to inline the ONE and TWO constants. However, is this behavior guaranteed? Can you deploy at runtime Class2 without Class1 in the classpath, and expect it to work regardless of compilers, or is this an optional compiler optimization?

EDIT: Why on earth do this? Well I have a constant that would be shared between two ends of an application (client and server over RMI) and it would be very convenient in this particular case to put the constant on a class that can only be on one side of that divide (as it is logically the one that owns that constant value) rather than have it in an arbitrary constants class just because it needs to be shared by both sides of the code. At compile time its all one set of source files, but at build time it is divided by package.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

鹤舞 2024-08-11 14:01:27

它保证被视为常量表达式,并保证被 JLS 第 15.28 条

编译时常量表达式是
表示值的表达式
原始类型或字符串
不是突然完成并且是组成的
仅使用以下内容:

  • 原始类型的文字和 String 类型的文字 (§3.10.5)
  • 转换为原始类型并转换为 String 类型
  • 一元运算符 +、-、~ 和 ! (但不是 ++ 或 --)
  • 乘法运算符 *、/ 和 %
  • 加法运算符 + 和 -
  • ...

...

字符串类型的编译时常量
总是被“拘禁”以便分享
唯一的实例,使用该方法
String.intern。

现在,这并不完全意味着它一定会被内联。然而,规范第 13.1 节规定:

对常量字段的引用
变量(§4.12.4)解析为
编译时间为常数值
即表示。没有提及此类
一个常数字段应该存在于
二进制文件中的代码(除了
包含的类或接口
常量字段,其中将有代码
来初始化它),这样的常量
字段必须始终显示为
已初始化;默认初始值
对于这样一个字段的类型必须
永远不会被观察到。

换句话说,即使表达式本身不是常量,也不应该引用 Class1。所以是的,你没事。这并不一定保证在字节码中使用连接的值,但前面引用的位保证连接的值被保留,所以我会感到非常惊讶如果它不只是内联连接值。即使没有,您也可以保证它在没有 Class1 的情况下也能工作。

It's guaranteed to be treated as a constant expression, and guaranteed to be interned by section 15.28 of the JLS:

A compile-time constant expression is
an expression denoting a value of
primitive type or a String that does
not complete abruptly and is composed
using only the following:

  • Literals of primitive type and literals of type String (§3.10.5)
  • Casts to primitive types and casts to type String
  • The unary operators +, -, ~, and ! (but not ++ or --)
  • The multiplicative operators *, /, and %
  • The additive operators + and -
  • ...

...

Compile-time constants of type String
are always "interned" so as to share
unique instances, using the method
String.intern.

Now, that doesn't quite say it's guaranteed to be inlined. However, section 13.1 of the spec says:

References to fields that are constant
variables (§4.12.4) are resolved at
compile time to the constant value
that is denoted. No reference to such
a constant field should be present in
the code in a binary file (except in
the class or interface containing the
constant field, which will have code
to initialize it), and such constant
fields must always appear to have been
initialized; the default initial value
for the type of such a field must
never be observed.

In other words, even if the expression itself weren't a constant, there should be no reference to Class1. So yes, you're okay. That doesn't necessarily guarantee that the concatenated value is used in the bytecode, but the bits referenced earlier guarantee that the concatenated value is interned, so I'd be hugely surprised if it didn't just inline the concatenated value. Even if it doesn't, you're guaranteed that it'll work without Class1.

童话里做英雄 2024-08-11 14:01:27

使用 javac 1.6.0_14 编译它会生成以下字节码:

public void someMethod();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String ABCDEF
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

因此,字符串在编译时连接,结果包含在 Class2 的常量池中。

Compiling that with javac 1.6.0_14 produces the following bytecode:

public void someMethod();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String ABCDEF
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

So the strings are concatenated at compile time and the result is included in Class2's constant pool.

给妤﹃绝世温柔 2024-08-11 14:01:27

它不会由编译器内联,而是由解释器在运行时内联,如果可能的话,会转换为汇编代码。

不能保证这一点,因为并非所有解释器(JVM)都以相同的方式工作。但最重要的实现就可以了。

不幸的是我没有链接来维持这个:(

It won't be inlined by the compiler but by the interpreter at runtime and if possible converted to assembly code.

It cannot be guaranteed, because not all the interpreters ( JVM's ) work the same way. But the most important implementations will do.

Unfortunately I don't have a link to sustain this :(

○闲身 2024-08-11 14:01:27

我怀疑,但不确定,这会起作用,但这听起来不是一个好主意。

执行此操作的“正常”方法是:

  1. 将常量放入客户端和服务器之间共享的包中。据推测,存在这样一个包,因为那是接口所在的地方。
  2. 如果没有这样的包,请创建 2 个具有共享常量的类:一个用于服务器,一个用于客户端。

I suspect, but don't know for sure, that this will work, but it doesn't sound like a good idea.

The "normal" ways to do this are:

  1. Put the constants in a package that's shared between the client and the server. Presumably, there is such a package, because that's where the interfaces go.
  2. If there's no such package, create 2 classes with the shared constants: one for the server and one for the client.
诗笺 2024-08-11 14:01:27

请参阅 JLS 13.4.9。虽然它没有明确要求编译器内联常量,但它暗示条件编译和对 switch 语句中常量的支持会导致编译器始终内联常量。

See JLS 13.4.9. While it does not explicitly require that constants are inlined by the compiler, it hints that conditional compilation and support for constants in switch statements cause the compiler to always inline constants.

罪#恶を代价 2024-08-11 14:01:27

看起来您正在编写自己版本的内置于 enum 中的功能,它为您执行 public static final ,通过 name() 进行正确命名code> 和 toString() (以及具有其他一些优点,但可能具有内存占用较大的缺点)。

您使用的是不包含枚举的旧版本 Java 吗?

It looks like you're coding your own version of the capability built into enum, which does public static final for you, proper naming via name() and toString() (as well as having some other advantages, but perhaps having the disadvantage of a larger memory footprint).

Are you using an older version of Java that doesn't include enum yet?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文