Зачем конструктор абстрактному классу

Абстрактный класс нельзя создать напрямую, но его часть есть в каждом объекте-наследнике. Конструктор устанавливает инварианты этой части до выполнения конструктора наследника.

abstract class Job {
    private final UUID id;
 
    protected Job(UUID id) {
        this.id = Objects.requireNonNull(id);
    }
 
    public final UUID id() {
        return id;
    }
 
    abstract void execute();
}
 
final class ExportJob extends Job {
    private final Path target;
 
    ExportJob(UUID id, Path target) {
        super(id);
        this.target = Objects.requireNonNull(target);
    }
 
    @Override
    void execute() {
        // экспорт
    }
}

Конструктор — не обычный метод: у него нет возвращаемого типа, он не наследуется и не переопределяется. Если конструктор не объявлен, компилятор создаёт конструктор без аргументов с доступом как у класса. Как только объявлен любой конструктор, автоматический исчезает.

Порядок

new ExportJob(...) выделяет один объект. Затем вызывается цепочка конструкторов от Object вниз. Сначала завершается родительская инициализация, после неё — поля и тело конструктора наследника.

Обычно конструктор абстрактного класса делают protected: его вызывают наследники, но он не является публичной фабрикой. private уместен, если разрешённые наследники вложены в сам класс и создание полностью контролируется.

Опасность

Не вызывай из конструктора переопределяемый метод:

abstract class Broken {
    Broken() {
        initialize(); // поля наследника ещё не присвоены
    }
    abstract void initialize();
}

На собеседовании

Если abstract class нельзя создать, зачем ему конструктор? Для инициализации родительской части экземпляра наследника.

Наследуется ли конструктор? Нет.

Когда компилятор создаёт default constructor? Только если класс не объявил ни одного конструктора.

Можно ли сделать конструктор abstract? Нет.

Связано: Абстрактные классы, super java, Блоки инициализации.