Java: руководство для начинающих (ЛП) - Шилдт Герберт 14 стр.


Все действия программ на Java производятся в пределах классов, поэтому мы вынуждены были пользоваться классами, начиная с первых же примеров программ в этой книге. Разумеется, мы ограничивались лишь самыми простыми классами и не пользовались большинством их возможностей. Как станет ясно в дальнейшем, классы — намного более эффективное языковое средство, чем можно было бы предположить, имея о них лишь самое ограниченное представление, почерпнутое из предыдущих глав.

Начнем рассмотрение классов с основных положений. Класс представляет собой шаблон, по которому определяется форма объекта. В нем указываются данные и код, который будет оперировать этими данными. В Java используется спецификация класса для построения объектов, которые являются экземплярами класса. Следовательно, класс, по существу, представляет собой ряд схематических описаний способа построения объекта. При этом очень важно подчеркнуть, что класс является логической абстракцией. Физическое представление класса появится в оперативной памяти лишь после того, как будет создан объект этого класса.

Следует также иметь в виду, что методы и переменные, составляющие класс, принято называть членами класса. А члены данных называются переменными экземпляра.Общая форма определения класса

Определяя класс, вы объявляете его конкретную форму и поведение. Для этого указываются содержащиеся в нем переменные экземпляра, а также методы оперирования этими переменными. Если самые простые классы могут содержать только код или только данные, то большинство настоящих классов содержат и то и другое.

Класс создается с помощью ключевого слова class. Ниже приведена упрощенная общая форма определения класса.class имя_класса { // Объявление переменных экземпляра. тип переменная1; тип переменная2; //. . . тип переменнаяЫ; // Объявление методов. тип метод1 (параметры) { // тело метода } тип метод2 (параметры) { // тело метода } //. . . тип методN (параметры) { // тело метода }}

Несмотря на отсутствие соответствующего правила в синтаксисе Java, правильно сконструированный класс должен определять одну и только одну логическую сущность. Например, класс, в котором хранятся Ф.И.О. и номера телефонов, обычно не содержит сведения о фондовом рынке, среднем уровне осадков, циклах солнечных пятен или другую не относящуюся к делу информацию. Таким образом, в правильно сконструированном классе должна быть сгруппирована логически связанная информация. Если же в один и тот же класс помещается логически несвязанная информация, то структурированность кода быстро нарушается.

Классы, использовавшиеся в приведенных ранее примерах программ, содержали только один метод: main (). Но в представленной выше общей форме определения класса метод main () не указывается. Этот метод требуется указывать в классе лишь в том случае, если программа начинается с данного класса. Кроме того, в некоторых приложениях Java, в том числе в апплетах, метод main () вообще не требуется.Определение класса

Для того чтобы проиллюстрировать особенности создания классов, построим класс, инкапсулирующий сведения о транспортных средствах, например о легковых автомобилях, фургонах и грузовиках. Назовем этот класс Vehicle. В нем будут храниться следующие сведения: количество пассажиров, емкость топливного бака и среднее потребление топлива (в милях на галлон).

Ниже приведен первый вариант класса Vehicle. В нем определены три переменные экземпляра: passengers, fuelcap и mpg. Обратите внимание на то, что в классе Vehicle пока еще отсутствуют методы. Они будут добавлены в последующих разделах, а до тех пор в этом классе содержатся только данные.class Vehicle { int passengers; // количество пассажиров int fuelcap; // емкость топливного бака int mpg; // потребление топлива в милях на галлон}

Объявление class указывает на создание нового типа данных. В данном случае этот тип называется Vehicle. Пользуясь этим именем, можно теперь создавать объекты типа Vehicle. Но не следует забывать, что объявление class — это всего лишь описание типа данных, а реальный объект при этом не создается. Следовательно, приведенный выше код не приводит к появлению объектов типа Vehicle.

Для того чтобы создать реальный объект Vehicle, потребуется оператор, аналогичный следующему:Vehicle minivan = new Vehicle(); // создать объект minivan типа Vehicle

После выполнения этого оператора объект minivan станет экземпляром класса Vehicle. Иными словами, класс обретет физическое воплощение. Не пытайтесь вникнуть пока что в детали приведенного выше оператора. В дальнейшем он станет вам полностью понятным.

Всякий раз, когда создается экземпляр класса, строится объект, содержащий копии всех переменных экземпляра, определенных в классе. Иными словами, каждый объект типа Vehicle будет содержать копии переменных passengers, fuelcap и mpg. Для обращения к этим переменным используется оператор-точка (.). Этот оператор связывает имя объекта с именем члена класса. Ниже приведена общая форма записи этого оператора.объект.член

В этой форме объект указывается слева, а член — справа от точки. Так, если переменной fuelcap из объекта minivan требуется присвоить значение 16, это можно сделать следующим образом:minivan.fuelcap = 16;

Вообще говоря, оператором-точкой можно пользоваться для обращения как к переменным экземпляра, так и к методам.

Ниже приведен пример программы, в которой используется класс Vehicle./* Программа, в которой используется класс Vehicle. Присвоить ее исходному файлу имя VehicleDemo.java*/class Vehicle { int passengers; // количество пассажиров int fuelcap; // емкость топливного бака int mpg; // потребление топлива в милях на галлон}//В этом классе объявляется объект типа Vehicle,class VehicleDemo { public static void main(String args[]) { Vehicle minivan = new Vehicle(); int range; // присвоить значения полям в объекте minivan // Обратите внимание на применение оператора-точки // для доступа к переменным экземпляра данного объекта. minivan.passengers = 7; minivan.fuelcap = 16; minivan.mpg = 21; // рассчитать дальность действия транспортного средства, // исходя из того, что топливный бак заполнен range = minivan.fuelcap * minivan.mpg; System.out.println("Minivan can carry " + minivan.passengers + " with a range of " + range); }}

Файлу, содержащему приведенный выше код, следует присвоить имя VehicleDemo.java, поскольку метод main () находится не в классе Vehicle, а в классе VehicleDemo. В результате компиляции программы будут созданы два файла с расширением .class: один — для класса Vehicle, а другой — для класса VehicleDemo. Компилятор Java автоматически помещает каждый класс в отдельный файл с расширением .class. Совсем не обязательно, чтобы классы Vehicle и VehicleDemo находились в одном и том же исходном файле. Их можно расположить в двух файлах — Vehicle.java и VehicleDemo.java.

Для того чтобы привести эту программу в действие, следует запустить на выполнение файл VehicleDemo. class. В итоге на экране появится следующий результат:Minivan can carry 7 with a range of 336

А теперь самое время рассмотреть следующий основополагающий принцип: каждый объект содержит свои копии переменных экземпляра, определенные в его классе. Следовательно, содержимое переменных в одном объекте может отличаться от содержимого тех же самых переменных в другом объекте. Между объектами нет никакой связи, за исключением того, что они относятся к одному и тому же типу. Так, если имеются два объекта типа Vehicle, каждый из них содержит собственную копию переменных passengers, fuelcap и mpg, причем значения одноименных переменных в этих двух объектах могут отличаться. Этот факт демонстрирует следующий пример программы (обратите внимание на то, что класс, содержащий метод main (), на этот раз назван TwoVehicles):// В этой программе создаются два объекта класса Vehicle,class Vehicle { int passengers; // количество пассажиров int fuelcap; // емкость топливного бака int mpg; // потребление топлива в милях на галлон }}// В этом классе объявляется объект типа Vehicle,class TwoVehicles { public static void main(String args[] ) { // Помните, что переменные minivan и sportscar // ссылаются на разные объекты. Vehicle minivan = new Vehicle(); Vehicle sportscar = new Vehicle(); int rangel, range2; // присвоить значения полям в объекте minivan minivan.passengers = 7; minivan.fuelcap = 16; minivan.mpg = 21; // присвоить значения полям в объекте sportscar sportscar.passengers = 2; sportscar.fuelcap = 14; sportscar.mpg = 12; // рассчитать дальность действия транспортного средства, // исходя из того, что топливный бак заполнен rangel = minivan.fuelcap * minivan.mpg; range2 = sportscar.fuelcap * sportscar.mpg; System.out.println("Minivan can carry " + minivan.passengers + " with a range of " + rangel); System.out.println("Sportscar can carry " + sportscar.passengers + " with a range of " + range2); }}

Ниже приведен результат выполнения данной программы.Minivan can carry 7 with a range of 336Sportscar can carry 2 with a range of 168

Как видите, данные из объекта minivan отличаются от соответствующих данных из объекта sportscar. Это обстоятельство иллюстрирует приведенный ниже рисунок.Порядок создания объектов

В рассмотренных ранее примерах программ для объявления объекта типа Vehicle использовалась следующая строка кода:Vehicle minivan = new Vehicle();

Эта строка кода выполняет две функции. Во-первых, в ней объявляется переменная класса Vehicle под именем minivan. Эта переменная еще не определяет объект, она лишь имеет возможность ссылаться на объект. И во-вторых, в этой строке кода создается физическая копия объекта, а ссылка на него присваивается переменной minivan. И делается это с помощью оператора new.

Оператор new динамически (т.е. в процессе выполнения программы) выделяет память для объекта и возвращает ссылку на него. Эта ссылка, по существу, представляет собой адрес области памяти, выделяемой для объекта оператором new. Ссылка на объект сохраняется в переменной. Таким образом, память для объектов всех классов в Java выделяется динамически.

Обе упомянутые функции приведенной выше строки кода можно записать следующим образом, чтобы разделить их и показать по отдельности:Vehicle minivan; // объявить ссылку на объектminivan = new Vehicle(); // выделить память для объекта типа Vehicle

В первой строке кода minivan объявляется как ссылка на объект типа Vehicle. Следует иметь в виду, что minivan — это переменная, которая может ссылаться на объект, а не сам объект. В данный момент переменная minivan пока еще не ссылается на объект. Во второй строке кода создается новый объект типа Vehicle, а ссылка на него присваивается переменной minivan. С этого момента переменная minivan оказывается связанной с объектом.Переменные ссылочного типа и присваивание

В операции присваивания переменные ссылочного типа действуют иначе, чем переменные такого простого типа, как, например, int. Когда одна переменная простого типа присваивается другой, ситуация оказывается довольно простой. Переменная, находящаяся в левой части оператора присваивания, получает копию значения переменной, находящейся в правой части этого оператора. Когда же одна переменная ссылки на объект присваивается другой, ситуация несколько усложняется, поскольку такое присваивание приводит к тому, что переменная, находящаяся в левой части оператора присваивания, ссылается на тот же самый объект, на который ссылается переменная, находящаяся в правой части этого оператора. Сам же объект не копируется. В силу этого отличия присваивание переменных ссылочного типа может привести к несколько неожиданным результатам. В качестве примера рассмотрим следующий фрагмент кода:Vehicle carl = new Vehicle();Vehicle car2 = carl;

На первый взгляд, переменные carl и car2 ссылаются на совершенно разные объекты, но на самом деле это не так. Переменные carl и саг2, напротив, ссылаются на один и тот же объект. Когда переменная carl присваивается переменой саг2, в конечном итоге переменная саг 2 просто ссылается на тот же самый объект, что и переменная carl. Следовательно, этим объектом можно оперировать с помощью переменной carl или саг2. Например, после очередного присваиванияcarl.mpg = 26;

оба метода println() в операторах их вызоваSystem.out.println(carl.mpg);System.out.println(car2.mpg);

выводят одно и то же значение: 26.Несмотря на то что обе переменные, carl и саг2, ссылаются на один и тот же объект, они никак иначе не связаны друг с другом. Например, в результате следующей последовательности операций присваивания просто изменяется объект, на который ссылается переменная саг2:Vehicle carl = new Vehicle();Vehicle car2 = carl;Vehicle carS = new Vehicle();car2 = сагЗ; // Теперь переменные car2 и сагЗ// ссылаются на один и тот же объект.

После выполнения этой последовательности операций присваивания переменная саг2 ссылается на тот же самый объект, что и переменная сагЗ. А ссылка на объект в переменной carl не меняется.Методы

Как пояснялось выше, переменные экземпляра и методы являются двумя основными составляющими классов. До сих пор класс Vehicle, рассматриваемый здесь в качестве примера, содержал только данные, но не методы. Хотя классы, содержащие толькоданные, вполне допустимы, у большинства классов должны быть также методы. Методы представляют собой подпрограммы, которые манипулируют данными, определенными в классе, а во многих случаях они предоставляют доступ к этим данным. Как правило, другие части программы взаимодействуют с классом посредством его методов.

Метод состоит из одного или нескольких операторов. В грамотно написанной программе на Java каждый метод выполняет только одну функцию. У каждого метода имеется свое имя, по которому он вызывается. В общем, методу в качестве имени можно присвоить любой действительный идентификатор. Следует, однако, иметь в виду, что идентификатор main () зарезервирован для метода, с которого начинается выполнение программы. Кроме того, в качестве имен методов нельзя использовать ключевые слова Java.

В этой книге методы именуются в соответствии с условными обозначениями, принятыми в литературе по Java. В частности, после имени метода следуют круглые скобки. Так, если методу присвоено имя getval, то в тексте книги он упоминается в следующем виде: getval (). Такая форма записи помогает отличать имена методов от имен переменных при чтении книги.

Ниже приведена общая форма объявления метода.возращаемый_тип имя (список_параметров) {// тело метода}

где возращаемыйтип обозначает тип данных, возвращаемых методом. Этот тип должен быть действительным, в том числе и типом создаваемого класса. Если метод не возвращает значение, то в качестве возвращаемого для него следует указать тип void. Далее, имя обозначает конкретное имя, присваиваемое методу. В качестве имени метода может служить любой допустимый идентификатор, не приводящий к конфликтам в текущей области объявлений. И наконец, списокпараметров — это последовательность пар, состоящих из типа и идентификатора и разделенных запятыми. Параметры представляют собой переменные, получающие значение аргументов, передаваемых методу при его вызове. Если у метода отсутствуют параметры, то список параметров оказывается пустым.Добавление метода в класс Vehicle

Как пояснялось ранее, методы класса обычно выполняют действия над данными в составе класса и предоставляют доступ к этим данным. Напомним, что метод main () в предыдущих примерах вычислял дальность действия транспортного средства, умножая емкость топливного бака на число миль, которые оно может проехать, потребив единичный объем топлива (в данном случае — галлон). И хотя такой расчет формально считается правильным, его лучше всего производить в пределах самого класса Vehicle. Аргументы в пользу такого решения очевидны: дальность действия транспортного средства зависит от потребления топлива в милях на галлон и емкости топливного бака, а обе эти величины инкапсулированы в классе Vehicle. Благодаря добавлению в класс Vehicle метода, предназначенного для расчета дальности, улучшается объектно-ориентированная структура кода.

Для того чтобы добавить метод в класс Vehicle, его следует объявить в пределах этого класса. Например, приведенный ниже вариант класса Vehicle содержит метод range (), определяющий и отображающий дальность действия транспортного средства.

// Добавление метода range в класс Vehicle,class Vehicle { int passengers; // количество пассажиров int fuelcap; // емкость топливного бака int mpg; // потребление топлива в милях на галлон // отобразить дальность действия транспортного средства // Метод range() относится к классу Vehicle. // Обратите внимание на то, что переменные fuelcap и трд // указываются напрямую без имени объекта и оператора-точки. void range() { System.out.println("Range is " + fuelcap * mpg); }}class AddMeth { public static void main(String args[]) { Vehicle minivan = new Vehicle(); Vehicle sportscar = new Vehicle(); int rangel, range2; // присвоить значения полям в объекте minivan minivan.passengers = 7; minivan.fuelcap = 16; minivan.mpg = 21; // присвоить значения полям в объекте sportscar sportscar.passengers = 2; sportscar.fuelcap = 14; sportscar.mpg = 12; System.out.print("Minivan can carry " + minivan.passengers + ". "); minivan.range(); // отобразить дальность действия мини-фургона System.out.print("Sportscar can carry " + sportscar.passengers + ". "); sportscar.range(); // отобразить дальность действия спортивной машины }}

Назад Дальше