Перейти к содержимому

Public static final java что это

  • автор:

В чем разница между «static final» VS «final»?

final — константа, static — переменная, которая принадлежит классу, а не объекту (единая для всех объектов). static и final — в принципе вещи из разных миров, разница такая же , как между самолетом и слоном

27 ноя 2019 в 8:31

Поля с модификатором final static будут инициализироваться один раз при загрузке (инициализации) класса и хранится всё время (или то время пока востребован класс), а поля с модификатором final будут каждый раз создаваться в момент создания экземпляра класса (по крайней мере исходя из определения), для каждого экземпляра, и уничтожатся сборщиком мусора после того как экземпляр перестаёт использоваться. Но возможно сегодня оптимизатор более сложное поведение упрощает.

27 ноя 2019 в 8:58

3 ответа 3

Сортировка: Сброс на вариант по умолчанию

final — значит неизменяемая, если быть точнее, то разрешается только одна операция присвоения

static — означает единая для всех экземпляров класса.

class Foo

В данном примере:

  • для всех экземпляров класса Foo переменная FOO1 всегда будет равна 1
  • переменная foo2 — может быть разной для разных экземпляров класса Foo

При этом оба безусловно неизменяемые.

Отслеживать
ответ дан 27 ноя 2019 в 8:48
81.3k 7 7 золотых знаков 72 72 серебряных знака 153 153 бронзовых знака

Начнем с общего. static и final являются модификаторами.

На этом пожалуй всё.

Static — с англ. «статичный», «постоянный» — делает переменную или метод «независимыми» от объекта. Link

А суть модификатора final — сделать дальнейшее изменение объекта невозможным. Link

Т.е. Эти модификаторы находятся совсем в разных плоскостях взаимодействия на то, к чему они были пременены.

Это и является ответов на Ваш вопрос.

Если же вы хотите понять, что такое static и final и с чем это всё едят, то рекомендую ознакомиться ссылками представленные выше. Так как не возможно описать эти модификаторы один предложения не целяя за собой ряд тем. Уже всё хорошо описано в книгах по теме или же смело гуглить по каждому модификатору.

Что такое ключевое слово static?

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

В таком случае можно воспользоваться ключевым словом static , то есть объявить членов класса статическими. В Java большинство членов служебного класса являются статическими. Вот несколько примеров.

  • java.util.Objects содержит статические служебные операции для метода объекта.
  • java.util.Collections состоит исключительно из статических методов, которые работают с коллекциями или возвращают их.

Где можно употреблять ключевое слово static?

Мы можем использовать это ключевое слово в четырех контекстах:

  • статические методы;
  • статические переменные;
  • статические вложенные классы;
  • статические блоки.

Рассмотрим подробнее каждый из перечисленных пунктов.

Статические методы

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

public class StaticExample public static void add(int a, int b) System.out.println(a+b); 
>

public void multiply(int a, int b) System.out.println(a*b);
>
public static void main(String[] args) /** Вызов статического метода **/
StaticExample.add(1, 2);

/** Вызов не-статического метода**/
StaticExample staticExample = new StaticExample();
staticExample.multiply(1, 2);
>
>

В приведенном выше примере метод add — статический, поэтому его можно вызывать напрямую с именем класса. Нет необходимости создавать новый экземпляр класса StaticExample . Но метод multiply не является статическим. Таким образом, для нестатического метода необходимо создать новый экземпляр класса StaticExample .

Статические переменные

При создании объектов класса в Java каждый из них содержит собственную копию всех переменных класса.

Однако, если мы объявим переменную статической, все объекты класса будут использовать одну и ту же статическую переменную. Это связано с тем, что, как и статические методы, статические переменные также связаны с классом. И объекты класса для доступа к статическим переменным создавать не нужно. Например:

public class VariableExample public String normalVariable = null; 
public static String staticVariable = null;
public static void main(String[] args) VariableExample firstExample = new VariableExample();
firstExample.normalVariable = "Hello";
firstExample.staticVariable = "Hello";//Это то же самое, что VariableExample.staticVariable = "Hello"
VariableExample secondExample = new VariableExample();
System.out.println("normalVariable: "+ secondExample.normalVariable);
System.out.println("staticVariable: "+ secondExample.staticVariable);
>
>

В приведенном выше примере normalVariable — переменная класса, а staticVariable — статическая переменная. Если вы объявите переменную, как показано ниже:

firstExample.staticVariable = “Hello”

Это похоже на доступ к статической переменной через имя класса:

VariableExample.staticVariable = “Hello”

Здесь статические переменные — переменные уровня класса. Поэтому, если вы обращаетесь к статическим переменным через переменную экземпляра объекта, то это отражается на соответствующей статической переменной уровня класса. Таким образом, статические переменные всегда имеют одно и то же значение. Но переменная экземпляра сохраняет отдельное значение в каждом экземпляре объекта. Таким образом, приведенный выше фрагмент кода выведет:

normalVariable: nullstaticVariable: Hello

Статические переменные — редкость в Java. Вместо них применяют статические константы. Они определяются ключевым словом static final и представлены в верхнем регистре. Вот почему некоторые предпочитают использовать верхний регистр и для статических переменных.

Статические блоки

Здесь мы видим статический блок с синтаксисом:

static >

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

public class VariableExample public static String staticVariable = null; 

static staticVariable = "Hello Variable";
>

public static void main(String[] args) System.out.println(staticVariable);
>
>

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

public class VariableExample public static String staticVariable = null; 

static staticVariable = "Hello Variable";
>

static staticVariable = "Hello Again";
>

public static void main(String[] args) System.out.println(staticVariable);
>
>

Вывод приведенного выше фрагмента кода выглядит следующим образом, потому что переменная staticVariable обновлена вторым значением статического блока.

Hello Again

Вложенный статический класс

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

Вложенный класс является членом заключающего его класса. Нестатические вложенные классы (внутренние классы) имеют доступ к другим членам заключающего класса, даже если они объявлены приватными. Статические вложенные классы не имеют доступа к другим членам заключающего класса.

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

Со статическим внутренним классом все иначе. Можно сказать, что если у внутреннего класса нет причин для доступа к внешнему, вы должны сделать его статическим по умолчанию.

public class InnerClassExample public String outerElement; 
private String outerPrivateElement;
private void outerMethod()
>
public static class MyStaticInnerClass public void myFunction() System.out.println("calling static class inner function");
>
>
public class MyInnerClass public void myAnotherFunction() outerElement = "sample";
outerPrivateElement ="Hello Private";
outerMethod();
>
>
public static void main(String[] args) /**Вызов статического внутренего класса **/
InnerClassExample.MyStaticInnerClass staticInner = new InnerClassExample.MyStaticInnerClass();
staticInner.myFunction();
/*Вызов внутреннего класса **/
InnerClassExample.MyInnerClass innerClass = new InnerClassExample().new MyInnerClass();
innerClass.myAnotherFunction();
>
>

В приведенном выше примере видно, что внутренний класс MyInnerClass может получить доступ ко всем методам и переменным внешнего, включая приватные переменные. Статическому внутреннему классу, напротив, недоступны какие-либо методы или переменные внешнего класса.

Где в памяти Java хранятся статические классы и переменные?

Вплоть до 8-й версии Java статические методы и переменные хранились в пространстве permgen. Но потом было введено новое пространство памяти, называемое метапространством — в нем хранятся все эти имена и поля класса, методы класса с байт-кодом методов, пул констант, JIT-оптимизации и т. д. Причина удаления permgen в Java 8.0 в том, что очень сложно предсказать необходимый размер permgen.

Зачем нужно ключевое слово final?

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

  • Конечная переменная предназначена для создания постоянных значений.
  • Конечный метод предотвращает переопределение метода.
  • Конечный класс предотвращает наследование.

Что такое конечная переменная и когда ей стоит воспользоваться?

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

Существует три способа инициализации конечной переменной.

  • Инициализировать конечную переменную, когда она будет объявлена. Это самый распространенный подход. Если конечная переменная не инициализирована при объявлении, она называется пустой конечной переменной. Ниже приведены два способа инициализации пустой конечной переменной.
  • Пустая конечная переменная может быть инициализирована внутри блока инициализатора экземпляра или внутри конструктора. Если в классе несколько конструкторов, то переменную необходимо инициализировать во всех из них, в противном случае во время компиляции появится ошибка.
  • Пустая конечная статическая переменная может быть инициализирована в статическом блоке.
public class FinalVariableExample public final String MY_VARIABLE_1 = "Variable Initialized"; 
public final static String MY_VARIABLE_2;
public final String MY_VARIABLE_3;
public final String MY_VARIABLE_4 ;

static MY_VARIABLE_2 = "Static Varible Initialized";
>

MY_VARIABLE_3 = "block Initialized";
>

public FinalVariableExample() MY_VARIABLE_4 = "Constructor Initialized";
>

>

Когда следует применять конечную переменную

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

Где использовать конечные классы

Когда класс объявляется с ключевым словом final , он называется конечным. Конечный класс не может быть расширен (унаследован). У него есть два применения.

Первое, безусловно, заключается в предотвращении наследования, так как конечные классы не могут быть расширены. Например, все классы-оболочки, такие как Integer , Float и т. д. — конечные классы.

public final class MyFinalClass 
>

public class MyClass extends MyFinalClass
>

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

Другое применение final с классами заключается в создании неизменяемого класса, подобного предопределенному классу String . Нельзя сделать класс неизменяемым, не сделав его конечным.

Когда использовать конечные методы

Когда метод объявляется с ключевым словом final , он называется конечным методом. Такой метод не может быть переопределен. Это присутствует в классе Object — ряд его методов конечные. С ключевым словом final объявляются методы, для которых необходимо следовать одной и той же реализации во всех производных классах. Фрагмент ниже иллюстрирует метод с ключевым словом final :

public class MyParentClass public final void myFinalMethod() . 
>
>

public class MyClass extends MyParentClass public final void myFinalMethod() .
>
>

Здесь происходит попытка переопределить метод final из родительского класса. Java этого не позволяет и выдает следующее исключение:

Надеемся, теперь вы хорошо поняли ключевые слова static и final .

  • Используй Async/Await в JavaScript, как профессионал
  • Введение в байт-код Java
  • Жизненный цикл потока в Java

Ключевые слова static и final

Это занятие начнем с рассмотрения довольно значимой конструкции ООП – статических полей и методов класса. Возможно, вы уже обращали внимание, на ключевое слово static перед функцией main:

public static void main(String[] args) { . }

Сейчас мы разберемся, что оно означает. В любом классе при объявлении переменных можно перед их типами прописывать это ключевое слово, например, так:

class Point { static int cnt; int x, y; }

Спрашивается: в чем отличие между полями x, y и статическим полем cnt? Лучше всего это продемонстрировать на следующем рисунке.

Когда создаются объекты класса Point:

Point pt1 = new Point(); Point pt2 = new Point();

то в каждый из них будут помещены копии полей x, y, но поле cnt скопировано не будет, оно останется на уровне класса Point и станет общим для всех экземпляров этого класса. То есть, при обращении к статической переменной cnt из любого экземпляра класса, например, вот так:

class Point { static int cnt; int x, y; { x = y = -1; } Point() { cnt++; } }

Мы при создании нового объекта в конструкторе будем увеличивать общую для всех экземпляров переменную cnt на единицу. И, так как эта переменная общая, то при создании двух объектов этот счетчик будет равен двум. В этом можно убедиться, если вывести его значение в консоль:

System.out.println(Point.cnt);

Обратите внимание, мы обращаемся к статическому полю cnt напрямую через класс Point, т.к. оно хранится непосредственно в нем. То же самое будет, если обратиться к этой переменной через ссылку pt1:

System.out.println(pt1.cnt);

Мы здесь обращаемся к той же самой статической переменной cnt. Думаю, этот момент с общим полем понятен, давайте теперь выполним его начальную инициализацию. Для обычных переменных мы использовали инициализатор в виде фигурных скобок. Статические тоже можно помещать в этот блок, но тогда они будут инициализироваться заново каждый раз при создании нового объекта:

{ cnt = 0; x = y = -1; }

Если выполнить программу, то в консоли вместо двух увидим 1, так как наш счетчик cnt обнуляется каждый раз при создании нового объекта. Вместе с тем, логика статических полей подразумевает их одноразовую инициализацию. Это можно сделать двумя способами. В первом, самом простом, мы присваиваем начальное значение сразу в момент объявления статической переменной:

static int cnt = 0;

Второй способ подразумевает использование статического инициализатора:

static { cnt = 0; }

Такой инициализатор срабатывает только один раз в момент создания класса Point в памяти устройства и используется исключительно для статических полей и методов класса.

Вот так ведут себя статические переменные. Чтобы лучше понять логику их функционирования, отмечу, что ключевое слово static указывает компилятору создавать переменную один раз в процессе работы программы в определенной области памяти и не перемещать ее куда бы то ни было (в другую область памяти), в том числе не создавать ее копии. Поэтому, статическая переменная объявленная в классе Point создается внутри него и не переходит в экземпляры этого класса, а остается общей для всех.

Статические методы

Похожим образом ведут себя и статические методы классов: они принадлежат непосредственно классу и не дублируются в его экземпляры. Давайте для примера добавим статический метод в класс Point:

class Point { private static int cnt = 0; int x, y; . public static int getCounter() { return cnt; } }

Мы здесь сделали поле cnt приватным, а доступ к нему – через геттер getCounter. И обратите внимание в какой последовательности записываются модификаторы доступа, слово static и тип поля или метода:

Соответственно, в функции main мы теперь должны прописать этот геттер вместо прямого обращения к cnt:

System.out.println(Point.getCounter());

В итоге картина расположения статических и нестатических полей будет следующей:

Здесь статический метод getCounter() общий для всех экземпляров класса Point. Фактически, он принадлежит только классу Point, но также может вызываться из его объектов. Именно поэтому, мы можем обращаться к нему непосредственно через класс Point, используя конструкцию:

Так можно делать с любыми статическими методами и полями класса. Но статичность налагает и определенные ограничения. В частности, метод getCounter() может получать доступ к статической переменной cnt, но не может работать с динамическими полями x, y. Действительно, если написать что-то вроде:

public static int getCounter() { return x; }

то возникнет ошибка, так как статический метод на уровне класса просто «не видит» переменные x, y в экземплярах. Все что ему доступно – это другие статические методы и поля класса Point. То есть, из статических методов можно вызывать только другие статические методы и обращаться исключительно к статическим переменным. Или же, создавать объекты и переменные непосредственно внутри статического метода:

public static int getCounter() { int x = 5; return x; }

Отчасти это мы делаем в статическом методе main().

Статический импорт

Наконец, есть еще такое понятие как статический импорт. Что это такое? Все достаточно просто. Если в пакете имеются классы со статическими элементами, например, в пакете System мы обращаемся к классу out со статическим методом println(), то простой импорт:

import java.lang.System.*;

не импортирует такой класс, только классы без статических данных. А вот если добавить ключевое слово static:

то получим статический импорт и класс out появится в нашем текущем модуле.

Вот так работают и используются статические переменные и методы классов.

Ключевое слово final

В заключении этого занятия я расскажу об еще одном ключевом слове final. Оно позволяет задавать константы в языке Java. Например, если у поля cnt добавить это ключевое слово, то дальнейшее изменение переменной cnt станет невозможным:

private static final int cnt = 0;

Опять же, обратите внимание на очередность ключевых слов. Конечно, это странный пример, поэтому давайте оставим переменную cnt как обычную статическую, а в класс Point добавим еще одно поле с указанием final:

final int MAX_COORD = 10;

Теперь, переменная MAX_COORD является константой и не может быть изменена. Мало того, мы должны таким переменным сразу присваивать определенное значение, то есть, записывать их вот так нельзя:

final int MAX_COORD;

Ключевое слово final можно использовать и у методов. Но особенность их поведения будет проявляться только в момент наследования. Поэтому я буду говорить об этом в теме наследования классов.

Путь кодера

Подвиг 1. Объявите класс ShopItem для представления продуктов в магазине с полями: id (идентификатор – целое число), название товара, габариты, вес, цена. Поле id должно быть уникальным для каждого объекта класса. Это следует реализовать через статическую переменную, которая подсчитывает количество создаваемых экземпляров.

Подвиг 2. Реализовать класс Rect для описания прямоугольника с полями: x1, y1, x2, y2 – координат вершин верхнего правого и нижнего левого углов. Прописать два статических метода для вычисления ширины и высоты прямоугольника. В качестве параметра этим методам передавать ссылку на экземпляр класса Rect, для которого выполняется вычисление.

Подвиг 3. Реализовать класс Singleton, в котором определить статический метод getInstance(). Этот метод должен возвращать экземпляр класса, если он еще не создавался. Иначе, возвращается ссылка на ранее созданный экземпляр. Также следует запретить создание объектов класса Singleton напрямую через оператор new. (Полученная реализация будет гарантировать существование только одного экземпляра класса в процессе работы программы и, фактически, является примером известного паттерна singleton).

Видео по теме

#11 Концепция объектно-ориентированного программирования (ООП)

#12 Классы и создание объектов классов

#13 Конструкторы, ключевое слово this, инициализаторы

#14 Методы класса, сеттеры и геттеры, public, private, protected

#15 Пакеты, модификаторы конструкторов и классов

#16 Ключевые слова static и final

#17 Внутренние и вложенные классы

#18 Как делается наследование классов

#19 Ключевое слово super, оператор instanceof

#20 Модификаторы private и protected, переопределение методов, полиморфизм

#21 Абстрактные классы и методы

#22 Интерфейсы — объявление и применение

#23 Интерфейсы — приватные, статические и дефолтные методы, наследование интерфейсов

#24 Анонимные внутренние классы

#25 Перечисления (enum)

#26 Обобщения классов (Generics)

#27 Ограничения типов, метасимвольные аргументы, обобщенные методы и конструкторы

#28 Обобщенные интерфейсы, наследование обобщенных классов

© 2024 Частичное или полное копирование информации с данного сайта для распространения на других ресурсах, в том числе и бумажных, строго запрещено. Все тексты и изображения являются собственностью сайта

Ключевое слово «final» в Java: как это работает?

Типичная проблема, с которой сталкиваются новички в Java — понимание работы ключевого слова final . В Java это ключевое слово используется для того, чтобы указать, что значение переменной не должно меняться. Но есть нюансы, которые могут запутать новичка. Рассмотрим пример.

import java.util.ArrayList; import java.util.List; class Test < private final List foo; public Test() < foo = new ArrayList(); foo.add("foo"); >public static void main(String[] args) < Test t = new Test(); t.foo.add("bar"); System.out.println("print - " + t.foo); >>

В этом коде используется ключевое слово final для переменной foo . И, несмотря на это, в конструкторе класса и в main методе к этому списку добавляются элементы. Код работает и не вызывает ошибок компиляции.

Теперь давайте попробуем изменить переменную на static :

private static final List foo;

И в этом случае мы увидим ошибку компиляции. Почему так происходит? Как на самом деле работает final ?

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

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

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *