Java

© 2008 Наталия Македа
Все материалы блога защищены авторским правом. Любая перепечатка или использование материалов этого блога в коммерческих целях возможна лишь с письменного согласия автора. При некоммерческом использовании ссылка на блог обязательна.

воскресенье, 3 января 2010 г.

static (Выпуск 19)

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

Рассмотрим пример простого класса с одной статичной переменной:

1. class Ecstatic{
2. static int x = 0;
3. Ecstatic() { x++; }
4. }

Переменная x объявлена статичной. Это означает, что не важно, сколько объектов класса Ecstatic существует в данный момент - x всего лишь одна. При загрузке класса Ecstatic выделяется 4 байта под переменную x. И её инициализация происходит (см. строку №2) тоже в момент загрузки класса. И каждый раз, когда создаётся объект класса Ecstatic, x инкрементируется. Этот приём позволяет знать точное количество созданных объектов класса Ecstatic.

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

  • Через любой объект класса: обьект.переменная.
  • Через сам класс: класс.переменная.

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

1. Ecstatic e1 = new Ecstatic();
2. Ecstatic e2 = new Ecstatic();
3. e1.x = 100;
4. e2.x = 200;
5. reallyImportantVariable = e1.x;

Если вы заранее не знаете, что x - статичная переменная, то можете подумать, что в 5-й строке переменной reallyImportantVariable присваивается 100. Но на самом деле ей присваивается 200, потому что e1.x и e2.x - это всё одна и та же переменная х.

Поэтому лучше доступаться к статичным переменным через имя класса. Вот фрагмент кода, эквивалентный предыдущему:

1. Ecstatic e1 = new Ecstatic();
2. Ecstatic e2 = new Ecstatic();
3. Ecstatic.x = 100; // Бессмысленное действие
4. Ecstatic.x = 200;
5. reallyImportantVariable = Ecstatic.x;

Теперь всё встаёт на свои места: строка 3 не несёт никакого смысла - это всего лишь лишняя операция, а в 5-й строке переменной reallyImportantVariable присваивается сразу 200.



Статичные методы не могут использовать нестатичные элементы (переменные и методы) своиего класса, но могут использовать другие статичные элементы. Статичные методы не принадлежат ни одному объекту класса. Поэтому они могут вообще вызваться до того, как класс инстанциирован.

Каждое Java приложение содержит в себе статичный метод main():

1. class SomeClass {
2. static int i = 48;
3. int j = 1;
4.
5. public static void main(String args[]) {
6. i += 100;
7. // j *= 5; Если раскомментировать, будет ошибка
8. }
9. }

Когда приложение запускается (то есть кто-то вызывает из коммандной строки java SomeClass), не существует ни одного объекта класса SomeClass. Однако существует переменная i, которая инициализурется во 2-й строке и инкрементируется в 6-й строке. А если бы мы раскомметировали 7-ю строку, то вообще получили бы ошибку компиляции, потому что статичный метод не может использовать нестатичные переменные класса.


Читать далее!

вторник, 13 октября 2009 г.

abstract (Выпуск 18)

Модификатор abstract применяется только для классов и методов.

Абстрактный класс не может быть инстанциирован, то есть нельзя вызвать конструктор для создания его объекта.

Абстрактные классы перекладывают имплементацию методов на "плечи" своих подклассов. Рассмотрим, к примеру, рисунок ниже:

Разработчик класса Animal решил, что все животные должны иметь метод travel(). Поскольку каждое животное перемещается по-своему, то невозможно заимплементировать этот метод уникальным образом и просто наследовать этот метод в подклассы. Поэтому метод travel() объявлен с модификатором abstract следующим образом:

abstract void travel();

Обратите внимание на точку с запятой (а не фигурных скобок) в конце объявления метода. Фигурные скобки вместе с телом метода отдаются на усмотрение подклассов. Суперкласс обеспечивает только сигнатуру метода, которой должны придерживаться подклассы. При этом подклассы класса Animal должны или заимплементировать метод travel() или опять объявить его абстрактным. Во втором случае имплементация абстрактного метода опять переносится в подклассы подкласса.

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

Класс рекомендуется объявлять абстрактным, если:

  • В классе есть абстрактные методы.
  • Класс наследует абстрактные методы (от абстрактного суперкласса) и не предоставляет имплементации для всех унаследованных абстрактных методов.
  • Класс имплементирует интрефейс но не предоставляет имплементации для всех методов интерфейса.

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

Таким образом, можификатор abstract можно считать противоположностью модификатору final. Финальный класс не может иметь подклассов. Абстрактный класс должен иметь подклассы.


Читать далее!

понедельник, 12 октября 2009 г.

final (Выпуск 18)

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

В частности, final класс не может иметь подклассов. Например, следующий код просто не откомпилируется, потому что java.lang.Math имеет модификатор final:

class SubMath extends java.lang.Math { }

Получаем ошибку компиляции: "Can’t subclass final classes." ("Невозможно создать подкласс от класса final")

Final переменная не может принимать значение, которое отличается от того, что было присвоено при инициализации. Поэтому в Java переменные с модификатором final играют роль const в C++ и #define в С. Например в классе java.lang.Math есть переменная с модификатором final типа double, которая называется PI. Очевидно, что число пи не должно изменяться по прихоти программиста.

Если объекту назначен модификатор final, то это означает, что ссылка (указатель) на объект должна оставаться неизменной, хотя сам объект может меняться. Проиллюстрируем это примером:

1. class Walrus {
2. int weight;
3. Walrus(int w) { weight = w; }
4. }
5.
6. class Tester {
7. final Walrus w1 = new Walrus(1500);
8. void test() {
9. w1 = new Walrus(1400); // Illegal
10. w1.weight = 1800; // Legal
11. }
12. }

Обратите внимание, как в строке 7 объект w1 был объявлен с модификатором final. Именно поэтому в строке 9 выполняется недопустимая операция создания нового указателя на объект. Хотя в строке 10 мы можем изменить значение некоторых полей объекта.

Иными словами:

  • Нельзя менять ссылку на объект, то есть пересоздавать объект.
  • Можно изменять значения полей.

Наконец, final метод не может быть переопределён (overriden). Например, следующий код не откомпилируется:

1. class Mammal {
2. final void getAround() { }
3. }
4.
5. class Dolphin extends Mammal {
6. void getAround() { }
7. }

Дельфины плавают совершенно по-другому в отличие от большинства млекопитающих. Поэтому имеет смысл переопределить метод унаследованный метод getAround(). Но этот метод final, поэтому в 6 строке мы получим ошибку компиляции "Final methods can’t be overridden." (Финальные методы не могут быть переопределены)


Читать далее!

среда, 15 апреля 2009 г.

Модификаторы доступа: резюме. Другие модификаторы (Выпуск 17)

Java предоставляет следующие модификаторы доступа (от самого разрешающего до самого ограничивающего):

  • public - элемент доступен из любого класса
  • protected - элемент доступен из подкласса или из любого класса пакета того класса, в котором находится элемент
  • default - элемент доступен из из любого класса пакета того класса, в котором находится элемент
  • private - элемент доступен лишь из того класса, в котором находится

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


В следующих статьях мы рассмотрим другие модификаторы: final, abstract, static, native, transient, synchronized, and volatile.

В Java порядок объявления модификаторов не имеет значения. Например, если вы напишите, что класс public final, то это равносильно тому, если бы вы написали final public. Если вы обхъявите метод protected static - это тоже самое, что и объявить его static protected.

Не все модификаторы одинаково применимы к разным элементам Java-языка (т.е. класс, переменная, метод, конструктор). Более детальные разъяснения будут даны в следующих статьях.


Читать далее!

Подклассы и методы (Выпуск 17)

Согласно спецификации Java методы не могут быть переписаны (например, в наследуемых классах). Например, у апплетов есть метод init(). Этот метод должен переписывать одноименный метод из супер-класса
java.applet.Applet, доступ к которому public. Таким образом, переписывающий метод init() из подкласса класса java.applet.Applet не можеть иметь более ограничивающий доступ, т.е., protected, private или default - его модификатор доступа может быть только public. В противном случае на этапе компиляции возникнет ошибка "Methods can’t be overridden to be more private" (Доступ к методам не может быть более ограниченным).

На рисунке ниже мы представили иерархию модификаторов доступа. Слева находится самый ограничивающий модификатор доступа. Справа - самый разрешающий. Методы подклассов могут иметь модификатор доступа отличный, от наследуемого, если он является более разрешающим (т.е. находится правее исходного модификатора доступа на рис. ниже).


Иерархия модификаторов доступа

Таким образом модификатор доступа к методу и подкласса может быть следующим:

  • private, protected, public, default, если модификатор доступа у метода суперкласса private
  • protected, public, default, если модификатор доступа у метода суперкласса default
  • protected, public, если модификатор доступа у метода суперкласса protected
  • public, если модификатор доступа у метода суперкласса public

На следующем рисунке показана "обратная" иерархия, которая приведёт к ошибке компиляции:


Обратная иерархия

То есть:

  • default не может быть ограничен до private
  • protected не может быть ограничен до default или private
  • public не может быть ограничен до protected, default или private


Читать далее!

понедельник, 26 января 2009 г.

protected (Выпуск 16)

Название protected (защищённый) слегка сбивает с толку. Читатель может подумать, что защищённый доступ имеет очень ограничивающую натуру, что-то вроде приватного. В реальности же защищённые элементы являются более доступны, чем, скажем, элементы с доступом по умолчанию.

Только переменные и методы могут иметь защищённый модификатор доступа. Защищённый элемент доступен всем остальным классам из того же пакета, как и в случае с доступом по умолчанию. Кроме того, защищённый элемент класса доступен всем его подклассам, даже если они находятся в других пакетах.

В качестве примера рассмотрим фрагмент кода:

1. package sportinggoods;
2. class Ski {
3. void applyWax() { . . . }
4. }

У метода applyWax() доступ по умолчанию. Теперь рассмотрим подкласс:

1. package sportinggoods;
2. class DownhillSki extends Ski {
3. void tuneup() {
4. applyWax();
5. // продолжение кода
6. }
7. }

Подкласс вызывает унаследованный метод applyWax(). Всё идёт, как надо, поскольку оба класса Ski и DownhillSki находятся в одном и том же пакете. Но если один из этих классов по какой-то причине будет перемещён в другой пакет, DownhillSki больше не будт иметь доступа к наследуемому методу applyWax() и код не откомпилируется. Данная проблема решается при добавлении защмщённого модификатора доступа к методу applyWax() в строке 3:

1. package adifferentpackage; // Класс Ski в
// другом пакете
2. class Ski {
3. protected void applyWax() { . . . }
4. }


Читать далее!

Default (Выпуск 16)

Default (по умолчанию)- это способ доступа к классам, переменным и методам, если иной другой модификатор доступа не указан. Данные класса или его методы могут быть доступны по умолчанию, равно как и сам класс может быть доступен по умолчанию.

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

Когда вы пишете приложение, для которого нужно разработать несколько классов, то скорее всего вы размещаете ваши файлы с расширением .java, а байткоды соответствующий классов (.class) - в другой директории. И вот эта другая директория с точки зрения JRE (Java Runtime Environment) является пакетом.

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

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

Классы из других пакетов не могут получить доступ к вашим классам, поскольку у ваших классов можификатор доступа default, а не public! Даже если у классов вне вашего пакета есть подклассы из вашего пакета (например, такая ситуация характерна для апплетов), то и подклассы не могут доступаться по умолчанию, потому что модификатор default, а не protected или public. Рисунок ниже иллюстрирует разные ситуации с доступом по умолчанию внутри пакета и вне его:


Только классы из одного пакета могут получать доступ к элементам других классов, не имеющим ямного модификатора доступа


Читать далее!