Как примечание к другим ответам. Вы можете использовать финал. На практике я редко вижу людей, использующих его, и я не уверен, почему.
Большая часть кода, который я пишу в эти дни, предназначена для многопоточных сред, и я стремлюсь сделать класс final неизменным (если он является классом значений), чтобы обеспечить его безопасность для потоков.
Проблема с маркировкой некоторых методов как окончательных (а не других) заключается в том, что вы заявляете, что в этом методе есть что-то особенное, а в других нет ничего особенного. Это редко, что люди на самом деле значат в моем опыте.
Если класс предназначен для наследования, вам нужно содержать его в чистоте и держать его небольшим, чтобы предотвратить нежелательные побочные эффекты. Все это зависит от того, пишете ли вы код для себя и своей команды или пишете для более широкой аудитории - то есть общедоступного API-интерфейса для проекта с открытым исходным кодом или коммерческого проекта.