2010-01-15 6 views
121

С того момента, когда я узнал, что класс java.lang.String объявлен окончательным в Java, мне было интересно, почему это так? Тогда я не нашел ответа, но этот пост: How to create a replica of String class in Java? напомнил мне мой запрос.Почему класс String объявлен окончательным в Java?

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

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

+0

Спасибо всем за ваши ответы, особенно TrueWill, Бруно Рейс и Тило! Мне жаль, что я не мог выбрать более одного ответа, но, к сожалению, ...! – Alex

+1

Также рассмотрите распространение «О, мне просто нужны еще несколько методов утилиты на String», которые будут всплывать, - все из которых не могли использовать друг друга, потому что они были другим классом. –

+0

Спасибо за этот ответ, очень полезный. теперь у нас есть два факта. Строка - это конечный класс & его неизменный, потому что он не может быть изменен, но может быть передан другому объекту. но как насчет: - String a = new String ("test1"); тогда s = "test2"; Если String - это объект класса Final, то как его можно изменить? Как я могу использовать модифицированный конечный объект. Пожалуйста, позвольте мне, если я ошибочно спросил что-нибудь. –

ответ

84

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

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

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

(от here).

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

+59

Если нет связи между финальными классами и неизменяемыми объектами, которые я не вижу, Я не вижу, как ваш ответ относится к вопросу. – sepp2k

+9

Потому что, если он не является окончательным, вы можете передать StringChild для некоторого метода в качестве параметра String, и он может быть изменен (потому что изменение состояния дочернего класса). – helios

+0

Предотвращение подклассов предотвращает изменение подкласса. – Thilo

2

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

+3

+1 для «проектирования для наследования сложно». Кстати, это очень хорошо объяснено в «Эффективной Java» Блоха. – sleske

15

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

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

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

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

+6

'final' на класс не гарантирует неизменность. Это просто гарантирует, что инварианты класса (одна из которых может быть неизменностью) не может быть изменена подклассом. –

+1

@ Кевин: да. Финал в классе гарантирует отсутствие подклассов. Не имеет никакого отношения к неизменности. – Thilo

+4

Создание финала класса само по себе не делает его бескомпромиссным. Но создание неизменяемого финала класса гарантирует, что никто не делает подкласс, который нарушает неизменность. Возможно, люди, говорящие о неизменности, были неясны в том, что они имели в виду, но их утверждения правильны, когда их понимают в контексте. – Jay

6

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

  • безопасность Thread
  • безопасность
  • ворох, который управляется сам (иначе в обычную кучу, что сбор мусора в различной форме)
  • управления памятью Java

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

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

HTH

Ссылки:

+1

Я не верю, что Струны идут в другую кучу или используют другое управление памятью. Они, безусловно, собирают мусор. – Thilo

+2

Кроме того, последнее ключевое слово в классе полностью отличается от конечного ключевого слова для поля. – Thilo

+1

Хорошо, на JVM Sun, строки, которые являются intern() ed, могут перейти в perm-gen, который не является частью кучи. Но это определенно не происходит для всех строк или всего JVM. – Thilo

59

This is a nice article, который описывает две причины, уже упомянутые выше по ответам:

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

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


Расширение Строка будет играть хаос с равными и стажером. JavaDoc говорит равен:

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

Предполагая, что java.lang.String не был окончательным, a SafeString мог равняться 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>, и все, что вам нужно будет ввести в пул, будет новым загрузчиком классов.

+2

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

2

В дополнение к причинам, упомянутым в других ответах (безопасность, неизменность, производительность), следует отметить, что 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. 
1

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

+1

По-моему, String никогда не ведет себя как примитивный тип. Строковый литерал, такой как «java», на самом деле является объектом класса String (вы можете использовать на нем оператор точки сразу после заключительной цитаты). Поэтому присваивание литерала строковой переменной просто назначает ссылки на объекты как обычно. Другое дело в том, что класс String поддерживает языковой уровень, встроенный в компилятор ... превращая вещи в двойные кавычки в объекты String и оператор +, как упоминалось выше. – Georgie

1

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

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

22

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

Если бы строка была измененной или окончательной, запрос на загрузку «java.io.Writer» мог быть изменен для загрузки «mil.vogoon».DiskErasingWriter»

ссылка:.. Why String is immutable in Java

1

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

2

С большим количеством полезных моментов, которые уже были рассмотрены, я хотел бы добавить еще один - одна из причин того, почему String неизменна в Java - разрешить 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; 
} 

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

1

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

0

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

+0

Просьба уточнить ваш ответ. –

0

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

0

Большинство ответов связано с неизменяемостью - почему объект типа 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 

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

Смежные вопросы