Главная
Вход
Регистрация
Суббота, 04 Май 2024, 00:52:16 Приветствую Вас Гость | RSS
Меню сайта

Категории раздела
Общедоступные статьи [17]

Статистика
Онлайн всего: 1
Гостей: 1
Пользователей: 0

Главная » Статьи » Общедоступные статьи

0004_02 - Классы

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

Если рассмотреть программу в целом, то объекты – это блоки, из которых состоит программа. А классы – это типы этих блоков. Блоки разных типов являются объектами разных классов.

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

С точки зрения внутренней структуры, класс состоит из методов класса, которые что-то делают и переменных класса, в которых эти методы хранят различные данные.

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

Создание класса происходит примерно по такой схеме:
1 Программист решает, какие еще объекты ему нужны.
2 Программист разбивает эти объекты на различные типы в зависимости от того, что они должны делать.
3 Для каждого типа программист пишет свой отдельный класс.
4 В классе он объявляет нужные методы и переменные.
5 В каждом методе пишутся команды, чтобы метод делал то, что хочет от него программист.
6 Класс готов, можно создавать его объекты.

Подход к программированию, при котором программа разбивается на объекты, называется «Объектно-Ориентированное Программирование (ООП)».

Java – это классический пример ООП подхода: в Java объектами является всё.

Изучение Java состоит из двух больших задач: учимся писать свои классы, учимся использовать чужие классы. Сегодня мы начнем с самого простого. Будем учиться писать простенькие классы и, конечно, создавать их объекты. Объекты часто еще называют экземплярами классов. Это синонимы: правильно и так и так.

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

Для того, чтобы создать объект класса, в коде надо написать:

new ИмяКласса();

А что бы создать переменные для хранения ссылок на объекты класса:

ИмяКласса объектКласса1;
ИмяКласса объектКласса2;

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

ИмяКласса объектКласса1 = new ИмяКласса();
ИмяКласса объектКласса2 = new ИмяКласса();

Например, создадим объект класса Point:

Point tochka = new Point();

У объекта класса есть две интересные особенности:

Первая. Каждый объект класса хранит свою собственную копию переменных класса. Т.е. если в классе объявлены переменные x, y и создано 10 объектов такого класса, то в каждом объекте будут свои переменные. Изменение переменных одного объекта не влияет на переменные другого объекта.

Вторая. При создании объекта, в него можно предавать различные параметры. Это так называемые «стартовые значения». Многие объекты не могут быть созданы, если в них не передать такие параметры.

У каждого объекта есть его копия данных – переменных класса.

Например, у класса Point пусть это будут координаты на плоскости - x и y. Обратиться к ним мы можем таким образом:

tochka.x = 10;
tochka.y = 20;
System.out.println("X = " + tochka.x + "; Y = " + tochka.y);

При создании объекта в скобках можно передавать различные параметры. Об этом чуть попозже. Давайте рассмотрим класс Point:

В Java принято скрывать переменные от доступа из других классов. Обычно переменные, объявленные внутри классов, имеют модификатор private. Чтобы другие классы могли менять значения таких переменных, для каждой из них создается пара методов: get и set. Задача метода get вернуть текущее значение переменной тому, кто его вызвал. Задача метода set установить новое значение переменной.

Если мы не хотим, чтобы кто-то менял значения переменных наших объектов, мы можем просто не писать метод set для него, или сделать его private. Также в этот метод можно добавить дополнительные проверки данных. И если переданное новое значение неверно, то ничего не менять.

Т.к. переменных в классе может быть много, то методы get и set обычно имеют в своем имени имя той переменной, с которой работают. Если переменная называется name, то методы setName и getName. И т.д. по аналогии.

В методе так же можно описать и взаимодействие объектов, например, вычислить длину отрезка:

Теперь несколько слов об инициализации объектов. Когда объект создаётся – его переменным нужно присвоить стартовые данные. Чтобы не было ситуаций, когда ты обращаешься к объекту, а внутри у него нет нужной ему информации для правильной работы.

Рассмотрим для примера объект типа Point. Минимальной необходимой информацией для него являются координаты на плоскости - x и y. Создать точку без её координат – это бессмыслица.

Для этого добавим в наш класс метод initialize(). Это будет выглядеть так:

public void initialize(int x, int y) {
    this.x = x;
    this.y = y;
}

Мы добавили метод initialize, чтобы с объектом можно было работать – вызывать его методы. Это можно делать сразу после вызова метода initialize. Если с объектом работать нельзя, его называют невалидным (invalid), если можно – валидным (valid). Основная задача метода initialize – передать в объект все необходимые данные, чтобы сделать его валидным.

Теперь усложним задачу. Вернее упростим. Как посмотреть. Представим, что другому программисту, который будет использовать наш класс, удобнее передавать в него не абсолютные координаты точки, а относительные, ведя отсчёт от другой точки. Мы бы смогли реализовать эту функциональность с помощью ещё одного метода initialize (Java позволяет создавать несколько методов с одинаковыми именами). Тогда наш класс стал бы выглядеть так:

public void initialize(Point tochka, int x, int y) {
    this.x = x + tochka.x;
    this.y = y + tochka.y;
}

Метод initialize надо вызывать сразу после создания объекта, чтобы перевести его в валидное состояние:

Point t1 = new Point();
t1.initialize(10,20);

Point t2 = new Point();
t2.initialize(t1, -8, 7);

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

Самое время теперь рассказать о конструкторах. Всё очень просто. Программисты придумали сокращённую запись создания и инициализации объекта.

Без использования конструктора:

Point t1 = new Point();
t1.initialize(10,20);

Point t2 = new Point();
t2.initialize(t1, -8, 7);

С использованием конструктора:

Point t1 = new Point(10,20);
Point t2 = new Point(t1, -8, 7);

При этом вместо методов:

public void initialize(int x, int y) {
    this.x = x;
    this.y = y;
}

public void initialize(Point tochka, int x, int y) {
    this.x = x + tochka.x;
    this.y = y + tochka.y;
}

Используются методы, названые по имени класса, называемые конструктором:

public Point(int x, int y) {
    this.x = x;
    this.y = y;
}

public Point(Point tochka, int x, int y) {
    this.x = x + tochka.x;
    this.y = y + tochka.y;
}

Объявить конструктор в классе очень легко. Конструктор - это тот же метод initialize, но с двумя отличиями:
а) Имя метода-конструктора совпадает с именем класса (вместо initialize).
б) У метода-конструктора нет типа (никакой тип не указывается вообще).

Фактически это тот же метод initialize, но с небольшими отличиями.

Время жизни объекта

Каждый объект после создания существует (живёт) пока хотя бы одна переменная хранит его адрес (на него есть хотя бы одна ссылка). Если ссылок больше не остаётся – объект умирает.

Point t1 = new Point(10,20);
t1 = null;

Объект «t1» существует всего одну строчку с момента создания. Уже на следующей строке единственную переменную, которая хранит на него ссылку, «обнуляют» и объект уничтожается Java-машиной.

Но если создать объект «t1» в каком-нибудь методе и сохранить ссылку на него в переменную класса, то объект Cat будет существовать все время, пока существует ссылка на него из другого живого объекта.

Обычно объект уничтожается не сразу. Java-машина время от времени запускает уборку мусора – уничтожение неиспользуемых объектов. Об этом позже.

Если мы хотим, чтобы какая-то переменная перестала хранить ссылку на объект – можно присвоить ей значение null или же ссылку на другой объект.

Кратенькое знакомство с методом finalize()

Этот метод вызывается Java-машиной у объекта перед тем, как объект будет уничтожен. Фактически этот метод – противоположность конструктору. В нем можно освобождать ресурсы, используемые объектом.

Этот метод есть у класса Object и, следовательно, есть в каждом классе (все классы в Java считаются унаследованными от класса Object и содержат копию его методов). Можно просто написать в нашем классе такой же метод, и он будет вызываться перед уничтожением объектов этого класса.

Пример:

protected void finalize() throws Throwable{
    System.out.println("Точка (" + x + "; " + y + ") уничожена");
}

Но! Java-машина сама решает – вызвать данный метод или нет. Чаще всего объекты, созданные в методе и объявленные мусором после его завершения, уничтожаются сразу же и без всяких вызовов метода finalize(). Этот метод скорее дополнительная страховка, чем надёжное решение. Лучшим вариантом будет освобождать любые используемые ресурсы (обнулять сохраненные ссылки на другие объекты), когда наш объект ещё жив.

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

Ещё про время жизни объекта

Ещё хочу рассказать пару интересных вещей про время жизни объектов. В Java случайно потерять объект очень сложно – если у Вас есть ссылка на него, значит, объект гарантированно жив.

Ссылки на объекты нельзя поменять. Нельзя увеличить или уменьшить. Также нельзя создать ссылку на объект – ее можно только присвоить. Или обнулить.

Если я обнулить все ссылки на объект, то Вы никогда не сможете получить на него ссылку и обращаться к нему. Но часто наблюдается обратная ситуация – слишком много неиспользуемых живых объектов. Часто программисты создают объекты десятками и хранят их в различных списках для обработки, но никогда эти списки не чистят.

Чаще всего ненужные объекты помечаются программистами, как неиспользуемые и всё. А удалением их из списков никто не занимается. Так что для больших Java-программ характерно раздувание – всё больше и больше неиспользуемых объектов остаются жить в памяти.

Статические переменные и методы

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

Вот как это выглядит:

class Point {    // класс
    int x, y;    // переменные  

    Point(int x, int y) {    // конструктор
        this.x = x;          // инициализация переменных
        this.y = y;
    }
}

Код в методе main:

Point p1 = new Point(10, 20); // создали один объект
Point p2 = new Point(30, 40); // создали ещё один объект
System.out.println("Точка (" + p1.x + "; " + p1.y + ")");
System.out.println("Точка (" + p2.x + "; " + p2.y + ")");

Вывод на экран будет таким:

Точка (10; 20)
Точка (30; 40)

Переменные p1.x и p2.x, p1.y и p2.y, хоть и описаны в одном классе – Point, но хранят разные значения, т.к. привязаны к разным объектам.

Статические же переменные – существуют в одном экземпляре, и обращаться к ним нужно по имени класса:

class Point {                // класс
    int x, y;                // обычные переменные
    static int pointCount;   // статическая переменная

    Point(int x, int y) {
        this.x = x;          // инициализация переменных
        this.y = y;
        Point.pointCount++;  // увеличиваем значение статический переменной на 1
    }
}

Код в методе main:
System.out.println("Всего точек: " + Point.pointCount);
Point p1 = new Point(10, 20); // создали один объект
System.out.println("Всего точек: " + Point.pointCount);
Point p2 = new Point(30, 40); // создали ещё один объект
System.out.println("Точка (" + p1.x + "; " + p1.y + ")");
System.out.println("Точка (" + p2.x + "; " + p2.y + ")");
System.out.println("Всего точек: " + Point.pointCount);

Вывод на экран будет таким:

Всего точек: 0
Всего точек: 1
Точка (10; 20)
Точка (30; 40)
Всего точек: 2

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

Статические методы не могут обращаться к нестатическим методам или нестатическим переменным!

Каждая обычная переменная класса находится внутри объекта. Обратиться к ней можно только имея ссылку на этот объект. В статический метод такая ссылка не передается.

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

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

Как работают обычные нестатические методы

Код выглядит так:

Point p1 = new Point();
p1.setXY(10, 20);
int x = p1.getX();
int y = p1.getY();

А это происходит на самом деле:

Point p1 = new Point();
Point.setXY(p1, 10, 20);
int x = Point.getX(p1);
int y = Point.getY(p1);

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

Как работают статические методы

Код выглядит так:

Point p1 = new Point();
Point p2 = new Point();
int pointCount = Point.getPointCount();

А это происходит на самом деле:

Point p1 = new Point();
Point p2 = new Point();
int pointCount = Point.getPointCount(null);

При вызове статического метода, никакого объекта внутрь не передаётся. Т.е. this равен null, поэтому статический метод не имеет доступа к нестатическим переменным и методам (ему нечего неявно передать в обычные методы).

Переменная или метод являются статическими, если перед ними стоит ключевое слово static.

У такого подхода тоже есть свои преимущества.

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

Во-вторых, иногда бывает нужно, чтобы переменная была в единственном экземпляре. Как, например, переменная System.out (статическая переменная out класса System).

И в третьих, иногда нужно вызвать метод, еще до того, как будет возможность создавать какие-то объекты. К примеру, метод main объявлен статическим, чтобы его можно было вызвать сразу после загрузки класса в память. Еще до того, когда можно будет создавать какие-то объекты.

Кроме статических методов есть ещё и статические классы. Что это такое мы рассмотрим в будущем.

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

Основной смысл модификатора static перед объявлением класса - это регулирование отношения класса Point к классу StaticClassExample. Смысл примерно такой: класс Point не привязан к объектам класса StaticClassExample, и не может обращаться к обычным (нестатическим) переменным класса StaticClassExample.

Категория: Общедоступные статьи | Добавил: FireSnake (20 Дек 2015)
Просмотров: 1771 | Теги: метод, статические, видимость, Переменная, класс, private, Static, Public, Class | Рейтинг: 5.0/1
Всего комментариев: 0
avatar
Вход на сайт

Поиск

Группа VK