Константы и переменные времени компиляции

Документация по языку Java гласит:

Если примитивный тип или строка определены как константа и значение известно во время компиляции, компилятор заменяет имя константы везде в коде на его значение. Это называется константой времени компиляции.

Я понимаю, если у нас есть кусок кода:

private final int x = 10;

Затем компилятор заменит каждое вхождение x в коде с литералом 10,


Но предположим, что константа инициализируется во время выполнения:

private final int x = getX(); // here getX() returns an integer value at run-time.

Будет ли какое-либо падение производительности (каким бы незначительным оно ни было) по сравнению с постоянной времени компиляции?


Другой вопрос, является ли приведенная ниже строка кода:

private int y = 10; // here y is not final

трактуется компилятором как постоянная времени компиляции?


Наконец, что я понимаю из ответов:

  1. final static означает постоянную времени компиляции
  2. просто final означает, что это константа, но инициализируется во время выполнения
  3. просто static означает инициализированный во время выполнения
  4. без final является переменной и не будет рассматриваться как константа.

Правильно ли мое понимание?

7 ответов

Решение

Постоянная времени компиляции должна быть:

  • объявлен окончательным
  • примитив или строка
  • инициализируется в декларации
  • инициализируется с постоянным выражением

Так private final int x = getX(); не является постоянным

Ко второму вопросу private int y = 10; не является константой (в данном случае не финальной), поэтому оптимизатор не может быть уверен, что значение не изменится в будущем. Поэтому он не может оптимизировать его так же хорошо, как постоянное значение. Ответ: нет, это не обрабатывается так же, как постоянная времени компиляции.

JLS делает следующие различия между final переменные и константы:

final переменные

Переменная может быть объявлена final, final Переменная может быть назначена только один раз. Это ошибка времени компиляции, если final Переменная присваивается, если только она точно не назначена непосредственно перед назначением ( §16 (Определенное назначение)).

Когда final переменная была назначена, она всегда содержит одно и то же значение. Если final переменная содержит ссылку на объект, тогда состояние объекта может быть изменено операциями над объектом, но переменная всегда будет ссылаться на один и тот же объект. Это относится и к массивам, потому что массивы являются объектами; если final переменная содержит ссылку на массив, тогда компоненты массива могут быть изменены операциями над массивом, но переменная всегда будет ссылаться на один и тот же массив.

Пустой final это final переменная, в объявлении которой отсутствует инициализатор.

константы

Постоянная переменная final переменная примитивного типа или типа String это инициализируется с постоянным выражением ( §15.28).

Из этого определения мы можем заметить, что константа должна быть:

  • объявленный final
  • примитивного типа или типа String
  • инициализируется в своей декларации (не пустой final)
  • инициализируется с постоянным выражением

Как насчет констант времени компиляции?

JLS не содержит фразу константы времени компиляции. Однако программисты часто используют термины " постоянная времени компиляции" и "взаимозаменяемо".

Если final переменная не соответствует критериям, изложенным выше, чтобы считаться константой, технически ее следует называть final переменная.

Согласно JLS, не требуется, чтобы "постоянная переменная" была статической.

Таким образом, "постоянная переменная" может быть статической или нестатической (переменная экземпляра).

Но JLS предъявляет некоторые другие требования, чтобы переменная была "постоянной переменной" (помимо того, что она просто конечная):

  • быть только строковым или примитивным
  • только инициализированный встроенный, потому что он является окончательным, и пустой финал не допускается
  • инициализируется с "константным выражением" = "константным выражением времени компиляции" (см. цитату JLS ниже)

4.12.4. окончательные переменные (JLS)

Постоянная переменная - это конечная переменная примитивного типа или типа String, которая инициализируется постоянным выражением (§15.28).

15,28. Постоянные выражения

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

Литералы примитивного типа и литералы типа String (§3.10.1, §3.10.2, §3.10.3, §3.10.4, §3.10.5)

Приводит к примитивным типам и приводит к типу String (§15.16)

Унарные операторы +, -, ~, и! (но не ++ или -) (§15.15.3, §15.15.4, §15.15.5, §15.15.6)

Мультипликативные операторы *, / и% (§15.17)

Аддитивные операторы + и - (§15.18)

Операторы сдвига <<, >> и >>> (§15.19)

Реляционные операторы <, <=,> и>= (но не instanceof) (§15.20)

Операторы равенства == и!= (§15.21)

Побитовые и логические операторы &, ^, и | (§15.22)

Условный оператор-&&& и оператор условного-или || (§15.23, §15.24)

Тернарный условный оператор?: (§15.25)

Выражения в скобках (§15.8.5), содержащее выражение которых является константным выражением.

Простые имена (§6.5.6.1), которые ссылаются на постоянные переменные (§4.12.4).

Квалифицированные имена (§6.5.6.2) формы TypeName . Идентификатор, который ссылается на постоянные переменные (§4.12.4).

final Ключевое слово означает, что переменная будет инициализирована один раз и только один раз. Настоящая константа должна быть объявлена static также. Итак, ни один из ваших примеров не рассматривается компилятором как константа. Тем не менее, последнее ключевое слово сообщает вам (и компилятору), что ваши переменные будут инициализированы только один раз (в конструкторе или буквально). Если вам нужно, чтобы их значения присваивались во время компиляции, ваши поля должны быть статическими.

На производительность это не сильно влияет, но имейте в виду, что примитивные типы являются неизменяемыми, после того, как вы создали один, он будет хранить это значение в памяти, пока сборщик мусора не удалит его. Итак, если у вас есть переменная y = 1; а затем вы измените его на y = 2; в памяти JVM будет иметь оба значения, но ваша переменная будет "указывать" на последнее.

private int y = 10; // здесь у не окончательный

трактуется компилятором как постоянная времени компиляции?

Нет. Это переменная экземпляра, созданная, инициализированная используемая во время выполнения.

private final int x = getX();Будет вызван при первом объявлении вашего объекта. "Падение" производительности будет зависеть от getX() но это не то, что создает узкое место.

Просто имейте в виду, что в следующем коде x не является постоянной времени компиляции:

public static void main(String[] args) {
     final int x;
     x= 5;
}

На некоторых машинах может быть очень небольшое падение производительности для private final int x = getX(); поскольку для этого потребуется хотя бы один вызов метода (помимо того факта, что это не константа времени компиляции), но, как вы сказали, это будет незначительным, так зачем беспокоиться?

Что касается второго вопроса: y не является окончательным и, следовательно, не является постоянной времени компиляции, поскольку она может измениться во время выполнения.

Проще говоря, во время компиляции компилятор заменяет ссылку на фактическое указанное значение вместо использования параметра ссылки.

public static void main(String[] args) {
final int x = 5;
}

т.е. во время компиляции компилятор принимает инициализированное значение 5 непосредственно для согласования, а не использует ссылочную переменную 'x';

Пожалуйста, проверьте это объяснение

Каждый примитивный литерал и строковый литерал является константой времени компиляции.

См.: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html

Другие вопросы по тегам