Кен Арнольд Джеймс Гослинг

Вид материалаДокументы

Содержание


2.7. Ссылка this
2.8. Перегрузка методов
2.9. Статические члены
2.9.1. Блоки статической инициализации
2.9.2. Статические методы
Подобный материал:
1   ...   9   10   11   12   13   14   15   16   ...   81

2.7. Ссылка this


Мы уже видели (на стр. ), как в начале работы конструктора происходит явный вызов другого конструктора класса. Специальная ссылка на объект this может также применяться внутри нестатических методов; она указывает на текущий объект, для которого был вызван данный метод. this чаше всего используется для передачи ссылки на текущий объект в качестве параметра для других методов. Предположим, в методе необходимо пополнить список объектов, ожидающих обслуживания. Такой вызов может выглядеть следующим образом:

Service.add(this);

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

class Name {

public String str;

Name() {

str = “”;

}

}

равносильно следующему:

this.str = “”;


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

class Moose {

String hairdresser;


Moose(String hairdresser) {

this.hairdresser = hairdresser;

}

}


Поле hairdresser внутри конструктора скрывается присутствием одноименного параметра. Чтобы обратиться к полю hairdresser, а не к параметру, мы ставим перед именем ссылку this, указывая тем самым, что поле принадлежит к текущему объекту. Намеренное скрытие идентификаторов, осуществляемое подобным образом, может быть отнесено к хорошему стилю программирования лишь при идиоматическом использовании в конструкторах и методах доступа.

Помимо ссылки this, может также применяться ссылка super, с помощью которой осуществляется доступ к скрытым полям и вызов переопределенных методов суперкласса. Ключевое слово super подробно рассматривается в разделе “Переопределение методов и скрытие полей”.

2.8. Перегрузка методов


В языке Java каждый метод обладает определенной сигнатурой, которая представляет собой совокупность имени с количеством и типом параметров. Два метода могут иметь одинаковые имена, если их сигнатуры отличаются по количеству или типам параметров. Это называется перегрузкой (overloading), поскольку простое имя метода “перегружается” несколькими значениями. Когда программист вызывает метод, компилятор по количеству и типу параметров ищет тот из существующих методов, сигнатура которого подходит лучше всех остальных. Приведем в качестве примера различные методы orbits Around нашего класса Body:

public Body orbitsAround() {

return orbits;

}

public void orbitsAround(Body around) {

orbits = around;

}


При подобном стиле программирования перегрузка служит для того, чтобы отличать выборку значения (параметры не передаются) от его задания (указывается аргумент, представляющий собой новое значение). Количество параметров в двух методах отличается, поэтому выбрать нужный метод будет несложно. Если orbitsAround вызывается без параметров, то используется метод, возвращающий текущее значение. Если orbitsAround вызывается с одним аргументом типа Body, то используется метод, задающий значение. Если же вызов не подходит ни под одну из этих сигнатур, то он является неверным, а программа не будет компилироваться.

Вопрос о том, как язык выбирает среди нескольких перегруженных методов тот, что нужен для данного вызова, подробно рассматривается в разделе “Доступ к членам” на стр. .

Упражнение 2.12

Включите в класс Vehicle два новых метода: один в качестве параметра получает количество градусов, на которое поворачивает машина, а другой — одну из констант Vehicle.TURN_LEFT или Vehicle.TURN_RIGHT.

2.9. Статические члены


Класс содержит члены двух видов: поля и методы. Для каждого из них задается атрибут, определяющий возможности наследования и доступа (private, protected, public или package). Кроме того, каждый из членов при желании можно объявить как static.

Для статического члена создается всего один экземпляр, общий для всего класса, вместо построения его копий в каждом объекте класса. В случае статических переменных (переменных класса), это ровно одна переменная, независимо от того, сколько объектов было создано на основе класса (даже если ни одного). Образцом может служить поле nextID класса Body в приведенном выше примере.

Инициализация статических полей класса происходит до того, как они используются или запускается любой из его методов. В следующем примере метод unset может быть уверен в том, что перед использованием переменной UNSET ей было присвоено значение Double.NaN:

class Value {

public static double UNSET = double.NaN;


private double V;

public void unset() {

V = UNSET;

}

// ...

}

2.9.1. Блоки статической инициализации


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

class Primes {

protected static int[] knownPrimes = new int[4];


static {

knownPrimes[0] = 2;

for(int i = 1; i < knownPrimes.length; i++)

knownPrimes[i] = nextPrime();

}


}


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

Что произойдет, если статический инициализатор класса X вызывает метод класса Y, а статический инициализатор Y, в свою очередь, вызывает метод из класса X для задания своих статических величин? Подобные циклические инициализации не могут быть надежно выявлены в процессе компиляции, поскольку в момент компиляции X класс Y может еще не существовать. Если возникает подобная ситуация, то статические инициализаторы X выполняются лишь до вызова метода Y. Когда Y, в свою очередь, обратится к методу X, то последний будет выполняться без завершения статической инициализации. Все статические поля X, для которых инициализация не была выполнена, будут иметь значения по умолчанию (false, ‘\u0000’, ноль или null в зависимости от типа).

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

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

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


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

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

За пределами класса статические методы обычно вызываются через имя класса, а не через ссылку на конкретный объект:

prime = Primes.nextPrime();

knownCnt = Primes.knownPrimes.length;


Упражнение 2.13

Включите в класс Vehicle статический метод, который возвращает максимальное значение идентификатора, использованное на данный момент.