Объекты и классы
Объекты и классы языка Java. Программы, создающие собственные классы. Описание классов, объектов, методов и функций. Абстрактные, окончательные, статистические и вложенные методы и классы, а также конструктор класса. Статическая переменная и ее изменения.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | лабораторная работа |
Язык | русский |
Дата добавления | 30.06.2009 |
Размер файла | 374,1 K |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
Факультет "Информатика и системы управления"
Методические указания к лабораторной работе
по курсу "Распределенные системы обработки информации"
Объекты и классы
Москва 2004 г.
Оглавление
- Цель работы
- Задание для домашней подготовки
- Задания к лабораторной работе
- Задание 1
- Задание 2
- Содержание отчета
- Контрольные вопросы
- Литература
- Приложение 1.Объекты и классы Java [3]
- Приложение 2. Листинг программы, иллюстрирующей работу класса GregorianCalendar
- Приложение 3. Листинг программы, иллюстрирующей работу класса Emp (Служащий)
- Цель работы
- Получить первичные знания об объектах и классах языка Java. Научиться писать программы, создающие собственные классы.
Задание для домашней подготовки
Ознакомиться с материалом, предоставленным в приложении к данным методическим указаниям. Ознакомиться с текстом задания к лабораторной работе в соответствии с вариантом и написать программу.
Задания к лабораторной работе
Задание 1
1. Дать описание класса Circle.
2. Объекты Circle использовать для описания плавательного бассейна и дорожки вокруг него. С помощью методов Circumference и Area следует вычислить стоимость бетонирования дорожки и строительства ограды вокруг бассейна.
3. Написать программу, которая должна определить стоимость строительства ограды и дорожки при планировании бассейна. Строительные правила требуют, чтобы бассейн окружала бетонная дорожка, и вся территория была огорожена. Текущая стоимость ограды составляет 10 у.е. за 1 погонный метр, а стоимость бетонирования 5 у.е. за . Предполагается, что ширина дорожки, окружающей бассейн, составляет 1.5 m и что клиент указывает радиус круглого бассейна.
4. Определить все функции - члены класса Rectangle, объявленные в заголовке этого класса.
5. Вычислить и вывести стоимость отделочных работ передней стороны сарая (см. рис. 1). Пользователь задаёт размеры передней стороны сарая и двери, а программа выдаёт стоимость обшивки и наличников. Стоимость обшивки 10 у.е. за 1 м2; стоимость наличников 1 у.е. за 1 погонный метр. Длина наличников равна сумме периметров передней стороны сарая и двери. Площадь обшивки равна разности площадей передней стороны сарая и двери.
Рисунок 1
Задание 2
Классы [Л.2 на с.53-57]. Номер задания соответствует порядковому номеру в журнале (по модулю 20).
В ходе лабораторной работы необходимо отладить написанную дома программу и предъявить работающую программу преподавателю.
Содержание отчета
Отчет должен содержать:
1. Постановку задачи, решаемой отлаженных программ.
2. Руководство пользователя отлаженных программ, содержащее описание интерфейсов всех функций программ.
3. Руководство системного программиста в объеме, необходимом для последующего сопровождения (модификации) отлаженных программ другим программистом (само документируемый листинг программы, необходимые пояснения алгоритма и его программной реализации).
Контрольные вопросы
1. Что такое класс?
2. Из чего состоит класс?
3. Что такое инкапсуляция и скрытие информации?
4. Для чего нужен конструктор?
Литература
Арнолд К., Гослинг Дж., Холмс Д. Язык программирования Java:Пер. с англ. - М.: Издательский дом «Вильямс», 2001 г. - 624 с., ил.
Павловская Т.А., Щупак Ю.А. С/С++. Объектно-ориентированное программирование: Практикум. -СПб.: Питер, 2002. -240с.
Электронный Java-учебник. Автор неизвестен.
Приложение 1.Объекты и классы Java [3]
Как описать класс и подкласс
Описание класса начинается со слова class, после которого записывается имя класса. Рекомендуется начинать имя класса с заглавной буквы. Перед словом class можно записать модификаторы класса. Это одно из слов public, abstract, final, strictfp. Перед именем вложенного класса можно поставить, кроме того, модификаторы protected, private, static. Тело класса, в котором в любом порядке перечисляются поля, методы, вложенные классы и интерфейсы, заключается в фигурные скобки.
При описании поля указывается его тип, затем, через пробел, имя и, может быть, начальное значение после знака равенства, которое можно записать константным выражением.
Описание поля может начинаться с одного или нескольких необязательных модификаторов public, protected, private, static, final, transient, volatile. Если надо поставить несколько модификаторов, то перечислять их рекомендуется в указанном порядке, поскольку некоторые компиляторы требуют определенного порядка записи модификаторов.
При описании метода указывается тип возвращаемого им значения или слово void, затем, через пробел, имя метода, потом, в скобках, список параметров. После этого в фигурных скобках расписывается выполняемый метод.
Описание метода может начинаться с модификаторов public, protected, private, abstract, static, final, synchronized, native, strictfp. В списке параметров через запятую перечисляются тип и имя каждого параметра. Перед типом какого-либо параметра может стоять модификатор final. Такой параметр нельзя изменять внутри метода. Список параметров может отсутствовать, но скобки сохраняются.
Перед началом работы метода для каждого параметра выделяется ячейка оперативной памяти, в которую копируется значение параметра, заданное при обращении к методу. Такой способ называется передачей параметров по значению.
Имя метода, число и типы параметров образуют сигнатуру (signature) метода. Компилятор различает методы не по их именам, а по сигнатурам. Это позволяет записывать разные методы с одинаковыми именами, различающиеся числом и/или типами параметров.
Замечание
Тип возвращаемого значения не входит в сигнатуру метода, значит, методы не могут различаться только типом результата их работы.
Такое дублирование методов называется перегрузкой Перегрузка методов очень удобна в использовании
При переопределении права доступа к методу можно только расширить. Открытый метод public должен остаться открытым, защищенный protected может стать открытым.
Можно ли внутри подкласса обратиться к методу суперкласса? Да, можно, если уточнить имя метода, словом super, например, super.moveTo(30, 40). Можно уточнить и имя метода, записанного в этом же классе, словом this, например, this.moveTo (50, 70), но в данном случае это уже излишне. Таким же образом можно уточнять и совпадающие имена полей, а не только методов.
Переопределение методов приводит к интересным результатам. В классе Pet мы описали метод voice(). Переопределим его в подклассах и используем в классе chorus, как показано в листинге 1
Листинг 1. Пример полиморфного метода
abstract class Pet{
abstract void voice();
}
class Dog extends Pet{
int k = 10;
void voice(){
System.out.printin("Gav-gav!");
}
}
class Cat extends Pet{
void voice () {
System.out.printin("Miaou!");
}
}
class Cow extends Pet{
void voice(){
System.out.printin("Mu-u-u!");
}
}
public class Chorus(
public static void main(String[] args){
Pet[] singer = new Pet[3];
singer[0] = new Dog();
singer[1] = new Cat();
singer[2] = new Cow();
for (int i = 0; i < singer.length; i++)
singer[i].voice();
}
}
Животные поют своими голосами! Все дело здесь в определении поля singer[]. Хотя массив ссылок singer [] имеет тип Pet, каждый его элемент ссылается на объект своего типа Dog, Cat, Сow. При выполнении программы вызывается метод конкретного объекта, а не метод класса, которым определялось имя ссылки. Так в Java реализуется полиморфизм.
В языке Java все методы являются виртуальными функциями.
В описании класса Pet имеется новое слово abstract. Класс Pet и метод voice() являются абстрактными.
Абстрактные методы и классы
При описании класса Pet мы не можем задать в методе voice () никакой полезный алгоритм, поскольку у всех животных совершенно разные голоса.
В таких случаях мы записываем только заголовок метода и ставим после закрывающей список параметров скобки точку с запятой. Этот метод будет абстрактным (abstract), что необходимо указать компилятору модификатором abstract.
Если класс содержит хоть один абстрактный метод, то создать его экземпляры, а тем более использовать их, не удастся. Такой класс становится абстрактным, что обязательно надо указать модификатором abstract.
Как же использовать абстрактные классы? Только порождая от них подклассы, в которых переопределены абстрактные методы.
Зачем же нужны абстрактные классы? Не лучше ли сразу написать нужные классы с полностью определенными методами, а не наследовать их от абстрактного класса? Для ответа снова обратимся к листингу 1.
Хотя элементы массива singer [] ссылаются на подклассы Dog, Cat, Cow, но все-таки это переменные типа Pet и ссылаться они могут только на поля и методы, описанные в суперклассе Pet. Дополнительные поля подкласса для них недоступны. Попробуйте обратиться, например, к полю k класса Dog, написав singer [0].k. Компилятор "скажет", что он не может реализовать такую ссылку. Поэтому метод, который реализуется в нескольких подклассах, приходится выносить в суперкласс, а если там его нельзя реализовать, то объявить абстрактным. Таким образом, абстрактные классы группируются на вершине иерархии классов.
Кстати, можно задать пустую реализацию метода, просто поставив пару фигурных скобок, ничего не написав между ними, например:
void voice(){}
Получится полноценный метод. Но это искусственное решение, запутывающее структуру класса.
Замкнуть же иерархию можно окончательными классами.
Окончательные члены и классы
Пометив метод модификатором final, можно запретить его переопределение в подклассах. Это удобно в целях безопасности. Вы можете быть уверены, что метод выполняет те действия, которые вы задали. Именно так определены математические функции sin(), cos() и прочие в классе Math. Мы уверены, что метод Math.cos (x) вычисляет именно косинус числа х. Разумеется, такой метод не может быть абстрактным.
Для полной безопасности, поля, обрабатываемые окончательными методами, следует сделать закрытыми (private).
Если же пометить модификатором final весь класс, то его вообще нельзя будет расширить. Так определен, например, класс Math:
public final class Math{ . . . }
Для переменных модификатор final имеет совершенно другой смысл. Если пометить модификатором final описание переменной, то ее значение (а оно должно быть обязательно задано или здесь же, или в блоке инициализации или в конструкторе) нельзя изменить ни в подклассах, ни в самом классе. Переменная превращается в константу. Именно так в языке Java определяются константы:
public final int MIN_VALUE = -1, MAX_VALUE = 9999;
По соглашению константы записываются прописными буквами, слова в них разделяются знаком подчеркивания.
На самой вершине иерархии классов Java стоит класс Object.
Класс Object
Если при описании класса мы не указываем никакое расширение, т. е. не пишем слово extends и имя класса за ним, как при описании класса Pet, то Java считает этот класс расширением класса object, и компилятор дописывает это за нас:
class Pet extends Object{ . . . }
Можно записать это расширение и явно.
Сам же класс Оbject не является ничьим наследником, от него начинается иерархия любых классов Java. В частности, все массивы -- прямые наследники класса Оbject.
Поскольку такой класс может содержать только общие свойства всех классов, в него включено лишь несколько самых общих методов, например, метод equals(), сравнивающий данный объект на равенство с объектом, заданным в аргументе, и возвращающий логическое значение. Его можно использовать так:
Object objl = new Dog(), obj 2 = new Cat();
if (obj1.equals(obj2))
Оцените объектно-ориентированный дух этой записи: объект obj1 активен, он сам сравнивает себя с другим объектом. Можно, конечно, записать и obj2.equals (obj1), сделав активным объект obj2, с тем же результатом.
Напомним, что ссылки можно сравнивать на равенство и неравенство:
obj1 = obj2; obj1 != obj 2;
В этом случае сопоставляются адреса объектов, мы можем узнать, не указывают ли обе ссылки на один и тот же объект.
Метод equals() же сравнивает содержимое объектов в их текущем состоянии, фактически он реализован в классе object как тождество: объект равен только самому себе. Поэтому его часто переопределяют в подклассах, более того, правильно спроектированные, классы должны переопределить методы класса Оbject, если их не устраивает стандартная реализация.
Второй метод класса Оbject, который следует переопределять в подклассах, -- метод toString (). Это метод без параметров, который пытается содержимое объекта преобразовать в строку символов и возвращает объект класса String.
К этому методу исполняющая система Java обращается каждый раз, когда требуется представить объект в виде строки, например, в методе printing.
Конструкторы класса
Вы уже обратили внимание на то, что в операции new, определяющей экземпляры класса, повторяется имя класса со скобками. Это похоже на обращение к методу, но что за "метод", имя которого полностью совпадает с именем класса?
Такой "метод" называется конструктором класса . Его своеобразие заключается не только в имени. Перечислим особенности конструктора.
· Конструктор имеется в любом классе. Даже если вы его не написали, компилятор Java сам создаст конструктор по умолчанию, который, впрочем, пуст, он не делает ничего, кроме вызова конструктора суперкласса.
· Конструктор выполняется автоматически при создании экземпляра класса, после распределения памяти и обнуления полей, но до начала использования создаваемого объекта.
· Конструктор не возвращает никакого значения. Поэтому в его описании не пишется даже слово void, но можно задать один из трех модификаторов public, protected или private.
· Конструктор не является методом, он даже не считается членом класса. Поэтому его нельзя наследовать или переопределить в подклассе.
· Тело конструктора может начинаться:
o с вызова одного из конструкторов суперкласса, для этого записывается слово super() с параметрами в скобках, если они нужны;
o с вызова другого конструктора того же класса, для этого записывается слово this() с параметрами в скобках, если они нужны.
Если же super() в начале конструктора не указан, то вначале выполняется конструктор суперкласса без аргументов, затем происходит инициализация полей значениями, указанными при их объявлении, а уж потом то, что записано в конструкторе.
Во всем остальном конструктор можно считать обычным методом, в нем разрешается записывать любые операторы, даже оператор return, но только пустой, без всякого возвращаемого значения.
В классе может быть несколько конструкторов. Поскольку у них одно и то же имя, совпадающее с именем класса, то они должны отличаться типом и/или количеством параметров.
В наших примерах мы ни разу не рассматривали конструкторы классов, поэтому при создании экземпляров наших классов вызывался конструктор класса Оbject.
Операция new
Операция с одним операндом, обозначаемая словом new применяется для выделения памяти массивам и объектам.
В первом случае в качестве операнда указывается тип элементов массива и количество его элементов в квадратных скобках, например:
double a[] = new double[100];
Во втором случае операндом служит конструктор класса. Если конструктора в классе нет, то вызывается конструктор по умолчанию.
Числовые поля класса получают нулевые значения, логические поля -- значение false, ссылки -- значение null.
Результатом операции new будет ссылка на созданный объект. Эта ссылка может быть присвоена переменной типа ссылка на данный тип:
Dog k9 = new Dog ();
но может использоваться и непосредственно
new Dog().voice();
Здесь после создания безымянного объекта сразу выполняется его метод voice(). Такая странная запись встречается в программах, написанных на Java, на каждом шагу.
Статические члены класса
Разные экземпляры одного класса имеют совершенно независимые друг от друга поля, принимающие разные значения. Изменение поля в одном экземпляре никак не влияет на то же поле в другом экземпляре. В каждом экземпляре для таких полей выделяется своя ячейка памяти. Поэтому такие поля называются переменными экземпляра класса или переменными объекта.
Иногда надо определить поле, общее для всего класса, изменение которого в одном экземпляре повлечет изменение того же поля во всех экземплярах. Например, мы хотим в классе Automobile отмечать порядковый заводской номер автомобиля. Такие поля называются переменными класса (class variables). Для переменных класса выделяется только одна ячейка памяти, общая для всех экземпляров. Переменные класса образуются в Java модификатором static. В листинге 2 мы записываем этот модификатор при определении переменной number.
Листинг 2. Статическая переменная
class Automobile {
private static int number;
Automobile(){
number++;
System.out.println("From Automobile constructor:"+
" number = "+number);
}
}
public class AutomobiieTest{
public static void main(String[] args){
Automobile lada2105 = new Automobile(),
fordScorpio = new Automobile(),
oka = new Automobile!);
}
}
Получаем результат, показанный на рис. 1.
Рис. 1. Изменение статической переменной
К статическим переменным можно обращаться с именем класса, Automobile.number, а не только с именем экземпляра, lada2105.number, причем это можно делать, даже если не создан ни один экземпляр класса.
Для работы с такими статическими переменными обычно создаются статические методы, помеченные модификатором static. Для методов слово static имеет совсем другой смысл. Исполняющая система Java всегда создает в памяти только одну копию машинного кода метода, разделяемую всеми экземплярами, независимо от того, статический это метод или нет.
Основная особенность статических методов -- они выполняются сразу во всех экземплярах класса. Более того, они могут выполняться, даже если не создан ни один экземпляр класса. Достаточно уточнить имя метода именем класса (а не именем объекта), чтобы метод мог работать. Именно так мы пользовались методами класса Math, не создавая его экземпляры, а просто записывая Math.abs(x), Math.sqrt(x). Точно так же мы использовали метод System. out. println(). Да и методом main() мы пользуемся, вообще не создавая никаких объектов.
Поэтому статические методы называются методами класса, в отличие от нестатических методов, называемых методами экземпляра.
Отсюда вытекают другие особенности статических методов:
· в статическом методе нельзя использовать ссылки this и super;
· в статическом методе нельзя прямо, не создавая экземпляров, ссылаться на нестатические поля и методы;
· статические методы не могут быть абстрактными;
· статические методы переопределяются в подклассах только как статические.
Статические переменные инициализируются еще до начала работы конструктора, но при инициализации можно использовать только константные выражения. Если же инициализация требует сложных вычислений, например, циклов для задания значений элементам статических массивов или обращений к методам, то эти вычисления заключают в блок, помеченный словом static, который тоже будет выполнен до запуска конструктора:
static int[] a = new a[10];
static {
for(int k = 0; k < a.length; k++)
a[k] = k * k;
}
Операторы, заключенные в такой блок, выполняются только один раз, при первой загрузке класса, а не при создании каждого экземпляра.
Блоки статической инициализации, и блоки инициализации экземпляра записываются вне всяких методов и выполняются до начала выполнения не то что метода, но даже конструктора.
Класс Complex
Комплексные числа широко используются не только в математике. Они часто применяются в графических преобразованиях, в построении фракталов, не говоря уже о физике и технических дисциплинах.
Листинг 3 длинный, но просмотрите его внимательно, при обучении языку программирования очень полезно чтение программ на этом языке. Более того, только программы и стоит читать, пояснения автора лишь мешают вникнуть в смысл действий (шутка).
Листинг 3. Класс Complex
class Complex {
private static final double EPS = le-12; // Точность вычислений
private double re, im; // Действительная и мнимая часть
// Четыре конструктора
Complex(double re, double im) {
this, re = re; this.im = im;
}
Complex(double re){this(re, 0.0); }
Complex(){this(0.0, 0.0); }
Complex(Complex z){this(z.re, z.im) ; }
// Методы доступа
public double getRe(){return re;}
public double getlmf){return im;}
public Complex getZ(){return new Complex(re, im);}
public void setRe(double re){this.re = re;}
public void setlm(double im){this.im = im;}
public void setZ(Complex z){re = z.re; im = z.im;}
// Модуль и аргумент комплексного числа
public double mod(){return Math.sqrt(re * re + im * im);}
public double arg()(return Math.atan2(re, im);}
// Проверка: действительное число?
public boolean isReal(){return Math.abs(im) < EPS;}
public void pr(){ // Вывод на экран
System.out.println(re + (im < 0.0 ? "" : '"+") + im + "i");
}
// Переопределение методов класса Object
public boolean equals(Complex z){
return Math.abs(re -'z.re) < EPS &&
Math.abs(im - z.im) < EPS;
}
public String toString(){
return "Complex: " + re + " " + im;
}
// Методы, реализующие операции +=, -=, *=, /=
public void add(Complex z){re += z.re; im += z.im;}
public void sub(Complex z){re -= z.re; im --= z.im;}
public void mul(Complex z){
double t = re * z.re -- im * z. im;
im = re * z.im + im * z.re;
re = t;
}
public void div(Complex z){
double m = z.mod();
double t = re * z.re -- im * z.im;
im = (im * z.re -- re * z.im) / m;
re = t / m;
}
// Методы, реализующие операции +, -, *, /
public Complex plus(Complex z){
return new Complex(re + z.re, im + z im);
}
public Complex minus(Complex z){
return new Complex(re - z.re, im - z.im);
}
public Complex asterisk(Complex z){
return new Complex(
re * z.re - im * z.im, re * z.im + im * z re);
}
ublic Complex slash(Complex z){
double m = z.mod();
return new Complex(
(re * z.re - im * z.im) / m, (im * z.re - re * z.im) / m);
}
}
// Проверим работу класса Complex
public class ComplexTest{
public static void main(Stringf] args){
Complex zl = new Complex(),
z2 = new Complex(1.5),
z3 = new Complex(3.6, -2.2),
z4 = new Complex(z3);
System.out.printlnf); // Оставляем пустую строку
System.out.print("zl = "); zl.pr();
System.out.print("z2 = "); z2.pr();
System.out.print("z3 = "); z3.pr();
System.out.print ("z4 = "}; z4.pr();
System.out.println(z4); // Работает метод toString()
z2.add(z3);
System.out.print("z2 + z3 = "}; z2.pr();
z2.div(z3);
System.out.print("z2 / z3 = "); z2.pr();
z2 = z2.plus(z2);
System.out.print("z2 + z2 = "); z2.pr();
z3 = z2.slash(zl);
System.out.print("z2 / zl = "); z3.pr();
}
}
Метод main()
Всякая программа, оформленная как приложение (application), должна содержать метод с именем main. Он может быть один на все приложение или содержаться в некоторых классах этого приложения, а может находиться и в каждом классе.
Метод main() записывается как обычный метод, может содержать любые описания и действия, но он обязательно должен быть открытым (public), статическим (static), не иметь возвращаемого значения (void). Его аргументом обязательно должен быть массив строк (string[]). По традиции этот массив называют args, хотя имя может быть любым.
Эти особенности возникают из-за того, что метод main() вызывается автоматически исполняющей системой Java в самом начале выполнения приложения. При вызове интерпретатора java указывается класс, где записан метод main(), с которого надо начать выполнение. Поскольку классов с методом main() может быть несколько, можно построить приложение с дополнительными точками входа, начиная выполнение приложения в разных ситуациях из различных классов.
Часто метод main() заносят в каждый класс с целью отладки. В этом случае в метод main() включают тесты для проверки работы всех методов класса.
При вызове интерпретатора java можно передать в метод main() несколько параметров, которые интерпретатор заносит в массив строк. Эти параметры перечисляются в строке вызова java через пробел сразу после имени класса. Если же параметр содержит пробелы, надо заключить его в кавычки. Кавычки не будут включены в параметр, это только ограничители.
Все это легко понять на примере листинга 4, в котором записана программа, просто выводящая параметры, передаваемые в метод main() при запуске.
Листинг 4. Передача параметров в метод main()
class Echo {
public static void main(String[] args){
for (int i = 0; i < args.length; i++)
System.out.println("args[" + i +"]="+ args[i]);
}
}
На рис. 2 показаны результаты работы этой программы с разными вариантами задания параметров.
Рис. 2. Вывод параметров командной строки
Как видите, имя класса не входит в число параметров. Оно и так известно в методе main().
Замечание
Поскольку в Java имя файла всегда совпадает с именем класса, содержащего метод main(), оно не заносится в args[0]. Вместо argc используется args. length.
Где видны переменные
В языке Java нестатические переменные можно объявлять в любом месте кода между операторами. Статические переменные могут быть только полями класса, а значит, не могут объявляться внутри методов и блоков. Какова же область видимости переменных? Из каких методов мы можем обратиться к той или иной переменной? В каких операторах использовать? Рассмотрим на примере листинга 5 разные случаи объявления переменных.
Листинг 5. Видимость и инициализация переменных
class ManyVariables{
static int x = 9, у; // Статические переменные -- поля класса
// Они известны во всех методах и блоках класса
// Переменная у получает значение 0
static{ // Блок инициализации статических переменных
// Выполняется один раз при первой загрузке класса после
// инициализаций в объявлениях переменных
х = 99; // Оператор выполняется вне всякого метода!
}
int а = 1, р; // Нестатические переменные -- поля экземпляра
// Известны во всех методах и блоках класса, в которых они
//не перекрыты другими переменными с тем же именем
// Переменная р получает значение 0
{ // Блок инициализации экземпляра
// Выполняется при создании, каждого экземпляра после
// инициализаций при объявлениях переменных
р = 999; // Оператор выполняется вне всякого метода!
}
static void f(int b){ // Параметр метода b -- локальная
// переменная, известна только внутри метода
int a = 2; // Это вторая переменная с тем же именем "а"
// Она известна только внутри метода f() и
// здесь перекрывает первую "а"
int с; // Локальная переменная, известна только в методе f()
//Не получает никакого начального значения
//и должна быть определена перед применением
{ int с = 555; // Ошибка! Попытка повторного объявления
int х = 333; // Локальная переменная, известна только в этом блоке
}
// Здесь переменная х уже неизвестна
for (int d = 0; d < 10; d++){
// Переменная цикла d известна только в цикле
int а = 4; // Ошибка!
int e = 5; // Локальная переменная, известна только в цикле for
е++; // Инициализируется при каждом выполнении цикла
System.out.println("e = " + e) ; // Выводится всегда "е = 6"
}
// Здесь переменные d и е неизвестны
}
public static void main(String!] args){
int a = 9999; // Локальная переменная, известна
// только внутри метода main()
f (a);
}
}
Обратите внимание на то, что переменным класса и экземпляра неявно присваиваются нулевые значения. Символы неявно получают значение '\u0000', логические переменные -- значение false, ссылки получают неявно значение null.
Локальные же переменные неявно не инициализируются. Им должны либо явно присваиваться значения, либо они обязаны определяться до первого использования. К счастью, компилятор замечает неопределенные локальные переменные и сообщает о них.
Внимание
Поля класса при объявлении обнуляются, локальные переменные автоматически не инициализируются.
В листинге 5 появилась еще одна новая конструкция: блок инициализации экземпляра. Это просто блок операторов в фигурных скобках, но записывается он вне всякого метода, прямо в теле класса. Этот блок выполняется при создании каждого экземпляра, после инициализации при объявлении переменных, но до выполнения конструктора. Он играет такую же роль, как и static-блок для статических переменных. Зачем же он нужен, ведь все его содержимое можно написать в начале конструктора? В тех случаях, когда конструктор написать нельзя, а именно, в безымянных внутренних классах.
Вложенные классы
В этом прилщжении уже несколько раз упоминалось, что в теле класса можно сделать описание другого, вложенного класса. А во вложенном классе можно снова описать вложенный, внутренний (inner) класс и т. д. Эта матрешка кажется вполне естественной, но возникает масса вопросов.
· Можем ли мы из вложенного класса обратиться к членам внешнего класса? Можем, для того это все и задумывалось.
· А можем ли мы в таком случае определить экземпляр вложенного класса, не определяя экземпляры внешнего класса? Нет, не можем, сначала надо определить хоть один экземпляр внешнего класса, матрешка ведь!
· А если экземпляров внешнего класса несколько, как узнать, с каким экземпляром внешнего класса работает данный экземпляр вложенного класса? Имя экземпляра вложенного класса уточняется именем связанного с ним экземпляра внешнего класса. Более того, при создании вложенного экземпляра операция new тоже уточняется именем внешнего экземпляра.
Все вложенные классы можно разделить на вложенные классы-члены класса, описанные вне методов, и вложенные локальные классы, описанные внутри методов и/или блоков. Локальные классы, как и все локальные переменные, не являются членами класса.
Классы-члены могут быть объявлены статическим модификатором static. Поведение статических классов-членов ничем не отличается от поведения обычных классов, отличается только обращение к таким классам. Поэтому они называются вложенными классами верхнего уровня, хотя статические классы-члены можно вкладывать друг в друга. В них можно объявлять статические члены. Используются они обычно для того, чтобы сгруппировать вспомогательные классы вместе с основным классом.
Все нестатические вложенные классы называются внутренними. В них нельзя объявлять статические члены.Локальные классы, как и все локальные переменные, известны только в блоке, в котором они определены. Они могут быть безымянными (anonymous classes).
В листинге 6 рассмотрены все эти случаи.
Листинг 6. Вложенные классы
class Nested{
static private int pr; // Переменная pr объявленa статической
// чтобы к ней был доступ из статических классов А и АВ
String s = "Member of Nested";
// Вкладываем статический класс.
static class .А{ // Полное имя этого класса -- Nested.A
private int a=pr;
String s = "Member of A";
// Во вложенньм класс А вкладываем еще один статический класс
static class AB{ // Полное имя класса -- Nested.А.АВ
private int ab=pr;
String s = "Member of AB";
}
}
//В класс Nested вкладываем нестатический класс
class В{ // Полное имя этого класса -- Nested.В
private int b=pr;
String s = "Member of B";
// В класс В вкладываем еще один класс
class ВС{ // Полное имя класса -- Nested.В.ВС
private int bc=pr;
String s = "Member of ВС";
}
void f(final int i){ // Без слова final переменные i и j
final int j = 99; // нельзя использовать в локальном классе D
class D{ // Локальный класс D известен только внутри f()
private int d=pr;
String s = "Member of D";
void pr(){
// Обратите внимание на то, как различаются
// переменные с одним и тем же именем "s"
System.out.println(s + (i+j)); // "s" эквивалентно "this.s"
System.out.println(B.this.s);
System.out.println(Nested.this.s);
// System.out.println(AB.this.s); // Нет доступа
// System.out.println(A.this.s); // Нет доступа
}
}
D d = new D(); // Объект определяется тут же, в методе f()
d.pr(); // Объект известен только в методе f()
}
}
void m(){
new Object(){ // Создается объект безымянного класса,
// указывается конструктор его суперкласса
private int e = pr;
void g(){
System.out.println("From g()) ;
}
}.g(); // Тут же выполняется метод только что созданного объекта
}
}
public class NestedClasses{
public static void main(String[] args){
Nested nest = new Nested(); // Последовательно раскрываются
// три матрешки
Nested.A theA = nest.new A(); // Полное имя класса и уточненная
// операция new. Но конструктор только вложенного класса
Nested.A.AB theAB = theA.new AB(); // Те же правила. Операция
// new уточняется только одним именем
Nested.В theB = nest.new B(); // Еще одна матрешка
Nested.В.ВС theBC = theB.new BC();
theB.f(999); // Методы вызываются обычным образом
nest.m();
}
}
Если вы все поняли и готовы применять эти конструкции в своих программах, значит вы -- выдающийся талант и можете перейти к следующему пункту. Если вы ничего не поняли, значит вы -- нормальный человек. Используйте вложенные классы как можно реже.
Для остальных дадим пояснения.
· Как видите, доступ к полям внешнего класса Nested возможен отовсюду, даже к закрытому полю pr. Именно для этого в Java и введены вложенные классы. Остальные конструкции введены вынужденно, для того чтобы увязать концы с концами.
· Язык Java позволяет использовать одни и те же имена в разных областях видимости -- пришлось уточнять константу this именем класса: Nested.this, В.this.
· В безымянном классе не может быть конструктора, ведь имя конструктора должно совпадать с именем класса, -- пришлось использовать имя суперкласса, в примере это класс object. Вместо конструктора в безымянном классе используется блок инициализации экземпляра.
· Нельзя создать экземпляр вложенного класса, не создав предварительно экземпляр внешнего класса, -- пришлось подстраховать это правило уточнением операции new именем экземпляра внешнего класса-- nest.new, theA.new, theB.new.
· При определении экземпляра указывается полное имя вложенного класса, но в операции new записывается просто конструктор класса.
Введение вложенных классов сильно усложнило синтаксис и поставило много задач разработчикам языка. Есть еще вопросы.
· Можно ли наследовать вложенные классы? Можно.
· Как из подкласса обратиться к методу суперкласса? Константа super уточняется именем соответствующего суперкласса, подобно константе this.
· А могут ли вложенные классы быть расширениями других классов? Могут.
Механизм вложенных классов станет понятнее, если посмотреть, какие файлы с байт-кодами создал компилятор:
· Nested$l$D.class -- локальный класс о, вложенный в класс Nested;
· NestedSl.class -- безымянный класс;
· Nested$A$AB.class -- класс Nested.A.AB;
· Nested$A.class -- класс Nested.А;
· Nested$B$BC.class -- класс Nested.в.вс;
· NestedSB.class -- класс Nested.в;
· Nested.class -- внешний класс Nested;
· NestedClasses.class - класс с методом main ().
Компилятор разложил матрешки и, как всегда, создал отдельные файлы для каждого класса. При этом, поскольку в идентификаторах недопустимы точки, компилятор заменил их знаками доллара. Для безымянного класса компилятор придумал имя. Локальный класс компилятор пометил номером.
Оказывается, вложенные классы существуют только на уровне исходного кода. Виртуальная машина Java ничего не знает о вложенных классах. Она работает с обычными внешними классами. Для взаимодействия объектов вложенных классов компилятор вставляет в них специальные закрытые поля. Поэтому в локальных классах можно использовать только константы объемлющего метода, т. е. переменные, помеченные словом final. Виртуальная машина просто не догадается передавать изменяющиеся значения переменных в локальный класс. Таким образом, не имеет смысла помечать вложенные классы private, все равно они выходят на самый внешний уровень.
Все эти вопросы можно не брать в голову. Вложенные классы в Java используются только в самом простом виде, главным образом, при обработке событий, возникающих при действиях с мышью и клавиатурой.
В каких же случаях создавать вложенные классы? В теории ООП вопрос о создании вложенных классов решается при рассмотрении отношений "быть частью" и "являться".
Отношения "быть частью" и "являться"
Теперь у нас появились две различные иерархии классов. Одну иерархию образует наследование классов, другую -- вложенность классов.
Определив, какие классы будут написаны в вашей программе, и сколько их будет, подумайте, как спроектировать взаимодействие классов? Вырастить пышное генеалогическое дерево классов-наследников или расписать матрешку вложенных классов?
Теория ООП советует прежде всего выяснить, в каком отношении находятся ваши классы р и Q -- в отношении "класс Q является экземпляром класса р" ("a class Q is a class р") или в отношении "класс Q -- часть класса р" ("a class Q has a class P").
Например: "Собака является животным" или "Собака -- часть животного"? Ясно, что верно первое отношение "is-a", поэтому мы и определили класс Dog как расширение класса Pet.
Отношение "is-a" -- это отношение "обобщение-детализация", отношение большей или меньшей абстракции, и ему соответствует наследование классов.
Отношение "has-a" -- это отношение "целое-часть", ему соответствует вложение.
Приложение 2. Листинг программы, иллюстрирующей работу класса GregorianCalendar
import java.util.*;
public class CaltndarTest {
public static void main(String[] args){
GregorianCalendar d = new GregorianCalendar ();
int today = d.get(Calendar.DAY_OF_MONTH);
int month = d.get(Calendar.MONTH);
// Устанавливаем объект d на первую дату месяца
d.set(Calendar.DAY_OF_MONTH, 1);
// Создаем объект d с текущей датой
int weekday = d.get(Calendar.DAY_OF_WEEK);
// Выводим на печать заголовок таблицы
System.out.println("ВсПнВтСрЧтПтСб");
// Вставляем первую строку календаря
for (int i = Calendar.SUNDAY; i< weekday; i++)
System.out.print("");
do
{
// Выводим на печать день
int day = d.get(Calendar.DAY_OF_MONTH);
if(day < 10) System.out.print("");
System.out.print(day);
// Отметить текущий день звездочкой
if (day == today)System.out.print("*");
else
System.out.print("");
// После каждой субботы начинаем новую строку
if(weekday == Calendar.SATURDAY)
System.out.println();
//| Передвинуть объект d на новый день
d.add(Calendar.DAY_OF_MONTH, 1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
while (d.get(Calendar.MONTH) == month);
// Цикл завершается, когда объект d установлен
// на первый день следующего месяца
//Выводим на экран последнюю строку
if(weekday != Calendar.SUNDAY)
System.out.println();
}
}
Приложение 3. Листинг программы, иллюстрирующей работу класса Emp (Служащий)
import java.util.*;
public class EmpTest {
public static void main(String[] args){
// Заполнить массив сотрудников тремя объектами
Emp[] staff = new Emp[3];
staff[0] = new Emp("Карл",75000,1987,12,15);
staff[1] = new Emp("Гарри",50000,1987,12,15);
staff[2] = new Emp("Тони",40000,1987,12,15);
// Всем поднять зарплату на 5%
for(int i=0; i<staff.length; i++)
staff[i].raiseSalary(5);
// Вывести информацию обо всех сотрудниках
for(int i=0; i<staff.length; i++){
Emp e=staff[i];
System.out.println("Имя="+e.getName()+
", Зарплата="+e.getSalary()+
", Дата приема на работу"+e.getHireDay());
}
System.exit(0);
}
}
class Emp
{
public Emp(String n, double s,
int year, int month, int day)
{
name=n;
salary=s;
GregorianCalendar calendar
=new GregorianCalendar(year, month-1,day);
// В классе GregorianCalendar январю соответствует 0
hireDay=calendar.getTime();
}
public double getSalary()
{
return salary;
}
public String getName()
{
return name;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise=salary*byPercent/100;
salary+=raise;
}
private String name;
private double salary;
private Date hireDay;
}
Подобные документы
Классы, объекты и объектные ссылки. Особенности статических методов. Конструкторы, специальные переменные, наследование. Создание объектов внутренних классов. Соглашения об именовании. Некоторые методы класса Object. Абстрактные классы и атрибуты.
лекция [130,6 K], добавлен 21.06.2014Виды коллекций-классов, реализующих интерфейс Collection. Основные классы пакета java.util и работа с системным временем (Класс java.util.Date), файлами и потоками ввода/вывода, обработка строки (объекты класса String). Спецификация класса Statistics.
методичка [185,8 K], добавлен 30.06.2009Обзор криптографических классов библиотеки Framework Class Libr: классы алгоритмов симметричного и асимметричного шифрования, хеширования. Классы-форматеры и деформатеры. Классы для формирования и проверки цифровой подписи. Примеры применения классов.
курсовая работа [30,0 K], добавлен 27.12.2011Разработка и создание игры "Змейка". Использование динамически-активных принципов языка Java. Графические объекты программы. Описание игры, правила, теоретические сведения. Классы приложения. Типы данных. Реализация. Метод. Объект. Блок-схема игры.
курсовая работа [12,4 K], добавлен 18.06.2008Характеристика интерфейса в Java, возможность его расширения с использованием механизма наследования. Организация обратного вызова в Java. Сущность внутреннего класса. Обращение из внутреннего класса к элементам внешнего класса и листинг программы.
методичка [90,8 K], добавлен 30.06.2009Объектно-ориентированный подход к проектированию программных систем. Простое наследование и доступ к наследуемым компонентам. Конструкторы производных классов, объемлющие классы, понятие об алгоритме и операторе. Примеры реализации связных списков.
реферат [24,5 K], добавлен 31.10.2011Цели объектно-ориентированного программирования, абстрактные классы и адреса базовых классов, множественное и виртуальное наследование. Инициализация элементов производного класса, программный вызов конструкторов базового и производного классов.
реферат [21,8 K], добавлен 31.10.2011Понятие пакета как объединения классов (java.awt, java.lang). Способы импорта, проблема конфликта (пакеты содержат классы с одинаковым именем). Особенности реализации интерфейса, его поля. Понятие наследования интерфейса. Общие методы классов-оболочек.
презентация [140,1 K], добавлен 21.06.2014Механизм классов в C++. Инициализация внутреннего объекта с помощью конструктора. Управление доступом к классу. Защищенные члены класса. Графические средства компилятора Borland C 3.1. Библиотека стандартных шаблонов. Реализация и использование класса.
курсовая работа [2,7 M], добавлен 16.05.2012Виртуальные функции, статические и абстрактные классы, шаблоны: элементы и члены класса, их роль в объектно-ориентированном программировании; механизм осуществления виртуального вызова при выполнении программы; обработка исключительных ситуаций в C++.
реферат [27,2 K], добавлен 06.12.2010