Атрибуты класса Java (Поля)
Объявление полей экземпляра в классе Java, установка начальных значений и доступ к ним через объекты.
Атрибуты класса — это переменные, объявленные внутри него: данные, которые несёт каждый объект. Большинство авторов называют их полями (именно так принято в Спецификации языка Java), а «атрибут» — более общий термин ООП; они обозначают одно и то же. Эта глава посвящена их объявлению, начальным значениям и способам чтения и записи извне класса.
Объявление поля
Поле выглядит как локальная переменная, но располагается в теле класса, а не внутри метода:
public class Book {
String title;
String author;
int pages;
boolean inPrint;
}Каждый экземпляр Book получает собственную копию этих четырёх полей. У двух книг — две отдельные строки title, два отдельных числа pages и так далее. Поля существуют столько же, сколько сам объект: до тех пор, пока на него нет ни одной ссылки.
Значения по умолчанию
Если поле не инициализировано, Java сделает это за вас. Значения по умолчанию зависят от типа:
| Тип | Значение по умолчанию |
|---|---|
byte, short, int, long | 0 |
float, double | 0.0 |
char | '�' (нулевой символ, code point 0 — не пробел) |
boolean | false |
| любой объектный тип | null |
Book b = new Book();
System.out.println(b.title); // null
System.out.println(b.pages); // 0
System.out.println(b.inPrint); // falseЭто одно из ключевых отличий полей от локальных переменных внутри методов: локальные переменные не получают значений по умолчанию, и компилятор не позволит прочитать такую переменную до того, как ей присвоено значение. Поля всегда начинают с чего-то определённого.
Встроенные инициализаторы
Полю можно задать значение прямо при объявлении:
public class Book {
String title = "Untitled";
int pages = 0;
boolean inPrint = true;
}Инициализатор выполняется один раз при создании объекта, в порядке объявления, до тела конструктора. Если для поля требуется более сложная настройка — это задача конструктора.
Чтение и запись полей
Снаружи класса используется синтаксис <ссылка>.<поле>, а внутри — просто <имяПоля> (или this.<имяПоля>):
Book b = new Book();
b.title = "Effective Java"; // write
String t = b.title; // readВнутри класса:
public class Book {
String title;
void rename(String t) {
title = t; // same as this.title = t;
}
}Глава ключевое слово this рассказывает, когда требуется явный квалификатор this., а когда он не нужен.
Соглашения об именовании
Имена полей Java записываются в стиле lowerCamelCase:
String firstName; // good
String first_name; // not Java style
String FirstName; // looks like a classДля boolean-полей часто используются префиксы is или has (isPublished, hasIndex), что делает код более читаемым: if (book.isPublished). Константы — поля, значение которых не меняется — записываются в UPPER_SNAKE_CASE и помечаются static final; это рассматривается в главах static и final.
Поля экземпляра и поля класса
По умолчанию поля принадлежат каждому экземпляру — у каждого объекта своя копия. Если добавить static, поле будет принадлежать самому классу: существует ровно одна копия, общая для всех экземпляров:
public class Counter {
int count; // instance — each Counter has its own
static int total; // class — one shared by all Counters
}
Counter a = new Counter();
Counter b = new Counter();
a.count++; b.count++; b.count++;
Counter.total++;
System.out.println(a.count); // 1
System.out.println(b.count); // 2
System.out.println(Counter.total); // 1, accessed through the classПодробности — в главе static; пока считайте, что каждое поле является полем экземпляра, если явно не помечено static.
Публичные поля — почти всегда ошибка
Java позволяет написать public String title; и дать внешнему коду прямой доступ к изменению поля. На практике так почти никогда не делают. Как только поле становится публичным, вы теряете возможность проверять записываемые значения, менять тип поля в будущем или добавлять логику при чтении — любой, кто обращается к полю, жёстко привязан к его текущему представлению.
Стандартный подход — пометить поля как private и открыть доступ через геттеры и сеттеры:
public class Book {
private String title;
public String getTitle() { return title; }
public void setTitle(String t) { this.title = t; }
}Это называется инкапсуляцией. Главы encapsulation и getters & setters подробно описывают этот паттерн. В ранних примерах мы показываем поля напрямую для простоты, но в реальном Java-коде они всегда private.
Поля хранят ссылки, а не объекты
Когда тип поля — класс (а не примитив), поле является ссылкой. Два объекта могут ссылаться на один и тот же объект через свои поля:
public class Author { String name; }
public class Book { Author author; }
Author a = new Author();
a.name = "Joshua Bloch";
Book b1 = new Book(); b1.author = a;
Book b2 = new Book(); b2.author = a; // same Author object
a.name = "J. Bloch";
System.out.println(b1.author.name); // J. Bloch
System.out.println(b2.author.name); // J. BlochВсе предупреждения из предыдущей главы остаются в силе: иногда это именно то, что нужно, а иногда — источник ошибок. Если у каждой книги должен быть свой Author, нужно присваивать каждой new Author().
Практический пример
Что дальше
Поля — это состояние объекта. Вторая составляющая — его поведение — описывается методами, объявленными в классе. Следующая глава о методах класса показывает, как прикреплять методы к экземплярам и как они получают доступ к определённым вами полям.