Программирование на языке Java

       

Вызов метода


В Java отсутствует возможность передачи параметров по ссылке на примитивный тип. В Java все пара­метры примитивных типов передаются по значению, а это означает, что у метода нет доступа к исходной переменной, использованной в качестве параметра. Заметим, что все объекты передаются по ссылке, можно изменять содержимое того объекта, на который ссыла­ется данная переменная. В главе 12

Вы узнаете, как предать переменные примитивных типов по ссылке (через обрамляющие классы-оболочки).

 

Скрытие переменных представителей

В языке Java не допускается использование в одной или во вложен­ных областях видимости двух локальных переменных с одинаковыми именами. Интересно отметить, что при этом не запрещается объявлять формальные параметры методов, чьи имена совпадают с именами переменных представителей. Давайте рассмотрим в качестве примера иную версию метода init, в которой формальным пара­метрам даны имена х и у, а для доступа к одноименным переменным текущего объекта используется ссылка this.

class Point { int х, у;

void init(int х, int у) {

this.x = х;

this.у = у } }

class TwoPointsInit {

public static void main(String args[]) {

Point p1 = new Point();

Point p2 = new Point();

p1.init(10,20);

p2.init(42,99);

System.out.println("x = " + p1.x + " у = •• + p-l.y);

System.out.printlnC'x = " + p2.x + " у = •• + p2.y);

} }

Конструкторы

Инициализировать все переменные класса всякий раз, когда создается его очередной представитель — довольно утомительное дело даже в том случае, когда в классе имеются функции, подобные методу init. Для этого в Java предусмотрены специальные методы, называемые конструкторами. Конструктор — это метод класса, который инициали­зирует новый объект после его создания. Имя конструктора всегда со­впадает с именем класса, в котором он расположен (также, как и в C++). У конструкторов нет типа возвращаемого результата - никакого, даже void. Заменим метод init из предыду­щего примера конструктором.


class Point { int х, у;



Point(int х, int у) {



this.x = х;



this.у = у;



} }



class PointCreate {



public static void main(String args[]) {



Point p = new Point(10,20);



System.out.println("x = " + p.x + " у = " + p.у);



} }



Программисты на Pascal (Delphi) для обозначения конструктора используют ключевое слово constructor.

Совмещение методов



Язык Java позволяет создавать несколько методов с одинаковыми именами, но с разными списками параметров. Такая техника называется совмещением методов (method overloading). В качестве примера при­ведена версия класса Point, в которой совмещение методов использовано для определения альтернативного конструктора, который инициализиру­ет координаты х и у значениями по умолчанию (-1).

class Point { int х, у;



Point(int х, int у) {



this.x = х;



this.у = у;



}



Point() {



х = -1;



у = -1;



} }



class PointCreateAlt {



public static void main(String args[]) {



Point p = new Point();



System.out.println("x = " + p.x + " у = " + p.y);



} }



В этом примере объект класса Point создается не при вызове первого конструктора, как это было раньше, а с помощью второго конструктора без параметров. Вот результат работы этой программы:

С:\> java PointCreateAlt

х = -1 у = -1



ЗАМЕЧАНИЕ

Решение о том, какой конструктор нужно вызвать в том или ином случае, принимается в соответствии с количеством и типом параметров, указанных в операторе new. Недопустимо объявлять в классе методы с одинаковыми именами и сигнатурами. В сигнатуре метода не учитываются имена формальных параметров учитываются лишь их типы и количество.

 



this в конструкторах



Очередной вариант класса Point показывает, как,       используя this и со­вмещение методов, можно строить одни конструкторы на основе других.

class Point { int х, у;



Point(int х, int у) {



this.x = х;



this.у = у;



}



Point() {





this(-1, -1);



} }



В этом примере второй конструктор для завершения инициализации объекта обращается к первому конструктору.

Методы, использующие совмещение имен, не обязательно должны быть конструкторами. В следующем примере в класс Point добавлены два метода distance. Функция distance возвращает расстояние между двумя точками. Одному из совмещенных методов в качестве параметров передаются координаты точки х и у, другому же эта информация пере­дается в виде параметра-объекта Point.

class Point { int х, у;



Point(int х, int у) {



this.x = х;



this. y = y;



}



double distance(int х, int у) {



int dx = this.x - х;



int dy = this.у - у;



return Math.sqrt(dx*dx + dy*dy);



}



double distance(Point p) {



return distance(p.x, p.y);



} }



class PointDist {



public static void main(String args[]) {



Point p1 = new Point(0, 0);



Point p2 = new Point(30, 40);



System.out.println("p1 = " + pi.x + ", " + p1.y);



System.out.println("p2 = " + p2.x + ", " + p2.y);



System.out.println("p1.distance(p2) = " + p1.distance(p2));



System.out.println("p1.distance(60, 80) = " + p1.distance(60, 80));



} }



Обратите внимание на то как во второй фороме метода distance

для получения результата вызывается его первая форма. Ниже приведен результат работы этой программы:

С:\> java PointDist

р1 = 0, 0



р2 = 30, 40



р1.distance(p2) = 50.0



p1.distance(60, 80) = 100.0



Наследование



Вторым фундаментальным свойством объектно-ориентированного под­хода является наследование (первый – инкапсуляция). Классы-потомки имеют возможность не только создавать свои собственные переменные и  методы, но и наследовать переменные и методы классов-предков. Классы-потомки принято называть подклассами. Непосредственного предка данного класса называют его суперклассом. В очередном примере показано, как расширить класс Point таким образом, чтобы включить в него третью координату z.



class Point3D extends Point { int z;



Point3D( int x, int y, int z) {



this.x = x;



this.у = у;



this.z = z; }



Point3D() {



this(-1,-1,-1);



} }



В этом примере ключевое слово extends используется для того, чтобы сообщить транслятору о намерении создать подкласс класса Point. Как видите, в этом классе не понадобилось объявлять переменные х и у, по­скольку Point3D унаследовал их от своего суперкласса Point.

ВНИМАНИЕ

Вероятно, программисты, знакомые с C++, очевидно ожидают, что сей­час мы начнем обсуждать концепцию множественного наследования. Под множественным наследованием понимается создание класса, имеющего несколько суперклассов. Однако в языке Java ради обеспечения высокой производительности и большей ясности исходного кода множественное наследование реализовано не было. В большинстве случаев, когда требуется множественное наследование, проблему можно решить с помощью имеющегося в Java механизма интерфейсов, описанного в следующей  главе. 

 



super



В примере с классом Point3D частично повторялся код, уже имев­шийся в суперклассе. Вспомните, как во втором конструк­торе мы использовали this

для вызова первого конструктора того же класса. Аналогичным образом ключевое слово super позволяет обратить­ся непосредственно к конструктору суперкласса (в Delphi / С++ для этого используется ключевое слово inherited).

class Point3D extends Point { int z;



Point3D(int x, int у, int z) {



super(x, y);     // Здесь мы вызываем конструктор суперкласса this.z=z;



public static void main(String       args[]) {



Point3D p = new Point3D(10, 20, 30);



System.out.println( " x = " +  p.x + " y = " + p.y +



                    " z = " + p.z);



} }



Вот результат работы этой программы:

С:\> java Point3D

x = 10 у = 20 z = 30





Замещение методов



Новый подкласс Point3D класса Point наследует реализацию метода distance своего суперкласса (пример PointDist.java). Проблема заключается в том, что в классе Point уже определена версия метода distance(mt х, int у), которая возвращает обычное расстояние между точ­ками на плоскости. Мы должны заместить (override) это определение метода новым, пригодным для случая трехмерного пространства. В сле­дующем примере проиллюстрировано и совмещение (overloading), и за­мещение

(overriding) метода distance.

class Point { int х, у;



Point(int х, int у) {



this.x = х;



this.у = у;



}



double distance(int х, int у) {



int dx = this.x - х;



int dy = this.у - у:



return Math,sqrt(dx*dx + dy*dy);



}



double distance(Point p) {



return distance(p.х, p.y);



}



}



class  Point3D extends Point { int z;



Point3D(int х, int y, int z)   {



super(x, y);



this.z = z;



(



double distance(int х, int y,  int z) {



int dx = this.x - х;



int dy = this.y - y;



int dz = this.z -  z;



return Math.sqrt(dx*dx + dy*dy + dz*dz);



}



double distance(Point3D other) {



return distance(other.х, other.y, other.z);



}



double distance(int х, int y)  {



double dx = (this.x / z) - х;



double dy = (this.у / z) - y;



return Math.sqrt(dx*dx + dy*dy);



}



}



class Point3DDist {



public static void main(String args[]) {



Point3D p1 = new Point3D(30, 40, 10);



Point3D p2 = new Point3D(0, 0, 0);



Point p = new Point(4, 6);



System.out.println("p1 = " + p1.x +  ", " + p1.y + ", " + p1.z);



System.out.println("p2 = " + p2.x +  ", " + p2.y + ", " + p2.z);



System.out.println("p = " + p.x + ", " + p.y);



System.out.println("p1.distance(p2) = " + p1.distance(p2));



System.out.println("p1.distance(4, 6) = " + p1.distance(4, 6));



System.out.println("p1.distance(p) = " + p1.distance(p));





} }



Ниже приводится результат работы этой программы:

С:\> Java Point3DDist

p1 = 30, 40, 10



р2 = 0, 0, 0 



р = 4, 6



p1.distance(p2) = 50.9902



p1.distance(4, 6) = 2.23607



p1.distance(p) = 2.23607



Обратите внимание — мы получили ожидаемое расстояние между трехмерными точками и между парой двумерных точек. В примере используется механизм, который называется динамическим назначением методов (dynamic method dispatch).

 



Динамическое назначение методов

Давайте в качестве примера рассмотрим два класса, у которых имеют простое родство подкласс / суперкласс, причем единственный метод суперкласса замещен в подклассе.                    

class A { void callme() {



System.out.println("Inside A's callrne method");



class В extends A { void callme() {



System.out.println("Inside B's callme method");



} }



class Dispatch {



public static void main(String args[]) {



A a = new B();



a.callme();



} }



Обратите внимание — внутри метода main мы объявили переменную а класса А, а проинициализировали ее ссылкой на объект класса В. В следующей строке мы вызвали метод callme. При этом транслятор про­верил наличие метода callme у класса А, а исполняющая система, уви­дев, что на самом деле в переменной хранится представитель класса В, вызвала не метод класса А, а callme класса В. Ниже приведен результат работы этой программы:

С:\> Java Dispatch


Содержание раздела