Почему класс String в Java объявлен как final?

«Почему класс String в Java объявлен как final?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Класс String объявлен final по нескольким ключевым причинам, связанным с безопасностью, производительностью и целостностью языка.

Основные причины:

  1. Гарантия неизменяемости (Immutability). String спроектирован как неизменяемый. Если бы класс не был final, можно было бы создать подкласс и добавить изменяемое состояние или переопределить методы, нарушив этот ключевой контракт.
  2. Безопасность. Строки часто используются для хранения конфиденциальных данных (пароли, ключи, URL). Наследование могло бы создать уязвимости, позволяя злонамеренному коду подменить поведение строки.
  3. Производительность и оптимизация.
    • Кеширование хеша: String кеширует значение hashCode() после первого вычисления. Если бы состояние строки могло измениться через подкласс, кеш стал бы невалидным, что сломало бы работу коллекций вроде HashMap.
    • Пул строк (String Pool): JVM полагается на неизменяемость для безопасного использования пула строк (интернирования).
    • Оптимизации JVM: Виртуальная машина может применять специальные оптимизации, точно зная поведение final-класса.
  4. Потокобезопасность. Неизменяемые объекты по своей природе потокобезопасны, что критично для широко используемого класса.

Пример риска, если бы String не был final:

// Гипотетический опасный подкласс (невозможен, т.к. String — final)
class MaliciousString extends String {
    private boolean modified = false;
    @Override
    public char charAt(int index) {
        if (!modified) {
            // Изменяем внутреннее состояние
            modified = true;
        }
        return super.charAt(index);
    }
    // Нарушена неизменяемость и потокобезопасность.
}