Рубрики
Без рубрики

Руководство по конструкторам в Java

Изучите основы конструкторов в Java, а также некоторые дополнительные советы

Автор оригинала: baeldung.

1. введение

Конструкторы-это привратники объектно-ориентированного проектирования .

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

Давайте продолжим и создадим простой объект, представляющий банковский счет.

2. Открытие банковского счета

Представьте, что нам нужно создать класс, представляющий банковский счет. Он будет содержать Имя, Дату создания и Баланс.

Кроме того, давайте переопределим метод toString , чтобы распечатать сведения на консоли:

class BankAccount {
    String name;
    LocalDateTime opened;
    double balance;
    
    @Override
    public String toString() {
        return String.format("%s, %s, %f", 
          this.name, this.opened.toString(), this.balance);
    }
}

Теперь этот класс содержит все необходимые поля, необходимые для хранения информации о банковском счете, но он еще не содержит конструктора.

Это означает, что если мы создадим новый объект, значения полей не будут инициализированы:

BankAccount account = new BankAccount();
account.toString();

Выполнение метода toString выше приведет к исключению, поскольку объекты name и open по-прежнему null :

java.lang.NullPointerException
    at com.baeldung.constructors.BankAccount.toString(BankAccount.java:12)
    at com.baeldung.constructors.ConstructorUnitTest
      .givenNoExplicitContructor_whenUsed_thenFails(ConstructorUnitTest.java:23)

3. Конструктор Без Аргументов

Давайте исправим это с помощью конструктора:

class BankAccount {
    public BankAccount() {
        this.name = "";
        this.opened = LocalDateTime.now();
        this.balance = 0.0d;
    }
}

Обратите внимание на несколько вещей о конструкторе, которые мы только что написали. Во-первых, это метод, но он не имеет возвращаемого типа. Это происходит потому, что конструктор неявно возвращает тип создаваемого им объекта. Вызов new Bank Account() теперь вызовет конструктор выше.

Во-вторых, это не требует аргументов. Этот конкретный вид конструктора называется конструктором n o-аргументов .

Но почему нам это не понадобилось в первый раз? Это потому, что, когда мы явно не пишем какой-либо конструктор, компилятор добавляет конструктор по умолчанию без аргументов .

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

Для объектов это null, что привело к исключению, которое мы видели ранее.

4. Параметризованный Конструктор

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

Итак, чтобы сделать что-то действительно полезное с этим банковским счетом, нам нужно иметь возможность фактически ввести некоторые начальные значения в объект.

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

class BankAccount {
    public BankAccount() { ... }
    public BankAccount(String name, LocalDateTime opened, double balance) {
        this.name = name;
        this.opened = opened;
        this.balance = balance;
    }
}

Теперь мы можем сделать что-то полезное с вашим Банковским счетом классом:

    LocalDateTime opened = LocalDateTime.of(2018, Month.JUNE, 29, 06, 30, 00);
    BankAccount account = new BankAccount("Tom", opened, 1000.0f); 
    account.toString();

Обратите внимание, что в нашем классе теперь есть 2 конструктора. Явный конструктор без аргументов и параметризованный конструктор.

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

Если мы обнаружим слишком много конструкторов в нашем коде, несколько шаблонов творческого проектирования могут быть полезны.

5. Конструктор Копирования

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

Новый счет должен иметь то же имя, что и старый счет, сегодняшнюю дату создания и отсутствие средств. Мы можем сделать это с помощью конструктора копирования :

public BankAccount(BankAccount other) {
    this.name = other.name;
    this.opened = LocalDateTime.now();
    this.balance = 0.0f;
}

Теперь у нас есть следующее поведение:

LocalDateTime opened = LocalDateTime.of(2018, Month.JUNE, 29, 06, 30, 00);
BankAccount account = new BankAccount("Tim", opened, 1000.0f);
BankAccount newAccount = new BankAccount(account);

assertThat(account.getName()).isEqualTo(newAccount.getName());
assertThat(account.getOpened()).isNotEqualTo(newAccount.getOpened());
assertThat(newAccount.getBalance()).isEqualTo(0.0f);

6. Цепной конструктор

Конечно, мы можем вывести некоторые параметры конструктора или дать некоторые из них значения по умолчанию.

Например, мы могли бы просто создать новый банковский счет только с именем.

Итак, давайте создадим конструктор с параметром name и дадим другим параметрам значения по умолчанию:

public BankAccount(String name, LocalDateTime opened, double balance) {
    this.name = name;
    this.opened = opened;
    this.balance = balance;
}
public BankAccount(String name) {
    this(name, LocalDateTime.now(), 0.0f);
}

С помощью ключевого слова this, мы вызываем другой конструктор.

Мы должны помнить, что если мы хотим связать конструктор суперкласса, мы должны использовать super вместо this .

Кроме того, помните, что это или супер выражение всегда должно быть первым оператором.

7. Типы значений

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

То есть объект неизменен . Неизменяемость в Java немного нюансирована , и при создании объектов следует соблюдать осторожность.

Давайте продолжим и создадим неизменяемый класс:

class Transaction {
    final BankAccount bankAccount;
    final LocalDateTime date;
    final double amount;

    public Transaction(BankAccount account, LocalDateTime date, double amount) {
        this.bankAccount = account;
        this.date = date;
        this.amount = amount;
    }
}

Обратите внимание, что теперь мы используем ключевое слово final при определении членов класса. Это означает, что каждый из этих членов может быть инициализирован только в конструкторе класса. Они не могут быть переназначены позже в любом другом методе. Мы можем читать эти значения, но не изменять их.

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

8. Заключение

Мы провели экскурсию по различным способам, которыми конструкторы строят объекты. При разумном использовании конструкции образуют основные строительные блоки объектно-ориентированного проектирования в Java.

Как всегда, примеры кода можно найти на GitHub.