Финал (Java) - Final (Java)

в Язык программирования Java, то окончательный ключевое слово используется в нескольких контекстах для определения объекта, который может быть назначен только один раз.

Когда окончательный присвоена переменная, она всегда содержит одно и то же значение. Если окончательный переменная содержит ссылку на объект, тогда состояние объекта может быть изменено операциями над объектом, но переменная всегда будет ссылаться на один и тот же объект (это свойство окончательный называется нетранзитивность[1]). Это применимо также к массивам, потому что массивы являются объектами; если окончательный переменная содержит ссылку на массив, тогда компоненты массива могут быть изменены операциями с массивом, но переменная всегда будет ссылаться на один и тот же массив.[2]

Заключительные занятия

А окончательный учебный класс нельзя разделить на подклассы. Поскольку это может обеспечить преимущества безопасности и эффективности, многие классы стандартной библиотеки Java являются окончательными, например java.lang.System и java.lang.String.

Пример:

общественный окончательный учебный класс MyFinalClass {...}общественный учебный класс Это не правильно расширяет MyFinalClass {...} // запрещенный

Окончательные методы

Заключительный метод не может быть отвергнутый или скрыты подклассами.[3] Это используется для предотвращения неожиданного поведения подкласса, изменяющего метод, который может иметь решающее значение для функции или согласованности класса.[4]

Пример:

общественный учебный класс Основание{    общественный       пустота m1() {...}    общественный окончательный пустота m2() {...}    общественный статический       пустота м3() {...}    общественный статический окончательный пустота м4() {...}}общественный учебный класс Полученный расширяет Основание{    общественный пустота m1() {...}  // ОК, переопределение Base # m1 ()    общественный пустота m2() {...}  // запрещенный    общественный статический пустота м3() {...}  // ОК, скрываем базу # m3 ()    общественный статический пустота м4() {...}  // запрещенный}

Распространенное заблуждение состоит в том, что объявление метода как окончательный повышает эффективность, позволяя компилятору напрямую вставлять метод везде, где он вызывается (см. встроенное расширение ). Поскольку метод загружается в время выполнения, компиляторы не могут этого сделать. Только среда выполнения и JIT компилятор точно знает, какие классы были загружены, и поэтому только они могут принимать решения о том, когда встраивать, является ли метод окончательным.[5]

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

Конечные переменные

А окончательный Переменная может быть инициализирован только один раз, либо с помощью инициализатора, либо с помощью оператора присваивания. Ее не нужно инициализировать в момент объявления: это называется «пустой конечной» переменной. Пустая конечная переменная экземпляра класса должна быть определенно назначена в каждом конструкторе класса, в котором она объявлена; аналогично, пустая конечная статическая переменная должна быть определенно назначена в статическом инициализаторе класса, в котором она объявлена; в противном случае в обоих случаях возникает ошибка времени компиляции.[6] (Примечание. Если переменная является ссылкой, это означает, что переменную нельзя повторно привязать для ссылки на другой объект. Но объект, на который она ссылается, все еще изменчивый, если он был изначально изменяемым.)

В отличие от значения постоянный значение конечной переменной не обязательно известно во время компиляции. Считается хорошей практикой представлять конечные константы в верхнем регистре, используя подчеркивание для разделения слов.[7]

Пример:

общественный учебный класс Сфера {    // pi - это универсальная константа, настолько постоянная, насколько это возможно.    общественный статический окончательный двойной ЧИСЛО ПИ = 3.141592653589793;    общественный окончательный двойной радиус;    общественный окончательный двойной xPos;    общественный окончательный двойной yPos;    общественный окончательный двойной zPos;    Сфера(двойной Икс, двойной у, двойной z, двойной р) {         радиус = р;         xPos = Икс;         yPos = у;         zPos = z;    }    [...]}

Любая попытка переназначить радиус, xPos, yPos, или же zPos приведет к ошибке компиляции. Фактически, даже если конструктор не устанавливает конечную переменную, попытка установить ее вне конструктора приведет к ошибке компиляции.

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

    общественный окончательный Позиция позиция;

куда позиция это объект с тремя свойствами поз. x, поз. у и pos.z. потом позиция не могут быть присвоены, но три свойства могут, если они не являются окончательными.

Как полный неизменность, использование конечных переменных имеет большие преимущества, особенно при оптимизации. Например, Сфера вероятно, будет функция, возвращающая его объем; знание того, что его радиус постоянен, позволяет нам запоминать расчетный объем. Если у нас относительно мало Сфераs, а их тома нам нужны очень часто, прирост производительности может быть существенным. Делая радиус Сфера окончательный сообщает разработчикам и компиляторам, что такая оптимизация возможна во всем коде, который использует Сферас.

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

за (окончательный SomeObject объект : someList) {   // что-то делаем с obj}

Поскольку переменная obj выходит за пределы области видимости с каждой итерацией цикла, она фактически повторно объявляется на каждой итерации, позволяя использовать один и тот же токен (т.е. объект), который будет использоваться для представления нескольких переменных.[8]

Конечные переменные во вложенных объектах

Конечные переменные можно использовать для построения деревьев неизменяемых объектов. После создания эти объекты гарантированно больше не изменятся. Для этого неизменный класс должен иметь только поля final, а эти поля final могут иметь только сами неизменяемые типы. Примитивные типы Java неизменны, как и строки и некоторые другие классы.

Если приведенная выше конструкция нарушается из-за наличия в дереве объекта, который не является неизменяемым, ожидание не выполняется, что все, что доступно через конечную переменную, является постоянным. Например, следующий код определяет систему координат, начало которой всегда должно быть в (0, 0). Происхождение реализуется с помощью java.awt.Point тем не менее, и этот класс определяет свои поля как общедоступные и изменяемые. Это означает, что даже при достижении источник объект по пути доступа только с конечными переменными, этот объект все еще может быть изменен, как показано в приведенном ниже примере кода.

импорт java.awt.Point;общественный учебный класс FinalDemo {    статический учебный класс Система координат {        частный окончательный Точка источник = новый Точка(0, 0);        общественный Точка getOrigin() { возвращаться источник; }    }    общественный статический пустота главный(Нить[] аргументы) {        Система координат система координат = новый Система координат();        система координат.getOrigin().Икс = 15;        утверждать система координат.getOrigin().getX() == 0;    }}

Причина этого в том, что объявление переменной final означает только то, что эта переменная будет указывать на тот же объект в любое время. Однако на объект, на который указывает переменная, эта последняя переменная не влияет. В приведенном выше примере координаты x и y начала координат можно свободно изменять.

Чтобы предотвратить эту нежелательную ситуацию, общее требование состоит в том, что все поля неизменяемого объекта должны быть окончательными, а типы этих полей сами должны быть неизменяемыми. Это дисквалифицирует java.util.Date и java.awt.Point и несколько других классов от использования в таких неизменяемых объектах.

Финальные и внутренние занятия

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

импорт javax.swing. *;общественный учебный класс FooGUI {    общественный статический пустота главный(Нить[] аргументы) {        // инициализируем компоненты графического интерфейса        окончательный JFrame jf = новый JFrame("Привет, мир!"); // разрешает доступ к jf из тела внутреннего класса        jf.Добавить(новый JButton("Нажми на меня"));        // упаковать и сделать видимыми в потоке отправки событий        SwingUtilities.invokeLater(новый Работоспособен() {            @Override            общественный пустота пробег() {                jf.пакет(); // это было бы ошибкой времени компиляции, если бы jf не было final                jf.setLocationRelativeTo(ноль);                jf.setVisible(истинный);            }        });    }}

Пустой финал

В пустой финал, который был представлен в Java 1.1, является конечной переменной, в объявлении которой отсутствует инициализатор.[9][10] До версии Java 1.1 последняя переменная должна была иметь инициализатор. Бланк финала, по определению «финала», может быть назначен только один раз. т.е. он должен быть не назначен, когда происходит присвоение. Для этого компилятор Java выполняет анализ потока, чтобы гарантировать, что для каждого присвоения пустой конечной переменной переменная определенно не назначается перед назначением; в противном случае возникает ошибка времени компиляции.[11]

окончательный логический hasTwoDigits;если (номер >= 10 && номер < 100) {  hasTwoDigits = истинный;}если (номер > -100 && номер <= -10) {  hasTwoDigits = истинный; // ошибка компиляции, потому что последняя переменная может быть уже назначена.}

Кроме того, перед доступом к финалу должен быть обязательно назначен пустой финал. [11]

окончательный логический даже;если (номер % 2 == 0) {  даже = истинный;}Система.из.println(даже); // ошибка компиляции, потому что переменная не была назначена в другом случае.

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

логический даже; // * не * окончательныйесли (номер % 2 == 0) {  даже = истинный;}Система.из.println(даже); // Та же ошибка компиляции, потому что не конечная переменная не была назначена в другом случае.

C / C ++ аналог конечных переменных

В C и C ++, аналогичная конструкция является const ключевое слово. Это существенно отличается от окончательный в Java, в основном в том, чтобы быть квалификатор типа: const является частью тип, не только часть идентификатора (переменной). Это также означает, что постоянство значения может быть изменено путем преобразования типов (явное преобразование типов), в данном случае известного как «преобразование констант». Тем не менее, отказ от константности и последующее изменение объекта приводит к неопределенное поведение если объект был изначально объявлен const. Java окончательный это строгое правило, из-за которого невозможно скомпилировать код, который напрямую нарушает или обходит последние ограничения. С помощью отражение однако часто все же можно изменить конечные переменные. Эта функция в основном используется, когда десериализация объекты с конечными членами.

Кроме того, поскольку C и C ++ предоставляют указатели и ссылки напрямую, существует различие между постоянством самого указателя и постоянством данных, на которые указывает указатель. Применение const к самому указателю, как в SomeClass * const ptr, означает, что указанное содержимое может быть изменено, но сама ссылка не может (без приведения). Это использование приводит к поведению, которое имитирует поведение окончательный ссылка на переменную в Java. Напротив, при применении const только к указанным данным, как в const SomeClass * ptr, содержимое не может быть изменено (без приведения), но сама ссылка может. И ссылка, и содержимое, на которое ссылаются, могут быть объявлены как const.

Рекомендации

  1. ^ Кобленц, Майкл; Саншайн, Джошуа; Олдрич, Джонатан; Майерс, Брэд; Вебер, Сэм; Шулл, Форрест (14–22 мая 2016 г.). «Изучение языковой поддержки неизменяемости». 38-я Международная конференция по программной инженерии.
  2. ^ Спецификация языка Java № 4.12.4
  3. ^ JLS 8.4.3.3. финальные методы
  4. ^ Написание заключительных классов и методов
  5. ^ Теория и практика Java: это ваш окончательный ответ?
  6. ^ Спецификация языка Java № 8.3.1.2.
  7. ^ http://geosoft.no/development/javastyle.html
  8. ^ Паттис, Ричард Э. «Больше Java». Продвинутое программирование / Практикум 15–200. Школа компьютерных наук Университет Карнеги Меллон. Получено 23 июля 2010.
  9. ^ Фланаган, Дэвид (май 1997 г.). «Глава 5 Внутренние классы и другие новые возможности языка: 5.6 Другие новые возможности Java 1.1». Java в двух словах (2-е изд.). О'Рейли. ISBN  1-56592-262-X.
  10. ^ «Глава 4. Типы, значения и переменные». Спецификация языка Java® (Java SE 8 Edition). Oracle America, Inc., 2015 г.. Получено 23 февраля 2015.
  11. ^ а б c «Определенное назначение». Спецификация языка Java® (Java SE 8 Edition). Oracle America, Inc., 2015 г.. Получено 29 октября 2016.