Гарантируется ли Java встроенные строковые константы, если они могут быть определены во время компиляции - PullRequest
18 голосов
/ 10 сентября 2009

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

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), и в этом конкретном случае было бы очень удобно поместить константу в класс, который может быть только на одной стороне этого деления так как это логически тот, который владеет этим значением константы), а не иметь его в произвольном классе констант только потому, что он должен быть общим для обеих сторон кода. Во время компиляции это все один набор исходных файлов, но во время сборки он делится на пакет.

Ответы [ 6 ]

23 голосов
/ 10 сентября 2009

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

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

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

...

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

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

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

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

10 голосов
/ 10 сентября 2009

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

0 голосов
/ 10 сентября 2009

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

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

0 голосов
/ 10 сентября 2009

См. JLS 13.4.9 . Хотя он явно не требует, чтобы константы были встроены компилятором, он намекает на то, что условная компиляция и поддержка констант в операторах switch заставляют компилятор всегда включать константы.

0 голосов
/ 10 сентября 2009

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

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

  1. Поместите константы в пакет, который используется клиентом и сервером. Предположительно, есть такой пакет, потому что именно туда идут интерфейсы.
  2. Если такого пакета нет, создайте 2 класса с общими константами: один для сервера и один для клиента.
0 голосов
/ 10 сентября 2009

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

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

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

...