Модификаторы без доступа в Java
Модификаторы без доступа в Java — static, final, abstract, synchronized, transient, volatile — и что каждый из них контролирует.
Модификаторы доступа Java — public, protected, private — управляют тем, кто может видеть член класса. Модификаторы без доступа управляют тем, как он ведёт себя. Это небольшой набор ключевых слов, изменяющих принадлежность, изменяемость, работу с потоками, сериализацию и кое-что ещё. Эта глава является картой; каждый модификатор подробнее рассмотрен в отдельной главе или разделе.
Полный список
| Модификатор | Применяется к | Что делает |
|---|---|---|
static | поля, методы, вложенные классы, блоки | Принадлежит классу, а не экземплярам |
final | классы, методы, поля, параметры, локальные переменные | Нельзя переприсвоить/переопределить/расширить |
abstract | классы, методы | Нет тела / нельзя инстанциировать; должен быть реализован подклассом |
synchronized | методы, блоки | Только один поток одновременно может выполнять его на заданной блокировке |
volatile | поля | Чтения/записи не кешируются в локальной памяти потока |
transient | поля | Пропускается при сериализации по умолчанию |
native | методы | Реализация находится в коде не на Java (обычно C/C++) |
strictfp | классы, методы | Принудительное строгое поведение IEEE-754 для чисел с плавающей точкой (в основном историческое) |
default | методы интерфейса | Предоставляет тело по умолчанию в интерфейсе |
sealed / non-sealed | классы, интерфейсы | Ограничивает, какие классы могут расширять (Java 17+) |
Вы будете постоянно встречать static, final, abstract и default. Остальные встречаются только тогда, когда возникает соответствующая задача.
static — принадлежит классу
Член static ассоциирован с самим классом, а не с каким-либо конкретным экземпляром:
public class Counter {
int instanceCount; // one per Counter object
static int classCount; // one shared by everyone
}static настолько распространён, что для него есть отдельная глава — см. java-static.
final — нельзя изменить
final означает «эта привязка зафиксирована после установки». Он применяется к нескольким разным вещам:
final int MAX = 100; // local variable cannot be reassigned
public final class Money {} // class cannot be extended
public final void close() {} // method cannot be overridden
private final int balance; // field assigned once, then immutablefinal является основой констант (static final), неизменяемых объектов и безопасного проектирования наследования. Подробно рассмотрен в java-final.
abstract — нет тела, необходимо заполнить
abstract, применённый к классу, означает «нельзя инстанциировать этот класс напрямую — только подклассы». Применённый к методу, означает «тела нет — каждый конкретный подкласс должен предоставить своё»:
public abstract class Shape {
public abstract double area(); // no body
}
public class Circle extends Shape {
double r;
public double area() { return Math.PI * r * r; }
}new Shape() — ошибка компиляции; new Circle() работает. Рассмотрено в абстрактных классах.
synchronized — один поток за раз
Когда два потока могут вызвать один и тот же метод на одном и том же объекте, пометка метода как synchronized гарантирует, что в один момент времени выполняется только один:
public synchronized void deposit(int amount) {
balance += amount;
}Это простейшая форма блокировки. В книге есть целый раздел про конкурентность; пока достаточно знать, что такое ключевое слово существует и что оно в целом делает. Полная информация в synchronization.
volatile — видимость между потоками
Без volatile потоки могут кешировать значение поля. Операции чтения в одном потоке могут никогда не увидеть записи из другого:
private volatile boolean stopped = false;volatile заставляет каждое чтение обращаться к основной памяти, а каждую запись — идти в основную память. Это лёгкий аналог synchronized для простых полей-флагов — подробности о модели памяти в volatile.
transient — пропустить при сериализации
Встроенная сериализация объектов Java (Serializable) по умолчанию записывает каждое поле. transient говорит «не включать это» — обычно используется для кешей, вычисленных значений или вещей, которые не имеют смысла за пределами запущенной программы:
public class Session {
String userId;
transient String passwordHash; // not serialized
}Современный код использует JSON-сериализаторы чаще, чем Serializable, но ключевое слово по-прежнему полезно в библиотеках, которые его поддерживают. Подробнее в serialization.
native — реализован в другом месте
native предназначен для методов, тело которых написано на другом языке (C/C++ через JNI). Вы редко пишете их сами; они встречаются в низкоуровневых библиотеках:
public native int currentTimeMillis();strictfp — строгая арифметика с плавающей точкой
Изначально strictfp обеспечивал предсказуемую арифметику IEEE-754 для чисел с плавающей точкой на разных платформах. Начиная с Java 17, вся арифметика с плавающей точкой является неявно строгой, что делает ключевое слово бездействующим. Его можно в основном игнорировать; оно встречается в старых кодовых базах.
default — в методах интерфейса
Внутри интерфейса default позволяет предоставить тело метода вместо того, чтобы оставлять его абстрактным:
public interface Greeter {
default String greet(String name) {
return "Hello, " + name;
}
}Без default метод интерфейса не имеет тела, и каждый реализующий класс должен написать своё. Подробно рассмотрено в default methods.
sealed и non-sealed
Класс sealed задаёт точный список классов, которым разрешено его расширять. Подклассы должны затем выбрать: быть final, sealed или non-sealed:
public sealed class Shape permits Circle, Square { }
public final class Circle extends Shape { }
public non-sealed class Square extends Shape { }Полезно для закрытых иерархий типов — см. sealed classes.
Комбинирование модификаторов
Обычно модификаторы можно свободно объединять в следующем принятом порядке:
public static final int MAX = 100;
private static volatile int counter;
protected abstract void onInit();Некоторые комбинации недопустимы: метод не может быть одновременно abstract и final, одновременно abstract и private, или одновременно abstract и static. Компилятор сообщит, когда вы нарушите правило.
Рабочий пример
Что дальше
Следующие две главы подробно рассматривают два наиболее часто используемых модификатора: static для членов уровня класса и final для неизменяемости. Читайте их по порядку.