Перейти к содержанию

Руководство по созданию объектов в Java

Прежде чем сможем работать с объектом в JVM, его необходимо инициализировать.

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

Объявление против инициализации

Начнем с того, что убедимся, что мы на одной странице.

Объявление – это процесс определения переменной вместе с ее типом и именем. Здесь объявляем переменную id:

int id;

Инициализация заключается в присвоении значения:

id = 1;

Для демонстрации создадим класс User со свойствами name и id:

public class User {
    private String name;
    private int id;
    
    // стандартный конструктор, геттеры, сеттеры
}

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

Объекты против примитивов

Java предоставляет два типа представления данных: примитивные типы и ссылочные типы. В этом разделе обсудим различия между ними в отношении инициализации.

Java имеет восемь встроенных типов данных, называемых примитивными типами Java; переменные этого типа хранят свои значения напрямую.

Ссылочные типы содержат ссылки на объекты (экземпляры классов). В отличие от примитивных типов, которые хранят свои значения в памяти, где размещена переменная, ссылки не содержат значения объекта, на который они ссылаются.

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

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

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

@Test
public void whenIntializedWithNew_thenInstanceIsNotNull() {
    User user = new User();
 
    assertThat(user).isNotNull();
}

Ключевое слово new отвечает за создание нового объекта User.

Создание объектов

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

Обсудим конструкторы и ключевое слово new более подробно.

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

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

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

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

Добавим конструктор в класс User:

public User(String name, int id) {
    this.name = name;
    this.id = id;
}

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

User user = new User("Алиса", 1);

Область действия переменной

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

Переменные экземпляра и класса

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

ТипЗначение по умолчанию
booleanfalse
byte, short, int, long0
float, double0.0
char‘\u0000’
ссылочный типnull

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

@Test
public void whenValuesAreNotInitialized_thenUserNameAndIdReturnDefault() {
    User user = new User();
 
    assertThat(user.getName()).isNull();
    assertThat(user.getId() == 0);
}

Локальные переменные

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

Например, следующий код генерирует ошибку компилятора:

public void print(){
    int i;
    System.out.println(i);
}

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

Ключевое слово final, примененное к полю, означает, что значение поля больше не может быть изменено после инициализации. Таким образом, можно определить константы в Java.

Добавим константу в класс User:

private static final int YEAR = 2000;

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

Инициализаторы в Java

В Java инициализатор – это блок кода, который не имеет связанного имени или типа данных и размещается вне любого метода, конструктора или другого блока кода.

Java предлагает два типа инициализаторов: статические и инициализаторы экземпляра.

Посмотрим, как можно использовать каждый из них.

Инициализаторы экземпляра

Можно использовать их для инициализации переменных экземпляра.

Чтобы продемонстрировать, предоставим значение для id пользователя, используя инициализатор экземпляра в классе User:

{
    id = 0;
}

Статический блок инициализации

Статический инициализатор или статический блок – это блок кода, который используется для инициализации статических полей. Другими словами, это простой инициализатор, помеченный ключевым словом static:

private static String forum;
static {
    forum = "Java";
}

Порядок инициализации

При написании кода, который инициализирует различные типы полей необходимо следить за порядком инициализации.

В Java порядок операторов инициализации следующий:

  • статические переменные и статические инициализаторы;
  • переменные экземпляра и инициализаторы экземпляра;
  • конструкторы.

Жизненный цикл объекта

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

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

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

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

Чтобы объект Java стал недоступным, он должен столкнуться с одной из следующих ситуаций:

  • объект больше не имеет ссылок, указывающих на него;
  • все ссылки, указывающие на объект, выходят за рамки видимости.

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

Наконец, когда он больше не нужен, сборщик мусора уничтожает его.

Другие способы создания объектов

В этом разделе кратко рассмотрим способы, отличные от ключевого слова new, для создания объектов и варианты их применения, в частности, рефлексию, клонирование и сериализацию.

Рефлексия – это механизм, который можно использовать для проверки классов, полей и методов во время выполнения. Ниже пример создания объекта User с использованием рефлексии:

@Test
public void whenInitializedWithReflection_thenInstanceIsNotNull() 
  throws Exception {
    User user = User.class.getConstructor(String.class, int.class)
      .newInstance("Алиса", 2);
 
    assertThat(user).isNotNull();
}

В этом случае используем рефлексию, чтобы найти и вызвать конструктор класса User.

Следующий метод – клонирование – это способ создания точной копии объекта. Для этого класс User должен реализовать интерфейс Cloneable:

public class User implements Cloneable { //... }

Теперь можно использовать метод clone() для создания нового объекта clonedUser, который имеет те же значения свойств, что и объект пользователя:

@Test
public void whenCopiedWithClone_thenExactMatchIsCreated() 
  throws CloneNotSupportedException {
    User user = new User("Алиса", 3);
    User clonedUser = (User) user.clone();
 
    assertThat(clonedUser).isEqualTo(user);
}

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

User u = (User) unsafeInstance.allocateInstance(User.class);

Заключение

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

Полную реализацию этого руководства можно найти на Github.

Оригинал