Модификаторы видимости
Доступ к любому члену класса – полю или методу – может быть ограничен. Для этого перед его объявлением ставится ключевое слово private. Оно означает, что к этому члену класса нельзя будет обратиться из методов других классов.
Ключевое слово public может употребляться в тех же случаях, но имеет противоположный смысл. Оно означает, что данный член класса является доступным. Если это поле, его можно использовать в выражениях или изменять при помощи присваивания, а если метод, его можно вызывать.
Ключевое слово protected означает, что доступ к полю или методу имеет сам класс и все его потомки.
Если при объявлении члена класса не указан ни один из перечисленных модификаторов, используется модификатор по умолчанию (default). Он означает, что доступ к члену класса имеют все классы, объявленные в том же пакете.
Перепишем класс Dog следующим образом:
class Dog {
private int age;// возраст
private String name; // кличка
public Dog(String n, int a) {
name = n;
age = a;
}
public void voice() {
for(int i = 1; i <= age; i++) {
System.out.println("гав-гав");
}
}
}
Поля age и name окажутся скрытыми. Это значит, что нельзя изменять их (или считывать их значение) где-либо за пределами класса. Невозможно в методе main() создать объект класса Dog, а затем присвоить его полю age или name новое значение, как в следующем примере:
public static void main(String[] args) {
Dog dog1 = new Dog("Тузик", 4);
dog1.age = 10; // нельзя, поле age скрыто
dog1.name = "Жучка"; // переименовать собаку тоже нельзя, поле name скрыто
dog1.voice(); // это можно, метод voice() открытый
}
Возможность скрывать поля и методы класса используется для того, чтобы уберечь программиста от возможных ошибок, сделать классы понятнее и проще в использовании. При этом реализуется принцип инкапсуляции.
Инкапсуляция означает сокрытие деталей реализации класса. Класс разделяется на две части: внутреннюю и внешнюю. Внешняя часть (интерфейс) тщательно продумывается исходя из того, каким образом могут взаимодействовать с объектами данного класса другие объекты программы. Внутренняя часть закрыта от посторонних, она нужна только самому классу для обеспечения правильной работы открытых методов.
Например, в классе Dog есть целочисленное поле age (возраст). Можно оставить его открытым, тогда его при необходимости можно будет изменить простым присваиванием (очень удобно). Но при этом ничто не мешает присвоить этому полю заведомо некорректное значение (например, 666 или -5 или 3000). Это может произойти из-за ошибки в программе. Или, к примеру, пользователь вводит возраст собаки в текстовое поле, а программа присваивает его в ответ на нажатие кнопки (и пользователь может ошибиться). Это нежелательный случай. Лучше сделать поле age закрытым (private) и добавить два открытых метода: getAge() и setAge(). Первый метод будет просто возвращать значение скрытого поля:
public int getAge() {
return age;
}
Второй метод позволит задать новый возраст собаки, производя при этом проверку присваиваемого значения.
public void setAge (int newAge) {
if (newAge < 0)
System.out.println("Как это понимать? Собака еще не родилась?");
else if (newAge > 30)
System.out.println("Они столько не живут");
else age = newAge;
}
Теперь возраст можно изменить, лишь вызвав метод setAge(), который в случае неподходящего параметра выведет в консоль сообщение и не будет ничего изменять.
Профессиональные программисты, разрабатывающие программы по объектно-ориентированной методологии, скрывают все поля своих классов, создавая для каждого из них открытые методы c приставками get и set, причем в методах set проводятся все необходимые проверки.
Необходимо отметить, что в примере неправильный возраст может «прорваться» через конструктор при создании нового объекта. Никто ведь не мешает написать:
Dog dog1 = new Dog("Тузик", 2000);
Для того, чтобы этого не случилось, необходимо переписать конструктор:
public Dog(String n, int a) {
name = n;age = setAge(a);
}
Теперь проверка осуществляется в конструкторе. Попытка завести в программе собаку с явно некорректными данными не увенчается успехом. Если возраст будет меньше 0 или больше 30, присваивание не выполнится и атрибут age будет иметь значение по умолчанию (для типа int это 0).
Перегрузка
В одном классе можно создать несколько методов с одним и тем же именем, различающихся по своим параметрам. Этот прием называется перегрузкой методов. Когда один из этих методов будет вызван, произойдет сопоставление переданных ему параметров (их количества и типов) с параметрами всех методов класса с таким именем. Если подходящий метод будет найден, выполнится именно он.
Например, в дополнение к конструктору, который уже есть в классе Dog, мы можем описать конструктор без параметров:
public Dog() {
name = "Незнакомец";
}
В этом конструкторе объекту класса Dog (очевидно, собака с неизвестной кличкой) присваивается при регистрации в программе имя «Незнакомец». Теперь можно воспользоваться одним из двух конструкторов:
Dog dog1 = new Dog("Тузик", 2); // Собака по кличке Тузик, возраст 2 года
Dog dog2 = new Dog(); // Собака по кличке «Незнакомец», возраст 0
Dog dog3 = new Dog(10); // Неверно! Не существует конструктора с такими параметрами
Нельзя создавать несколько одноименных методов с одинаковым числом и типом параметров.
Полиморфизм
Полиморфизм – это возможность класса выступать в программе в роли любого из своих предков, несмотря на то, что в нем может быть изменена реализация любого из методов.
Изменить работу любого из методов, унаследованных от класса-предка, класс-потомок может, описав новый метод с точно таким же именем и параметрами. Это называется переопределением. При вызове такого метода для объекта класса-потомка будет выполнена новая реализация.
Пусть, к примеру, необходимо расширить наш класс Dog классом BigDog, для того, чтобы программа особым образом моделировала поведение больших злых собак. В частности, большие собаки лают по-другому. Во-первых, громче, а во-вторых, они не умеют считать. Поэтому необходимо переопределить метод voice():
class BigDog extends Dog {
public void voice() {
for (int i = 1; i <= 30; i++) {
System.out.print("ГАВ-");
}
}
}
Теперь необходимо изменить метод main(), создать двух разных собак: обычную и большую и заставим их лаять.
Dog dog = new Dog("Тузик", 2);
dog.voice();
BigDog bigdog = new BigDog();
bigdog.voice();
Объект подкласса всегда будет одновременно являться объектом любого из своих суперклассов. Поэтому в том же примере мы могли бы обойтись и одной переменной:
Dog dog = new Dog("Тузик", 2);
dog.voice();
dog = new BigDog();
dog.voice();
Т.е. переменная dog имеет тип Dog, но в третьей строке она начинает указывать на объект класса BigDog, то есть БОЛЬШУЮ собаку, которая при вызове метода voice() будет лаять как БОЛЬШАЯ собака. Это одна из впечатляющих возможностей объектно-ориентированного программирования.
Дата добавления: 2017-01-26; просмотров: 1319;