Приветствую, коллеги. Решил показать несколько способов инициализации полей в Java. Статья может быть полезна тем, кто только начал изучать программирование.
Если нужен короткий ответ, способов три. Значение можно присвоить прямо при объявлении поля, и тогда оно будет одинаковым у всех объектов класса. Можно принимать его в конструкторе, и тогда каждый объект получит свое. А можно вынести общую для всех конструкторов подготовку в блок инициализации. Если не сделать ни того ни другого, поле все равно не останется пустым: Java подставит значение по умолчанию, у ссылочных типов это
null, у чисел ноль, у
boolean это
false.
Кратко
- Неинициализированное поле получает значение по умолчанию:
null у ссылочных типов, 0 у целых, 0.0 у дробных, false у boolean.
- Первый способ задать значение это присвоить его прямо при объявлении поля. Значение будет одним и тем же у всех объектов.
- Второй способ это конструктор с параметрами: каждый объект получает свои значения в момент создания.
- Имена параметров конструктора обычно совпадают с именами полей, поэтому к полям внутри обращаются через
this.
- Третий способ это блок инициализации, фигурные скобки прямо в теле класса. Он выполняется при каждом создании объекта, до тела конструктора.
- Как только вы объявили хотя бы один конструктор, конструктор без параметров перестает создаваться сам собой. Нужен он, придется написать его руками.
- С локальными переменными все иначе: значений по умолчанию у них нет, и компилятор потребует присвоить значение до первого использования.
![Ломтики хлеба, на которых лазерным гравером выжжена надпись HELLO WORLD]()
Для запуска демонстрационных примеров создадим Java класс
Demo с методом
main(). В методе
main() выведем что-нибудь на экран, чтобы убедиться, что программа запускается:
public class Demo {
public static void main(String[] args) {
System.out.println("Hello JavaRush.");
}
}
Что будет, если поле не инициализировать
Далее создадим Java класс кота, добавим в него несколько полей: имя, возраст.
class Cat {
String name;
int age;
}
В методе
main() создадим двух котов. Попробуем вывести на экран их имя и возраст:
public class Demo {
public static void main(String[] args) {
Cat cat1 = new Cat();
Cat cat2 = new Cat();
System.out.println("Имя первого кота: " + cat1.name);
System.out.println("Возраст первого кота: " + cat1.age);
System.out.println();
System.out.println("Имя второго кота: " + cat2.name);
System.out.println("Возраст второго кота: " + cat2.age);
}
}
class Cat {
String name;
int age;
}
Запустив программу, получим вывод:
Имя первого кота: null
Возраст первого кота: 0
Имя второго кота: null
Возраст второго кота: 0
Так как мы не инициализировали эти поля, они получили значения по умолчанию.
String относится к ссылочным типам, а для всех ссылочных типов значение по умолчанию это
null.
int относится к
примитивным типам. У каждого примитивного типа есть свое значение по умолчанию, и все они перечислены в спецификации языка, в разделе
Initial Values of Variables:
| Тип |
Значение по умолчанию |
byte, short, int, long |
0 |
float, double |
0.0 |
char |
'\u0000', символ с нулевым кодом |
boolean |
false |
Любой ссылочный тип: String, массивы, ваши собственные классы |
null |
Важная оговорка: все сказанное относится именно к полям. У локальных переменных, объявленных внутри метода, значений по умолчанию нет вовсе. Спецификация в том же разделе требует, чтобы локальной переменной явно присвоили значение до первого использования, иначе программа просто не скомпилируется.
Как можно передать в наши поля реальные значения (например, имя "Том" и возраст "два года")?
Способ 1: инициализация при объявлении
Самый короткий путь это присвоить значение прямо там, где поле объявлено:
public class Demo {
public static void main(String[] args) {
Cat cat1 = new Cat();
Cat cat2 = new Cat();
System.out.println("Имя первого кота: " + cat1.name);
System.out.println("Возраст первого кота: " + cat1.age);
System.out.println();
System.out.println("Имя второго кота: " + cat2.name);
System.out.println("Возраст второго кота: " + cat2.age);
}
}
class Cat {
String name = "Том";
int age = 2;
}
Запускаем программу и видим, что поля инициализированы нашими значениями:
Имя первого кота: Том
Возраст первого кота: 2
Имя второго кота: Том
Возраст второго кота: 2
При этом сколько бы котов мы ни создавали, они все будут иметь одинаковые имя и возраст. То есть мы просто поменяли значения null и 0 на “Том” и 2.
Способ 2: инициализация через конструктор
Создадим коту конструктор с двумя параметрами. Обычно имена параметров делают такими же, как и имена полей. В таком случае для обращения к полям нужно использовать
слово this, а к параметрам конструктора можно обращаться обычным способом:
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
Еще раз:
Добавим также пустой конструктор без параметров, чтобы наш предыдущий пример был работоспособным. Причина вот в чем: пока в классе не объявлено ни одного конструктора, Java дописывает конструктор без параметров сама, это
конструктор по умолчанию. Но стоит объявить свой, хотя бы один, и бесплатный вариант исчезает. Поэтому вызов
new Cat() после добавления конструктора с параметрами перестал бы компилироваться, и пустой конструктор приходится писать руками. Подробнее про них есть
отдельная статья.
В методе
main() добавим еще двух котов с помощью конструктора с параметрами.
public class Demo {
public static void main(String[] args) {
Cat cat1 = new Cat();
Cat cat2 = new Cat();
System.out.println("Имя первого кота: " + cat1.name);
System.out.println("Возраст первого кота: " + cat1.age);
System.out.println();
System.out.println("Имя второго кота: " + cat2.name);
System.out.println("Возраст второго кота: " + cat2.age);
Cat cat3 = new Cat("Рыжик", 5);
Cat cat4 = new Cat("Барсик", 9);
System.out.println();
System.out.println("Имя третьего кота: " + cat3.name);
System.out.println("Возраст третьего кота: " + cat3.age);
System.out.println();
System.out.println("Имя четвертого кота: " + cat4.name);
System.out.println("Возраст четвертого кота: " + cat4.age);
}
}
class Cat {
String name;
int age;
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public Cat() {
}
}
Запустим программу:
Имя первого кота: null
Возраст первого кота: 0
Имя второго кота: null
Возраст второго кота: 0
Имя третьего кота: Рыжик
Возраст третьего кота: 5
Имя четвертого кота: Барсик
Возраст четвертого кота: 9
Как видим, созданные таким способом коты обладают большей индивидуальностью.
Способ 3: блок инициализации
Есть и третий способ, о котором новички обычно узнают позже. Прямо в теле класса можно поставить фигурные скобки и написать внутри любой код. Такой блок называется блоком инициализации и выполняется при каждом создании объекта:
public class Demo {
public static void main(String[] args) {
Cat cat5 = new Cat();
Cat cat6 = new Cat("Мурзик", 3);
System.out.println("Имя пятого кота: " + cat5.name);
System.out.println("Возраст пятого кота: " + cat5.age);
System.out.println();
System.out.println("Имя шестого кота: " + cat6.name);
System.out.println("Возраст шестого кота: " + cat6.age);
}
}
class Cat {
String name;
int age;
{
name = "Безымянный";
age = 1;
}
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public Cat() {
}
}
Запустим программу:
Имя пятого кота: Безымянный
Возраст пятого кота: 1
Имя шестого кота: Мурзик
Возраст шестого кота: 3
Пятый кот получил имя из блока, а шестому конструктор перезаписал значения своими. Так и работает порядок: сначала выполняются блоки инициализации, потом тело конструктора.
Спецификация описывает это как отдельный шаг создания объекта, который идет сразу после вызова конструктора родителя и до остального кода конструктора.
Зачем это нужно, если есть конструктор? Затем, что конструкторов у класса может быть несколько, и повторять в каждом одну и ту же подготовку неудобно. Общую часть выносят в блок, и она отработает при любом способе создания объекта.
У блока есть и статический вариант, со словом
static перед скобками. Он выполняется один раз, когда класс загружается, и нужен для статических полей, значение которых не помещается в одно выражение:
import java.util.Arrays;
class Cat {
static String[] breeds;
static {
breeds = new String[]{"Сиамская", "Британская", "Мейн-кун"};
Arrays.sort(breeds);
}
}
Массив тут сначала создается, а потом сортируется, двумя действиями. Присваиванием при объявлении так не сделать, а в блоке можно.
Существуют и другие способы инициализации полей в Java, но для начала достаточно и этих.
Вопросы и ответы
Какое значение у поля, если его не инициализировать?
Поле не остается пустым, ему автоматически присваивается значение по умолчанию. У всех ссылочных типов, включая String, это null. У byte, short, int и long это ноль, у float и double ноль с дробной частью, у char символ с нулевым кодом, у boolean false.
Чем инициализация при объявлении отличается от инициализации в конструкторе?
Значением, которое получат объекты. Если написать значение прямо в объявлении поля, оно будет одинаковым у всех создаваемых объектов. Конструктор с параметрами позволяет передать свои значения каждому объекту в момент создания.
Зачем в конструкторе пишут this.name = name?
Потому что имя параметра совпадает с именем поля. Без this компилятор поймет обе части как параметр, и присваивание получится бессмысленным: параметр присвоится сам себе, а поле останется со значением по умолчанию. Слово this явно указывает, что слева стоит поле текущего объекта.
Почему после добавления конструктора с параметрами перестал работать new Cat()?
Потому что конструктор без параметров создается автоматически только до тех пор, пока в классе не объявлен ни один свой конструктор. Как только вы написали конструктор с параметрами, бесплатный вариант пропадает. Если он нужен, его добавляют вручную, пустым телом.
Есть ли значения по умолчанию у локальных переменных?
Нет. Правило про значения по умолчанию действует только для полей класса, элементов массива и статических переменных. Локальную переменную внутри метода нужно явно проинициализировать до первого использования, иначе компилятор откажется собирать программу.
Можно ли задать значение поля после создания объекта?
Да, обычным присваиванием: cat1.name = "Том". Так делают, когда значение становится известно позже создания объекта. В реальном коде поля обычно закрывают модификатором private и меняют через методы-сеттеры, но механика та же.
Чем блок инициализации отличается от конструктора?
Блок инициализации это просто фигурные скобки в теле класса, у него нет ни имени, ни параметров, поэтому передать в него значения снаружи нельзя. Он выполняется при каждом создании объекта и всегда до тела конструктора. Смысл в том, чтобы не повторять общую подготовку в нескольких конструкторах сразу. Со словом static блок выполняется один раз, при загрузке класса, и работает со статическими полями.
Читайте также
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ