Константы и переменные времени компиляции - PullRequest
32 голосов
/ 31 января 2012

Документация по языку 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 ]

51 голосов
/ 31 января 2012

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

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

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

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

3 голосов
/ 20 апреля 2018

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

final переменных

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

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

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

константы

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

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

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

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

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

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

1 голос
/ 26 января 2018

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

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

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

  • , являющаяся только String или примитивом
  • initializedтолько встроенный, потому что он является окончательным, и пустой final не допускается
  • инициализируется с помощью "постоянного выражения" = "постоянного выражения времени компиляции" (см. цитату JLS ниже)

4.12.4.final Переменные (JLS)

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

15,28. Выражения констант

A Выражение констант времени компиляции - это выражение, обозначающее значение типа примитива или 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).

1 голос
/ 31 января 2012

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

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

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

обрабатывается компилятором так же, как постоянная времени компиляции?

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

1 голос
/ 31 января 2012

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

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

0 голосов
/ 30 мая 2018

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

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

0 голосов
/ 31 января 2012

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

...