Подтвердить что ты не робот

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

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

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 константы. Однако гарантируется ли такое поведение? Можете ли вы развертывать во время выполнения Class2 без Class1 в пути к классам и ожидать, что он будет работать независимо от компиляторов или это необязательная оптимизация компилятора?

РЕДАКТИРОВАТЬ: Почему это так? Ну, у меня есть константа, которая будет делиться между двумя концами приложения (клиентом и сервером над RMI), и было бы очень удобно в этом конкретном случае поместить константу в класс, который может находиться только на одной стороне этого деления ( поскольку он логически тот, который владеет этим постоянным значением), а не имеет его в произвольном классе констант только потому, что он должен быть разделен обеими сторонами кода. Во время компиляции его все один набор исходных файлов, но во время сборки он делится на пакет.

4b9b3361

Ответ 1

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

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

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

...

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

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

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

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

Ответ 2

Компиляция с помощью 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

Итак, строки конкатенируются во время компиляции, и результат включается в пул констант класса 2.

Ответ 3

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

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

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

Ответ 4

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

"Обычные" способы сделать это:

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

Ответ 5

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

Ответ 6

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

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