Подтвердить что ты не робот

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

Когда я узнал, что класс java.lang.String объявлен окончательным в Java, мне было интересно, почему это? Тогда я не нашел ответа, но этот пост: Как создать реплика класса String в Java? напомнил мне мой запрос.

Конечно, String предоставляет все функции, которые мне когда-либо нужны, и никогда не думал о какой-либо операции, которая потребует расширения класса String, но вы никогда не узнаете, что кому-то понадобится!

Итак, кто-нибудь знает, что было целью дизайнеров, когда они решили сделать это окончательным?

4b9b3361

Ответ 1

Очень полезно иметь строки, реализованные как неизменяемые объекты. Вы должны прочитать о неизменяемости, чтобы больше узнать об этом.

Одним из преимуществ неизменяемых объектов является то, что

Вы можете делиться дубликатами, указывая их на один экземпляр.

(из здесь).

Если String не была окончательной, вы могли бы создать подкласс и иметь две строки, которые выглядят одинаково, когда они "видны как строки", но это на самом деле разные.

Ответ 2

Это хорошая статья, в которой излагаются две причины, упомянутые выше:

  • Безопасность: система может раздавать чувствительные биты только для чтения информации, не беспокоясь о том, что они будут изменены
  • Производительность: неизменные данные очень полезно для создания потокобезопасности.

И это, вероятно, самый подробный комментарий в этой статье. Это связано с пулом строк в Java и проблемами безопасности. О том, как решить, что входит в пул строк. Предполагая, что обе строки равны, если их последовательность символов одинакова, тогда у нас есть условие гонки, кто первым попадает туда и вместе с ним проблемы с безопасностью. Если нет, то пул строк будет содержать избыточные строки, тем самым теряя преимущество наличия в первую очередь. Просто прочитайте это для себя, да?


Расширение String приведет к хаосу с помощью equals и intern. JavaDoc говорит равно:

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

Предполагая, что java.lang.String не является окончательным, a SafeString может равняться a String и наоборот; потому что они будут представлять одну и ту же последовательность символов.

Что произойдет, если вы примените intern к SafeString - будет ли SafeString перейти в пул строк JVM? ClassLoader и все объекты, на которые ссылаются SafeString, затем будут заблокированы на время существования JVM. Вы получите условие гонки, которое может быть первым, кто ставит последовательность символов - возможно, ваш победитель будет SafeString, возможно, String или, возможно, SafeString, загруженный другим загрузчиком классов (таким образом, другой класс).

Если вы выиграли гонку в пул, это будет настоящий синглтон, и люди смогут получить доступ ко всей вашей среде (песочнице) через отражение и secretKey.intern().getClass().getClassLoader().

Или JVM может заблокировать это отверстие, убедившись, что в пул добавлены только конкретные объекты String (и никакие подклассы).

Если были реализованы равенства, то SafeString!= String, затем SafeString.intern!= String.intern, а SafeString необходимо добавить в пул. Тогда пул станет пулом <Class, String> вместо <String>, и все, что вам нужно будет ввести в пул, будет новым загрузчиком классов.

Ответ 3

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

Если String была изменчивой или окончательной, запрос на загрузку "java.io.Writer" можно было бы изменить, чтобы загрузить "mil.vogoon.DiskErasingWriter"

ссылка: Почему String неизменна в Java

Ответ 4

String - очень основной класс в Java, многие вещи полагаются на него, работая определенным образом, например, будучи неизменяемым.

Создание класса final предотвращает подклассы, которые могут нарушить эти допущения.

Обратите внимание, что даже теперь, если вы используете отражение, вы можете разбить строки (изменить их значение или хэш-код). Отражение можно остановить с помощью диспетчера безопасности. Если String не был final, все могли это сделать.

Другие классы, которые не объявлены final, позволяют определить несколько разбитых подклассов (например, вы могли бы добавить List, который добавляется в неправильную позицию), но, по крайней мере, JVM не зависит от того, что для его ядра операции.

Ответ 5

Как сказал Бруно о неизменности. Это касается не только строк, но и любых оболочек, например. Double, Integer, Character и т.д. Существует много причин для этого:

  • Безопасность потока
  • Безопасность
  • Куча, которой управляет сама Java (по-разному, с обычной кучей, которая является мусором, собранным по-разному)
  • Управление памятью

В основном это так, что вы, как программист, можете быть уверены, что ваша строка никогда не будет изменена. Это также, если вы знаете, как это работает, может улучшить управление памятью. Попробуйте создать две одинаковые строки один за другим, например "hello". Вы заметите, что если вы отлаживаете, что у них одинаковые идентификаторы, это означает, что они являются ТОЧНЫМИ ИСКАМИ. Это связано с тем, что Java позволяет вам это делать. Это было бы невозможно, если бы строки были неповрежденными. Они могут иметь то же самое, что и я, и т.д., Потому что они никогда не изменятся. Поэтому, если вы когда-либо решаете создать 1 000 000 строк "привет", то, что вы действительно делаете, это создать 1 000 000 указателей на "привет". Кроме того, всякая функция в строке или любые обертки по этой причине приведут к созданию другого объекта (снова посмотрите на идентификатор объекта - он изменится).

Aditionally final в Java не обязательно означает, что объект не может измениться (он отличается от С++). Это означает, что адрес, на который он указывает, не может измениться, но вы все равно можете изменить его свойства и/или атрибуты. Поэтому понимание разницы между неизменностью и окончательным в некоторых случаях может быть действительно важным.

НТН

Литература:

Ответ 6

Возможно, это было для упрощения реализации. Если вы создадите класс, который будет наследоваться пользователями класса, тогда у вас есть целый новый набор вариантов использования, чтобы рассмотреть ваш дизайн. Что произойдет, если они будут делать то или это с X-защищенным полем? Сделав это окончательно, они могут сосредоточиться на правильном использовании открытого интерфейса и убедиться, что он прочный.

Ответ 7

В дополнение к причинам, упомянутым в других ответах (безопасность, неизменность, производительность), следует отметить, что String имеет специальную языковую поддержку. Вы можете написать литералы String и поддерживать оператор +. Предоставление программистам подкласса String, поощряло бы такие хаки, как:

class MyComplex extends String { ... }

MyComplex a = new MyComplex("5+3i");
MyComplex b = new MyComplex("7+4i");
MyComplex c = new MyComplex(a + b);   // would work since a and b are strings,
                                      // and a string + a string is a string.

Ответ 8

С большим количеством полезных моментов, которые уже были учтены, я хотел бы добавить еще один - одна из причин того, почему String неизменна в Java - разрешить String кэшировать свой хэш-код, будучи неизменяемым. String in Java кэширует свой хэш-код, а не вычисляет каждый раз, когда мы вызываем метод hashcode String, что делает его очень быстрым, поскольку ключ hashmap используется в hashmap в Java.

Короче, потому что String неизменен, никто не может изменить его содержимое после создания, которое гарантирует, что hashCode String будет одинаковым при множественном вызове.

Если вы видите, что String класс объявлен как

/** Cache the hash code for the string */
private int hash; // Default to 0

и hashcode() функция следующая:

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

Если это уже компьютер, просто верните значение.

Ответ 9

Ну, у меня есть разные мысли, я не уверен, верю я или нет, но в Java String - единственный объект, который можно рассматривать как примитивный тип данных, а я имею в виду, что мы можем создать объект String как String name= "java" . Теперь, как и другие примитивные типы данных, которые копируются по значению не копировать по ссылке. Ожидается, что String будет иметь такое же поведение, поэтому, почему String является окончательным. То, что я думал об этом. Пожалуйста, проигнорируйте, если это нелогично.

Ответ 10

Чтобы убедиться, что мы не получим лучшую реализацию. Разумеется, это был интерфейс.

[править] Ах, получив больше невежественных голосов. Ответ совершенно серьезный. Мне пришлось несколько раз прокладывать себе путь вокруг глупой реализации String, что привело к серьезной производительности и снижению производительности

Ответ 11

Конечность строк также защищает их как стандарт. В С++ вы можете создавать подклассы строки, поэтому каждый магазин программирования может иметь собственную версию строки. Это приведет к отсутствию сильного стандарта.

Ответ 12

Помимо очевидных причин, предложенных в других ответах, одна мысль о создании финального класса String также может быть связана с накладными расходами на виртуальные методы. Помните, что String - это тяжелый класс, что делает его окончательным, означает, что нет необходимости в суб-реализации, означает, что накладные расходы не будут называться. Конечно, теперь у нас есть такие вещи, как virtual invoke и другие, которые всегда делают для вас такую ​​оптимизацию.

Ответ 13

Если вы создаете строку один раз. Она будет считать, что это объект, если вы хотите изменить это, это невозможно, он создаст новый объект.

Ответ 14

Знает ли JVM то, что является неизменным? Ответ: Нет. Постоянный пул содержит все неизменяемые поля, но все неизменяемые поля/объекты не сохраняются только в постоянном пуле. Только мы реализуем ее таким образом, чтобы она достигала невосприимчивости и ее особенностей. CustomString может быть реализована без окончательного использования с помощью MarkerInterface, которая обеспечивала бы java-специфическое поведение для его объединения, эта функция все еще ждет!

Ответ 15

Большинство ответов связаны с неизменяемостью - почему объект типа String не может быть обновлен. Здесь есть много хороших дискуссий, и сообщество Java должно было бы принять неизменность в качестве принципала. (Не сдерживая дыхание.)

Однако вопрос OP о том, почему он окончательный, - почему он не может быть расширен. Некоторые здесь это делали, но я согласен с OP, что здесь существует реальный разрыв. Другие языки позволяют разработчикам создавать новые номинальные типы для типа. Например, в Haskell я могу создать следующие новые типы, идентичные во время выполнения как текст, но обеспечивающие безопасность привязки во время компиляции.

newtype AccountCode = AccountCode Text
newtype FundCode = FundCode Text

Поэтому я бы предложил следующее предложение в качестве улучшения для языка Java:

newtype AccountCode of String;
newtype FundCode of String;

AccountCode acctCode = "099876";
FundCode fundCode = "099876";

acctCode.equals(fundCode);  // evaluates to false;
acctCode.toString().equals(fundCode.toString());  // evaluates to true;

acctCode=fundCode;  // compile error
getAccount(fundCode);  // compile error

(Или, может быть, мы могли бы начать отвлекаться от Java)