$ sudo teach IT
МОДУЛЬ 7 · УРОК 2

Поля и методы класса

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

~30 минут Для новичков Java ООП

Поля: состояние объекта

Поля — это переменные, объявлённые внутри класса. Они хранят данные объекта. У каждого объекта КОПИЯ своих полей. Представьте форму для анкеты: форма одна (класс), но каждая заполненная анкета (объект) содержит свои данные.

public class Student {
    String name;
    int age;
    double grade;
    boolean active;
    String email;
}

Здесь 5 полей: name (строка), age (целое число), grade (вещественное число), active (логическое), email (строка). Каждый объект типа Student будет иметь свои значения этих полей.

Student s1 = new Student();
s1.name = "Алиса";
s1.age = 20;
s1.grade = 4.5;
s1.active = true;

Student s2 = new Student();
s2.name = "Борис";
s2.age = 22;
s2.grade = 3.8;
s2.active = false;

Сравните: Алиса и Борис — два разных объекта. У Алисы name = "Алиса", а у Бориса — name = "Борис". Они не пересекаются.

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

Если вы не задали значение поля при создании объекта, Java автоматически присваивает значение по умолчанию:

  • int, long, short, byte → 0
  • double, float → 0.0
  • boolean → false
  • char → '\u0000' (нулевой символ)
  • Все ссылочные типы (String, объекты) → null
Student s = new Student();
System.out.println(s.name);     // null
System.out.println(s.age);      // 0
System.out.println(s.grade);    // 0.0
System.out.println(s.active);   // false

ВАЖНО: локальные переменные внутри методов НЕ имеют значений по умолчанию! Они обязаны быть инициализированы перед использованием, иначе компилятор выдаст ошибку.

public void doSomething() {
    int x;
    // System.out.println(x); // Ошибка компиляции!
    x = 10;
    System.out.println(x); // 10 — работает
}

Методы: поведение объекта

Методы — это функции внутри класса. Они описывают, что объект МОЖЕТ ДЕЛАТЬ. Методы определяют поведение объекта.

public class BankAccount {
    String number;
    String owner;
    double balance;

    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Зачислено " + amount + " руб. Баланс: " + balance);
        }
    }

    boolean withdraw(double amount) {
        if (amount <= 0) {
            System.out.println("Сумма должна быть положительной");
            return false;
        }
        if (amount > balance) {
            System.out.println("Недостаточно средств. Баланс: " + balance);
            return false;
        }
        balance -= amount;
        System.out.println("Списано " + amount + " руб. Баланс: " + balance);
        return true;
    }

    void transfer(BankAccount target, double amount) {
        if (this.withdraw(amount)) {
            target.deposit(amount);
            System.out.println("Перевод " + amount + " руб. выполнен");
        }
    }

    void displayInfo() {
        System.out.println("Счёт: " + number);
        System.out.println("Владелец: " + owner);
        System.out.println("Баланс: " + balance + " руб.");
    }
}

Обратите внимание на структуру метода:

  • Модификатор доступа (void, boolean) — что метод возвращает.
  • Имя метода — что делает (deposit, withdraw, transfer).
  • Параметры — входные данные (amount, target).
  • Тело метода — логика между фигурными скобками.

Методы без возвращаемого значения используют void. Если метод должен что-то вернуть — укажите тип (boolean, int, String и т.д.).

Ключевое слово this

this — это ссылка на текущий объект. Представьте, что вы в команде: «Это Я делаю». Параметры метода могут иметь те же имена, что и поля. В этом случае this помогает различить их.

public class Person {
    String name;
    int age;

    void setName(String name) {
        this.name = name;
    }

    void setAge(int age) {
        this.age = age;
    }

    void display() {
        System.out.println("Имя: " + this.name + ", возраст: " + this.age);
    }
}

Здесь this.name — это поле объекта, а name (без this) — это параметр метода. Без this компилятор не понял бы, что вы имели в виду.

Аналогия: вы в классе и говорите «дайте учебник». Учитель спрашивает: «Какой учебник — тот, что на парте, или тот, что у вас в руках?» Вы отвечаете: «Тот, что на парте» (= this.name = name).

this нельзя использовать в статических методах (static), потому что статические методы не привязаны к конкретному объекту. Подробнее об этом в уроке 5.

Геттеры и сеттеры

Геттеры (getters) — методы, которые ВОЗВРАЩАЮТ значение поля. Сеттеры (setters) — методы, которые УСТАНАВЛИВАЮТ значение поля.

Зачем нужны? Потому что поля обычно делаются private (об этом в уроке 4), а доступ к ним осуществляется через методы. Это позволяет контролировать, как читать и изменять данные.

public class Employee {
    private String name;
    private double salary;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name != null && !name.isEmpty()) {
            this.name = name;
        }
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        if (salary > 0) {
            this.salary = salary;
        } else {
            System.out.println("Зарплата не может быть отрицательной!");
        }
    }
}

Обратите внимание: сеттер setSalary проверяет значение перед установкой. Нельзя задать отрицательную зарплату. Это защита данных — инкапсуляция.

Соглашения об именовании:

  • Геттер: getИмяПоля() — например, getName(), getSalary()
  • Сеттер: setИмяПоля(значение) — например, setName("Иван"), setSalary(50000)
  • Для boolean: isИмяПоля() — например, isActive(), isOpen()

Бизнес-методы

Бизнес-методы — это методы, реализующие логику предметной области. Не просто get/set, а реальные действия.

public class Counter {
    private int count;
    private int min;
    private int max;

    public Counter(int min, int max) {
        this.min = min;
        this.max = max;
        this.count = min;
    }

    public void increment() {
        if (count < max) {
            count++;
        }
    }

    public void decrement() {
        if (count > min) {
            count--;
        }
    }

    public void reset() {
        count = min;
    }

    public int getCount() {
        return count;
    }

    public boolean isMin() {
        return count == min;
    }

    public boolean isMax() {
        return count == max;
    }
}
Counter c = new Counter(0, 10);
c.increment(); // count = 1
c.increment(); // count = 2
c.increment(); // count = 3
System.out.println(c.getCount()); // 3
c.reset();
System.out.println(c.getCount()); // 0

Ещё пример — класс Temperature:

public class Temperature {
    private double celsius;

    public Temperature(double celsius) {
        this.celsius = celsius;
    }

    public double getCelsius() {
        return celsius;
    }

    public void setCelsius(double celsius) {
        this.celsius = celsius;
    }

    public double getFahrenheit() {
        return celsius * 9.0 / 5.0 + 32;
    }

    public void setFahrenheit(double fahrenheit) {
        this.celsius = (fahrenheit - 32) * 5.0 / 9.0;
    }

    public boolean isFreezing() {
        return celsius <= 0;
    }

    public boolean isBoiling() {
        return celsius >= 100;
    }

    public void display() {
        System.out.printf("%.1f°C = %.1f°F%n", celsius, getFahrenheit());
    }
}
Temperature t = new Temperature(37.5);
t.display();      // 37.5°C = 99.5°F
t.setFahrenheit(212);
t.display();      // 100.0°C = 212.0°F
System.out.println(t.isBoiling()); // true

Соглашения об именовании

Следование соглашениям об именовании — признак профессионального разработчика:

  • Методы — глаголы: getName, calculateTotal, setAge, isRunning, hasChildren
  • Поля — существительные: name, age, count, isOpen, maxCount
  • camelCase — первое слово с маленькой буквы, остальные с большой: firstName, lastName, numberOfStudents
  • Геттеры: getXxx() — например, getName(), getAge()
  • Сеттеры: setXxx() — например, setName("Иван"), setAge(25)
  • Булевы геттеры: isXxx(), hasXxx(), canXxx() — isActive(), hasChildren(), canEdit()

Хорошие имена — это документация. Метод calculateDiscountedPrice() понятен сразу. Метод calc() — нет. Всегда давайте осмысленные имена!

Полный пример: класс Rectangle

Давайте создадим класс Прямоугольник со всеми концепциями этого урока:

public class Rectangle {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getWidth() {
        return width;
    }

    public void setWidth(double width) {
        if (width > 0) {
            this.width = width;
        }
    }

    public double getHeight() {
        return height;
    }

    public void setHeight(double height) {
        if (height > 0) {
            this.height = height;
        }
    }

    public double getArea() {
        return width * height;
    }

    public double getPerimeter() {
        return 2 * (width + height);
    }

    public boolean isSquare() {
        return width == height;
    }

    public boolean isLandscape() {
        return width > height;
    }

    public void display() {
        System.out.printf("Прямоугольник: %.1f x %.1f%n", width, height);
        System.out.printf("  Площадь: %.1f%n", getArea());
        System.out.printf("  Периметр: %.1f%n", getPerimeter());
        System.out.printf("  Квадрат: %s%n", isSquare() ? "да" : "нет");
    }
}
Rectangle r1 = new Rectangle(5, 3);
r1.display();

Rectangle r2 = new Rectangle(4, 4);
r2.display();

System.out.println("Площадь r1: " + r1.getArea());
System.out.println("Площадь r2: " + r2.getArea());

Возврат значений из методов

Методы могут возвращать значения любого типа:

public class Calculator {
    private double result;

    public double add(double a, double b) {
        return a + b;
    }

    public double subtract(double a, double b) {
        return a - b;
    }

    public double multiply(double a, double b) {
        return a * b;
    }

    public double divide(double a, double b) {
        if (b == 0) {
            System.out.println("Деление на ноль!");
            return 0;
        }
        return a / b;
    }

    public int max(int a, int b) {
        return a > b ? a : b;
    }

    public boolean isPositive(double number) {
        return number > 0;
    }

    public String formatResult(double value) {
        return "Результат: " + String.format("%.2f", value);
    }
}

Обратите внимание: метод может возвращать int, double, boolean, String или любой другой тип. Главное — указать возвращаемый тип перед именем метода и использовать return.

Методы, вызывающие другие методы

Методы могут вызывать другие методы того же объекта. Это помогает избежать дублирования кода:

public class Circle {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getArea() {
        return Math.PI * radius * radius;
    }

    public double getCircumference() {
        return 2 * Math.PI * radius;
    }

    public double getDiameter() {
        return 2 * radius;
    }

    public void display() {
        System.out.printf("Круг с радиусом %.1f%n", radius);
        System.out.printf("  Диаметр: %.1f%n", getDiameter());
        System.out.printf("  Длина окружности: %.1f%n", getCircumference());
        System.out.printf("  Площадь: %.1f%n", getArea());
    }
}
Circle c = new Circle(5);
c.display();

Метод display() вызывает getDiameter(), getCircumference() и getArea(). Это нормально — методы могут использовать друг друга.

Параметры методов: передача по значению

В Java параметры передаются по значению. Для примитивов — копируется значение. Для объектов — копируется ссылка.

public class Example {
    static void changeValue(int x) {
        x = 100; // Изменяет локальную копию
    }

    static void changeName(Dog dog) {
        dog.name = "Новое имя"; // Изменяет объект через ссылку
    }

    public static void main(String[] args) {
        int a = 5;
        changeValue(a);
        System.out.println(a); // 5 — не изменилось!

        Dog d = new Dog();
        d.name = "Старое имя";
        changeName(d);
        System.out.println(d.name); // "Новое имя" — объект изменился!
    }
}

Для примитивов: передаётся копия значения. Изменение параметра внутри метода НЕ влияет на оригинал.

Для объектов: передаётся копия ссылки. Изменение полей объекта ВЛИЯЕТ на оригинал (объект один). Но переприсваивание параметра НЕ влияет на оригинал.

Практический пример: класс Student

public class Student {
    private String name;
    private int age;
    private double[] grades;
    private int gradeCount;

    public Student(String name, int age, int maxGrades) {
        this.name = name;
        this.age = age;
        this.grades = new double[maxGrades];
        this.gradeCount = 0;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public void addGrade(double grade) {
        if (grade >= 0 && grade <= 5 && gradeCount < grades.length) {
            grades[gradeCount] = grade;
            gradeCount++;
        }
    }

    public double getAverage() {
        if (gradeCount == 0) return 0;
        double sum = 0;
        for (int i = 0; i < gradeCount; i++) {
            sum += grades[i];
        }
        return sum / gradeCount;
    }

    public boolean isHonors() {
        return getAverage() >= 4.5;
    }

    public String getStatus() {
        double avg = getAverage();
        if (avg >= 4.5) return "отличник";
        if (avg >= 3.5) return "хорошист";
        if (avg >= 2.5) return "троечник";
        return "отстающий";
    }

    public void displayInfo() {
        System.out.println("Студент: " + name);
        System.out.println("Возраст: " + age);
        System.out.printf("Средний балл: %.1f%n", getAverage());
        System.out.println("Статус: " + getStatus());
    }
}
Student s1 = new Student("Алиса", 20, 5);
s1.addGrade(5);
s1.addGrade(4);
s1.addGrade(5);
s1.addGrade(4);
s1.addGrade(5);
s1.displayInfo();

Student s2 = new Student("Борис", 22, 3);
s2.addGrade(3);
s2.addGrade(4);
s2.addGrade(3);
s2.displayInfo();

Передача объектов в методы

Объекты передаются в методы по ссылке (точнее — по значению ссылки). Изменение полей объекта влияет на оригинал:

public class BankService {
    public static void transfer(BankAccount from, BankAccount to, double amount) {
        if (from.withdraw(amount)) {
            to.deposit(amount);
            System.out.println("Перевод выполнен");
        }
    }

    public static void printAccountInfo(BankAccount acc) {
        System.out.println("Счёт: " + acc.getNumber());
        System.out.printf("Баланс: %.2f%n", acc.getBalance());
    }
}

BankAccount acc1 = new BankAccount("001", 10000);
BankAccount acc2 = new BankAccount("002", 5000);

BankService.transfer(acc1, acc2, 3000);
BankService.printAccountInfo(acc1); // 7000
BankService.printAccountInfo(acc2); // 8000

Возврат объектов из методов

Методы могут возвращать объекты:

public class UserFactory {
    public static User createUser(String name, String email) {
        return new User(name, email);
    }

    public static User createGuest() {
        return new User("Гость", "guest@mail.com");
    }
}

User u1 = UserFactory.createUser("Иван", "ivan@mail.com");
User u2 = UserFactory.createGuest();

Возвращаемый тип — имя класса (User), а не void. Метод создаёт объект и возвращает ссылку на него.

Массивы объектов

Массивы могут хранить объекты:

Student[] students = new Student[3];
students[0] = new Student("Алиса", 10);
students[1] = new Student("Борис", 11);
students[2] = new Student("Варвара", 10);

for (Student s : students) {
    System.out.println(s.getName() + " — " + s.getGrade());
}

Типичные ошибки

public class Mistakes {
    private String name;

    // Ошибка 1: забыли this
    void setName(String name) {
        name = name; // Параметр присваивает себя себе!
    }

    // Правильно:
    void setNameCorrect(String name) {
        this.name = name;
    }

    // Ошибка 2: возврат из void-метода
    void getValue() {
        return 5; // Ошибка! void не должен ничего возвращать
    }

    // Ошибка 3: забыли return
    int getValue2() {
        int x = 5;
        // return забыли! Ошибка компиляции
    }

    // Правильно:
    int getValueCorrect() {
        return 5;
    }
}

Итоги урока

  • Поля хранят состояние объекта; у каждого объекта своя копия.
  • Методы определяют поведение объекта.
  • this ссылается на текущий объект (различает поле и параметр).
  • Геттеры/сеттеры обеспечивают контролируемый доступ к полям.
  • Поля имеют значения по умолчанию (0, false, null).
  • Локальные переменные обязаны быть инициализированы явно.
  • Методы могут вызывать другие методы того же объекта.
  • Следуйте соглашениям об именовании: camelCase, глаголы для методов.

Следующий урок: Конструкторы — специальные методы для инициализации объектов.

Тест: Поля и методы класса

10 вопросов

Геттеры и сеттеры

Premium