Почему строки нельзя изменять в Java и .NET?

Почему они решили сделать строку неизменной в Java и .NET(и некоторых других языках)? Почему они не сделали это изменчивым?

Ответ 1

Согласно Эффективная Java, глава 4, стр. 73, 2-е издание:

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

[...]

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

[...]

Неизменяемые объекты по сути являются потокобезопасными; они не требуют синхронизации. Они не могут быть повреждены несколькими потоками доступ к ним одновременно. Это далеко и далеко самый простой подход для обеспечения безопасности потоков. На самом деле ни одна нить не может эффект другого потока на неизменяемом объекте. Следовательно, неизменяемые объекты могут свободно использоваться

[...]

Другие небольшие точки из той же главы:

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

[...]

Неизменяемые объекты делают большие строительные блоки для других объектов, изменяемых или неизменяемых.

[...]

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

Ответ 2

Есть как минимум две причины.

Первый - безопасность http://www.javafaq.nu/java-article1060.html

Основная причина, по которой String неизменной была безопасность. Посмотри на это Пример: у нас есть метод открытия файла с проверкой входа. Мы передаем строку этот метод для обработки аутентификации который необходим перед вызовом будет передано ОС. Если String изменчивый, можно было как-то изменить его содержимое после проверка подлинности перед тем, как ОС получит запрос от программы, то это можно запросить любой файл. Так что если вы имеете право открыть текстовый файл в каталог пользователя, но затем на лету когда каким-то образом вам удастся изменить имя файла, которое вы можете запросить для открытия "passwd" или любой другой. Затем файл может быть изменен, и он будет можно войти непосредственно в ОС.

Второе - эффективность памяти http://hikrish.blogspot.com/2006/07/why-string-class-is-immutable.html

JVM внутренне поддерживает строку "String" Пул ". Чтобы получить память эффективность, JVM будет ссылаться на строку объект из пула. Он не будет создавать новые объекты String. Итак, всякий раз вы создаете новый строковый литерал, JVM проверит в пуле, будет ли он уже существует или нет. Если уже присутствовать в бассейне, просто ссылку на тот же объект или создание новый объект в пуле. Там будет много ссылок указывают на то же самое Строковые объекты, если кто-то изменяет значение, это повлияет на все Рекомендации. Итак, солнце решило сделать это неизменны.

Ответ 3

Собственно, строка причин неизменяема в java не имеет большого отношения к безопасности. Две основные причины:

Thead Safety:

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

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

Производительность:

В то время как String interning упоминается, он лишь представляет небольшой выигрыш в эффективности памяти для Java-программ. Только строковые литералы интернированы. Это означает, что только те строки, которые в исходном коде одинаковы, будут иметь один и тот же объект String. Если ваша программа динамически создает строку, которая является одинаковой, они будут представлены в разных объектах.

Что еще более важно, неизменяемые строки позволяют им делиться своими внутренними данными. Для многих операций с строками это означает, что базовый массив символов не нужно копировать. Например, скажем, вы хотите взять пять первых символов строки. В Java вы вызываете myString.substring(0,5). В этом случае метод substring() просто создает новый объект String, который разделяет myString под char [], но кто знает, что он начинается с индекса 0 и заканчивается в индексе 5 этого char []. Чтобы поместить это в графическую форму, вы получите следующее:

 |               myString                  |
 v                                         v
"The quick brown fox jumps over the lazy dog"   <-- shared char[]
 ^   ^
 |   |  myString.substring(0,5)

Это делает операции такого рода чрезвычайно дешевыми, а O (1), поскольку операция не зависит от длины исходной строки, ни от длины подстроки, которую нам нужно извлечь. Такое поведение также имеет некоторые преимущества памяти, так как многие строки могут совместно использовать свой базовый char [].

Ответ 4

Безопасность и производительность потоков. Если строка не может быть изменена, это безопасно и быстро передать ссылку вокруг нескольких потоков. Если строки были изменчивыми, вам всегда приходилось копировать все байты строки в новый экземпляр или обеспечивать синхронизацию. Типичное приложение будет читать строку 100 раз за каждый раз, когда эта строка должна быть изменена. См. Wikipedia в неизменности.

Ответ 5

Нужно действительно спросить: "Почему X должен быть изменчивым?" Лучше дефолт неизменности из-за преимуществ, уже упомянутых Princess Fluff. Это должно быть исключением, что что-то изменено.

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

Ответ 6

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

Ответ 7

Строка не является примитивным типом, но вы обычно хотите использовать ее с семантикой значения, то есть как значение.

Значение, которое вы можете доверять, не изменится за вашей спиной. Если вы пишете: String str = someExpr(); Вы не хотите, чтобы это изменилось, если вы не сделали что-то со строкой.

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

Ответ 8

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

Это совершенно новая идея о том, что неизменность String направлена ​​на проблемы с потоками. Хм... У меня есть объект, который меняется на два разных потока. Как это разрешить? синхронизировать доступ к объекту? Naawww... не позволяйте никому изменять объект вообще - это исправит все наши беспорядочные проблемы concurrency! На самом деле, пусть все объекты неизменяемы, а затем мы можем удалить синхронизированный контур с языка Java.

Настоящая причина (указанная выше) - оптимизация памяти. В любом приложении довольно часто используется один и тот же строковый литерал. На самом деле это так часто, что несколько десятилетий назад многие компиляторы сделали оптимизацию хранения только одного экземпляра строкового литерала. Недостатком этой оптимизации является то, что код времени выполнения, который модифицирует строковый литерал, представляет проблему, поскольку он модифицирует экземпляр для всего другого кода, который его разделяет. Например, было бы плохо, если бы функция в приложении могла заменить строковый литерал "собака" на "cat". Печать ( "собака" ) приведет к тому, что "кошка" будет записана в стандартный вывод. По этой причине должен существовать способ защиты от кода, который пытается изменить строковые литералы (т.е. Сделать их неизменяемыми). Некоторые компиляторы (с поддержкой ОС) выполнили бы это, поставив строковый литерал в специальный сегмент памяти readonly, который мог бы вызвать ошибку памяти, если была предпринята попытка записи.

В Java это называется интернированием. Компилятор Java здесь просто следит за стандартной оптимизацией памяти, созданной компиляторами на протяжении десятилетий. И для решения той же проблемы с этими строковыми литералами, которые изменяются во время выполнения, Java просто делает класс String неизменным (то есть, не дает вам сеттеров, которые позволяли бы изменять содержимое String). Строки не должны быть неизменными, если интернирование строковых литералов не произошло.

Ответ 9

Я знаю, что это удар, но... Действительно ли они неизменны? Рассмотрим следующее.

public static unsafe void MutableReplaceIndex(string s, char c, int i)
{
    fixed (char* ptr = s)
    {
        *((char*)(ptr + i)) = c;
    }
}

...

string s = "abc";
MutableReplaceIndex(s, '1', 0);
MutableReplaceIndex(s, '2', 1);
MutableReplaceIndex(s, '3', 2);
Console.WriteLine(s); // Prints 1 2 3

Вы даже можете сделать это методом расширения.

public static class Extensions
{
    public static unsafe void MutableReplaceIndex(this string s, char c, int i)
    {
        fixed (char* ptr = s)
        {
            *((char*)(ptr + i)) = c;
        }
    }
}

Что делает следующая работа

s.MutableReplaceIndex('1', 0);
s.MutableReplaceIndex('2', 1);
s.MutableReplaceIndex('3', 2);

Заключение: они находятся в неизменном состоянии, которое известно компилятору. Из приведенного выше правила применяются только к строкам .NET, поскольку Java не имеет указателей. Однако строка может быть полностью изменена с помощью указателей на С#. Это не то, как указатели предназначены для использования, имеет практическое применение или безопасно используется; однако это возможно, таким образом, изгибая все "изменчивое" правило. Обычно вы не можете изменять индекс непосредственно из строки, и это единственный способ. Существует способ предотвратить это путем запрещения экземпляров указателей строк или создания копии, когда указана строка, но не выполняется, что делает строки в С# не полностью неизменными.

Ответ 10

В большинстве случаев "строка" (используется/рассматривается как/считается/считается/) значимой атомной единицей, точно так же, как число.

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

Вы должны знать, почему. Подумайте об этом.

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

Мы выполняем вычисления и сравнения со строками, аналогичные тем, как мы делаем с числами. Если строки (или целые числа) являются изменяемыми, нам нужно написать специальный код, чтобы заблокировать их значения в неизменяемых локальных формах, чтобы надежно выполнить какой-либо расчет. Поэтому лучше всего думать о строке, как числовой идентификатор, но вместо того, чтобы иметь длину 16, 32 или 64 бит, это может быть длиной в сотни бит.

Когда кто-то говорит "строка", мы все думаем о разных вещах. Те, кто думают об этом просто как набор персонажей, не имея особой цели в виду, конечно, будут потрясены тем, что кто-то просто решил, что они не смогут манипулировать этими персонажами. Но класс "строка" - это не просто массив символов. Это a STRING, а не char[]. Существуют некоторые основные предположения о концепции, которую мы называем "строкой", и ее обычно можно охарактеризовать как значимую, атомную единицу кодированных данных, таких как число. Когда люди говорят о "манипулировании строками", возможно, они действительно говорят о манипулировании символами, чтобы построить строки, а StringBuilder отлично подходит для этого. Подумайте немного о том, что действительно означает слово "строка".

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

string GetPersonalInfo( string username, string password )
{
    string stored_password = DBQuery.GetPasswordFor( username );
    if (password == stored_password)
    {
        //another thread modifies the mutable 'username' string
        return DBQuery.GetPersonalInfoFor( username );
    }
}

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

Ответ 11

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

Недостатком является то, что конкатенации делают много дополнительных строк, которые являются только переходными и просто становятся мусором, что фактически наносит ущерб производительности памяти. У вас есть StringBuffer и StringBuilder (в Java, StringBuilder также в .NET), чтобы использовать для сохранения памяти в этих случаях.

Ответ 12

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

Ответ 13

Невосприимчивость не так тесно связана с безопасностью. Для этого, по крайней мере, в .NET вы получаете класс SecureString.

Ответ 14

Решение о замене строки на С++ вызывает много проблем, см. эту замечательную статью Келвина Хенни о Mad COW Disease.

COW = Copy On Write.

Ответ 15

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

Ответ 16

Неизменность хорошая. См. Эффективная Java. Если вам приходилось копировать String каждый раз, когда вы его передавали, тогда это было бы много подверженного ошибкам кода. У вас также есть путаница в отношении того, какие изменения влияют на ссылки. Точно так же, как Integer должен быть неизменным, чтобы вести себя как int, Строки должны вести себя как неизменные, чтобы действовать как примитивы. В С++ передача строк по значению делает это без явного упоминания в исходном коде.

Ответ 17

Существует исключение почти для почти каждого правила:

using System;
using System.Runtime.InteropServices;

namespace Guess
{
    class Program
    {
        static void Main(string[] args)
        {
            const string str = "ABC";

            Console.WriteLine(str);
            Console.WriteLine(str.GetHashCode());

            var handle = GCHandle.Alloc(str, GCHandleType.Pinned);

            try
            {
                Marshal.WriteInt16(handle.AddrOfPinnedObject(), 4, 'Z');

                Console.WriteLine(str);
                Console.WriteLine(str.GetHashCode());
            }
            finally
            {
                handle.Free();
            }
        }
    }
}

Ответ 18

Строка неизменна (после создания не может быть изменена). Объект, созданный как String, сохраняется в пуле константных строк. Каждый неизменяемый объект в Java является потокобезопасным, что подразумевает, что String также является потокобезопасной .String не может использоваться двумя потоками одновременно. Строка, некогда назначенная, не может быть изменена.

String  demo = " hello " ;

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

demo="Bye" ;  

//новая строка "Bye" создается в постоянном пуле и ссылается на демонстрационную переменную

//строка "hello" все еще существует в пуле строковых констант, и ее значение не переопределяется, но мы потеряли ссылку на строку "привет"