Гарантируется ли Java наличие встроенных строковых констант, если они могут быть определены во время компиляции

StackOverflow https://stackoverflow.com/questions/1406616

  •  05-07-2019
  •  | 
  •  

Вопрос

Рассмотрим этот случай:

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

Обычно вы ожидаете, что компилятор встроит ОДНУ и ДВЕ константы.Однако гарантировано ли такое поведение?Можете ли вы развернуть во время выполнения Class2 без Class1 в classpath и ожидать, что он будет работать независимо от компиляторов, или это необязательная оптимизация компилятора?

Редактировать:С какой стати это делать?Ну, у меня есть константа, которая была бы общей для двух концов приложения (клиента и сервера через RMI), и в данном конкретном случае было бы очень удобно поместить константу в класс, который может находиться только на одной стороне этого разделения (поскольку логически именно ему принадлежит это постоянное значение), а не иметь ее в произвольном классе constants только потому, что она должна быть общей для обеих сторон кода.Во время компиляции это все один набор исходных файлов, но во время сборки он разделен по пакетам.

Это было полезно?

Решение

Это гарантированно обрабатывается как постоянное выражение и гарантированно интернируется раздел 15.28 JLS:

Константное выражение времени компиляции - это выражение, обозначающее значение примитивного типа или строку, которая не завершается внезапно и составляется с использованием только следующего:

  • Литералы примитивного типа и литералы типа String (§3.10.5)
  • Приводит к примитивным типам и приводит к типу String
  • Унарные операторы +, -, ~ и !(но не ++ или --)
  • Мультипликативные операторы *, / и %
  • Аддитивные операторы + и -
  • ...

...

Константы времени компиляции типа String всегда "интернируются", чтобы совместно использовать уникальные экземпляры, используя метод String.intern .

Так вот, это не совсем означает, что он гарантированно будет встроен.Однако в разделе 13.1 спецификации говорится:

Ссылки на поля, которые являются постоянными переменные (§4.12.4) преобразуются во время компиляции в постоянное значение , которое обозначается.Никаких ссылок на такие постоянное поле должно присутствовать в коде в двоичном файле (за исключением класса или интерфейса, содержащих постоянное поле, у которого будет код для его инициализации), и такая константа поля всегда должны выглядеть так, как будто они были инициализированы;начальное значение по умолчанию для типа такого поля никогда не должно соблюдаться.

Другими словами, даже если само выражение не было константой, не должно быть никаких ссылок на Class1.Так что да, с тобой все в порядке.Это не значит обязательно гарантирую, что объединенное значение используется в байт-коде, но биты, упомянутые ранее, гарантируют, что объединенное значение интернировано, поэтому я был бы чрезвычайно удивлен, если он просто не вставил объединенное значение.Даже если этого не произойдет, вам гарантировано, что это сработает без Class1.

Другие советы

Компиляция этого с 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.

Он будет вставлен не компилятором, а интерпретатором во время выполнения и, если возможно, преобразован в код сборки.

Это не может быть гарантировано, потому что не все интерпретаторы (JVM) работают одинаково. Но наиболее важные реализации сделают.

К сожалению, у меня нет ссылки для поддержки этого :(

Я подозреваю, но не знаю наверняка, что это сработает, но это не очень хорошая идея.

" нормальный " способы сделать это:

<Ол>
  • Поместите константы в пакет, который используется клиентом и сервером. Предположительно, есть такой пакет, потому что именно туда идут интерфейсы.
  • Если такого пакета нет, создайте 2 класса с общими константами: один для сервера и один для клиента.
  • См. JLS 13.4.9 , Хотя он явно не требует, чтобы константы были встроены компилятором, он намекает на то, что условная компиляция и поддержка констант в операторах switch заставляют компилятор всегда вставлять константы.

    Похоже, вы кодируете свою собственную версию возможности, встроенную в enum , которая делает public static final для вас, правильное именование через name ( ) и toString () (а также имеют некоторые другие преимущества, но, возможно, имеют недостаток, заключающийся в увеличенном объеме памяти).

    Вы используете более старую версию Java, которая еще не включает enum?

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top