Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛабРаб.doc
Скачиваний:
4
Добавлен:
02.09.2019
Размер:
236.03 Кб
Скачать

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

Когда мы создаём объект командой Circle o1 = new Circle(); используется так называемый конструктор по умолчанию (или конструктор без параметров) — это специальный метод класса, мы его не определяли явно, но даже если его не определить он создаётся автоматически, выполняется при создании каждого нового объекта и присваивает первоначальные значения его свойствам (инициализирует их). Значения по умолчанию для свойств зависят от их типа (0 или 0.0 для чиловых типов, false для логического типа и т.д.). 

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

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

class Circle {     public double x; // абсцисса центра     public double y; // ордината центра     public double r; // радиус     public void printCircle() {         System.out.println("Окружность с центром ("+x+";"+y+") и радиусом "+r);     }         public void moveCircle(double a, double b) {         x = x + a;         y = y + b;     }     public void zoomCircle(double k) {         r = r * k;     }     // конструктор по умолчанию, теперь сразу после создания объекта будем     // получать окружность единичного радиуса с центром в начале координат     public Circle() {         x = 0.0;         y = 0.0;         r = 1.0;     } } public class Main {     public static void main(String[] args) {         Circle o1 = new Circle();         o1.printCircle(); // Окружность с центром (0.0;0.0) и радиусом 1.0     } }

Поскольку методы можно перегружать, а конструктор является методом, то с помощью перегрузки можно создать дополнительные варианты конструкторов. Например, удобно иметь конструктор, который позволит при создании объекта явно указывать координаты его центра и длину радиуса.

Описать подобный конструктор можно в дополнение к основному следующим образом:

    public Circle(double a, double b, double s) {         x = a;         y = b;         r = s;     }

Теперь при создании объектов можно пользоваться любым конструктором на выбор:

        Circle o1 = new Circle();         o1.printCircle(); // Окружность с центром (0.0;0.0) и радиусом 1.0         Circle o2 = new Circle(1,-1,14);         o2.printCircle(); // Окружность с центром (1.0;-1.0) и радиусом 14.0

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

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

Добавим в наш класс метод, вычисляющий площадь той окружности, к которой метод применён. Метод будет описывать так:

    public double squareCircle() {         double s = Math.PI * r * r;         return s;     }

Результат работы метода можно увидеть следующим образом:

        System.out.println("Площадь круга o2: "+o2.squareCircle()); //615.75...

Обратите внимание: внутри каждого метода класса доступны свойства того объекта, для которого метод будет вызываться. То есть если мы вызываем метод для объекта o2, то внутри метода при его выполнении мы будем работать именно со свойствами объекта o2 (o2.x будет доступно x, o2.r будет доступно как r и т.д.).

Может возникнуть ситуация, когда для формальных параметров метода вы захотите использовать имена уже принадлежащие свойствам класса.

Например, можно было бы начать описание метода для масштабирования таким образом:

public void zoomCircle(double r) {…

Как же в таком случае обращаться к свойствам объекта (ведь имена этих свойств перекрываются формальным параметром)?

Решение такой неоднозначности существует: к любому свойству внутри метода можно обращаться не только по имени, но и через ссылку this. То есть внутри метода можно написать x=13;, а можно this.x=13; — эффект будет идентичный. Соответственно, когда имя формального параметра перекрывает имя свойства, к имени свойства нужно обращаться через ссылку this. Тогда метод можно переписать таким образом:

    public void zoomCircle(double r) {         this.r = this.r * r;     }

Понятно, что удобнее не допускать перекрывания имён свойств именами локальных параметров в методах. Иногда, впрочем, требуется внутри метода применить какой-то другой метод к текущему объекту, тогда без ссылки this не обойтись.

Добавим в класс метод, проверяющий, совпадают ли две окружности по площади.

В этом методе должны участвовать два объекта: тот, для которого метод вызван и второй участник сравнения, который может быть передан в метод через параметр. При этом параметр будет иметь соответствующий тип (не какой-то встроенный, а в виде класса Circle).

Метод можно описать так:

    public boolean equalsCircle(Circle cir) {         if(this.squareCircle() == cir.squareCircle()) {             return true;         } else {             return false;         }     }

Пример использования метода:

        if(o1.equalsCircle(o2)) {             System.out.println("Круги o2 и o1 имеют равную площадь");         } else {             System.out.println("Круги o2 и o1 имеют различную площадь");         }

Задачи

  1. Создайте в классе Circle метод, вычисляющий длину окружности.

  2. Создайте в классе Circle метод, перемещающий центр круга в случайную точку квадрата координатной плоскости с диагональю от [-99;-99] до [99;99]. Обратите внимание на то, что требуется создать обычный метод, применимый к уже существующему объекту, а не конструктор создающий новый объект.

  3. Измените в классе Circle конструктор по умолчанию так, чтобы в момент создания объекта с его помощью, координаты центра и радиус окружности пользователь вводил с клавиатуры.

  4. Создайте в классе Circle метод, вычисляющий расстояние между центрами двух окружностей.

  5. Создайте в классе Circle метод, проверяющий, касаются ли окружности в одной точке. Учтите, что возможен вариант, когда одна окружность содержится внутри другой и при этом всё равно возможно касание в одной точке.

Пример

class Circle {     public double x; // абсцисса центра     public double y; // ордината центра     public double r; // радиус     public void printCircle() {         System.out.println("Окружность с центром ("+x+";"+y+") и радиусом "+r);     }         public void moveCircle(double a, double b) {         x = x + a;         y = y + b;     }     public void zoomCircle(double r) {         this.r = this.r * r;     }     public Circle() {         x = 0.0;         y = 0.0;         r = 1.0;     }     public Circle(double a, double b, double s) {         x = a;         y = b;         r = s;     }       // метод вычисляющий площадь круга     public double squareCircle() {         double s = Math.PI * r * r;         return s;     }     // метод проверяющий равны ли окружности по площадям     public boolean equalsCircle(Circle cir) {         if(this.squareCircle() == cir.squareCircle()) {             return true;         } else {             return false;         }     }     } public class Main {     public static void main(String[] args) {         Circle o1 = new Circle();         o1.printCircle(); // Окружность с центром (0.0;0.0) и радиусом 1.0         Circle o2 = new Circle(1,-1,14);         o2.printCircle(); // Окружность с центром (1.0;-1.0) и радиусом 14.0         System.out.println("Площадь круга o2: "+o2.squareCircle()); //615.75...         o1.zoomCircle(14);         if(o1.equalsCircle(o2)) {             System.out.println("Круги o2 и o1 имеют равную площадь");         } else {             System.out.println("Круги o2 и o1 имеют различную площадь");         }     } }

Рассмотрим пример класса точек на плоскости:

class Point {     public double x; // абсцисса точки     public double y; // ордината точки     // возвращает строку с описанием точки     public String toString() {         return "("+x+";"+y+")";     }     // выводит на экран описание точки     public void printPoint() {         System.out.print(this.toString());     }     // метод перемещает точку на указанный вектор     public void movePoint(double a, double b) {         x = x + a;         y = y + b;     }     // метод изменяет координаты точки на указанные     public void setPoint(double a, double b) {         x = a;         y = b;     }     // конструктор по умолчанию, создающий точку в начале координат     public Point() {         x = 0.0;         y = 0.0;     }     // конструктор, создающий точку с указанными координатами     public Point(double a, double b) {         x = a;         y = b;     }       // метод вычисляющий расстояние между точками     public double length(Point p) {         return Math.sqrt( Math.pow(p.x-x,2) + Math.pow(p.y-y,2) );     }     // метод проверяющий совпадают ли точки     public boolean equalsPoint(Point p) {         if(this.x == p.x && this.y == p.y) {             return true;         } else {             return false;         }     }     } public class Main {     public static void main(String[] args) {         Point p1 = new Point();         Point p2 = new Point(1,1);         System.out.println("Растстояние между точками "+p1+" и "+p2+" равно "+p1.length(p2));     } }

В этом классе создаётся отдельный метод toString(), предназначенный для представления каждого объекта в виде строки. Этот метод можно использовать для собственных нужд (например, он вызывается в методе printPoint(), печатающем строку на экран), но кроме этого метод toString() будет вызываться автоматически, когда возникнет попытка автоприведения объекта к строковому типу.

Например, мы можем пользоваться методом явно:

Point p3 = new Point(3,4.67); System.out.println("Координаты точки: "+p3.toString());

А можем просто обединять наш объект с другой строкой, провоцируя автоприведение:

Point p4 = new Point(3.2,2.3); System.out.println("Координаты точки: "+p4);

Результат при этом будем получать идентичный.

Такой особенный метод toString() существует на самом деле для всех объектов в Java. Любой класс в Java является наследниом класса Object (хотя наследование мы явно при создании своего класса никак не используем) и от этого родительского класса получает ряд готовых методов (среди которых toString() тоже присутствует). Теперь в классе Point мы метод toString() перегрузили, сделав для него такую реализацию, которая требуется нам в нашей программе.

Ещё один яркий пример метода, наследуемого от коренного класса Object — это метод equals(Object o) для сравнения объектов — его можно применять к любым двум объектам (даже если они из разных классов), вызывая метод для одного из них, а второй передавая через параметр. Метод будет возвращать истинное значение тогда и только тогда, когда будет вызван для двух ссылок на один и тот же объект. В своих программах с практической точки зрения равными можно считать разные объекты имеющие одинаковый набор текущих значений в полях, поэтому метод equals обычно тоже перегружают. Вместе с ним обязательно перегружать и метод hashCode(), возвращающий некоторое целое число для каждого объекта, являющегося его уникальным числовым идентификатором (хешем). По умолчанию (в той реализации, которая представлена в классе Object) это число строится на основании адреса объекта в памяти, но при перегрузке метода можно придумать свою собственную реализацию, главное, чтобы она удовлетворяла одному важному правилу: если два обекта совпадают в соответствии с методом equals, то у них должны быть одинаковые хеши, возвращаемые методом hashCode(), при этом обратного не требуется. Например, для нашего класса Point мы могли бы в качестве хеша возвращать произведение координат точки.

Не требуется, но рекомендуется для своих классов перегружать перечисленные выше методы. В примере это сделано для метода toString, но не сделано для equals и hashCode.

Задачи

  1. Создайте в классе метод, который будет выводить на экран сообщение о том, в какой координатной четверти лежит точка.

  2. Создайте в классе метод, проверяющий, являются ли две точки симметричными относительно начала отсчёта.

  3. Измените в классе конструктор по умолчанию таким образом, чтобы начальные координаты точки при её создании пользователь задавал с клавиатуры.

  4. Создайте в классе метод, проверяющий, являются ли три точки коллинеарными (т.е. лежащими на одной прямой).

  5. Вместо представленного метода equalsPoint перегрузите в классе методы equals и hashCode.

Полями создаваемого класса могут быть не только переменные встроенных типов, но и объекты любых других доступных классов.

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

Рассмотрим пример, в котором класс окружностей создаётся с использованием класса точек (одним из полей класса окружностей является объект-точка):

import java.util.Scanner; class Point {     public double x; // абсцисса точки     public double y; // ордината точки     // возвращает строку с описанием точки     public String toString() {         return "("+x+";"+y+")";     }     // выводит на экран описание точки     public void print() {         System.out.print(this.toString());     }     // метод перемещает точку на указанный вектор     public void move(double a, double b) {         x = x + a;         y = y + b;     }     // метод изменяет координаты точки на указанные     public void set(double a, double b) {         x = a;         y = b;     }     // конструктор по умолчанию, создающий точку с указанными пользователем координатами     public Point() {         boolean err;         do {             err = false;             System.out.print("Введите абсциссу точки: ");             Scanner scan = new Scanner(System.in);             if(scan.hasNextDouble()) {                 x = scan.nextDouble();             } else {                 System.out.println("Вы ввели не число, попробуйте снова");                 err = true;             }         } while (err);         do {             err = false;             Scanner scan = new Scanner(System.in);             System.out.print("Введите ординату точки: ");             if(scan.hasNextDouble()) {                 y = scan.nextDouble();             } else {                 System.out.println("Вы ввели не число, попробуйте снова");                 err = true;             }         } while (err);             }     // конструктор, создающий точку с указанными координатами     public Point(double a, double b) {         x = a;         y = b;     }       // метод вычисляющий расстояние между точками     public double length(Point p) {         return Math.sqrt( Math.pow(p.x-x,2) + Math.pow(p.y-y,2) );     }     // метод проверяющий совпадают ли точки     public boolean equalsPoint(Point p) {         if(this.x == p.x && this.y == p.y) {             return true;         } else {             return false;         }     }   } class Circle {     public double r; // радиус     public Point c; // центр         // возвращает строку с описанием окружности     public String toString() {         return "Окружность с центром в точке " + c + " и радиусом " + r;     }       // выводит на экран описание окружности     public void print() {         System.out.print(this.toString());     }         // метод перемещает центр окружности на указанный вектор     public void move(double a, double b) {         c.move(a, b);     }     // метод изменяет окружность, перемещая центр в указанные координаты и меняя радиус     public void set(double a, double b, double m) {         c.set(a, b);         r = m;     }         // метод изменяет окружность, перемещая центр в указанную точку и меняя радиус     public void set(Point p, double m) {         c.set(p.x, p.y);         r = m;     }       // конструктор по умолчанию, создающий окружность с указанными пользователем параметрами     Circle () {         System.out.println("Задайте центр окружности:");         c = new Point();         boolean err;         do {             err = false;             Scanner scan = new Scanner(System.in);             System.out.print("Задайте радиус: ");             if(scan.hasNextDouble()) {                 r = scan.nextDouble();                 if (r <= 0) {                    System.out.println("Радиус окружности должен быть положительным");                    err = true;                 }             } else {                 System.out.println("Вы ввели не число, попробуйте снова");                 err = true;             }         } while (err);             }     Circle (double a, double b, double m) {         c.set(a, b);         r = m;     }           // метод вычисляющий длину окружности     public double length(Point p) {         return 2*Math.PI*r;     }     // метод проверяющий, совпадают ли две окружности     public boolean equalsCircle(Circle o) {         if(this.r == o.r && c.equalsPoint(o.c)) {             return true;         } else {             return false;         }     }       } public class Main {     public static void main(String[] args) {         Circle o1 = new Circle();         o1.print();     } }

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