Основы объектно-ориентированного программирования
Классы и объекты в языке Java
Для знакомства с базовым для объекно-ориентированного программирования понятием класса, рассмотрим класс R2Point, задающий точку на плоскости.
Пример программы
//Класс, описывающий точку (Point) на плоскости (R2).
class R2Point {
// Переменные экземпляра, задающие координаты точки.
private double x, y;
// Конструктор с параметрами.
public R2Point(double x, double y) {
this.x = x; this.y = y;
}
// Метод экземпляра - расстояние до начала координат.
public double dist0() {
return Math.sqrt(x*x + y*y);
}
}Для того, чтобы создать экземпляр этого класса, в программе следует предварительно объявить переменную типа R2Point, а затем воспользоваться оператором new. Объявление переменной можно совмещать с созданием нового объекта, что демонстрируется в следующей программе, находящей расстояние от точки до начала координат.
R2Point p = new R2Point(1.,2.); double d = p.dist0();
Этот пример объясняет, почему такой стиль записи называют объектно-ориентированным: при вызове метода в центре внимания находится объект p, а не метод dist0. Этот метод не нуждается в аргументе — объект, над которым производится данное действие, уже указан в данной конструкции. Именно по этой причине внутри метода dist0 можно работать с величинами x и y, принадлежащими конкретному экземпляру объекта. Имена x и y в теле метода dist0 являются сокращениями от this.x и this.y соответственно, где ключевое слово this является указателем на тот экземпляр класса, с которым должен работать метод. Часто нет необходимости явно использовать этот неявный аргумент любого метода экземпляра, однако иногда это необходимо. Примером является конструктор класса R2Point.
Конструктор — это метод, имеющий имя, совпадающее с именем класса. Две основные задачи конструктора заключаются в выделении памяти под вновь создаваемый объект и его инициализация. В рассматриваемом нами примере точки на плоскости совершенно бессмысленно пытаться как-либо работать с объектом (точкой), у которого не заданы координаты. Поэтому конструктор объекта типа R2Point, который вызывается с помощью метода new, должен записать в переменные конкретного экземпляра его координаты. В том случае, если в качестве имен аргументов конструктора выбраны x и y (а это вполне естественный выбор), для обеспечения доступа к переменным экземпляра необходимо использование ключевого слова this. В объявлении конструктора не разрешается указывать возвращаемый тип (хотя неявно всегда возвращается объект this ), а в его теле нельзя использовать оператор return.
Если в некоторой задаче необходимо создавать новые объекты типа R2Point, вводя их координаты с клавиатуры, то может возникнуть желание реализовать это непосредственно в конструкторе.
Пример программы
//Класс, описывающий точку (Point) на плоскости (R2).
class R2Point{
// Переменные экземпляра, задающие координаты точки.
private double x, y;
// Конструктор с параметрами.
public R2Point(double x, double y) {
this.x = x; this.y = y;
}
// Еще один конструктор, позволяющий вводить
// координаты вновь создаваемой точки с клавиатуры.
public R2Point() throws Exception {
x = Xterm.inputDouble("x -> ");
y = Xterm.inputDouble("y -> ");
}
// Метод класса - расстояние до начала координат.
public static double dist0(R2Point a) {
return Math.sqrt(a.x*a.x + a.y*a.y);
}
// Метод экземпляра - расстояние до начала координат.
public double dist0() {
return Math.sqrt(x*x + y*y);
}
}Такой класс выглядит на первый взгляд весьма странно — в нем разные методы имеют одинаковые имена. Компилятор, однако, может различить их. Признак, по которому это происходит — количество и типы аргументов у различных методов. Так, если оператор new для объекта R2Point имеет два аргумента, то вызывается первый из конструкторов, а если аргументов нет — второй.
Вторым интересным моментом в этом примере является иллюстрация использования метода класса. Первый из двух методов dist0 описан с ключевым словом static, что и делает его методом класса. Такой метод не может быть вызван от конкретного объекта с помощью оператора "точка", ему не передается указатель this на экземпляр, а само это ключевое слово не может быть использовано в его теле. Приведенная ниже программа находит расстояние от точки p до начала координат двумя различными эквивалентными методами.
R2Point p = new R2Point(1.,2.); double d1 = p.dist0(); double d2 = R2Point.dist0(p);
Могут существовать и переменные класса — это такие переменные, которые всегда имеются ровно в одном экземпляре, независимо от того как много имеется объектов данного класса. Для их описания также применяется ключевое слово static.
Обычно класс содержит целый ряд методов, как экземпляра, так и класса, набор которых определяется конкретной задачей. Вот как выглядит описание класса R2Point, которое будет использовано в следующем параграфе в реальном проекте.
Пример программы
//Класс, описывающий точку (Point) на плоскости (R2).
class R2Point {
private double x, y;
public R2Point(double x, double y) {
this.x = x; this.y = y;
}
public R2Point() throws Exception {
x = Xterm.inputDouble("x -> ");
y = Xterm.inputDouble("y -> ");
}
public static double dist(R2Point a, R2Point b) {
return Math.sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
public static double area(R2Point a, R2Point b, R2Point c) {
return 0.5*((a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x));
}
public static boolean equal(R2Point a, R2Point b) {
return a.x==b.x && a.y==b.y;
}
public static boolean isTriangle(R2Point a, R2Point b, R2Point c) {
return area(a, b, c) != 0.0;
}
public boolean inside(R2Point a, R2Point b) {
return (a.x <= x && x <= b.x || a.x >= x && x >= b.x) &&
(a.y <= y && y <= b.y || a.y >= y && y >= b.y);
}
public boolean light(R2Point a, R2Point b) {
double s = area(a, b, this);
return s < 0.0 || ( s == 0.0 && ! inside(a, b));
}
}