Глиф Учебники

Урок 9. Классы и объекты

Курс «Java» · урок 9 из 10 · 4 минуты чтения

До сих пор вся программа была одним классом Main с методами. Но Java — объектно-ориентированный язык: в нём программу собирают из объектов, каждый из которых хранит свои данные и умеет что-то делать.

Класс — это описание, чертёж: какие у объекта есть данные и что он умеет. Объект — конкретный экземпляр по этому чертежу. Класс Student описывает студента вообще, а объекты — Анну и Олега, у каждого свои имя и возраст.

class Student поля String name; int age; методы introduce() birthday() класс — описание, чертёж new new объект Student name "Анна" age 16 объект Student name "Олег" age 17 a b объекты — у каждого свои поля
Класс Student — чертёж с полями name и age и методами introduce() и birthday(); по нему new создаёт объекты для Анны и Олега, и у каждого свои значения полей.

Первый класс

Java
public class Main {
    public static void main(String[] args) {
        Student a = new Student("Анна", 16);
        Student b = new Student("Олег", 17);
        a.introduce();
        b.introduce();
        a.birthday();
        a.introduce();
    }
}

class Student {
    String name;
    int age;

    Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    void introduce() {
        System.out.println(name + ", " + age + " лет");
    }

    void birthday() {
        age++;
    }
}
Вывод
Анна, 16 лет
Олег, 17 лет
Анна, 17 лет

Что здесь есть:

вызов new Student("Анна", 16) "Анна" 16 конструктор параметры name и age this.name = name; this.age = age; новый объект — this name "Анна" age 16 name age a переменная a без this: name = name; — поле name остаётся null
При new Student("Анна", 16) аргументы попадают в параметры конструктора name и age, а строки this.name = name и this.age = age кладут их в поля нового объекта, ссылку на который получает a.

Оба класса здесь в одном файле Main.java. Публичным может быть только один из них — тот, чьё имя совпадает с именем файла. Если запускаете командой java Main.java, класс с main должен идти в файле первым. В больших проектах каждый класс обычно лежит в своём файле: Student.java, Main.java.

Если в классе нет ни одного конструктора, Java добавляет пустой — тогда объект создаётся как new Student(). Но как только объявлен свой конструктор с параметрами, пустого больше нет, и new Student() не скомпилируется.

Переменная хранит ссылку

Переменная типа класса хранит не сам объект, а ссылку на него — как и у массивов:

Java
Student c = a;
c.age = 30;

После этого a.age тоже 30: a и c указывают на один объект. Переменная, которой не присвоили объект, хранит null, и вызов метода через неё даёт NullPointerException.

Инкапсуляция: private и геттеры

Сейчас любой код может написать a.age = -5, и объект окажется в бессмысленном состоянии. Инкапсуляция — правило прятать поля внутри класса и менять их только через методы, которые проверяют значения.

Java
public class Main {
    public static void main(String[] args) {
        Account acc = new Account("Иван");
        acc.deposit(100);
        acc.withdraw(30);
        acc.withdraw(500);
        System.out.println(acc.getOwner() + ": " + acc.getBalance());
    }
}

class Account {
    private final String owner;
    private int balance;

    Account(String owner) {
        this.owner = owner;
    }

    void deposit(int amount) {
        if (amount > 0) {
            balance += amount;
        }
    }

    void withdraw(int amount) {
        if (amount > balance) {
            System.out.println("Недостаточно средств");
            return;
        }
        balance -= amount;
    }

    int getBalance() {
        return balance;
    }

    String getOwner() {
        return owner;
    }
}
Вывод
Недостаточно средств
Иван: 70
код в main объект Account deposit withdraw getBalance private owner "Иван" balance 70 acc.deposit(100); acc.withdraw(30); acc.withdraw(500); проверка: денег мало acc.getBalance() 70 acc.balance = 1000; ошибка компиляции: balance has private access in Account
Поля owner и balance закрыты словом private: main меняет и читает их только через методы deposit, withdraw и getBalance, withdraw(500) не проходит проверку, а acc.balance = 1000 не компилируется.

Модификаторы доступа: private — только свой класс; без модификатора — классы того же пакета (группы классов, как java.util); protected — ещё и наследники; public — все.

Метод toString

Если вывести объект через println, Java напечатает имя класса и служебный номер — что-то вроде Point@6d06d69c. Чтобы объект выводился понятно, в класс добавляют метод toString:

Java
public class Main {
    public static void main(String[] args) {
        Point p = new Point(3, 4);
        System.out.println(p);
        System.out.println("Точка: " + p);
    }
}

class Point {
    private final int x;
    private final int y;

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

    @Override
    public String toString() {
        return "(" + x + ", " + y + ")";
    }
}
Вывод
(3, 4)
Точка: (3, 4)

println и склейка со строкой сами вызывают toString. Пометка @Override говорит компилятору: этот метод заменяет метод, унаследованный от родителя (о наследовании — в следующем уроке). Если в имени опечатка, компилятор сообщит об этом. Слово public здесь обязательно: исходный toString публичный, и заменяющий не может быть доступен меньше.

Частые ошибки

Коротко

Проверьте себя

1. Чем класс отличается от объекта?
2. Как называется конструктор класса Car?
3. Что значит private у поля?
4. Что выведет System.out.println(p) для объекта класса без своего toString?