Найти в Дзене
Изучаем Java

Java10: классы

Оглавление

Класс - это отдельная конструкция, с которой связан набор данных и методов. С классами мы имеем дело, начиная с первой “Hello World” программы. Давайте опять вспомним ее, чтобы разобраться с темой урока:

Здесь у нас класс Main с методом main. Класс - это тип данных, как int или массив. Можно создать переменную с типом Main:

-2

заметьте, что для создания нового значения с типом Main мы используем ключевое слово new. Значение с типом какого-нибудь класса часто называют объектом.

Резюмируя, класс - это тип, а значение с типом класс - это объект. Можно провести аналогию, что класс - это, как чертеж здания, а объект - построенное здание.

Поля

Данные класса (или поля класса) - это переменные, которые связанны с классом.

Например:

-3

Здесь a и b - поля класса Test.

Создав объект с типом Test, мы можем получить доступ к полю используя оператор "." (точка).

Например:

-4

Что здесь происходит:

  • Мы определили класс Test, который используем для демонстрации работы с полями
  • Дальше есть класс Main с методом main, где поисходят все манипуляции с кодом.
  • Определяем переменную тест и инициализируем ее новым объектом
    Test test = new Test();
  • Изменяем состояние этого объекта, записываем в поле b значение 1
    test.b = 1;
  • Читаем текущее значение поля b и выводим на консоль
    System.out.println(test.b);
Напомню, что специальный метод public static void main(String[] args) - это точка входа в программу, где стартует поток выполнения.

Методы

Мы уже начинали обсуждать методы в предыдущем уроке. Теперь нужно добавить, что методы всегда связаны с каким-нибудь классом и не существуют изолированно.

Добавим к классу Test метод Sum, который будет вычислять сумму полей:

-5

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

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

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

Например:

-6

и создание объекта:

-7

Что здесь происходит:

  • У нас есть класс Test с полями а и b и конструктором Test(int c)
  • Как мы видим конструктор у нас с параметром
  • Дальше создаем объект, передаем в конструктор значение 4
    Test test = new Test(4);
  • В процессе выполнения конструктора поле a инициализируется результатом выражения 4 * 2
  • Поле b инициализируется результатом выражения 4 * 2

Если конструктор не задан, то компилятор сгенерит конструктор по-умолчанию, без параметров.

Например вот у нас класс, где мы не задали конструктор:

-8

Компилятор добавит конструктор без параметра и объект этого класса мы создаем таким образом:

-9

Такой конструктор проинициализирует поля объекта значениями по-умолчанию.

Значения по-умолчанию

Обратите внимание на отличие локальных переменных в методе и полей объекта. Локальные переменные останутся не проинициализированными, пока мы вручную не присвоим им значения, а полям объекта присвоятся значения по-умолчанию.

Например, int поля проиницилизируются значением 0 (проверьте что выведет следующий код):

-10

Что здесь происходит:

  • Мы определяем свой класс Test, где у нас 2 инт поля (а и b).
  • Далее у нас написан отдельный класс Main с точкой входа main, где начинается вся программа.
  • Мы создаем объект типа Test
    Test test = Test();
  • Дальше распечатываем значения полей
    System.out.println(test.a);
    System.out.println(test.b);

Ниже таблица дефолтных значений:

-11

Модификаторы доступа

Для методов и полей можно указать модификатор доступа.

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

public - поля и методы доступны внутри и снаружи класса, другие классы могут их использовать.

Вот пример кода, где мы проводим манипуляции с приватными полями и приватным методом в другом методе того же класса:

-12

А вот мы добавляем второй класс, пробуем доступиться к приватным членам класса Test и получаем ошибку компиляции:

-13

А в этом примере мы указываем полям модификатор public и остальные классы получают доступ к ним:

-14

Инкапсуляция

Инкапсуляцией называют скрытие данных с помощью модификатора private и организацию доступа к ним через набор публичных методов.

Пример:

-15

В классе Test мы инкапсулировали поля fieldA, fieldB, а для взаимодействия с этими данными есть get-еры и set-еры.

Здесь мы используем распространенное соглашение об именовании публичных методов доступа к данным:

  • get<Название-поля> - читаем значение
  • set<Название-поля> - записываем значение

IDEA умеет генерить код для таких методов.

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

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

Допустим, у нас есть класс Машина, она реализует публичный контракт - проехать из пункта А в пункт Б, а внутри скрыты детали реализации:

  • 4 колеса, двери, руль (данные)
  • зажигание, ускориться, затормозить (методы)

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

Декомпозиция на классы

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

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

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

Давайте рассмотрим нашу программу-опросник из предыдущих уроков и декомпозируем ее на набор классов.

В программе мы задаем вопросы и печатаем ответы. Создадим два класса Interviewer, который будет вести интервью, и Printer, который распечатает ответы,

Interviewer:

-16

В этом коде встречается конструкция

-17

this - это текущий объект, а this.questions - мы обращаемся к полю этого объекта.

Т.е. в примере кода

-18

у нас две переменные - локальная переменная метода String[] questions и поле объекта this.questions.

Printer:

-19

и связываем все это в main:

-20

т.е. у класса Interviewer публичный контракт:

  • берем список вопросов
  • задаем вопросы
  • возвращаем ответы

и класс Printer:

  • берем ответы и пояснения
  • распечатываем ответы

Задание:

  • Наберите программу опросник, запустите на выполнение, разберите по-шагам, что в ней происходит.

Полезные ссылки: