Порядок вызова конструкторов. Переопределение методов
Порядок вызова конструкторов В каком порядке вызываются конструкторы классов, образующих иерархию, при ее создании? Например, какой конструктор вызывается раньше: A или B, если B – это подкласс, а A – суперкласс? В иерархии классов конструкторы вызываются в порядке наследования, начиная с суперкласса, и заканчивая подклассом. Более того, поскольку super() должен быть первым оператором, выполняемым в конструкторе подкласса, этот порядок остается неизменным, независимо от того, используется ли форма super(). Если конструктор super() не применяется, программа использует конструктор каждого суперкласса, заданный по умолчанию или не содержащий параметров. В следующей программе демонстрируется порядок выполнения конструкторов. // Демонстрация порядка вызова конструкторов. // Создание суперкласса. class A { A() { System. out. println(" Внутри конструктора A. " ); } } // Создание подкласса посредством расширения класса A. class B extends A { B() { System. out. println(" Внутри конструктора B. " ); } } // Создание еще одного подкласса посредством расширения класса B. class C extends B { C() { System. out. println(" Внутри конструктора C. " ); } } class CallingCons { public static void main(String args[]) { C c = new C(); } } Эта программа генерирует следующий вывод: Внутри конструктора A Внутри конструктора B Внутри конструктора C Как видите, конструкторы вызываются в порядке наследования. Если немного подумать, становится ясно, что выполнение конструкторов в порядке наследования имеет смысл. Поскольку суперкласс ничего не знает о своих подклассах, любая инициализация, которую он должен выполнить, полностью независима и, возможно, обязательна для выполнения любой инициализации, выполняемой подклассом. Поэтому она должна выполняться первой.
Переопределение методов Если в иерархии классов имя и сигнатура типа метода подкласса совпадает с атрибутами метода суперкласса, говорят, что метод подкласса переопределяет метод суперкласса. Когда переопределенный метод вызывается из подкласса, он всегда будет ссылаться на версию этого метода, определенную подклассом. Версия метода, определенная суперклассом, будет сокрыта. Рассмотрим следующий пример: // Переопределение метода. class A { int i, j; A(int a, int b) { i = a; j = b; } // отображение i и j void show() { System. out. println(" i и j: " + i + " " + j); } } class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } // отображение k – этот метод переопределяет метод show() класса A void show() { System. out. println(" k: " + k); } } class Override { public static void main(String args[]) { B subOb = new B(1, 2, 3); subOb. show(); // этот оператор вызывает метод show() класса B } } Эта программа создает следующий вывод: k: 3 Когда программа вызывает метод show() по отношению к объекту типа B, она использует версию этого метода, определенную внутри класса B. То есть версия метода show(), определенная внутри класса B, переопределяет версию, объявленную внутри класса A. Если нужно получить доступ к версии переопределенного метода, определенного в суперклассе, это можно сделать с помощью ключевого слова super. Например, в следующей версии класса B версия метода show(), объявленная в суперклассе, вызывается внутри версии подкласса. Это позволяет отобразить все переменные экземпляров. class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } void show() { super. show(); // этот оператор вызывает метод show() класса A System. out. println(" k: " + k); } } Подстановка этой версии класса A в предыдущую программу приведет к следующему выводу: i и j: 1 2 k: 3 В этой версии super. show() вызывает версию метода show(), определенную в суперклассе. Переопределение метода выполняется только в том случае, если имена и сигнатуры типов двух методов идентичны. В противном случае два метода являются просто перегруженными. Например, рассмотрим измененную версию предыдущего примера.
// Методы с различающимися сигнатурами являются // перегруженными, а не переопределенными. class A { int i, j; A(int a, int b) { i = a; j = b; } // отображение i и j void show() { System. out. println(" i и j: " + i + " " + j); } } // Создание подкласса посредством расширения класса A. class B extends A { int k; B(int a, int b, int c) { super(a, b); k = c; } // перегрузка метода show() void show(String msg) { System. out. println(msg + k); } } class Override { public static void main(String args[]) { B subOb = new B(1, 2, 3); subOb. show(" Это k: " ); // вызов метода show() класса B subOb. show(); // вызов метода show() класса A } } Эта программа создает следующий вывод: Это k: 3 i и j: 1 2 Версия метода show(), определенная в классе B, принимает строковый параметр. В результате ее сигнатура типа отличается от сигнатуры метода в классе B, который не принимает никаких параметров. Поэтому никакое переопределение (или сокрытие имени) не происходит. Вместо этого просто выполняется перегрузка версии метода show(), определенной в классе A, версией, определенной в классе B.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|