Что значит статическая переменная
10 заметок о модификаторе Static в Java
Статические поля
Статический блок
Статический метод
Статический класс в Java
Что должен знать каждый программист о модификаторе Static в Java
Вы НЕ можете получить доступ к НЕ статическим членам класса, внутри статического контекста, как вариант, метода или блока. Результатом компиляции приведенного ниже кода будет ошибка:
В отличие от локальных переменных, статические поля и методы НЕ потокобезопасны (Thread-safe) в Java. На практике это одна из наиболее частых причин возникновения проблем связанных с безопасностью мультипоточного программирования. Учитывая что каждый экземпляр класса имеет одну и ту же копию статической переменной, то такая переменная нуждается в защите — «залочивании» классом. Поэтому при использовании статических переменных, убедитесь, что они должным образом синхронизированы (synchronized), во избежание проблем, например таких как «состояние гонки» (race condition).
Статические методы имеют преимущество в применении, т.к. отсутствует необходимость каждый раз создавать новый объект для доступа к таким методам. Статический метод можно вызвать, используя тип класса, в котором эти методы описаны. Именно поэтому, подобные методы как нельзя лучше подходят в качестве методов-фабрик ( factory ), и методов-утилит ( utility ). Класс java.lang.Math — замечательный пример, в котором почти все методы статичны, по этой же причине классы-утилиты в Java финализированы ( final ).
Другим важным моментом является то, что вы НЕ можете переопределять ( Override ) статические методы. Если вы объявите такой же метод в классе-наследнике ( subclass ), т.е. метод с таким же именем и сигнатурой, вы лишь «спрячете» метод суперкласса ( superclass ) вместо переопределения. Это явление известно как сокрытие методов ( hiding methods ). Это означает, что при обращении к статическому методу, который объявлен как в родительском, так и в дочернем классе, во время компиляции всегда будет вызван метод исходя из типа переменной. В отличие от переопределения, такие методы не будут выполнены во время работы программы. Рассмотрим пример:
Внутри родительского класса/статического метода
Модификатор static также может быть объявлен в статичном блоке, более известным как «Статический блок инициализации» ( Static initializer block ), который будет выполнен во время загрузки класса. Если вы не объявите такой блок, то Java соберёт все статические поля в один список и выполнит его во время загрузки класса. Однако, статичный блок НЕ может пробросить перехваченные исключения, но может выбросить не перехваченные. В таком случае возникнет «Exception Initializer Error». На практике, любое исключение возникшее во время выполнения и инициализации статических полей, будет завёрнуто Java в эту ошибку. Это также самая частая причина ошибки «No Class Def Found Error», т.к. класс не находился в памяти во время обращения к нему.
Полезно знать, что статические методы связываются во время компиляции, в отличие от связывания виртуальных или не статических методов, которые связываются во время исполнения на реальном объекте. Следовательно, статические методы не могут быть переопределены в Java, т.к. полиморфизм во время выполнения не распространяется на них. Это важное ограничение, которое необходимо учитывать, объявляя метод статическим. В этом есть смысл, только тогда, когда нет возможности или необходимости переопределения такого метода классами-наследниками. Методы-фабрики и методы-утилиты хорошие образцы применения модификатора static. Джошуа Блох выделил несколько преимуществ использования статичного метода-фабрики перед конструктором, в книге «Effective Java», которая является обязательной для прочтения каждым программистом данного языка.
На этом всё. Все вышеперечисленные пункты о модификаторе static в Java обязан знать каждый программист. В данной статье была рассмотрена базовая информация о статических переменных, полях, методах, блоках инициализации и импорте. В том числе некоторые важные свойства, знание которых является критичным при написании и понимании программ на Java. Я надеюсь, что каждый разработчик доведёт свои навыки использования статических концептов до совершенства, т.к. это очень важно для серьёзного программирования.»
Статические переменные
1. Статические переменные
Когда класс загружается в память, для него сразу создается статический объект класса. Этот объект хранит статические переменные класса (статические поля класса). Статический объект класса существует, даже если не был создан ни один обычный объект класса.
Когда мы описываем переменные в классе, мы указываем, будут ли эти переменные созданы всего один раз или же нужно создавать их копии для каждого объекта. По умолчанию создаётся новая копия переменной для каждого объекта.
Статическая (static) же переменная привязана к статическому объекту класса и всегда существует в единственном экземпляре.
Если статической переменной не присвоить стартовое значение, она инициализируется значением по умолчанию:
Тип | Значение по умолчанию |
---|---|
(то же самое, что и ) | |
и любые классы |
Обращаться к статической переменной в ее классе можно просто по имени. Если обращение идет из другого класса, то перед именем статической переменной нужно писать имя класса.
2. Отличие статических и нестатических переменных
Чем же отличаются обычные и статические переменные?
Обычные переменные класса привязаны к объектам своего класса (экземплярам класса), статические переменные — к статическому объекту класса.
Если экземпляров класса несколько, в каждом из них существует своя копия нестатических (обычных) переменных класса. Статические переменные класса всегда находятся внутри статического объекта класса и существуют только в одном экземпляре.
Обращаться к обычным переменным класса (полям класса) можно только имея ссылку на объект класса. Ну или в методах внутри этого же класса.
Обращение к полю класса с использованием ссылки на объект класса |
---|
Обращаться к статическим переменным можно откуда угодно (с учетом модификаторов видимости): из обычных методов, из статических методов того же класса, из методов других классов и т.п.
Обращение к статическому полю класса не используя ссылку на объект класса |
---|
Устройство в памяти:
Допустим, у нас есть класс Person с 4 полями: два статических, а два — нет.
Сразу после загрузки класса
После создания первого объекта
Обратите внимание, что хоть у объектов по две переменные, это разные переменные: у обычного объекта — обычные, у статического — статические.
Нужно больше объектов
Обратите внимание: у каждого объекта есть собственная переменная age и name.
Готовимся к собеседованию по PHP: ключевое слово «static»
Не секрет, что на собеседованиях любят задавать каверзные вопросы. Не всегда адекватные, не всегда имеющие отношение к реальности, но факт остается фактом — задают. Конечно, вопрос вопросу рознь, и иногда вопрос, на первый взгляд кажущийся вам дурацким, на самом деле направлен на проверку того, насколько хорошо вы знаете язык, на котором пишете.
Попробуем разобрать «по косточкам» один из таких вопросов — что значит слово «static» в PHP и зачем оно применяется?
Ключевое слово static имеет в PHP три различных значения. Разберем их в хронологическом порядке, как они появлялись в языке.
Значение первое — статическая локальная переменная
Однако всё меняется, если мы перед присваиванием поставим ключевое слово static:
Подводные камни статических переменных
Разумеется, как всегда в PHP, не обходится без «подводных камней».
Камень первый — статической переменной присваивать можно только константы или константные выражения. Вот такой код:
с неизбежностью приведет к ошибке парсера. К счастью, начиная с версии 5.6 стало допустимым присвоение не только констант, но и константных выражений (например — «1+2» или «[1, 2, 3]»), то есть таких выражений, которые не зависят от другого кода и могут быть вычислены на этапе компиляции
Камень второй — методы существуют в единственном экземпляре.
Тут всё чуть сложнее. Для понимания сути приведу код:
Такое поведение может быть неожиданным для неподготовленного к нему разработчика и послужить источником ошибок. Нужно заметить, что наследование класса (и метода) приводит к тому, что всё-таки создается новый метод:
Вывод: динамические методы в PHP существуют в контексте классов, а не объектов. И только лишь в рантайме происходит подстановка «$this = текущий_объект»
Значение второе — статические свойства и методы классов
В объектной модели PHP существует возможность задавать свойства и методы не только для объектов — экземпляров класса, но и для класса в целом. Для этого тоже служит ключевое слово static:
Для доступа к таким свойствам и методам используются конструкции с двойным двоеточием («Paamayim Nekudotayim»), такие как ИМЯ_КЛАССА::$имяПеременной и ИМЯ_КЛАССА:: имяМетода().
Само собой разумеется, что у статических свойств и статических методов есть свои особенности и свои «подводные камни», которые нужно знать.
Особенность вторая — static не аксиома!
Обратное не совсем верно:
И кстати, всё написанное выше относится только к методам. Использование статического свойства через «->» невозможно и ведет к фатальной ошибке.
Значение третье, кажущееся самым сложным — позднее статическое связывание
Разработчики языка PHP не остановились на двух значениях ключевого слова «static» и в версии 5.3 добавили еще одну «фичу» языка, которая реализована тем же самым словом! Она называется «позднее статическое связывание» или LSB (Late Static Binding).
Понять суть LSB проще всего на несложных примерах:
Ключевое слово self в PHP всегда значит «имя класса, где это слово написано». В данном случае self заменяется на класс Model, а self::$table — на Model::$table.
Такая языковая возможность называется «ранним статическим связыванием». Почему ранним? Потому что связывание self и конкретного имени класса происходит не в рантайме, а на более ранних этапах — парсинга и компиляции кода. Ну а «статическое» — потому что речь идет о статических свойствах и методах.
Немного изменим наш код:
Теперь вы понимаете, почему PHP ведёт себя в этой ситуации неинтуитивно. self был связан с классом Model тогда, когда о классе User еще ничего не было известно, поэтому и указывает на Model.
Для решения этой дилеммы был придуман механизм связывания «позднего», на этапе рантайма. Работает он очень просто — достаточно вместо слова «self» написать «static» и связь будет установлена с тем классом, который вызывает данный код, а не с тем, где он написан:
Это и есть загадочное «позднее статическое связывание».
Нужно отметить, что для большего удобства в PHP кроме слова «static» есть еще специальная функция get_called_class(), которая сообщит вам — в контексте какого класса в данный момент работает ваш код.
6.10 – Статические локальные переменные
Термин статический ( static ) – один из самых запутанных терминов в языке C++, в значительной степени потому, что static в разных контекстах имеет разные значения.
В предыдущих уроках мы рассмотрели, что глобальные переменные имеют статическую продолжительность, что означает, что они создаются при запуске программы и уничтожаются при ее завершении.
Мы также обсудили, как ключевое слово static обеспечивает внутреннее связывание глобального идентификатора, что означает, что идентификатор может использоваться только в том файле, в котором он определен.
В этом уроке мы рассмотрим использование ключевого слова static в применении к локальной переменной.
Статические локальные переменные
В уроке «2.4 – Локальная область видимости в C++» видимости вы узнали, что локальные переменные по умолчанию имеют автоматическую продолжительность, что означает, что они создаются в точке определения и уничтожаются при выходе из блока.
Использование ключевого слова static для локальной переменной изменяет ее продолжительность с автоматической на статическую. Это означает, что теперь переменная создается при запуске программы и уничтожается при ее завершении (как глобальная переменная). В результате статическая переменная сохранит свое значение даже после выхода за пределы области видимости!
Показать разницу между автоматической и статической продолжительностями переменных проще всего на примере.
Автоматическая продолжительность (по умолчанию):
Теперь рассмотрим статическую версию этой программы. Единственная разница между этой и приведенной выше программами состоит в том, что мы с помощью ключевого слова static изменили продолжительность локальной переменной с автоматической на статическую.
Статическая продолжительность (с использованием ключевого слова static ):
В этой программе, поскольку s_value была объявлена как статическая, s_value создается и инициализируется один раз (при запуске программы). Если бы мы для инициализации s_value не использовали константное выражение, при запуске программы она была бы инициализирована нулем, а затем, при первом обнаружении определения переменной, инициализировалась бы нашим предоставленным значением инициализации (но при последующих вызовах она не повторно инициализируется).
Точно так же, как мы используем » g_ » для префикса глобальных переменных, для префикса статических (со статической продолжительностью) локальных переменных часто используется » s_ «.
Одно из наиболее распространенных применений локальных переменных статической продолжительности – генераторы уникальных идентификаторов. Представьте себе программу, в которой есть много похожих объектов (например, игра, в которой на вас нападает множество зомби, или симуляция, в которой вы показываете много треугольников). Если вы заметили дефект, будет практически невозможно определить, с каким объектом возникли проблемы. Однако если каждому объекту при создании присваивается уникальный идентификатор, тогда при дальнейшей отладке объекты будет проще различать.
Сгенерировать уникальный идентификационный номер очень просто с помощью локальной переменной статической продолжительности:
При первом вызове этой функции она возвращает 0. Во второй раз она возвращает 1. Каждый раз, когда она вызывается, она возвращает число на единицу больше, чем при предыдущем вызове. Вы можете присвоить эти номера своим объектам как уникальные идентификаторы. Поскольку s_itemID является локальной переменной, другие функции не могут «подделать» ее.
Статические переменные обладают некоторыми преимуществами глобальных переменных (они не уничтожаются до конца работы программы), ограничивая при этом свою видимость до области видимости блока. Это делает их безопасными для использования, даже если вы регулярно меняете их значения.
Чрезмерное использование статических локальных переменных
Рассмотрим следующий код
Пример вывода программы:
Предположим, мы хотим добавить в калькулятор вычитание, чтобы вывод программы выглядел следующим образом
Мы могли бы попытаться использовать getInteger() для чтения следующих двух целых чисел, как мы это делали для сложения.
Но это не сработало, на выходе мы получаем:
При запросе первого числа для вычитания мы получаем « Enter another integer » (введите другое целое число) вместо « Enter an integer » (введите целое число).
getInteger() нельзя использовать повторно, потому что у нее есть внутреннее состояние (статическая локальная переменная s_isFirstCall ), которое не может быть сброшено извне. s_isFirstCall – это не та переменная, которая должна быть уникальной во всей программе. Хотя наша программа отлично работала, когда мы написали ее впервые, статическая локальная переменная не позволяет нам повторно использовать функцию в дальнейшем.
Лучший способ реализовать getInteger – передать s_isFirstCall в качестве параметра. Это позволяет вызывающей функции выбрать, какая подсказка будет напечатана.
Статические локальные переменные следует использовать только в том случае, если во всей вашей программе и в обозримом будущем вашей программы переменная уникальна и не имеет смысла эту переменную сбрасывать.
Лучшая практика
Избегайте использования статических локальных переменных, если только переменную не нужно сбрасывать. Статические локальные переменные уменьшают возможность повторного использования и затрудняют понимание функций.
Небольшой тест
Вопрос 1
Как влияет использование ключевого слова static на глобальную переменную? Как оно влияет на локальную переменную?
При применении к глобальной переменной ключевое слово static определяет глобальную переменную как имеющую внутреннее связывание, что означает, что переменная не может быть экспортирована в другие файлы.
При применении к локальной переменной ключевое слово static определяет локальную переменную как имеющую статическую продолжительность, что означает, что переменная будет создана только один раз и не будет уничтожена до конца выполнения программы.
Все о ключевых словах static и final
Что такое ключевое слово static?
Чтобы получить доступ к членам класса в Java, нужно сначала создать экземпляр класса, а затем вызвать членов класса с помощью переменной экземпляра. Но иногда нужно получить доступ к членам класса, не создавая никаких переменных.
Где можно употреблять ключевое слово static?
Мы можем использовать это ключевое слово в четырех контекстах:
Рассмотрим подробнее каждый из перечисленных пунктов.
Статические методы
Статические методы также называются методами класса, потому что статический метод принадлежит классу, а не его объекту. Кроме того, статические методы можно вызывать напрямую через имя класса.
Статические переменные
При создании объектов класса в Java каждый из них содержит собственную копию всех переменных класса.
Однако, если мы объявим переменную статической, все объекты класса будут использовать одну и ту же статическую переменную. Это связано с тем, что, как и статические методы, статические переменные также связаны с классом. И объекты класса для доступа к статическим переменным создавать не нужно. Например:
В приведенном выше примере normalVariable — переменная класса, а staticVariable — статическая переменная. Если вы объявите переменную, как показано ниже:
Это похоже на доступ к статической переменной через имя класса:
Здесь статические переменные — переменные уровня класса. Поэтому, если вы обращаетесь к статическим переменным через переменную экземпляра объекта, то это отражается на соответствующей статической переменной уровня класса. Таким образом, статические переменные всегда имеют одно и то же значение. Но переменная экземпляра сохраняет отдельное значение в каждом экземпляре объекта. Таким образом, приведенный выше фрагмент кода выведет:
Статические переменные — редкость в Java. Вместо них применяют статические константы. Они определяются ключевым словом static final и представлены в верхнем регистре. Вот почему некоторые предпочитают использовать верхний регистр и для статических переменных.
Статические блоки
Здесь мы видим статический блок с синтаксисом:
Статические блоки применяют для инициализации статических переменных. Статический блок выполняется только один раз, когда класс загружается в память. Это происходит, если в коде запрашивается либо объект класса, либо статические члены этого класса.
Класс может содержать несколько статических блоков, а каждый из них выполняется в той же последовательности, в которой они написаны в коде.
Вывод приведенного выше фрагмента кода выглядит следующим образом, потому что переменная staticVariable обновлена вторым значением статического блока.
Вложенный статический класс
В Java можно объявить класс внутри другого класса. Такие классы называются вложенными классами. Они бывают двух типов: статические и нестатические.
Вложенный класс является членом заключающего его класса. Нестатические вложенные классы (внутренние классы) имеют доступ к другим членам заключающего класса, даже если они объявлены приватными. Статические вложенные классы не имеют доступа к другим членам заключающего класса.
Внутренний класс по умолчанию содержит неявную ссылку на объект внешнего класса. Если вы создадите экземпляр этого объекта из кода внешнего класса, все будет сделано за вас. Если вы поступите иначе, вам необходимо предоставить объект самостоятельно.
Со статическим внутренним классом все иначе. Можно сказать, что если у внутреннего класса нет причин для доступа к внешнему, вы должны сделать его статическим по умолчанию.
В приведенном выше примере видно, что внутренний класс MyInnerClass может получить доступ ко всем методам и переменным внешнего, включая приватные переменные. Статическому внутреннему классу, напротив, недоступны какие-либо методы или переменные внешнего класса.
Где в памяти Java хранятся статические классы и переменные?
Вплоть до 8-й версии Java статические методы и переменные хранились в пространстве permgen. Но потом было введено новое пространство памяти, называемое метапространством — в нем хранятся все эти имена и поля класса, методы класса с байт-кодом методов, пул констант, JIT-оптимизации и т. д. Причина удаления permgen в Java 8.0 в том, что очень сложно предсказать необходимый размер permgen.
Зачем нужно ключевое слово final?
Что такое конечная переменная и когда ей стоит воспользоваться?
Существует три способа инициализации конечной переменной.
Когда следует применять конечную переменную
Единственное различие между обычной и конечной переменной в том, что первой можно переприсвоить значение, а второй — нельзя. Следовательно, конечные переменные должны использоваться только для значений, которые необходимо сохранять постоянными на протяжении выполнения программы.
Где использовать конечные классы
При попытке расширить конечный класс компилятор выдаст следующее исключение:
Когда использовать конечные методы
Здесь происходит попытка переопределить метод final из родительского класса. Java этого не позволяет и выдает следующее исключение: