Когда я узнал, что класс java.lang.String
объявлен в Java как final, мне стало интересно, почему это так. Тогда я не нашел ответа, но этот пост: Как создать реплику класса String в Java? напомнил мне о моем вопросе.
Конечно, String предоставляет все функции, которые мне когда-либо были нужны, и я никогда не думал о какой-либо операции, которая потребовала бы расширения класса String, но все же вы никогда не узнаете, что кому-то может понадобиться!
Итак, кто-нибудь знает, в чем заключались намерения дизайнеров, когда они решили сделать его окончательным?
Ответы:
Очень полезно реализовывать строки как неизменяемые объекты . Вы должны прочитать о неизменности, чтобы понять о ней больше.
Одно из преимуществ неизменяемых объектов состоит в том, что
( отсюда ).
Если бы String не был final, вы могли бы создать подкласс и иметь две строки, которые выглядят одинаково, когда «рассматриваются как строки», но на самом деле они разные.
источник
Это хорошая статья, в которой изложены две причины, уже упомянутые в ответах выше:
И это, наверное, самый подробный комментарий в той статье. Это связано с пулом строк в Java и проблемами безопасности. Речь идет о том, как решить, что входит в пул строк. Предполагая, что обе строки равны, если их последовательность символов одинакова, тогда у нас есть условие гонки на то, кто попадет первым, а также проблемы безопасности. Если нет, то пул строк будет содержать избыточные строки, что приведет к потере преимущества его наличия. Просто прочти это для себя, а?
Расширение String нанесло бы ущерб равным и стажерам. JavaDoc говорит, что равно:
Предполагая, что
java.lang.String
не является окончательным, aSafeString
может быть равно aString
, и наоборот; потому что они будут представлять одну и ту же последовательность символов.Что произойдет, если вы подадите заявку
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>
и все, что вам нужно, чтобы войти в пул, - это новый загрузчик классов.источник
Абсолютно самая важная причина того, что String является неизменяемым или окончательным, заключается в том, что он используется механизмом загрузки классов и, следовательно, имеет глубокие и фундаментальные аспекты безопасности.
Если бы String была изменяемой или не окончательной, запрос на загрузку "java.io.Writer" мог быть изменен на загрузку "mil.vogoon.DiskErasingWriter"
ссылка: Почему String неизменяема в Java
источник
String
- это очень базовый класс в Java, многие вещи полагаются на его работу определенным образом, например, неизменность.Создание класса
final
предотвращает подклассы, которые могут нарушить эти предположения.Обратите внимание, что даже сейчас, если вы используете отражение, вы можете разбить строки (изменить их значение или хэш-код). Отражение можно остановить с помощью диспетчера безопасности. Если бы
String
не былоfinal
, каждый мог бы это сделать.Другие классы, которые не объявлены,
final
позволяют вам определять несколько сломанные подклассы (например, у вас может быть a,List
который добавляет неправильную позицию), но, по крайней мере, JVM не зависит от подклассов для своих основных операций.источник
final
на классе не гарантирует неизменяемость. Это просто гарантирует, что инварианты класса (один из которых может быть неизменяемым) не могут быть изменены подклассом.Как сказал Бруно, дело в неизменности. Речь идет не только о строках, но и о любых обертках, например, Double, Integer, Character и т. Д. Для этого есть много причин:
По сути, вы, как программист, можете быть уверены, что ваша строка никогда не будет изменена. Это также, если вы знаете, как это работает, может улучшить управление памятью. Попробуйте создать одну за другой две одинаковые строки, например «привет». Если вы отлаживаете, вы заметите, что у них одинаковые идентификаторы, это означает, что они в точности ОДИНАКОВЫЕ объекты. Это связано с тем, что Java позволяет вам это делать. Это было бы невозможно, если бы строки были изменяемыми. У них может быть то же самое, что и я, и т.д., потому что они никогда не изменятся. Итак, если вы когда-нибудь решите создать 1 000 000 строк "hello", то на самом деле вам нужно создать 1 000 000 указателей на "hello". Кроме того, объединение любой функции в строке или любых оболочек по этой причине приведет к созданию другого объекта (снова посмотрите на идентификатор объекта - он изменится).
Дополнительно final в Java не обязательно означает, что объект не может быть изменен (это отличается, например, от C ++). Это означает, что адрес, на который он указывает, не может измениться, но вы все равно можете изменить его свойства и / или атрибуты. Поэтому понимание разницы между неизменяемостью и окончанием в некоторых случаях может быть действительно важным.
HTH
Ссылки:
источник
Возможно, это было сделано для упрощения реализации. Если вы создаете класс, который будет унаследован пользователями этого класса, у вас появится целый новый набор вариантов использования, которые следует учитывать в своем дизайне. Что произойдет, если они сделают то или это с защищенным полем X? В завершение они могут сосредоточиться на правильной работе общедоступного интерфейса и убедиться в его надежности.
источник
Имея уже много хороших моментов, я хотел бы добавить еще один - одна из причин того, почему String неизменяема в Java, заключается в том, чтобы позволить String кэшировать свой хэш-код , будучи неизменным String в 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; }
Если это уже компьютер, просто верните значение.
источник
Чтобы убедиться, что мы не получим лучшую реализацию. Конечно, это должен был быть интерфейс.
[править] Ах, получить больше невежественных голосов против. Ответ совершенно серьезный. Мне приходилось несколько раз обходить глупую реализацию String, что приводило к серьезной потере производительности и производительности.
источник
Помимо очевидных причин, предложенных в других ответах, одна мысль о создании класса String также может быть связана с накладными расходами на производительность виртуальных методов. Помните, что String - это тяжелый класс, поэтому этот окончательный вариант означает, что нет никакой суб-реализации, означает отсутствие накладных расходов на косвенный вызов. Конечно, теперь у нас есть такие вещи, как виртуальный вызов и другие, которые всегда делают такую оптимизацию за вас.
источник
В дополнение к причинам, указанным в других ответах (безопасность, неизменяемость, производительность), следует отметить, что
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.
источник
Что ж, у меня другая мысль. Я не уверен, прав ли я, но в Java String - единственный объект, который можно рассматривать как примитивный тип данных, я имею в виду, что мы можем создать объект String как String name = "java " . Теперь, как и другие примитивные типы данных, которые копируются по значению, а не копируются по ссылке, ожидается, что String будет иметь такое же поведение, поэтому String является окончательным. Вот что я подумал. Пожалуйста, игнорируйте, если это совершенно нелогично.
источник
Окончательность струнных также защищает их как стандарт. В C ++ вы можете создавать подклассы строки, поэтому каждый магазин программирования может иметь свою собственную версию строки. Это привело бы к отсутствию строгого стандарта.
источник
Допустим, у вас есть
Employee
класс, у которого есть методgreet
. Когдаgreet
метод вызывается, он просто печатаетHello everyone!
. Так что это ожидаемое поведение поgreet
методуpublic class Employee { void greet() { System.out.println("Hello everyone!"); } }
Теперь позвольте
GrumpyEmployee
подклассуEmployee
и переопределитьgreet
метод, как показано ниже.public class GrumpyEmployee extends Employee { @Override void greet() { System.out.println("Get lost!"); } }
Теперь в приведенном ниже коде посмотрите на
sayHello
метод. Он принимаетEmployee
экземпляр в качестве параметра и вызывает метод приветствия, надеясь, что он скажетHello everyone!
Но что мы получаемGet lost!
. Это изменение в поведении связано сEmployee grumpyEmployee = new GrumpyEmployee();
public class TestFinal { static Employee grumpyEmployee = new GrumpyEmployee(); public static void main(String[] args) { TestFinal testFinal = new TestFinal(); testFinal.sayHello(grumpyEmployee); } private void sayHello(Employee employee) { employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!" } }
Этой ситуации можно избежать, если
Employee
класс был сделанfinal
. Теперь вам остается только представить, какой хаос может вызвать дерзкий программист, еслиString
Class не будет объявлен какfinal
.источник
Знает ли JVM, что является неизменным? Ответ: Нет. Постоянный пул содержит все неизменяемые поля, но все неизменяемые поля / объекты не хранятся только в постоянном пуле. Только мы реализуем его таким образом, чтобы обеспечить неизменность и свои особенности. CustomString можно реализовать, не делая его окончательным, с помощью MarkerInterface, который обеспечит java особое поведение для его объединения, функция все еще ожидается!
источник
Большинство ответов связано с неизменностью - почему объект типа String не может быть обновлен на месте. Здесь много хороших дискуссий, и сообществу Java следовало бы принять неизменность в качестве принципа. (Не затаив дыхание.)
Однако вопрос OP заключается в том, почему он окончательный - почему его нельзя продлить. Некоторые здесь взяли на себя это, но я согласен с OP, что здесь есть настоящий пробел. Другой язык позволяет разработчикам создавать новые номинальные типы для типа. Например, в Haskell я могу создать следующие новые типы, которые во время выполнения идентичны тексту, но обеспечивают безопасность привязки во время компиляции.
Поэтому я бы выдвинул следующее предложение как усовершенствование языка 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)
источник
Если вы создадите строку один раз, он будет считать, что это объект, если вы хотите его изменить, это невозможно, он создаст новый объект.
источник