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



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



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



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

761   16  

16 ответов:

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

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

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

(от здесь).

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

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

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

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


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

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

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

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

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

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

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

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

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

ссылки : почему строка является неизменяемой в Java

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

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

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

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

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

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

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

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

HTH

ссылки:

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

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

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

если вы видите 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, будет поощрять хаки, такие как:

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". Теперь, как и другие примитивные типы данных, которые являются копировать по значению не скопировать ссылки строка, как ожидается, будет иметь такое же поведение, поэтому строка является окончательной. Вот что я подумал об этом. Пожалуйста, игнорируйте, если его полностью нелогичный.

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

чтобы убедиться, что мы не получим лучшего применения. Конечно, это должен был быть интерфейс.

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

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

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

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

однако вопрос 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)

Допустим, у вас есть 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 экземпляр в качестве параметра и вызывает метод greet надеясь, что он скажет 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 класс не был объявлен как final.

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

Comments

    Ничего не найдено.