1. Обзор
Проще говоря, прежде чем мы сможем работать с объектом в JVM, он должен быть инициализирован.
В следующих разделах мы рассмотрим различные способы инициализации примитивных типов и объектов.
2. Декларация против Инициализация
Давайте начнем с того, что убедимся, что мы на одной странице.
Объявление-это процесс определения переменной вместе с ее типом и именем.
Здесь мы объявляем переменную id :
int id;
Инициализация, с другой стороны, заключается в присвоении значения; например:
id = 1;
Чтобы продемонстрировать это, мы создадим класс User с именем и id свойствами:
public class User { private String name; private int id; // standard constructor, getters, setters, }
Далее мы увидим, что инициализация работает по-разному в зависимости от типа поля, которое мы инициализируем.
3. Объекты против примитивов
Java предоставляет два типа представления данных: примитивные типы и ссылочные типы. В этом разделе мы обсудим различия между ними в отношении инициализации.
Java имеет восемь встроенных типов данных, называемых примитивными типами Java; переменные этого типа содержат свои значения напрямую.
Ссылочные типы содержат ссылки на объекты (экземпляры классов). В отличие от примитивных типов, которые хранят свои значения в памяти, где выделена переменная, ссылки не содержат значения объекта, на который они ссылаются.
Вместо этого ссылка указывает на объект, сохраняя адрес памяти, в котором находится объект.
Обратите внимание, что Java не позволяет нам узнать, что такое адрес физической памяти. Скорее, мы можем использовать ссылку только для ссылки на объект.
Давайте рассмотрим пример, который объявляет и инициализирует ссылочный тип из нашего класса User :
@Test public void whenIntializedWithNew_thenInstanceIsNotNull() { User user = new User(); assertThat(user).isNotNull(); }
Как мы видим здесь, ссылка может быть назначена новому с помощью ключевого слова new, которое отвечает за создание нового объекта User .
Давайте продолжим изучать больше о создании объектов.
5. Создание объектов
В отличие от примитивов, создание объектов немного сложнее. Это происходит потому, что мы не просто добавляем значение в поле; вместо этого мы запускаем инициализацию с помощью ключевого слова new . Это, в свою очередь, вызывает конструктор и инициализирует объект в памяти.
Давайте обсудим конструкторы и ключевое слово new более подробно.
Ключевое слово new отвечает за выделение памяти для нового объекта с помощью конструктора.
Конструктор обычно используется для инициализации переменных экземпляра, представляющих основные свойства созданного объекта .
Если мы не предоставим конструктор явно, компилятор создаст конструктор по умолчанию, который не имеет аргументов и просто выделяет память для объекта.
Класс может иметь много конструкторов, если их списки параметров различны ( перегрузка ) . Каждый конструктор, который не вызывает другой конструктор в том же классе, имеет вызов своего родительского конструктора, независимо от того, был ли он написан явно или вставлен компилятором через super() .
Давайте добавим конструктор в наш класс User :
public User(String name, int id) { this.name = name; this.id = id; }
Теперь мы можем использовать наш конструктор для создания объекта User с начальными значениями его свойств:
User user = new User("Alice", 1);
6. Переменная Область действия
В следующих разделах мы рассмотрим различные типы областей, в которых может существовать переменная в Java, и как это влияет на процесс инициализации.
6.1. Переменные экземпляра и класса
Переменные экземпляра и класса не требуют от нас их инициализации. Как только мы объявляем эти переменные, им присваивается значение по умолчанию следующим образом:
Теперь давайте попробуем определить некоторые переменные, связанные с экземплярами и классами, и проверить, имеют ли они значение по умолчанию или нет:
@Test public void whenValuesAreNotInitialized_thenUserNameAndIdReturnDefault() { User user = new User(); assertThat(user.getName()).isNull(); assertThat(user.getId() == 0); }
6.2. Локальные переменные
Локальные переменные должны быть инициализированы перед использованием , так как они не имеют значения по умолчанию, и компилятор не позволит нам использовать неинициализированное значение.
Например, следующий код генерирует ошибку компилятора:
public void print(){ int i; System.out.println(i); }
7. Последнее Ключевое Слово
Ключевое слово final , примененное к полю, означает, что значение поля больше не может быть изменено после инициализации. Таким образом, мы можем определить константы в Java.
Давайте добавим константу в наш класс User :
private static final int YEAR = 2000;
Константы должны быть инициализированы либо при их объявлении, либо в конструкторе.
8. Инициализаторы в Java
В Java инициализатор – это блок кода, который не имеет связанного имени или типа данных и помещается вне любого метода, конструктора или другого блока кода.
Java предлагает два типа инициализаторов: статические и инициализаторы экземпляров. Давайте посмотрим, как мы можем использовать каждый из них.
8.1. Инициализаторы экземпляров
Мы можем использовать их для инициализации переменных экземпляра.
Чтобы продемонстрировать, давайте предоставим значение для пользователя id с помощью инициализатора экземпляра в нашем классе User :
{ id = 0; }
8.2. Блок Статической Инициализации
Статический инициализатор или статический блок – это блок кода, который используется для инициализации статических полей. Другими словами, это простой инициализатор, отмеченный ключевым словом static:
private static String forum; static { forum = "Java"; }
9. Порядок инициализации
При написании кода, который инициализирует различные типы полей, конечно, мы должны следить за порядком инициализации.
В Java порядок операторов инициализации выглядит следующим образом:
- статические переменные и статические инициализаторы по порядку
- переменные экземпляра и инициализаторы экземпляра по порядку
- конструкторы
10. Жизненный цикл объекта
Теперь, когда мы научились объявлять и инициализировать объекты, давайте выясним, что происходит с объектами, когда они не используются.
В отличие от других языков, где нам приходится беспокоиться об уничтожении объектов, Java заботится об устаревших объектах через свой сборщик мусора.
Все объекты в Java хранятся в кучной памяти нашей программы . На самом деле куча представляет собой большой пул неиспользуемой памяти, выделенный для нашего Java-приложения.
С другой стороны, сборщик мусора – это программа Java, которая заботится об автоматическом управлении памятью путем удаления объектов, которые больше недоступны.
Чтобы объект Java стал недоступным, он должен столкнуться с одной из следующих ситуаций:
- У объекта больше нет ссылок, указывающих на него
- Все ссылки, указывающие на объект, находятся вне области видимости
В заключение, объект сначала создается из класса, обычно используя ключевое слово new. Тогда объект живет своей жизнью и предоставляет нам доступ к своим методам и полям.
Наконец, когда он больше не нужен, сборщик мусора уничтожает его.
11. Другие методы создания объектов
В этом разделе мы кратко рассмотрим методы , отличные от ключевого слова new , для создания объектов и их применения, в частности отражение, клонирование и сериализацию .
Отражение-это механизм, который мы можем использовать для проверки классов, полей и методов во время выполнения. Вот пример создания нашего Пользователя объекта с помощью отражения:
@Test public void whenInitializedWithReflection_thenInstanceIsNotNull() throws Exception { User user = User.class.getConstructor(String.class, int.class) .newInstance("Alice", 2); assertThat(user).isNotNull(); }
В этом случае мы используем отражение для поиска и вызова конструктора класса User .
Следующий метод, клонирование, – это способ создания точной копии объекта. Для этого наш класс User должен реализовать интерфейс Cloneable :
public class User implements Cloneable { //... }
Теперь мы можем использовать метод clone() для создания нового клонированного пользователя объекта, который имеет те же значения свойств, что и объект user :
@Test public void whenCopiedWithClone_thenExactMatchIsCreated() throws CloneNotSupportedException { User user = new User("Alice", 3); User clonedUser = (User) user.clone(); assertThat(clonedUser).isEqualTo(user); }
Мы также можем использовать Мы также можем использовать класс для выделения памяти для объекта без вызова конструктора:
User u = (User) unsafeInstance.allocateInstance(User.class);
12. Заключение
В этом уроке мы рассмотрели инициализацию полей в Java. Мы обнаружили различные типы данных в Java и способы их использования. Мы также подробно рассмотрели несколько способов создания объектов в Java.
Полную реализацию этого руководства можно найти на Github .