Главная » Разработка для Android » Объявления final и static- JAVA ДЛЯ ANDROID

0

 

В языке Java существует 11 ключевых слов-модификаторов, которые могут применяться к объявлению. Эти модификаторы изменяют поведение объявленного объекта, иногда довольно существенно. В предыдущих примерах некоторые модификаторы, например publіс и private, уже использовались без пояснений. Эти, а также некоторые другие модификаторы управляют областями действия и видимости. Далее мы рассмотрим их подробнее. В данном разделе мы поговорим еще о двух модификаторах, важных для полного понимания системы типов Java: final и static.

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

Применительно к классу final означает, что любая попытка определить подкласс вызовет ошибку. Например, класс String является final, поскольку строки должны быть постоянными (это значит, что после того, как строка создана, ее содержание должно оставаться неизменным). Немного поразмыслив над этой ситуацией, приходим к выводу, что выполнение такого условия можно гарантировать лишь в случае, если от Stri ng не смогут образовываться подтипы. Если бы от класса String можно было образовывать подтипы, то какая-нибудь коварная библиотека могла бы создать подкласс String – DeadlyString, – передать его экземпляр в ваш код и изменить его значение с fred на DROP TABLE. (Проиллюстрирована попытка внедрения в ваш код инородного SQL, который способен удалить части базы данных.) Это могло бы произойти сразу после того, как ваш код проведет проверку (валидацию) содержимого этой строки!

Применительно к методу final означает, что данный метод не может быть переопределен в подклассе. Разработчики используют final-методы для проектирования наследования, когда супертип должен сообщить подклассу поведение, которое сильно зависит от конкретной реализации, и супертип не может разрешить, чтобы это поведение изменялось. Фреймворк, в котором реализован универсальный (generic) кэш, может определить базовый класс под названием, например, CacheableObject. Программист, использующий фреймворк, создает подтип этого класса для каждого нового кэшируемого типа объекта. Однако для поддержания целостности фреймворка классу Cacheabl eObject может потребоваться рассчитать ключ кэша, который должен быть единообразным в объектах любых типов. В данном случае метод computeCacheKey может быть объявлен как final.

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

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

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

Объявление static относится к классу, в котором оно описывается, а не к экземпляру этого класса, static – это модификатор «статичности», противоположный «динамичности». Подразумевается, что, если сущность не объявлена как статическая, она является динамической. Проиллюстрируем это на примере:

В данном примере QuietStatic – это имя класса, а ех – ссылка на экземпляр этого класса. Статический член classMember – это атрибут класса. Чтобы сослаться на него, его нужно просто квалифицировать именем класса. С другой стороны, іnstanceMember является членом экземпляра класса. Попытка сослаться на него через ссылку класса вызовет ошибку. И это объяснимо. Существует много различных переменных с названием іnstanceMember, и каждая из них относится к отдельному экземпляру QuietStatic. Если явно не указать ту переменную, о которой вы говорите, Java не сможет понять, какая из этих переменных имеется в виду.

Как видно из второй пары утверждений, Java на самом деле допускает ссылки на (статические) переменные класса через ссылку на экземпляр. Но такая практика не приветствуется, так как приводит к путанице. В большинстве компиляторов и интегрированных сред разработки вы получите предупреждение, если попытаетесь сделать что-то подобное.

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

И его вывод:

Исходным значением переменной elassMember в предыдущем примере является 0. Оно увеличивается на 1 каждым из двух отдельных экземпляров. Теперь оба экземпляра видят новое значение – 2. Значение переменной instanceMember также начинается с 0 в каждом экземпляре. С другой стороны, каждый экземпляр выполняет приращение собственной копии и просматривает значение собственной переменной, равное 1.

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

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

В современном языке Java практически нецелесообразно использовать статические методы. В ранних реализациях Java динамическое назначение методов протекало значительно медленнее, чем статическое. Разработчики предпочитали пользоваться статическими методами для оптимизации кода. В Android, где среда Dalvik обеспечивает динамическую компиляцию, в оптимизации такого рода больше нет нужды. Чрезмерное использование статических методов обычно считается признаком некачественной архитектуры.

Разница между статически и динамически объявленными классами особенно тонкая: Большинство классов, из которых состоит приложение, являются статическими. Как правило, класс определяется на верхнем уровне – за пределами внешнего блока. Предполагается, что все такие объявления должны быть статическими. С другой стороны, большинство других объявлений происходит внутри внешнего блока определенного класса, то есть по умолчанию они являются динамическими. В то время как большинство полей по умолчанию являются динамическими и требуют модификатора, чтобы стать статическими, большинство классов сразу являются статическими.

На самом деле система очень стройная. Согласно тому, как мы определили признак statiс – принадлежность к классу, а не к экземпляру этого класса, – объявления верхнего уровня должны быть статическими (поскольку они не относятся ни к какому классу). Однако при объявлении внутри внешнего блока, например внутри определения класса верхнего уровня, определение класса по умолчанию также становится динамическим. Чтобы создать динамически объявленный класс, просто определите его внутри какого-нибудь другого класса.

Мы подошли к разнице между статическим и динамическим классами. Динамический класс имеет доступ к членам экземпляра вышестоящего класса (так как относится к этому экземпляру). Статический кларс – не имеет. Вот пример кода:

Немного поразмышляйте – и сразу поймете, что здесь происходит. Поле х – это член экземпляра класса Outer. Иными словами, существует множество переменных х, по одной для каждого экземпляра времени исполнения (runtime instance) класса Outer. Класс InnerTube входит в состав класса Outer, но не какого-либо экземпляра класса Outer. Он никак не может идентифицировать х. С другой стороны, класс InnerOne относится к экземпляру Outer, поскольку является динамическим. Можно представить, что здесь мы имеем отдельный класс InnerOne для каждого экземпляра Outer (хотя фактически это не так). Следовательно, InnerOne имеет доступ к членам того экземпляра Outer, к которому этот InnerOne относится.

OuterTest позволяет убедиться, что, как и с полями, можно использовать статическое внутреннее определение (в данном случае – создать образец класса), просто воспользовавшись его классифицированным именем. Однако динамическое определение полезно только в контексте экземпляра.

Источник: Android. Программирование на Java для нового поколения мобильных устройств

По теме:

  • Комментарии