代码之家  ›  专栏  ›  技术社区  ›  Yishai

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

  •  18
  • Yishai  · 技术社区  · 15 年前

    考虑这种情况:

    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);
      }
    }
    

    通常,您会期望编译器内联一个和两个常量。然而,这种行为有保证吗?您是否可以在运行时在类路径中部署没有类1的类2,并期望它在不考虑编译器的情况下工作,或者这是可选的编译器优化?

    编辑:为什么要这样做?好吧,我有一个常量,它将在应用程序的两端(RMI上的客户机和服务器)共享,在这种特殊情况下,将该常量放在一个类上非常方便,该类只能位于该除法的一侧(逻辑上是拥有该常量值的类),而不是放在任意常量类中。只是因为它需要由代码的两边共享。在编译时它的所有一组源文件,但在编译时它被包分割。

    6 回复  |  直到 9 年前
        1
  •  23
  •   René Link    9 年前

    它保证被当作一个常量表达式来处理,并且保证被 section 15.28 of the JLS :

    编译时常量表达式是 表示值为的表达式。 基元类型或字符串 不是突然完成的 仅使用以下内容:

    • 原始类型的文字和字符串类型的文字(_§3.10.5)
    • 强制转换为基元类型,强制转换为类型字符串
    • 一元运算符+、-、~、和!(但不是+或--)
    • 乘法运算符*、/和%
    • 加法运算符+和-
    • ……

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

    现在,这并不意味着它一定是内联的。然而,规范第13.1节规定:

    对常量字段的引用 变量(_§4.12.4)在 编译时间为常量值 这是表示。没有提及此类 常量字段应出现在 二进制文件中的代码(除了 包含 常量字段,其中将包含代码 初始化它),以及这样的常量 字段必须始终显示为 初始化;默认初始值 对于此类字段的类型,必须 从不被观察。

    换言之, 即使表达式本身不是常量 ,不应引用 Class1 . 是的,你没事。那不 必要地 保证连接的值在字节码中使用,但是前面引用的位保证连接的值是内部的,所以我将 极大地 如果它不只是内联连接的值,会很惊讶。即使没有,你也可以保证它在没有 第1类 .

        2
  •  10
  •   Esko Luontola    15 年前

    用javac 1.6.0 U14编译将生成以下字节码:

    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的常量池中。

        3
  •  0
  •   OscarRyz    15 年前

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

    不能保证,因为并非所有的解释程序(JVM)都以相同的方式工作。但最重要的实现是可行的。

    不幸的是,我没有链接来支持这一点:(

        4
  •  0
  •   ykaganovich Mike Samuel    15 年前

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

    这样做的“正常”方法是:

    1. 将常量放在客户机和服务器共享的包中。大概有这样一个包,因为这就是接口的位置。
    2. 如果没有这样的包,那么使用共享常量创建两个类:一个用于服务器,一个用于客户机。
        5
  •  0
  •   Yardena    15 年前

    JLS 13.4.9 .虽然它不显式要求编译器内联常量,但它提示条件编译和对中常量的支持 switch 语句使编译器始终内联常量。

        6
  •  0
  •   Carl    15 年前

    看起来您正在编写自己的内置功能版本 enum 哪一个 public static final 对你来说,正确的命名方式 name() toString() (以及其他一些优点,但可能具有较大内存占用的缺点)。

    你使用的是不包含EnUM的旧版本的Java吗?