Как реализована неизменность string
Иммутабельность в Java
Привет, Хабр. В преддверии скорого старта курса «Подготовка к сертификации Oracle Java Programmer (OCAJP)» подготовили для вас традиционный перевод материала.
Приглашаем также всех желающих поучаствовать в открытом демо-уроке «Конструкторы и блоки инициализации». На этом бесплатном вебинаре мы:
— Разберём конструктор на запчасти
— Определим финалистов (финальные переменные)
— Наведём порядок (инициализации)
Иммутабельный (неизменяемый, immutable) класс — это класс, который после инициализации не может изменить свое состояние. То есть если в коде есть ссылка на экземпляр иммутабельного класса, то любые изменения в нем приводят к созданию нового экземпляра.
Чтобы класс был иммутабельным, он должен соответствовать следующим требованиям:
Должен быть объявлен как final, чтобы от него нельзя было наследоваться. Иначе дочерние классы могут нарушить иммутабельность.
Все поля класса должны быть приватными в соответствии с принципами инкапсуляции.
Для корректного создания экземпляра в нем должны быть параметризованные конструкторы, через которые осуществляется первоначальная инициализация полей класса.
Для исключения возможности изменения состояния после инстанцирования, в классе не должно быть сеттеров.
Для полей-коллекций необходимо делать глубокие копии, чтобы гарантировать их неизменность.
Иммутабельность в действии
Начнем со следующего класса, который, на первый взгляд, выглядит иммутабельным:
Теперь посмотрим на него в действии.
Очевидно, что мы хотим запретить добавление элементов в коллекцию, поскольку это изменение состояния объекта, то есть отсутствие иммутабельности.
Хотя использование иммутабельных объектов дает преимущества, но их использование не всегда оправдано. Обычно нам нужно как создавать объекты, так и модифицировать их для отражения изменений, происходящих в системе.
То есть нам нужно изменять данные, и нелогично создавать новые объекты при каждом изменении, так как это увеличивает используемую память, а мы хотим разрабатывать эффективные приложения и оптимально использовать ресурсы системы.
Иммутабельность строк в Java
Например, в классе String есть методы для получения символов, выделения подстрок, поиска, замены и многие другие. Как и другие классы-обертки в Java (Integer, Boolean и т.д.), класс String является иммутабельным.
Иммутабельность строк дает следующие преимущества:
Для строк можно использовать специальную область памяти, называемую «пул строк». Благодаря которой две разные переменные типа String с одинаковым значением будут указывать на одну и ту же область памяти.
Строки отличный кандидат для ключей в коллекциях, поскольку они не могут быть изменены по ошибке.
Чувствительные данные, такие как имена пользователей и пароли, нельзя изменить по ошибке во время выполнения, даже при передаче ссылок на них между разными методами.
Java String. Почему строки в Java неизменные и финализированные?
Строки в Java не являются примитивным типом данных, как например int или double. Первым делом String – это класс, прописанный в пакете java.lang и представляющий строковый набор символов. String — наиболее широко используемый класс в Java. Трудно себе представить приложение, не используемое строки.
Особенности класса String в Java заключаются в том, что строки это неизменяемый (immutable) и финализированный тип данных, и возможности хранения всех объектов класса String в пуле строк. Так же к особенностям можно отнести возможность получения объектов класса String используя двойные кавычки и перегруженный оператор “+” для сцепления(конкатенации) строк.
Так почему же строки в Java неизменяемые? При ответе на этот вопрос также становится понятно, являются ли строки в Java потокобезопасными, и почему строка является популярным ключем в HashMap?
У неизменности строк есть ряд неоспоримых преимуществ:
Если резюмировать вышесказанное, то получаем, что основные причины неизменяемости String в Java это безопасность и наличие пула строк (String pool).
Если нужно изменять строки то, есть StringBuffer/StringBuilder. (Чем они отличаются?)
Русские Блоги
Подробное объяснение неизменности String в Java
Почему String неизменяем?
1. Что такое неизменный объект?
Как мы все знаем, в Java класс String неизменен. Так что же такое неизменный объект? Подумайте об этом так: если объект не может изменить свое состояние после создания, значит, объект неизменен. Невозможность изменить состояние означает, что переменные-члены в объекте не могут быть изменены, в том числе значение базового типа данных не может быть изменено, переменная ссылочного типа не может указывать на другие объекты, а состояние объекта, на который указывает ссылочный тип, не может быть изменено.
2. Различать объекты и ссылки на объекты
Новички в Java всегда сомневаются в том, что String является неизменяемым объектом. Взгляните на следующий код:
3. Почему объект String неизменяем?
Чтобы понять неизменность String, сначала посмотрите, какие переменные-члены находятся в классе String. В JDK1.6 переменные-члены String имеют следующее:
В JDK1.7 класс String внес некоторые изменения, в основном для изменения поведения метода подстроки во время выполнения, что не имеет отношения к теме данной статьи. В JDK1.7 есть только две основные переменные-члены класса String:
Затем в String, очевидно, есть несколько методов, их вызов может получить измененное значение. Эти методы включают substring, replace, replaceAll, toLowerCase и т. Д. Например, такой код:
4. Действительно ли объект String неизменяем?
Из вышеизложенного видно, что переменные-члены String являются закрытыми окончательными, то есть их нельзя изменить после инициализации. Среди этих членов значение является особенным, потому что это ссылочная переменная, а не реальный объект. Значение final изменяется, что означает, что final больше не может указывать на другие объекты массива. Могу ли я изменить массив, на который указывает значение? Например, измените символ в определенной позиции в массиве на подчеркивание «_». По крайней мере, это невозможно сделать в обычном коде, написанном нами самими, потому что мы вообще не можем получить доступ к ссылке на значение, не говоря уже об изменении массива с помощью этой ссылки.
Итак, как мне получить доступ к закрытым членам? Да, с помощью отражения вы можете отразить атрибут value в объекте String, а затем изменить структуру массива с помощью ссылки на полученное значение. Вот пример кода:
В этом процессе s всегда относится к одному и тому же объекту String, но до и после отражения объект String изменился, то есть так называемый «неизменяемый» объект может быть изменен посредством отражения. Но обычно мы этого не делаем. Этот пример отражения также может проиллюстрировать проблему: если объект может изменять состояние других объектов, которые он объединяет, то этот объект, вероятно, не является неизменяемым объектом. Например, объект Car объединяет объект Wheel.Хотя объект Wheel объявлен как закрытый final, но внутреннее состояние объекта Wheel может быть изменено, он не может гарантировать неизменность объекта Car.
Почему String спроектирован так, чтобы быть неизменным?
Это старый, но все еще популярный вопрос. Конструкция String, чтобы быть неизменной в Java, является результатом всестороннего рассмотрения различных факторов. Чтобы понять эту проблему, вам необходимо интегрировать память, синхронизацию и данные. Соображения по конструкции и безопасности Далее я кратко изложу различные причины.
1. Потребность в пулах строковых констант, экономия места в памяти и повышение эффективности
Принципиальная схема выглядит следующим образом:
Если строковому объекту разрешено изменять, это вызовет различные логические ошибки, например, изменение одного объекта повлияет на другой независимый объект. Строго говоря, идея этого пула констант такова. Метод оптимизации.
Подумайте: если код выглядит следующим образом, будут ли s1 и s2 указывать на один и тот же фактический объект String?
Возможно, эта проблема противоречит интуиции новичков, но, учитывая, что современные компиляторы будут выполнять регулярную оптимизацию, все они будут указывать на один и тот же объект в пуле констант. Или вы можете использовать такие инструменты, как jd-gui, для просмотра скомпилированного класса файл.
2. Разрешить объектам String кэшировать HashCode, чтобы гарантировать уникальность значений ключей.
Хэш-код объектов String в Java часто используется, например, в контейнерах, таких как hashMap.
Неизменяемость строк гарантирует уникальность хэш-кода, поэтому его можно с уверенностью кэшировать. Это также метод оптимизации производительности, что означает, что нет необходимости каждый раз вычислять новый хеш-код. В определении класса String есть следующие коды :
3. Высокая безопасность в параллельных сценариях.
В общем, причины неизменяемости String включают соображения дизайна, проблемы оптимизации эффективности и безопасности.На самом деле, это также ответ на многие «почему» в интервью Java.
Обратите внимание, что приведенный выше контент объединен из следующих двух сообщений в блоге!
Почему String в Java неизменяема?
Почему String спроектирован так, чтобы быть неизменным?
Русские Блоги
Почему String неизменяема в Java
Если строку можно изменить, изменение строки ссылкой приведет к тому, что другая ссылка укажет на неправильное значение.
2, значение хеш-функции кеша
В Java часто используется хеш-значение строки. Например, в HashMap. Оставаясь неизменным, вы можете гарантировать, что всегда будет возвращаться одно и то же значение хеш-функции. Так что его можно кэшировать, не беспокоясь об изменении. Это означает, что нет необходимости вычислять хеш-значение каждый раз, когда оно используется.
Это будет более эффективным.
В классе String он имеет следующий код:
3. Упростите использование других классов.
Чтобы быть более конкретным, рассмотрим следующую программу:
В этом примере, если String может быть изменен, если его значение изменится, это нарушит дизайн Set (Set не может содержать повторяющиеся элементы). Этот пример предназначен для упрощения дизайна, в фактическом классе String нет атрибута value.
4. Безопасность
String Во многих java-классах сетевые соединения, открытые файлы часто используются в качестве параметров. Если String можно изменить, соединение или файл могут быть изменены, что вызовет серьезные угрозы безопасности. Этот метод предполагает, что он подключается к машине, но это не так. Строки переменных будут отражены или использованы в качестве параметров, что вызовет проблемы с безопасностью.
Ниже приведен пример кода:
5. Неизменяемые объекты естественно потокобезопасны.
Поскольку неизменяемые объекты нельзя изменить, они могут свободно совместно использоваться несколькими потоками. Это исключает синхронизацию.
Таким образом, String спроектирован так, чтобы его нельзя было изменить для повышения эффективности и безопасности. Вот почему существует множество классов, которые нельзя изменить.
1. Принцип (почему класс String неизменен)
1. Что такое неизменный объект
Если объект не может изменить свое состояние после создания, то объект является неизменяемым (Immutable). Невозможность изменить состояние означает, что переменные-члены в объекте не могут быть изменены, включая значение переменной базового типа данных. Переменная ссылочного типа не может указывать на другие объекты, и состояние объекта, на который указывает ссылочный тип, не может быть изменено.
2. Роль последних ключевых слов
Если вы хотите создать неизменяемый объект, ключевым шагом является объявление всех переменных-членов как окончательных типов. Итак, вот краткий обзор роли последнего ключевого слова:
3. Анализ неизменности класса String.
Сначала посмотрите на следующий код:
Когда строка кода в (2) выполняется, новый объект String «123» будет создан в пуле констант времени выполнения области метода, а затем ссылка s будет перенаправлена на этот новый объект, а исходный объект «abc» В памяти нет изменений, как показано на следующем рисунке:
4. Принцип неизменности класса String.
Чтобы понять неизменность класса String, сначала посмотрите, какие переменные-члены находятся в классе String. В JDK1.8 переменные-члены String в основном имеют следующее:
Прежде всего, вы можете видеть, что класс String использует модификатор final, указывая на то, что класс String не наследуется.
Затем мы в основном сосредотачиваемся на значении переменной-члене класса String. Это значение имеет тип char [], поэтому объект String фактически инкапсулируется с этим массивом символов. Если посмотреть на модификатор значения, используется private, а метод setter не предоставляется. Следовательно, значение не может быть изменено за пределами класса String, а значение также изменяется с помощью final. Тогда значение не может быть изменено внутри класса String, но приведенное выше является окончательным. В содержании измененных переменных ссылочного типа упоминалось, что это может только гарантировать, что значение не может указывать на другие объекты, но состояние объекта, на который указывает значение, может быть изменено.Посмотрев на исходный код класса String, мы можем обнаружить, что класс String является неизменным.Ключ состоит в том, что инженеры компании SUN очень осторожны, чтобы не перемещать элементы в массиве символов во всех стоящих за ним методах String. Таким образом, ключ к неизменности класса String заключается в базовой реализации, а не только в финале.
5. Действительно ли объект String неизменен?
Как упоминалось выше, хотя value украшен final, это может гарантировать только то, что vaue не может указывать на другие объекты, но состояние объекта, на который указывает value, может быть изменено, то есть элементы в массиве символов, на который указывает value, могут быть изменены. Поскольку value имеет частный тип, вы можете использовать отражение только для получения атрибута value объекта String, а затем изменить элементы в массиве символов, на которые указывает значение. Подтвердите с помощью следующего кода:
В приведенном выше коде s всегда указывает на один и тот же объект String, но после операции отражения содержимое этого объекта String изменяется. Другими словами, неизменяемые объекты, такие как String, могут быть изменены посредством отражения.
2. Цели проектирования (почему String должен быть неизменным)
В Java дизайн String как неизменяемый является результатом всестороннего рассмотрения различных факторов, таких как память, синхронизация, структура данных и безопасность. Ниже приводится сводка различных факторов.
1. Потребность в пуле постоянных времени выполнения
При выполнении приведенного выше кода JVM сначала проверяет, есть ли объект String «abc» в пуле констант времени выполнения. Если объект уже существует, ему не нужно создавать новый объект String «abc», но он указывает ссылку s непосредственно на пул констант времени выполнения. Существующий объект String «abc» в; если он не существует, сначала создайте новый объект String «abc» в пуле констант времени выполнения, а затем укажите ссылку s на новый объект String, созданный в пуле констант времени выполнения.
Когда приведенный выше код выполняется, только один объект String «abc» создается в пуле констант времени выполнения, что экономит место в памяти.Принципиальная схема выглядит следующим образом:
2. Синхронизация
Поскольку объекты String неизменяемы, они многопоточны, а один и тот же экземпляр String может использоваться несколькими потоками. Это устраняет необходимость в использовании синхронизации из-за проблем безопасности потоков.
3. Разрешить объектам String кэшировать хэш-код.
Глядя на исходный код класса String в JDK1.8 выше, вы можете обнаружить, что существует хэш поля. Неизменяемость класса String гарантирует уникальность хэш-кода, поэтому хеш-код объекта String можно кэшировать с помощью хэш-поля. Рассчитайте хэш-код. Поэтому объекты String в Java часто используются в качестве ключей контейнеров, таких как HashMap.
4. Безопасность
Если объект String является изменяемым, это вызовет серьезные проблемы с безопасностью. Например, имя пользователя и пароль базы данных передаются в виде строк для получения соединения с базой данных или при программировании сокетов имя хоста и порт передаются в виде строк. Поскольку объект String неизменяем, его значение нельзя изменить.В противном случае хакеры могут воспользоваться лазейкой и изменить значение объекта, на который указывает ссылка на String, что вызовет бреши в безопасности.
Русские Блоги
JAVA-неизменяемый механизм и неизменность String
Для более захватывающего контента посетите персональный сайт: www.lifesmile.cn
1. Введение в неизменяемые классы
Неизменяемый класс: так называемый неизменяемый класс означает, что после создания экземпляра этого класса его значение переменной-члена не может быть изменено. Например, многие неизменяемые классы, поставляемые с JDK: Interger, Long и String.
Классы переменных: по сравнению с неизменяемыми классами классы переменных могут изменять значение своих переменных-членов после создания экземпляра. Большинство классов, созданных в процессе разработки, являются классами переменных.
Во-вторых, преимущества неизменных классов
Легко создавать, тестировать и использовать
Безопасность потоков, нет проблем с синхронизацией
Нет необходимости в методе копирования
Нет необходимости реализовывать метод клонирования
Возвращаемое значение класса может быть кэшировано, что позволяет hashCode использовать ленивую инициализацию
· Подходит для элементов ключа карты и набора (так как состояние этих объектов в коллекции изменить нельзя)
Как только класс построен, он инвариантен, нет необходимости проверять снова
· Всегда «атомарность сбоя»: если неизменный объект выдает исключение, он никогда не будет сохранять раздражающее или неопределенное состояние
Три, неизменный метод проектирования класса
Для разработки неизменяемых классов отдельные лица суммировали следующие принципы:
1. Последний модификатор добавляется в класс, чтобы гарантировать, что класс не наследуется.
Если класс может быть унаследован, это разрушит механизм неизменности класса. Пока унаследованный класс переопределяет методы родительского класса, а унаследованный класс может изменять значение переменной-члена, как только дочерний класс появляется в качестве родительского класса, нет гарантии, что текущий класс является изменчивым.
2. Убедитесь, что все переменные-члены должны быть закрытыми, и добавьте окончательное оформление
Таким образом, переменные-члены гарантированно будут неизменными. Но этого шага недостаточно, потому что, если это переменная-член объекта, она может изменить свое значение извне. Таким образом, пункт 4 восполняет этот недостаток.
3. Не предоставляет методы для изменения переменных-членов, включая установщики.
4. Избегайте изменения значений переменных-членов через другие интерфейсы и уничтожайте неизменные характеристики.
5. Инициализируйте все члены через конструктор и выполните глубокое копирование.
Если объект, переданный конструктором, непосредственно назначен переменной-члену, значение внутренней переменной можно изменить, изменив переданный объект. Например:
Этот метод не может гарантировать неизменность. MyArray и массив указывают на один и тот же адрес памяти. Пользователи могут изменять значение myArray, изменяя значение объекта массива вне ImmutableDemo.
Чтобы гарантировать, что внутреннее значение не изменено, вы можете использовать глубокое копирование, чтобы создать новую память для сохранения входящего значения. Правильный подход:
6. В методе получения не возвращайте непосредственно сам объект, а клонируйте объект и возвращайте копию объекта
Этот подход также предотвращает утечку объектов и предотвращает прямые манипуляции с переменными-членами после получения объектов-членов внутренней переменной через геттеры, что приводит к изменениям переменных-членов.
В-четвертых, неизменность объектов String
Строковый объект не может быть изменен после создания памяти. Создание неизменяемых объектов обычно соответствует пяти вышеуказанным принципам. Давайте посмотрим, как реализован код String.
Как показано в коде выше, можно наблюдать следующие детали дизайна:
1. Строковый класс окончательно изменен и не может быть унаследован
2. Все члены строки устанавливаются как частные переменные
3. Нет установщика значений
4. Установите значение и смещение на окончательное.
5. Когда передается значение массива переменных [], копируйте вместо непосредственного копирования значение [] во внутреннюю переменную.
6. Вместо того, чтобы возвращать ссылку на объект непосредственно при получении значения, он возвращает копию объекта.
Это соответствует описанным выше характеристикам инвариантных типов, а также гарантирует, что тип String является неизменным классом.
Пять, преимущества и недостатки неизменяемости объектов String
Из анализа в предыдущем разделе данные String являются неизменяемыми. Каковы преимущества настройки такой функции? Я суммирую следующие моменты:
1. Потребность в строковом постоянном пуле
Пул строковых констант Вы можете повторно использовать некоторые символьные константы в пуле констант, чтобы избежать повторного создания одного и того же объекта каждый раз и сэкономить место для хранения. Однако, если строка является переменной, строка с тем же содержимым также указывает на то же пространство памяти пула констант. Когда переменная изменяет значение памяти, другие измененные значения также изменяются. Так что это не соответствует первоначальному замыслу постоянного дизайна бассейна.
2. Вопросы безопасности резьбы
Один и тот же экземпляр строки может совместно использоваться несколькими потоками. Это устраняет необходимость использовать синхронизацию из-за проблем безопасности потоков. Сама строка является потокобезопасной.
3. Загрузчики классов используют строки. Неизменность обеспечивает безопасность, так что загружается правильный класс. Например, если вы хотите загрузить класс java.sql.Connection, и это значение будет изменено на myhacked.Connection, то это приведет к непостижимому повреждению вашей базы данных.
4. Поддержка хэширования и кэширования
Поскольку строка является неизменяемой, хеш-код кэшируется при ее создании и не требует пересчета. Это делает строку очень подходящей в качестве ключа на карте, и скорость обработки строки выше, чем у других ключевых объектов. Вот почему ключи в HashMap часто используют строки.
Шесть, является ли объект String неизменным
Хотя объект String устанавливает значение final, а также гарантирует, что его переменные-члены не могут быть изменены с помощью различных механизмов. Однако его значение все еще можно изменить с помощью механизма отражения. Например:
Обнаружено, что значение String изменилось. Другими словами, так называемые «неизменяемые» объекты могут быть изменены посредством отражения



