Ответ
Да, абстрактный класс может и часто должен иметь конструкторы. Хотя экземпляр абстрактного класса создать напрямую нельзя (new AbstractClass() — ошибка компиляции), его конструкторы вызываются при создании экземпляров конкретных классов-наследников.
Для чего это нужно? Конструкторы абстрактного класса используются для инициализации общего состояния (полей), которое есть у всех наследников.
Пример:
abstract class Vehicle {
private final String id; // Общее поле для всех транспортных средств
private int fuelLevel;
// Конструктор абстрактного класса
public Vehicle(String id, int initialFuel) {
this.id = id;
this.fuelLevel = initialFuel;
}
public abstract void startEngine();
// Общий метод, использующий инициализированные поля
public String getInfo() {
return "Vehicle ID: " + id + ", Fuel: " + fuelLevel + "%";
}
}
class Car extends Vehicle {
private int doors;
// Конструктор наследника ОБЯЗАН вызвать super(...)
public Car(String id, int initialFuel, int doors) {
super(id, initialFuel); // Вызов конструктора родительского абстрактного класса
this.doors = doors;
}
@Override
public void startEngine() {
System.out.println("Car engine started.");
}
}
Ключевые правила:
- Конструктор абстрактного класса не может быть объявлен как
abstractилиfinal. - Он может иметь любой модификатор доступа (
public,protected, package-private). - Если в абстрактном классе не объявлен ни один конструктор, компилятор автоматически создаст конструктор по умолчанию (
public AbstractClass() {}).
Ответ 18+ 🔞
Да ты посмотри, какая история интересная вырисовывается! Абстрактный класс, блядь, и конструкторы у него... Ну, казалось бы, зачем, если его самого-то и создать нельзя? А вот поди ж ты, оказывается, без них нихуя не работает!
Представь себе, что абстрактный класс — это как чертёж, ёпта, или, там, устав в армии. Сам по себе устав — это не солдат, его в строй не поставишь. Но когда появляется новый салага (то есть наследник), его, сука, ОБЯЗАНЫ по этому уставу обучить и выдать казённые портянки! Вот эти портянки — это и есть общие поля. А конструктор — это тот самый старшина, который эти портянки и выдаёт.
Смотри, как это выглядит в коде, тут всё чётко:
abstract class Vehicle {
private final String id; // Номерной знак, штука общая
private int fuelLevel; // Бензин, тоже всем нужно
// А вот и наш "старшина"-конструктор! Не абстрактный, а самый что ни на есть реальный.
public Vehicle(String id, int initialFuel) {
this.id = id;
this.fuelLevel = initialFuel;
}
public abstract void startEngine(); // А вот это уже абстрактное, пусть наследники сами думают, как заводиться
// А это общая бумажка-отчёт, которая уже может пользоваться выданными "портянками"
public String getInfo() {
return "Vehicle ID: " + id + ", Fuel: " + fuelLevel + "%";
}
}
И вот появляется конкретный ублюдок-наследник:
class Car extends Vehicle {
private int doors; // А это уже его личная приблуда
// Конструктор наследника. И он ПЕРВЫМ ДЕЛОМ, блядь, должен крикнуть "Так точно, старшина!" — то есть вызвать super().
public Car(String id, int initialFuel, int doors) {
super(id, initialFuel); // Смотри сюда! Передаём данные для инита общих полей.
this.doors = doors; // А потом уже свои дела устраиваем.
}
@Override
public void startEngine() {
System.out.println("Дрын-дрын-дрын, завелась тачка.");
}
}
А если в абстрактном классе конструктора не написать? Да хуй с ним, компилятор сам, тихонько так, подсуетит пустышку public Vehicle() {}. Но это только если тебе вообще не нужно ничего инитить на уровне родителя. А так-то обычно нужно, ёпта.
Короче, суть в чём: конструктор в абстрактном классе — это не для себя, любимого. Это, блядь, контракт для всех своих детей. Мол, «хочешь быть моим наследником — сначала проинициализируй то, что я считаю важным, а потом уже делай что хочешь». Без этого вызова super(...) компилятор тебе мозги выест, и будет прав, пидарас шерстяной.
Так что да, может и должен. Иначе как он общее состояние-то настроит? Волшебной палочкой, что ли?