пятница, 29 июля 2011 г.

Глава 5. Наследование.

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

"Способность переменной ссылаться на объекты, имеющие разные фактические типы, называется полиморфизмом. Автоматический выбор нужного метода во время выполнения программы называется динамическим связыванием (dynamic binding)."

Метод equals()

"Метод equals() класса Object проверяет, эквивалентны ли два объекта. Однако в ряде случаев эквивалентными должны считаться объекты одного типа, имеющие одинаковые состояния."
"Чтобы объекты были эквивалентны, они как минимум должны быть объектами одного и того же класса."
[А как же с наследованием? Могут же быть эквивалентными объект класса-родителя и объект класса-наследника.]
"Определяя метод equals() для подкласса, надо сначала вызвать тот же метод суперкласса. Если проверка даст отрицательный результат, объекты не могут быть идентичными." [Возник такой вопрос. Почему при обращении из метода объекта-наследника к методу обекта-родителя, ссылка this в этом методе ссылается на объект-наследник ?] [Ссылка super, получается, работает как фильтр, отсекая всё неродительское. Другого объекта, на который ссылалась бы ссылка super, не существует. Это всё тот же наш текущий объект.]

суббота, 16 июля 2011 г.

Внутренние классы

"Закрытыми могут быть только внутренние классы. Обычные классы должны быть либо общедоступными, либо допускать обращение из того же пакета."
"В определении внутреннего класса ссылка [на внешний класс] явным образом не присутствует."
"Ссылка на объект внешнего класса задаётся в конструкторе."
"Если во внутреннем классе определены конструкторы, компилятор модифицирует их, добавляя параметр, выполняющий роль ссылки на внешний класс."


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

"Выражение ВнешнийКласс.this означает ссылку на внешний класс."
"Конструктор внутреннего класса:
объектВнешнегоКласса.new ВнутреннийКласс (параметры)."
Ссылка на внутренний класс в области видимости внешнего класса: ВнешнийКласс.ВнутреннийКласс.

"... обработку внутренних классов осуществляет компилятор, а не виртуальная машина. Для их обозначения используется символ $, разделяющий имена внешних и внутренних классов ( TalkingClock$TimePrinter ); таким образом, для виртуальной машины внутренние классы неотличимы от внешних".

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

Синтаксис создания безымянного, или анонимного внутреннего класса:

new Интерфейс (параметры) { Свойства и методы внутреннего класса, реализующего Интерфейс };
или
new Класс (параметры) { Свойства и методы внутреннего класса, наследующего Класс};

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

"[Proxy-классы] также часто называют классами-посредниками или заместителями."
"[Proxy-классы используются] для того, чтобы во время выполнения программы создавать новые классы, реализующие заданные интерфейсы. Proxy-классы необходимы, если на этапе компиляции программист ещё не знает, какие интерфейсы ему следует реализовать. ... Используя эту концепцию, часто удаётся избежать механической генерации и компиляции кода заглушек."
Создавать классы во время выполнения программы... Насколько я знаю, это можно делать с помощью механизма отражения.
Программист не знает, какие интерфейсы реализовывать... Интерфейсы из заданных интерфейсов? А как программист узнаёт, какие интерфейсы нужно реализовать? Нужно подготовить все реализации, а потом по каким-то условиям использовать некоторые из них? Для реализованных интерфейсов не нужно создавать заглушки, так?
Пока не очень понятно. Буду разбираться.

"Proxy-класс может создавать во время выполнения программы совершенно новые классы и реализует интерфейсы, которые указывает программист. В частности, в proxy-классе содержатся следующие методы:
- Все методы, которые требуют указанные интерфейсы.
- Все методы, определённые в классе Object.
Однако определить новый код для этих методов в ходе выполнения программы нельзя.
[Это для случаев, когда разные интерфейсы определяют методы с одинаковой сигнатурой ?] Вместо этого программист должен предоставить обработчик вызовов (invocation handler), представляющий собой объект, реализующий интерфейс InvocationHandler. В этом интерфейсе объявлен единственный метод:
Object invoke (Object proxy, Method method, Object[] args)
[Предполагаемый алгоритм: в proxy ищется метод, соответствующий сигнатуре, составленной из method и args, и выполняется. А что возвращается? Что это за Object? Если метод что-то возвращает, то понятно, а если метод void ?]"
"При вызове какого-либо метода из proxy-объекта автоматически вызывается метод invoke() обработчика вызовов.
[А как он автоматически вызывается?]"

"Чтобы создать proxy-объект, используется метод newProxyInstance() класса Proxy. Этот метод получает три параметра: 1) загрузчик класса (class loader); 2) массив объектов класса Class - по одному для каждого интерфейса, подлежащего реализации; 3) обработчик вызовов."


Тут тоже пока непонятно. Для того, чтобы использовать обработчик, нужно передать методу invoke() proxy-объект, но для того, чтобы создать этот proxy-объект, нужен обработчик...
Если proxy-объект - это аргумент метода invoke(), то этим подразумевается, что один и тот же обработчик вызовов можно использовать с разными proxy-объектами. Но для создания proxy-объекта нужно передать только один обработчик, т.е. есть какая-то жёсткая привязка. Какое-о противоречие.

"Proxy можно использовать для достижения следующих целей:
1) Переадресовывать вызовы методов на удалённый сервер.
2) Устанавливать связь между событиями пользовательского интерфейса и определёнными действиями, выполняемыми про работе программы.
3) Отслеживать вызовы методов при отладке."


"... proxy-классы создаются во время выполнения программы."

"Во всех proxy-классах переопределяются методы toString(), equals() и hashCode() класса Object. Эти методы лишь вызывают метод invoke(), принадлежащий обработчику событий. Другие методы класса Object не переопределяются."

"Для конкретного загрузчика классов и заданного набота интерфейсов может существовать только один proxy-класс."


"... все интерфейсы, которые реализуются proxy-классом, в объявлении которых не указан модификатор public, и сам proxy-класс должны принадлежать одному пакету."

[Имеются ввиду интерфейсы методов, правильно? Ведь список интерфейсов мы получаем от объектов, методы которых реализует proxy-объект. Таким образом, нужно анализировать взаимную видимость объектов. "Интерфейс принадлежит пакету" - это означает "класс, содержащий метод, которому соответствует интерфейс, принадлежит пакету" ?]

четверг, 14 июля 2011 г.

с. 262 (Черновик) Внутренние классы

"В языке C++ есть вложенные классы (nested)."
"Вложение представляет собой отношение между классами, а не между объектами. Объект [родительского] класса не содержит подобъектов [вложенных классов]."
А разве с внутренними классами в Java не так же? Разве родительский объект должен содержать объекты его внутренних классов?
-----
"У вложения классов есть два преимущества: управление именами (name control) и управление доступом (access control)."
"В языке Java ... управление именами осуществляют пакеты."
Методы родительского класса имеют доступ к полям и методам вложенного класса, но не наоборот.
-----
"... внутренние классы в языке Java имеют ещё одно достоинство, которое делает их более полезными, чем вложенные классы в языке C++. Объект внутреннего класса содержит неявную ссылку на объект внешнего класса, который создал его. С помощью этой ссылки объект внутреннего класса получает доступ ко всем полям и методам внешнего объекта.
Эту дополнительную ссылку не имеют лишь статические внутренние классы. Именно они представляют собой полный аналог вложенных классов языка C++."

с. 261 (Черновик) Внутренние классы

"Внутренним (inner) называется класс, определённый внутри другого класса. Зачем он нужен? Назовём четыре причины.

  • Объект внутреннего класса имеет доступ к реализации объекта, который его создал, включая закрытые данные.
  • Внутренний класс можно скрыть от других классов того же пакета.
  • Безымянный (anonimous) внутренний класс удобен, если нужно на лету уточнить обратные вызовы.
  • Внутренние классы очень удобны при создании событийно-управляемых программ."
3-ий и 4-ый пункты пока туманны...

с. 260 (Черновик) Клонирование объектов

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

с. 259 (Черновик) Клонирование объектов

"Программисты, которые будут использовать ваш метод clone, должны будут по-прежнему выполнять приведение типа. Метод clone всегда возвращает объект класса Object."
Может быть, лучше сразу написать метод getClone(), в котором и приводить тип...

"Для глубокого копирования нужно ... клонировать изменяемые поля экземпляра".

среда, 13 июля 2011 г.

с. 258 (Черновик) Клонирование объектов

"Подкласс может вызвать защищённый метод clone только для клонирования своих собственных объектов. Чтобы клонировать другие объекты, метод clone следует переопределить и сделать открытым."

"Подкласс"... Подкласс класса Object, т.е. практически любой класс.
"... только для клонирования своих собственных объектов." И только в собственных методах.
"Чтобы клонировать другие объекты, метод clone следует переопределить и сделать открытым." Переопределение и открытость не означает, что другие объекты можно будет просто так клонировать. Их метод clone тоже работает только в их коде.
"Для того, чтобы программист мог [реализовать клонирование], класс должен выполнить следующее: 1) реализовать интерфейс Clonable; [и, для глубокого копирования,] 2) переопределить метод clone с модификатором доступа public".
"Интерфейс
[Clonable] служит просто в качестве дескриптора, указывающего на то, что в данном случае разработчик класса понимает, как выполняется процесс клонирования. Объекты настолько настороженно относятся к клонированию, что возбуждают проверяемую [checked] исключительную ситуацию, если объект требует выполнения клонирования, но не реализует интерфейс Clonable".
"Интерфейс Clonable - один из немногих помеченных интерфейсов (tagged) в языке Java. Помеченные интерфейсы не имеют методов; их единственное предназначение - позволить использовать оператор instanceof для проверки типа."

"Даже если применение метода clone по умолчанию (поверхностное копирование) вполне допустимо, в любом случае нужно реализовать интерфейс Clonable, переопределить метод clone, сделав его открытым, вызвать метод super.clone() и перехватить исключительную ситуацию CloneNotSupportedException."