Как запустить метод из другого класса java
[an error occurred while processing this directive]
Конспект лекций по Java. Занятие 9
[an error occurred while processing this directive](none) [an error occurred while processing this directive](none)[an error occurred while processing this directive] ::
[an error occurred while processing this directive] (none)
[an error occurred while processing this directive] ([an error occurred while processing this directive] В.Фесюнов [an error occurred while processing this directive])
[an error occurred while processing this directive](none)
- Практические навыки. Шаблон «синглетон» (singleton pattern)
- Вложенные классы
- Классы, вложенные в методы или блоки
- Применение inner-классов
- Анонимные вложенные классы
- Применение анонимных вложенных классов в рассмотренных ранее примерах.
- Слушатели (listeners) полей и кнопок.
Практические навыки. Шаблон «синглетон» (singleton pattern)
На прошлом занятии мы рассмотрели спецификаторы доступа. Сейчас мы рассмотрим реальный пример использования спецификаторов доступа для построения синглетонов.
Синглетон — это один из многих стандартных шаблонов ( patterns ). Он обозначает класс, для которого можно создать ровно один объект. Если в программе требуется применение подобного класса, то имеет смысл создавать его по данному стандартному образцу.
Пусть нам требуется создать класс-синглетон SomeThing. Тогда это может быть реализовано так.
public class SomeThing < private static SomeThing instance = null; protected SomeThing () < . . . >public static final SomeThing getInstance() < if (instance == null) < instance = new SomeThing (); >return instance; > . . . >
Здесь в классе описано статическое, недоступное извне ( private ) поле instance класса SomeThing , конструктор и метод getInstance . В классе могут (и должны) быть другие поля и методы.
Конструктор описан как protected , что не позволяет создавать объекты данного класса операцией new вне класса. Единственной возможностью получить объект данного класса является вызов метода getInstance . В свою очередь, этот метод обеспечивает создание единственного экземпляра объекта SomeThing .
- Если быть более точным, то конструктор класса можно вызвать из классов пакета и из порожденных классов. Если не предполагается наследовать данный класс, то конструктор можно описать как private .
Следует отметить, что работать с таким классом очень легко. В любом месте программы вызов «SomeThing.getInstance()» обеспечит получение ссылки на единственный объект данного класса. Это позволяет, во многих случаях, избежать передачи и хранения перекрестных ссылок объектов друг на друга.
Возьмем данный пример себе на заметку и продолжим знакомиться с возможностями Java.
Вложенные классы
Java позволяет создавать классы внутри классов. Т.е. текстуально описание класса может находиться внутри фигурных скобок тела другого класса. Такие классы называются вложенными (inner) .
Аппарат вложенных классов в Java имеет много деталей, он хорошо проработан и весьма широко используется при программировании. При помощи вложенных классов строится framework (инфраструктура) охватывающего класса. Например, если в классе нужно использовать другой класс и этот класс используется только в этом классе и более ни к кому другому классу отношение не имеет, то имеет смысл этот класс сделать вложенным. Это, правда, не единственный случай, когда имеет смысл строить вложенные классы. Но для того, чтобы понять, для чего они используются нужно сначала хотя бы немного с ними познакомиться.
Вложенный класс может быть помещен как на уровень описания охватывающего класса, так и внутрь некоторого метода охватывающего класса, а также внутрь некоторого блока.
Пример (файл Outer.java)
public class Outer < int y; Outer(int x) < y = x; >class Inner1 < // Вложенный класс. Размещен внутри класса Outer . . . >int g(String s) < class Inner2 < // Вложенный класс. Размещен внутри метода g класса Outer . . . >. . . > . . . >
Здесь внутрь класса Outer помещены два других класса — Inner1 и Inner2. Причем, класс Inner1 помещен непосредственно внутрь Outer (его описание находится на том же уровне вложенности, что и методы класса Outer). А класс Inner2 помещен внутрь метода g(. ) данного класса.
При компиляции Outer.java будут образованы три class-файла: Outer.class, Outer$Inner1.class и Outer$Inner2.class.
В отличие от обычных классов для вложенных классов имеют смысл описатели private и protected для самого класса. Вложенный класс с описателем private доступен только внутри охватывающего класса, protected — внутри охватывающего класса и классов, порожденных от охватывающего.
Эти описатели имеют смысл только для inner-классов, описанных на уровне класса. Вложенные классы, описанные внутри методов или блоков, все равно доступны только внутри этих методов или блоков.
Объекты inner-классов могут порождаться как внутри охватывающего класса, так и извне его. Для того чтобы можно было явно создать объект inner-класса извне охватывающего класса, он должен быть описан с описателем public или вообще без описателя. Кроме того, конструктор вложенного класса также должен быть с описателем public или без описателя.
При порождении объекта inner-класса из охватывающего класса синтаксис обычный:
Inner1 i1 = new Inner1();
При порождении объекта inner-класса извне охватывающего класса нужно чтобы inner-класс был доступен извне и, кроме того, чтобы существовал объект охватывающего класса.
Пусть t1 — объект класса Outer. Тогда порождение объекта класса Inner1 выглядит так:
Outer.Inner1 i2 = t1.new Inner1();
Дело в том, что объект inner-класса имеет свободный доступ ко всем полям и методам объекта охватывающего класса. Это обеспечивается тем, что он содержит в себе неявную ссылку на объект охватывающего класса.
Так в приведенном выше примере класс Inner1 может содержать такой метод:
public int f1()
Здесь y — поле класса Outer. Оно совершенно свободно доступно изнутри вложенного класса. С другой стороны, объекты классов Outer и Inner1 — это два совершенно разных объекта, размещенных в разных областях памяти. Именно поэтому любой объект класса Inner1 должен хранить ссылку на объект класса Outer. Эта ссылка может быть использована в программе и явно (это, правда, очень редкий случай). Синтаксис такого использования на примере:
Outer ref = Outer.this; // Здесь Outer.this — ссылка на объект охватывающего класса.
В частности, метод f1() мог быть написан и так
public int f1()
Классы, вложенные в методы или блоки
Рассмотрим особенности, связанные с классами, вложенными в методы или блоки. С такими классами связаны дополнительные возможности и ограничения.
Во-первых, как уже отмечалось, такой класс доступен только внутри данного метода или блока.
Во-вторых, он имеет доступ к локальным переменным, а также параметрам данного метода или блока, но с одним ограничением — эти переменные и/или параметры должны быть описаны как final.
public class Outer < int x = 0; class Inner1 < public int f() < return x++; >> public void g(final int j) < final int k = j; class Inner2 < public int f(int r) < return r*k + j; // Здесь и j и k должны быть final >> Inner2 i1 = new Inner2(); int s = i1.f(10); > > class Outside < public void g1() < Outer t1 = new Outer(); Outer.Inner1 i2 = t1.new Inner1(); i2.f(); >>
Это совершенно абстрактный пример, но он демонстрирует различные синтаксические конструкции с использованием inner-классов.
Здесь описаны два обычных класса — Outer и Outside, и два вложенных — Inner1 и Inner2.
- Класс Outer имеет поле x и метод g(. ).
- Класс Outside имеет единственный метод — g1(. ).
- Класс Inner1 описан внутри Outer на уровне самого класса и имеет метод f(). Внутри этого метода продемонстрировано обращение к полю x класса Outer.
- Класс Inner2 описан внутри метода g(. ) класса Outer. Он имеет метод f(. ), в котором идет обращение к переменной k, параметрам r и j. Параметр r является обычным параметром данного метода, но k и j — это внешние по отношению к классу Inner2 переменные. Поэтому они должны быть описаны как final.
- В методе g(. ) класса Outer продемонстрировано создание и использование объекта класса Inner2. При этом используется обычный синтаксис создания объекта класса. Нигде за пределами метода g(. ) явно использовать Inner2 нельзя.
- В методе g1() класса Outside демонстрируется внешнее (по отношению к охватывающему классу Outer) использование класса Inner1.
Вложенные классы, как и обычные классы, могут порождаться (наследоваться) от других классов и/или удовлетворять интерфейсам. Также они могут использоваться в качестве базовых классов при построении новых классов (это используется крайне редко).
Применение inner-классов
Рассмотрим пример применения inner-класса. Предположим, мы пишем приложение, работающее с плоскими геометрическими фигурами. Один из возможных способов реализации состоит в следующем. Мы определяем интерфейс Shape (фигура), который определяет, что мы можем делать с фигурами. Потом мы определяем класс ShapeFactory с методами createCircle(. ), createTriangle(. ) и т.д. Для создания окружностей и треугольников мы внутри класса ShapeFactory описываем inner-классы Circle и Triangle.
public interface Shape
public class ShapeFactory < private class Triangle implements Shape < . . . Triangle(float a, float b, float c) < . . . >. . . > private class Circle implements Shape < . . . Circle (float r) < . . . >. . . > public Shape createTriangle(float a, float b, float c) < return new Triangle(a, b, c); >public Shape createCircle(float r) < return new Circle(r); >. . . >
Что мы видим, так сказать, извне класса ShapeFactory?
Классы Triangle и Circle объявлены как private и потому недоступны. Т.е. доступными для внешнего использования являются только методы createTriangle(. ) и createCircle(. ). Эти методы порождают объекты Triangle и Circle, но типом возвращаемого значения у них вляется Shape (при выполнении return выполняется upcasting). Такое возможно благодаря тому, что и Triangle и Circle удовлетворяют интерфейсу Shape.
Таким образом, мы можем получить как треугольники, так и окружности, но дальше мы можем работать с ними только как с абстрактными фигурами, используя методы, определенные в интерфейсе Shape. Например.
ShapeFactory f = new ShapeFactory(); // Создаем объект ShapeFactory Shape f1 = f.createCircle(10); Shape f2 = f.createTriangle(2, 3, 4); f1.g(6); // Здесь подразумевается, что g(int) метод интерфейса Shape
В свою очередь классы Triangle и Circle должны содержать реализацию всех методов интерфейса Shape.
Анонимные вложенные классы
Анонимные классы — это классы без имени. Они являются подмножеством вложенных классов. Т.е. анонимный класс — это всегда вложенный класс. Анонимные классы используются весьма широко, возможно, даже чаще чем обычные вложенные классы. Дело в том, что они дают удобную возможность, позволяющую сократить размер кода программы. Собственно говоря, в этом их основное преимущество.
Вместо того, чтобы сначала описывать класс, а потом создавать объект этого класса, мы с помощью анонимных классов можем сделать это одновременно (два в одном 😉 ).
Рассмотрим это на примере. Пусть есть класс A и мы хотим создать на его базе новый класс, а также создать объект этого класса. В обычном случае это выглядит примерно так. Мы где-то описываем класс B, порожденный от A, а потом в нужной точке программы создаем объект класса B:
// Описание класса B class B extends A < . . . описание полей и методов порожденного класса . . . >// Использование класса B A a = new B();
Применяя аппарат анонимных классов это можно записать гораздо короче и, что самое главное, одним фрагментом кода:
A a = new A() < . . . описание полей и методов порожденного класса . . . >;
Естественно с анонимными классами связано ряд ограничений. Так анонимный класс не может иметь конструктора (конструктор по определению — это метод с именем, совпадающим с именем класса, а анонимный класс имени не имеет). Из-за этого возникают две проблемы. Первая — где производить инициализирующие действия, вторая — как вызвать не конструктор по умолчанию базового класса, а конструктор с параметрами.
Решение первой проблемы состоит в том, что все инициализирующие действия можно выполнить либо при помощи явной инициализации, либо в специальном блоке инициализации.
Расширим предыдущий пример.
A a = new A() < int x = 0; String name; < // Это блок инициализации name = . ; >. . . описание полей и методов порожденного класса . . . >;
- Блок инициализации допустим не только в анонимных, но и в обычных классах. Но там он не особенно нужен, поскольку есть конструктор.
Вторая проблема решается при помощи указания параметров в операции new:
A a = new A(10) < // Вызывается конструктор базового класса A c одним параметром . . . . . . описание полей и методов порожденного класса . . . . . . >;
Еще одно ограничение связано с тем, что анонимный вложенный класс всегда вложен в некоторый метод. Следовательно, он может обращаться к переменным, доступным в этой точке (в точке его описания), но тогда эти переменные должны быть final.
Для анонимных вложенных классов, как и для обычных вложенных классов, при трансляции java-файла порождается отдельный class-файл. Имена этих class-файлов строятся при помощи последовательной нумерации. Так, если предыдущий фрагмент с порождением класса на базе класса A находился внутри класса X, то создался бы файл X$1.class.
Практическая работа
Применение анонимных вложенных классов в рассмотренных ранее примерах.
Анонимные вложенные классы очень часто используются при построении Listener’ов (слушателей). Мы будем подробнее рассматривать событийную модель позже, но пример ее использования нам уже встречался в программах Dialog1 — Dialog3. Сейчас мы вернемся к этим примерам и продвинемся дальше в понимании тех моментов, которые мы изначально оставили за рамками рассмотрения.
// Dialog3.java // Визульное приложения с текстовой областью. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Dialog3 extends JFrame < JTextArea txt; Dialog3() < super("Визульное приложения с текстовой областью"); try < UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); >catch(Exception e) < >setSize(400, 200); Container c = getContentPane(); c.add(new JLabel("Hello, привет"), BorderLayout.NORTH); txt = new JTextArea(5, 30); JScrollPane pane = new JScrollPane(txt); c.add(pane, BorderLayout.CENTER); WindowListener wndCloser = new WindowAdapter() < public void windowClosing(WindowEvent e) < System.exit(0); >>; addWindowListener(wndCloser); setVisible(true); > public void test() < txt.append("Первая строка\n"); txt.append("Вторая строка\n"); >public static void main(String[] args) < Dialog3 d = new Dialog3(); d.test(); >>Это демонстрационная программа Dialog3, которую мы рассматривали на 7-м занятии. Интересующий нас фрагмент это
WindowListener wndCloser = new WindowAdapter() < public void windowClosing(WindowEvent e) < System.exit(0); >>; addWindowListener(wndCloser);
Здесь порождается анонимный класс на базе класса WindowAdapter и создается объект ( wndCloser ) этого класса. Потом этот объект передается в метод addWindowListener(. ) в качестве параметра. Пока еще остается непонятным, что это дает и как все это работает, но формально уже ясно, что здесь используется механизм анонимных классов.
Слушатели (listeners) полей и кнопок.
Изучение inner-классов и, в частности, анонимных классов дает нам возможность продвинуться дальше в знакомстве с возможностями визуальной среды Java — с пакетом Swing.
Рассмотрим такие элементы библиотеки Swing как класс JButton и класс JTextField .
Класс JButton предназначен для создания кнопок. Его возможности и, так сказать, широта использования весьма значительны. Но сейчас мы рассмотрим только стандартные средства его использования по принципу «делай как я».
Посмотрим документацию по классу JButton и обратим внимание на его конструктор
public JButton(String text)
Создает кнопку с надписью. Текст надписи задан параметром.
А также на метод
public void addActionListener(ActionListener l)
Добавляет слушателя (listener) типа ActionListener к данной кнопке
Этот метод унаследован классом JButton от класса AbstractButton .
Если, в свою очередь, посмотреть на описание ActionListener , то можно увидеть, что это интерфейс из пакета java.awt.event и что этот интерфейс имеет единственный метод
public void actionPerformed(ActionEvent e)
Т.е. для того, чтобы добавить слушателя, мы должны создать класс, удовлетворяющий интерфейсу ActionListener, и создать объект этого класса (тут нам как раз пригодятся анонимные классы).
Смысл всего этого в том, что при нажатии на кнопку будет вызван метод actionPerformed(. ) , где мы можем запрограммировать все необходимые нам действия.
Это все, что нам сейчас нужно по классу JButton.
Класс JTextField используется для создания полей ввода. Наиболее часто используемый конструктор это
Создает поле ввода
Как и JButton он имеет метод addActionListener(. ) . Отличие от JButton заключается в том, что метод actionPerformed(. ) в данном случае вызывается при выходе из поля ввода по клавише Enter.
Рассмотрим абстрактный пример Dialog4.java. В этом примере есть кнопка, поле ввода и метка ( JLabel , рассматривали ранее), которая используется для отображения результатов наших действий над кнопкой и полем ввода.
// Dialog4.java // Пример визульного приложения на Java. import java.awt.*; import java.awt.event.*; import javax.swing.*; public class Dialog4 extends JFrame < JTextField fld = new JTextField(); JButton btn = new JButton("Нажать нежно"); JLabel lbl = new JLabel(" "); Dialog4() < super("Слушатели (listeners) полей и кнопок"); try < UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); >catch(Exception e) < >setSize(400, 150); Container c = getContentPane(); c.add(lbl, BorderLayout.NORTH); c.add(fld, BorderLayout.CENTER); c.add(btn, BorderLayout.SOUTH); fld.addActionListener(new ActionListener() < public void actionPerformed(ActionEvent e) < lbl.setText("Введен текст:"+fld.getText()); >>); btn.addActionListener(new ActionListener() < public void actionPerformed(ActionEvent e) < lbl.setText("Нажата кнопка"); >>); WindowListener wndCloser = new WindowAdapter() < public void windowClosing(WindowEvent e) < System.exit(0); >>; addWindowListener(wndCloser); setVisible(true); > public static void main(String[] args) < new Dialog4(); >>В этом примере в класс Dialog4 мы поместили три поля fld, btn и lbl, которые предназначены для создания поля ввода, кнопки и метки, соответственно. Строки
c.add(lbl, BorderLayout.NORTH); c.add(fld, BorderLayout.CENTER); c.add(btn, BorderLayout.SOUTH);
предназначены для размещения указанных визуальных компонент на экране. Далее следует создание слушателей поля и кнопки. Оба слушателя выводят некоторую информацию в метку lbl, для чего используется метод setText(. ) класса JLabel. Кроме того, для выборки текста, введенного в поле fld, в примере использован метод getText() класса JTextField.
Оттранслируем и запустим данную программу.
Задания
- 1. В примере Dialog4 fld, btn и lbl реализованы как поля класса. Разобраться почему и определить какие из них должны быть полями класса, а какие могут быть сделаны локальными переменными конструктора.
- 2. Поле ввода в примере занимает всю центральную область окна, что не очень хорошо. Улучшить внешний вид можно несколькими способами, но наиболее простой состоит в применении дополнительной панели. Панель — это контейнер визуальных компонент, в свою очередь она тоже является визуальной компонентой. Т.е. мы можем добавить компоненты на контейнер, а потом контейнер добавить на окно приложения. Для построения панелей в Swing имеется класс JPanel.
Улучшим внешний вид приложения следующим образом. Поместим поле не непосредственно в центр основного окна, а создадим панель JPanel, поместим поле на эту панель, а уже панель — в центр окна. Кроме того, для создания поля нужно использовать конструктор с параметром int columns:
JTextField fld = new JTextField(20);
Для поля ввода создается и метка (JLabel) и поле ввода (JTextField), поэтому их нужно сгруппировать вместе в одну визуальную компоненту (используем JPanel). Аналогично и для поля вывода. Для размещения двух панелек и кнопки (JButton) используем BorderLayout.NORTH, BorderLayout.CENTER и BorderLayout.SOUTH, соответственно.
[an error occurred while processing this directive]
[an error occurred while processing this directive] (none)- Конспект лекций по Java
- Оглавление
- Занятие 1. Вступление.
- Занятие 2. JAVA — объектно-ориентированный язык программирования.
- Занятие 3. Реализация принципов объектно-ориентированного подхода в Java. Знакомство с документацией.
- Занятие 4. Операции. Литералы. Операторы.
- Занятие 5. Массивы. Конструкторы классов. Работа со строками.
- Занятие 6. Знакомство с библиотеками и пакетами. Первая диалоговая программа.
- Занятие 7. Наследование классов.
- Занятие 8. Наследование классов (продолжение). Полиморфизм. Абстрактные классы. Интерфейсы.
- Занятие 9. Вложенные классы.
- Занятие 10. Статические вложенные классы. Коллекции объектов.
- Занятие 11. Коллекции объектов (продолжение). Задание порядка на множестве.
- Занятие 12. Коллекции — ассоциативные массивы (Map). Сортировка и поиск.
- Занятия 13. Обработка исключительных ситуаций (Exceptions).
- Занятие 14. Исключительные ситуации (продолжение). Ввод/вывод.
- Занятие 15. Ввод/вывод (продолжение).
- Занятие 16. Интерфейс FileFilter. Класс JFileChooser.
- Занятие 17. Сериализация объектов.
- Занятие 18. События и их реализация. Класс JList библиотеки Swing.
- Занятие 19. Аплеты.
- Занятие 20. Продолжение знакомства с библиотекой Swing. Класс JTabbedPane.
- Занятие 21. Идентификация типа во время выполнения.
- Занятие 22. Идентификация типа во время выполнения (продолжение). Множественные нити выполнения (Multiple threads).
- Занятие 23. Множественные нити выполнения (продолжение).
- Занятие 24. Множественные нити выполнения (продолжение).
- Занятие 25. Технология JavaBeans.
- Занятие 26. Технология JavaBeans (продолжение).
< Вернуться на caйт:: Copyright © 1999 — 2010, IT • archiv. Внутренние классы
В основе языка программирования Java лежит понятие классов. Все производимые программой операции должны быть заключены в классы. Классы можно определять несколькими возможными способами. Обычно, на самом верхнем уровне файла стоят определения классов:
Однако, это не единственный способ. Классы могут определяться также внутри других классов:
class X <
class Y <
>
>Класс, задаваемый внутри другого класса, принято называть внутренним. Существует четыре различных типа внутренних классов: класс верхнего уровня, член класса, локальный и анонимный классы.
Определение классов верхнего уровня почти ничем не отличается от определения обычных, внешних классов. Единственное различие состоит в том, что объявление данного класса находится в объявлении другого, поэтому перед ключевым словом class необходимо поставить ключевое слово static:
class OuterClass <
static class InnerClass <
>
>Во время компиляции класса X генерируется два файла .class, один – для InnerClass и другой – для OuterClass.
При этом возникает вопрос: как правильно ссылаться на внутренние классы? Из-за того, что внутренние классы определены внутри внешних, недостаточно просто использовать имя внутреннего класса (это возможно только, если обращение происходит из самого внешнего класса). Для создания ссылок на внутренние классы верхнего уровня требуется добавить префикс, указывающий на имя их внешнего класса. Например, ссылкой на InnerClass в предыдущем примере будет строка OuterClass.InnerClass.
Зачем вообще нужно подобное вложение классов друг в друга? Размещение класса верхнего уровня (или интерфейса) в другом классе дает наглядное представление о зависимости внешнего и внутреннего класса.
Например, интерфейс java.util.Map обеспечивает структуру данных для пар ключ/значение. У данного интерфейса есть внутренний интерфейс Entry, представляющий каждую запись пары ключ/значение на map. При определении Entry внутри интерфейса Map, он автоматически становится зависимым от родительского интерфейса. В результате, пользователи интерфейса Map получают ясное представление о роли Entry, кроме того, уменьшается вероятность возникновения конфликта имен для других классов с именем Entry общей структуры данных.
Определения для классов и интерфейсов верхнего уровня могут налагать на доступ определенные ограничения. Как и при работе с методами и переменными, здесь можно использовать любые модификаторы доступа: private, protected, public, а также безымянный доступ (по-умолчанию). Для внутренних классов данные модификаторы используются так же, как и для методов и переменных.
Второй формой внутреннего класса является член класса. При его создании не используется ключевое слово static. Различие между членом класса и классом верхнего уровня состоит в том, что в случае использования члена класса у вас имеется доступ к соответствующим полям и методам включающего класса. То есть каждый экземпляр внутреннего класса связан с одним и только одним экземпляром внешнего класса.
class OuterClass <
class InnerClass <
>
>Для получения доступа из внутреннего класса к экземпляру его внешнего класса необходимо в ссылке указать имя класса и ключевое слово this, поставив между ними точку (например, OuterClass.this). Ключевое слово this обеспечивает доступ к потенциально спрятанным методам и полям, в которых внутренние и внешние классы используют метод или переменную с одинаковыми именами.
Например, в следующем определении класса и у внешнего и у внутреннего классов присутствует переменная count. Для получения доступа к переменной внешнего класса, необходимо в ссылке на переменную перед ее именем приписать префикс this и имя внешнего класса.
class OuterClass <
int count = 0 ;class InnerClass int count = 10000 ;
public void display () System.out.println ( «Outer: » + OuterClass. this .count ) ;
System.out.println ( «Inner: » + count ) ;
>
>
>Классы третьего типа определяются не на верхнем уровне внешнего класса, а внутри его методов. Их принято называть локальными классами, по аналогии с локальными переменными. Преимущество использования подобных классов заключается в том, что определять их можно непосредственно в области применения, кроме того, упрощается процесс ограничения доступа. К классу, определенному внутри метода нельзя получить доступ вне этого метода.
Не вызывает ли это у вас противоречий? Если класс не доступен, кроме как в самом методе, зачем вообще нужно определять класс в самом начале. Чаще всего это необходимо для тех случаев, когда локальный класс определяет реализацию интерфейса.
Далее представлен пример метода, содержащего несколько локальных внутренних классов:
public ActionListener getListener ( int type ) <
class TypeOneListener implements ActionListener <
public void actionPerformed ( ActionEvent e ) <
System.out.println ( «Got One» ) ;
>
>
class TypeTwoListener implements ActionListener <
public void actionPerformed ( ActionEvent e ) <
System.out.println ( «Got Two» ) ;
>
>ActionListener returnValue = null ;
switch ( type ) case 1 :
returnValue = new TypeOneListener () ;
break ;
case 2 :
returnValue = new TypeTwoListener () ;
break ;
>
return returnValue;
>При использовании локальных внутренних классов все локальные переменные метода, к которым производится обращение из внутреннего класса, должны быть объявлены как final.
И, наконец, последний тип внутренних классов, которые называют анонимными. В примере локальных классов были определены классы TypeOneListener and TypeTwoListener. Тем не менее, их можно было оставить и без названий, так как вне определения внешнего класса имена данных классов становятся недоступными. Таким образом, локальные классы, если они расположены в области их применения, можно оставлять и без имен:
returnValue = new ActionListener () <
public void actionPerformed ( ActionEvent e ) <
System.out.println ( «Got Three» ) ;
>
> ;В этом коде как бы говорится: «new InterfaceName()», что означает: «вызвать конструктор для интерфейса». Здесь не присутствует сам интерфейс, а только его реализация. Представьте себе безымянный класс, расширяющий Object и реализующий имя интерфейса. Так как у класса нет имени, вызвать конструктор невозможно. Вам остается только реализовывать каждый метод интерфейса в определении. С другой стороны, реализация будет неправильной, и код нельзя будет компилировать.
Обратите внимание на точку с запятой в конце. Так как определение внутреннего класса является частью приписанного выражения, точка с запятой требуется для его завершения.
Использование анонимных внутренних классов очень распространено в IDE утилитах. Такие инструментальные средства, как JBuilder, генерируют код, связывающий коды обработки событий компонентов. Когда вы создаете код вручную, внимательно проверяйте все круглые и фигурные скобки.
Проверьте свои знания о внутренних классах, полученные в данной статье, при помощи он-лайн теста.
Выполняемые функции отдельных классов
В предыдущем разделе мы рассказали вам о том, как правильно использовать внутренние классы для обеспечения выполнения функций компонента внутри того же класса. Однако в некоторых случаях вам может потребоваться использовать данные выполняемые функции совершенно в другом классе. Такая необходимость может возникнуть при большом количестве кода или для хранения UI отдельно от выполняемых функций (для обеспечения поиска неисправностей, а также модульности).
Процесс обеспечения выполняемых функций компонентов в отдельных классах очень прост и осуществляется в четыре этапа:
-
При регистрировании компонента в качестве слушателя, создайте экземпляр класса, содержащего код выполняемых функций, при помощи ключевого слова new. Например:
save.addActionListener ( new Handler1 ()) ;
public class Handler1 implements ActionListene
public class Handler1 implements ActionListener// actionPerformed должен быть реализован для
// интерфейса ActionListener.
public void actionPerformed ( ActionEvent ae )В следующем приложении GUI-код, создающий фрейм и две кнопки, находится в классе TestButton. Выполняемые функции этих кнопок расположены в двух отдельных классах: Handler1 and Handler2.
Загрузите три данных класса и скомпилируйте внешний для них класс TestButton. В полученном процессе будут компилироваться все три класса.
При нажатии на одну из этих кнопок текст будет выводиться в консоль.
При запуске приложение должно выглядеть примерно так:
Класс JPasswordField
Компонент JPasswordField можно назвать облегченной версией текстового поля. Символы в таком поле не отображаются, а заменяются знаком *. При желании, звездочку можно заменить на любой другой символ.
Так же как и у текстового поля, у компонента JPasswordField имеется в распоряжении несколько конструкторов:
- JPasswordField() – создает поле JPasswordField нулевой ширины, установив в нем первоначальное значение текстовой строки равным null.
- JPasswordField(Document doc, String txt, int columns) – создает новое поле JPasswordField, использующее заданную модель хранения текста и ширину.
- JPasswordField(int columns) – создает новое пустое поле JPasswordField с заданной шириной.
- JPasswordField(String text) – создает новое поле JPasswordField, инициализированное с помощью текста в строке.
- JPasswordField(String text, int columns) – создает новое поле JPasswordField, инициализированное с помощью текста и значения ширины.
При вводе текста в строке будут отображаться символы *. Заменить их на какие-либо другие можно при помощи метода setEchoChar(char c). Например:
JPasswordField jpf = new JPasswordField ( 15 ) ;
jpf.setEchoChar ( ‘#’ ) ;В данном фрагменте создается объект JPasswordField шириной в 15 колонок. При чем, каждому набранному пользователем символу будет соответствовать не звездочка (*), а знак диеза (#).
Извлечь набранный в поле текст можно с помощью метода getPassword. В следующем примере метод getPassword вызывается в новой строке и присваивает полученное значение пароля переменной entrdpwd.
String entrdpwd = new String ( jpf.getPassword ()) ;
Приложение PasswordTest создает фрейм с текстовым полем PasswordField. Введите пароль и в желтом текстовом поле внизу фрейма вы увидите надпись, указывающая на его правильность или ошибочность. Приложение допускает только три попытки ввода пароля, при исчерпании лимита оно закрывается.
После компиляции и запуска приложения вы увидите результат, представленный на рисунке:
A может Вас также заинтересует что-нибудь из этого:
- Разное → Теория и практика Java: Динамическая компиляция и измерение производительности
- Java Standard Edition → Блокировки
- Java Standard Edition → Производительность операций ввода/вывода в Java
- Java сниппеты → Методы для работы с переменным количеством аргументов
- Разное → C# глазами Java программиста
- Java сниппеты → Использование readResolve
В Ruby из других языков
Если вы впервые посмотрите на Ruby код, он скорее всего напомнит вам некоторые используемые вами языки. Это не случайно. Большинство синтаксических конструкций покажутся знакомыми пользователям Perl, Python и Java, так что, если вы уже писали на них, изучить Ruby окажется проще простого.
Эта страница состоит из двух частей. В первой содержится попытка сверхкратко описать, что вас ждет при переходе с языка Х на Ruby. Вторая рассказывает об основных особенностях языка, и как эти особенности соотносятся с тем, что вы уже знаете.
Чего ожидать после языка Х на Ruby
- Пришедшим из C и C++
- В Ruby из Java
- Знакомым с Perl
- Разработчикам на PHP
- Пришедшим из Python
Важные замечания по поводу языка и подсказки
Тут собрано несколько подсказок и советов по основным особенностям Ruby, которые вы увидите по мере изучения языка
Итерации и циклы
Две особенности Ruby, отличающиеся от всего ранее увиденного, но к которым надо привыкнуть — это “блоки” и итераторы. Вместо того, чтобы итерироваться по индексу (как в С, С++ и pre-1.5 Java), или по списку (как в Perl for (@a) <. >, или в Python for i in aList: . ), в Ruby зачастую вы увидите
some_list.each do |this_item| # Внутри "блока" # у нас есть объект this_item. endЗа более подробной информацией о each (и сопутствующих collect , find , inject , sort , и т.д.), обращайтесь к ri Enumerable (и ri Enumerable#имя_функции ).
Все имеет значение
В Ruby нет разницы между выражением и оператором. Все имеет значение, даже если это значение — nil. Вот так:
x = 10 y = 11 z = if x y true else false end z # => trueСимволы — это не “легковесные” строки
Многие начинающие натыкаются на проблему понимания, что такое “символ” в Ruby, и для чего он предназначен.
Символы лучше всего сравнить с уникальными идентификаторами. Символ это скорее сам знак, а не то, что он означает. Запустите irb , чтобы почувствовать разницу:
irb(main):001:0> :george.object_id == :george.object_id => true irb(main):002:0> "george".object_id == "george".object_id => false irb(main):003:0>Метод object_id возвращает уникальный идентификатор объекта. Если два объекта имеют одинаковый object_id , то это один и тот же объект (указатель на один объект в памяти).
Как вы видите, как только вы используете символ, любой другой символ, идентичный по написанию с первым, будет обращаться к тому же объекту в памяти. У всех “символов” с одинаковым набором букв (с одним именем) один и тот же object_id .
Теперь взглянем на строки. Их object_id не совпадает. Это означает, что это два разных объекта в памяти. При создании строки Ruby всегда выделяет память для нее.
Если вы сомневаетесь, что использовать — символ или строку — задумайтесь, что важнее: уникальность объекта (например, для ключа в хеше) или содержание (как в примере выше — “george”)
Все является объектом
“Все — это объект” — не преувеличение. Даже классы и числа, и с ними можно делать то, что и с обычными объектами:
# То же самое, что и # class MyClass # attr_accessor :instance_var # end MyClass = Class.new do attr_accessor :instance_var endИзменяемые константы
Константы на самом деле не константы. Если вы поменяете значение константы, это выдаст предупреждение, но не остановит программу. Однако это не говорит о том, что вы должны переопределять константы.
Соглашение о наименовании
Ruby диктует некоторые правила о наименовании. Константы начинаются с заглавной буквы. Глобальные переменные начинаются со знака $ . Переменные экземпляра начинаются с @ . Переменные класса начинаются с @@ .
Имена методов могут начинаться с заглавных букв, однако это может запутать, например:
Constant = 10 def Constant 11 endЗначение Constant — 10, а Constant() — 11.
Именованные параметры
Как и в Python, начиная с версии 2.0 Ruby методы принимают именованные параметры.
def deliver(from: "A", to: nil, via: "mail") "Sending from #from> to #to> via #via>." end deliver(to: "B") # => "Sending from A to B via mail." deliver(via: "Pony Express", from: "B", to: "A") # => "Sending from B to A via Pony Express."“Истинное” Ruby
В Ruby все кроме nil и false рассматриваются как истина. В С, Python и многих других языках 0 и некоторые другие значения, например пустой список, являются ложью. Взгляните на следующий код Python (пример применим и у другим языкам):
# Python if 0: print("0 - истина") else: print("0 - ложь")Это выведет на экран “0 — ложь”. Эквивалент на Ruby:
# Ruby if 0 puts "0 - истина" else puts "0 - ложь" endВыведет на экран “0 — истина”.
Модификаторы доступа действуют до конца контекста
В следующем Ruby коде
class MyClass private def a_method; true; end def another_method; false; end endможно подумать, что another_method публичный. Нет, это не так. Модификатор доступа private действует до конца контекста, или до другого модификатора доступа. По умолчанию все методы публичны.
class MyClass # a_method публичный def a_method; true; end private # another_method приватный def another_method; false; end endpublic , private и protected на самом деле методы, и они могут принимать параметры. Можно передать им имена методов как символы, чтобы поменять область доступа метода.
Вызов методов
В Java public означает, что метод можно вызвать везде. protected методы можно вызвать только инстансами этого класса, инстансами дочернего класса и инстансами классов этого же пакета. private методы не может вызвать никто кроме инстанса класса.
В Ruby все немного по-другому. public методы на самом деле публичные. private метод может быть вызван только без явного объявления вызывающей стороны. Только self может быть вызывающей стороной приватного метода.
О protected методах надо поговорить подробнее. Protected метод может быть вызван инстансом текущего или дочернего класса, однако может иметь вызывающей стороной другой инстанс. Пример, позаимствованный из Ruby Language FAQ:
class Test # публичный метод по умолчанию def identifier 99 end def ==(other) identifier == other.identifier end end t1 = Test.new # => # t2 = Test.new # => # t1 == t2 # => true # сделаем `identifier' protected методом # это возможно, потому что можно вызвать метод у объекта other class Test protected :identifier end t1 == t2 # => true # теперь сделаем `identifier' приватным class Test private :identifier end t1 == t2 # NoMethodError: private method `identifier' called for #Открытые классы
Классы в Ruby “открыты”. То есть, вы можете открыть их и добавить или изменить их в любое время. Даже базовые классы, такие как Integer или Object , родительский для всех объектов. Ruby on Rails определяет несколько методов на Integer , чтобы работать со временем. Смотрите:
class Integer def hours self * 3600 # число секунд в 1 часе end alias hour hours end # 14 часов после 00:00, 1 января # (когда все только-только просыпаются ;) Time.mktime(2006, 01, 01) + 14.hours # => Sun Jan 01 14:00:00Прикольные имена методов
В Ruby имена методов могут оканчиваться на вопросительный или восклицательный знаки. По соглашению методы, которые отвечают на вопрос, заканчиваются вопросительным знаком (например, Array#empty? возвращает true если массив пустой). Некоторые, “потенциально опасные” методы (которые меняют вызывающую сторону, self или параметры) заканчиваются восклицательным знаком (например, exit! ). Однако не все методы, которые меняют аргументы заканчиваются так, например Array#replace заменяет содержимое массива переданным массивом. Просто нет смысла иметь метод, который бы не менял исходный массив в этом случае.
Singleton методы
Singleton методы — это методы, определенные на единственном инстансе и доступные только на нем.
class Car def inspect "Cheap car" end end porsche = Car.new porsche.inspect # => Cheap car def porsche.inspect "Expensive car" end porsche.inspect # => Expensive car # Другие объекты не изменяют поведение other_car = Car.new other_car.inspect # => Cheap car“Пропавшие” методы
Ruby не сдается, если не находит вызванный метод, а вызывает метод method_missing , передав ему имя “потерянного” метода и аргументы. По умолчанию method_missing вызывает исключение NameError, но вы можете переопределить его по вашим потребностям, что и делает множество библиотек, например:
# id - имя вызванного метода, *arguments - такой синтаксис # передает все аргументы в функцию как массив 'arguments' def method_missing(id, *arguments) puts "Метод #id> был вызван, но не найден." + "Его аргументы: #arguments.join(", ")>" end __ :a, :b, 10 # => Метод __ был вызван, но не найден. # Его аргументы: a, b, 10Пример выше просто выводит на экран детали вызова метода, но вы можете сделать что-то полезное по вашему усмотрению.
Передача сообщений, а не вызов функций
На самом деле вызов метода — это передача (send) ему сообщения. Наглядно:
# Это 1 + 2 # то же самое, что и . 1.+(2) # и то же самое, что и: 1.send "+", 2Блоки — тоже объекты, только они об этом еще не знают
Блоки (на самом деле — замыкания) часто используются в стандартной библиотеке. Чтобы вызвать блок можно либо использовать yield , либо сделать его объектом класса Proc , прибавив специальный аргумент к списку аргументов, например так:
def block(&the_block) # Тут the_block это блок, переданный методу the_block # вернет блок (как объект) end adder = block |a, b| a + b > # adder - это объект класса Proc adder.class # => ProcВы можете создавать блоки-объекты также через Proc.new с блоком или вызывая lambda метод.
В принципе, методы — это тоже объекты:
method(:puts).call "puts is an object!" # => puts is an object!Операторы — это синтаксический сахар
Большинство операторов в Ruby — это просто синтаксический сахар (с учетом некоторых правил) для вызова методов. Например, можно переопределить + метод для класса Integer :
class Integer # Так можно, но не значит, что нужно ;) def +(other) self - other end endТак что вам не потребуется operator+ из С++, и т.д.
А еще можно симулировать обращения к объекту как к массиву с помощью методов [] и []= . Можно определить унарные операторы + и — (например +1 или -2) методами +@ и -@ соответственно. Операторы ниже, однако, не являются синтаксическим сахаром. Они не являются методами и не могут быть переопределены:
=, .., . , not, &&, and, ||, or, ::В дополнение к этому, += , *= и т.д. — это всего лишь сокращения для var = var + other_var , var = var * other_var и т.д. и, соответственно, не могут быть переопределены.
Узнать больше
Если вам хочется узнать о Ruby больше — перейдите к документации.
Начните сейчас, это легко!
- Попробуйте Ruby! (в своем браузере)
- Ruby за двадцать минут
- В Ruby из других языков
Наследование
Наследование является неотъемлемой частью Java. При использовании наследования вы говорите: Этот новый класс похож на тот старый класс. В коде это пишется как extends, после которого указываете имя базового класса. Тем самым вы получаете доступ ко всем полям и методам базового класса. Используя наследование, можно создать общий класс, которые определяет характеристики, общие для набора связанных элементов. Затем вы можете наследоваться от него и создать новый класс, который будет иметь свои уникальные характеристики. Главный наследуемый класс в Java называют суперклассом. Наследующий класс называют подклассом. Получается, что подкласс — это специализированная версия суперкласса, которая наследует все члены суперкласса и добавляет свои собственные уникальные элементы. К примеру, в Android есть класс View и подкласс TextView.
Чтобы наследовать класс, достаточно вставить имя наследуемого класса с использованием ключевого слова extends:
public class MainActivity extends Activity
В этом коде мы наследуемся от класса Activity и добавляем свой код, который будет отвечать за наше приложение.
Подкласс в свою очередь может быть суперклассом другого подкласса. Так например, упоминавший ранее класс TextView является суперклассом для EditText.
В производный класс можно добавлять новые методы.
Для каждого создаваемого подкласса можно указывать только один суперкласс. При этом никакой класс не может быть собственным суперклассом.
Хотя подкласс включает в себя все члены своего суперкласса, он не может получить доступ к тем членам суперкласса, которые объявлены как private.
Помните, мы создавали класс Box для коробки кота. Давайте наследуемся от этого класса и создадим новый класс, который будет иметь не только размеры коробки, но и вес.
В том же файле Box.java после последней закрывающей скобки добавьте новый код:
class HeavyBox extends Box < int weight; // вес коробки // конструктор HeavyBox(int w, int h, int d, int m) < width = w; height = h; depth = d; weight = m; // масса >>Возвращаемся в главную активность и пишем код:
HeavyBox box = new HeavyBox(15, 10, 20, 5); int vol = box.getVolume(); mInfoTextView.setText("Объём коробки: " + vol + " Вес коробки: " + box.weight);Обратите внимание, что мы вызываем метод getVolume(), который не прописывали в классе HeavyBox. Однако мы можем его использовать, так как мы наследовались от класса Box и нам доступны все открытые поля и методы. Заодно мы вычисляем вес коробки с помощью новой переменной, которую добавили в подкласс.
Теперь у нас появилась возможность складывать в коробку различные вещи. В хозяйстве всё пригодится.
При желании вы можете создать множество разных классов на основе одного суперкласса. Например, мы можем создать цветную коробку.
class ColorBox extends Box < int color; // цвет коробки // конструктор ColorBox(int w, int h, int d, int c) < width = w; height = h; depth = d; color = c; // цвет >>Ключевое слово super
В Java существует ключевое слово super, которое обозначает суперкласс, т.е. класс, производным от которого является текущий класс. В данном случае, супер не означает превосходство, скорее даже наоборот, дочерний класс имеет больше методов, чем родительский. Само слово пошло из теории множеств, где используется термин супермножество. Посмотрим, зачем это нужно.
В конструкторе HeavyBox мы дублировали поля width,height и depth, которые уже есть в классе Box. Это не слишком эффективно. Кроме того, возможны ситуации, когда суперкласс имеет закрытые члены данных, но мы хотим иметь к ним доступ. Через наследование это не получится, так как закрытые члены класса доступны только родному классу. В таких случаях вы можете сослаться на суперкласс.
Ключевое слово super можно использовать для вызова конструктора суперкласса и для обращения к члену суперкласса, скрытому членом подкласса.
Использование ключевого слова super для вызова конструктора суперкласса
class HeavyBox extends Box < int weight; // вес коробки // конструктор // инициализируем переменные с помощью ключевого слова super HeavyBox(int w, int h, int d, int m) < super(w, h, d); // вызов конструктора суперкласса weight = m; // масса >>Вызов метода super() всегда должен быть первым оператором, выполняемым внутри конструктора подкласса.
При вызове метода super() с нужными аргументами, мы фактически вызываем конструктор Box, который инициализирует переменные width, height и depth, используя переданные ему значения соответствующих параметров. Вам остаётся инициализировать только своё добавленное значение weight. При необходимости вы можете сделать теперь переменные класса Box закрытыми. Проставьте у полей класса Box модификатор private и убедитесь, что вы можете обращаться к ним без проблем.
У суперкласса могут быть несколько перегруженных версий конструкторов, поэтому можно вызывать метод super() с разными параметрами. Программа выполнит тот конструктор, который соответствует указанным аргументам.
Вторая форма ключевого слова super действует подобно ключевому слову this, только при этом мы всегда ссылаемся на суперкласс подкласса, в котором она использована. Общая форма имеет следующий вид:
super.член
Здесь член может быть методом либо переменной экземпляра.
Подобная форма подходит в тех случаях, когда имена членов подкласса скрывают члены суперкласса с такими же именами.
class A < int i; >// наследуемся от класса A class B extends A < int i; // имя переменной совпадает и скрывает переменную i в классе A B(int a, int b) < super.i = a; // обращаемся к переменной i из класса A i = b; // обращаемся к переменной i из класса B >void show() < System.out.println("i из суперкласса: " + super.i); System.out.println("i в подклассе: " + i); >> class MainActivityВ результате мы должны увидеть:
i из суперкласса: 1 i в подклассе: 2
Таким образом, знакомое нам выражение super.onCreate(savedInstanceState) обращается к методу onCreate() из базового класса.
Создание многоуровневой иерархии
Мы использовали простые примеры, состоящие из суперкласса и подкласса. Можно строить более сложные конструкции, содержащие любое количество уровней наследования. Например, класс C может быть подклассом класса B, который в свою очередь является подклассом класса A. В подобных ситуациях каждый подкласс наследует все характеристики всех его суперклассов.
Напишем пример из трёх классов. Суперкласс Box, подкласс HeavyBox и подкласс MoneyBox. Последний класс наследует все характеристики классов Box и HeavyBox, а также добавляет поле cost, которое содержит стоимость коробки.
Box.java
package ru.alexanderklimov.expresscourse; class Box < private int width; // ширина коробки private int height; // высота коробки private int depth; // глубина коробки // Конструктор для создания клона объекта Box(Box ob) < // передача объекта конструктору width = ob.width; height = ob.height; depth = ob.depth; >// Конструктор, используемый при указании всех измерений Box(int w, int h, int d) < width = w; height = h; depth = d; >// Конструктор, используемый, когда ни одно из измерений не указано Box() < // значение -1 используется // для указания неинициализированного параллелепипеда width = -1; height = -1; depth = -1; >// Конструктор для создания куба Box(int len) < width = height = depth = len; >// вычисляем объём коробки int getVolume() < return width * height * depth; >>HeavyBox.java
package ru.alexanderklimov.expresscourse; //Добавление веса class HeavyBox extends Box < int weight; // вес коробки // Конструктор клона объекта HeavyBox(HeavyBox ob) < // передача объекта конструктору super(ob); weight = ob.weight; >// Конструктор, используемый // при указании всех параметров HeavyBox(int w, int h, int d, int m) < super(w, h, d); // вызов конструктора суперкласса weight = m; // масса >// Конструктор по умолчанию HeavyBox() < super(); weight = -1; >// Конструктор для создания куба HeavyBox(int len, int m) < super(len); weight = m; >>MoneyBox
package ru.alexanderklimov.expresscourse; //Цена коробки class MoneyBox extends HeavyBox < int cost; // Конструирование клона объекта MoneyBox(MoneyBox ob) < // передача объекта конструктору super(ob); cost = ob.cost; >// Конструктор, используемый // при указании всех параметров MoneyBox(int w, int h, int d, int m, int c) < super(w, h, d, m); // вызов конструктора суперкласса cost = c; >// Конструктор по умолчанию MoneyBox() < super(); cost = -1; >// Конструктор для создания куба MoneyBox(int len, int m, int c) < super(len, m); cost = c; >>Код для основной активности, например, при щелчке кнопки:
public void onClick(View v)
В результате мы получим различные значения, вычисляемые в коде. Благодаря наследованию, класс MoneyBox может использовать классы Box и HeavyBox, добавляя только ту информацию, которая нам требуется для его собственного специализированного применения. В этом и состоит принцип наследования, позволяя повторно использовать код.
Метод super() всегда ссылается на конструктор ближайшего суперкласса в иерархии. Т.е. метод super() в классе MoneyBox вызывает конструктор класса HeavyBox, а метод super() в классе HeavyBox вызывает конструктор класса Box.
Если в иерархии классов конструктор суперкласса требует передачи ему параметров, все подклассы должны передавать эти параметры по эстафете.
В иерархии классов конструкторы вызываются в порядке наследования, начиная с суперкласса и заканчивая подклассом. Если метод super() не применяется, программа использует конструктор каждого суперкласса, заданный по умолчанию или не содержащий параметров.
Вы можете создать три класса A, B, C, которые наследуются друг от друга (A←B←C), у которых в конструкторе выводится текст и вызвать в основном классе код:
C c = new C();Вы должные увидеть три строчки текста, определённые в каждом конструкторе класса. Поскольку суперкласс ничего не знает о своих подклассах, любая инициализация полностью независима и, возможно, обязательна для выполнения любой инициализацией, выполняемой подклассом.
Переопределение методов
Если в иерархии классов имя и сигнатура типа метода подкласса совпадает с атрибутами метода суперкласса, то метод подкласса переопределяет метод суперкласса. Когда переопределённый метод вызывается из своего подкласса, он всегда будет ссылаться на версию этого метода, определённую подклассом. А версия метода из суперкласса будет скрыта.
Если нужно получить доступ к версии переопределённого метода, определённого в суперклассе, то используйте ключевое слово super.
Не путайте переопределение с перегрузкой. Переопределение метода выполняется только в том случае, если имена и сигнатуры типов двух методов идентичны. В противном случае два метода являются просто перегруженными.
В Java SE5 появилась запись @Override; она не является ключевым словом. Если вы собираетесь переопределить метод, используйте @Override, и компилятор выдаст сообщение об ошибке, если вместо переопределения будет случайно выполнена перегрузка.
Для закрепления материала создадим класс Animal с одним методом.
package ru.alexanderklimov.expresscourse; public class Animal < String sleep()< return "Животные иногда спят"; >>Теперь создадим класс Cat, наследующий от первого класса.
package ru.alexanderklimov.expresscourse; public class Cat extends Animal
Java знает, у родительского класса есть метод sleep(). Удостовериться можно следующим образом. Находясь в классе Cat, выберите в меню Source | Override/Implement Methods. . Появится диалоговое окно, где можно отметить флажком нужный метод.
В результате в класс будет добавлена заготовка:
@Override String sleep() < // TODO Auto-generated method stub return super.sleep(); >Попробуем вызвать данный метод в основном классе активности:
public void onClick(View v)
Мы получим текст, который определён в суперклассе, хотя вызывали метод дочернего класса.
Но если мы хотим получить другой текст, совсем не обязательно придумывать новые методы. Достаточно закомментировать вызов метода из суперкласса и добавить свой вариант.
@Override String sleep() < //return super.sleep(); return "Коты постоянно спят!"; >Запускаем программу и нажимаем на кнопку. И получим уже другой ответ, более соответствующий описанию среднестатистического кота. Заметьте, что код для щелчка кнопки мы не меняем, но система сама разберётся, что выводить нужно текст не из суперкласса, а из дочернего класса.
Рассмотрим другой пример переопределения методов. Создадим суперкласс Figure, который будет содержать размеры фигуры, а также метод для вычисления площади. А затем создадим два других класса Rectangle и Triangle, у которых мы переопределим данный метод.
class Figure < double dim1; double dim2; Figure(double a, double b) < dim1 = a; dim2 = b; >double area() < System.out.printLn("Площадь фигуры"); return 0; >> class Rectangle extends Figure < Rectangle(double a, double b) < super(a, b); >// Переопределяем метод double area() < System.out.println("Площадь прямоугольника"); return dim1 * dim2; >> class Triangle extends Figure < Triangle(double a, double b) < super(a, b); >// переопределяем метод double area() < System.out.println("Площадь треугольника"); return dim1 * dim2 / 2; >> // В главной активности Figure figure = new Figure(10, 10); Rectangle rectangle = new Rectangle(8, 5); Triangle triangle = new Triangle(10, 6); Figure fig; fig = figure; mInfoTextView.setText("Площадь равна " + fig.area); fig = rectangle; mInfoTextView.setText("Площадь равна " + fig.area); fig = triangle; mInfoTextView.setText("Площадь равна " + fig.area);Как видите, во всех классах используется одно и тоже имя метода, но каждый класс по своему вычисляет площадь в зависимости от фигуры. Это очень удобно и позволяет не придумывать новые названия методов в классах, которые наследуются от базового класса.
