Это может быть самый глупый вопрос, который когда-либо задавали, но я думаю, что он довольно сбивает с толку новичка в Java.
String неизменяемый?StringBuilder, должен быть предпочтительнее String и наоборот?Мы будем очень признательны за хороший пример (на Java).
Кстати, я не думаю, что это самый тупой вопрос :) Я думаю, что это довольно важная концепция, которую нужно понять.
Когда вы сказали StringBuilder, разве вы не имели в виду изменяемый класс StringBuffer? String и StringBuffer более похожи по функциям, чем String и StringBuilder. StringBuffer фактически является изменяемой строкой.
Могу я предложить добавить к этому вопросу тег «новичок», чтобы программисты, не знакомые с Java, могли найти его при поиске других вводных вопросов?




После создания не может быть изменен. Рассмотрим класс, экземпляр которого может использоваться в качестве ключа для хеш-таблицы или подобного. Ознакомьтесь с лучшими практиками Java.
Неизменяемость означает, что после создания объекта ни один из его членов не изменится. String неизменен, поскольку вы не можете изменить его содержимое.
Например:
String s1 = " abc ";
String s2 = s1.trim();
В приведенном выше коде строка s1 не изменилась, другой объект (s2) был создан с помощью s1.
"неизменяемый" означает, что вы не можете изменить значение. Если у вас есть экземпляр класса String, любой вызываемый вами метод, который, кажется, изменяет значение, фактически создаст другой String.
String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"
Чтобы сохранить изменения, вы должны сделать что-то вроде этого foo = foo.sustring (3);
Неизменяемые и изменяемые могут быть забавными, когда вы работаете с коллекциями. Подумайте, что произойдет, если вы используете изменяемый объект в качестве ключа для карты, а затем измените значение (совет: подумайте о equals и hashCode).
Одно значение связано с тем, как значение хранится в компьютере. Например, для строки .Net это означает, что строку в памяти нельзя изменить. Когда вы думаете, что меняете ее, вы фактически создаете новую строку. строка в памяти и указание существующей переменной (которая является просто указателем на фактический набор символов в другом месте) на новую строку.
Неизменяемость означает, что после того, как конструктор объекта завершил выполнение, этот экземпляр нельзя изменить.
Это полезно, поскольку означает, что вы можете передавать ссылки на объект, не беспокоясь о том, что кто-то другой собирается изменить его содержимое. Особенно при работе с параллелизмом нет проблем с блокировкой объектов, которые никогда не меняются.
например
class Foo
{
private final String myvar;
public Foo(final String initialValue)
{
this.myvar = initialValue;
}
public String getValue()
{
return this.myvar;
}
}
Foo не должен беспокоиться о том, что вызывающий getValue() может изменить текст в строке.
Если вы представите себе класс, аналогичный Foo, но с StringBuilder, а не с String в качестве члена, вы увидите, что вызывающий getValue() сможет изменить атрибут StringBuilder экземпляра Foo.
Также остерегайтесь различных видов неизменяемости, которые вы можете найти: Эрик Липперт написал об этом статья в блоге. В основном у вас могут быть объекты, интерфейс которых неизменяем, но за кулисами фактическое изменяемое частное состояние (и, следовательно, не может безопасно совместно использоваться между потоками).
Я думаю, вам следует добавить конструктор с одним аргументом, чтобы присвоить значение хотя бы один раз. Смысл текущего кода не ясен, так как на самом деле нет смысла менять :).
Вы должны сделать это поле доступным только для чтения. Это полностью указывает на то, что поле неизменяемо. Прямо сейчас это непреложно по соглашению
Это хороший ответ. Но чтобы уточнить, это не означает, что вы не можете установить свою переменную в другой экземпляр неизменяемого типа (a-la const). Это просто означает, что сам экземпляр не может быть изменен.
Член myVar должен быть окончательным, чтобы он был действительно неизменяемым.
Я не думаю, что final на самом деле что-то добавляет - извне класса (даже подклассов) myVar не может указывать на другую строку, а сама строка неизменна.
Вы правы, что myVar недоступен за пределами Foo. Однако наличие final указывает любому, кто может изменять класс в будущем, что его значение не должно изменяться. Я предпочитаю быть максимально откровенным в таких обстоятельствах.
IIRC, использующий final, также имеет некоторые преимущества в модели памяти Java (хотя у меня сейчас нет источника для этого).
Как насчет «Ссылочные типы нельзя сделать неизменяемыми с помощью ключевого слова final. Final предотвращает только переназначение». от en.wikipedia.org/wiki/Immutable_object
ИМО, подход, подобный const, мог бы быть более надежным
@DouglasLeeder, что произойдет, если я сделаю myvar.toUpperCase();, создаст ли он новый объект для myvar с новым значением или изменит существующий объект myvar в памяти кучи.
@randomaspirer Строки неизменяемы, поэтому он вернет новую строку. docs.oracle.com/javase/7/docs/api/java/lang/… явно не говорит, но он может возвращать ту же строку или совместно использовать буфер в случае, когда ему не нужно изменять какие-либо символы.
класс также должен быть final, или вы можете создать подкласс и переопределить метод getValue()
Неизменяемые объекты - это объекты, которые нельзя изменить программно. Они особенно хороши для многопоточных сред или других сред, где несколько процессов могут изменять (мутировать) значения в объекте.
Однако, чтобы прояснить, StringBuilder на самом деле является изменяемым объектом, а не неизменяемым. Обычная строка java неизменяема (это означает, что после ее создания вы не можете изменить базовую строку без изменения объекта).
Например, предположим, что у меня есть класс ColoredString, который имеет значение String и цвет String:
public class ColoredString {
private String color;
private String string;
public ColoredString(String color, String string) {
this.color = color;
this.string = string;
}
public String getColor() { return this.color; }
public String getString() { return this.string; }
public void setColor(String newColor) {
this.color = newColor;
}
}
В этом примере ColoredString называется изменяемым, потому что вы можете изменить (видоизменить) одно из его ключевых свойств, не создавая новый класс ColoredString. Причина, по которой это может быть плохо, заключается, например, в том, что у вас есть приложение с графическим интерфейсом, которое имеет несколько потоков, и вы используете ColoredStrings для печати данных в окне. Если у вас есть экземпляр ColoredString, созданный как
new ColoredString("Blue", "This is a blue string!");
Тогда вы ожидаете, что строка всегда будет «синей». Однако, если другой поток захватил этот экземпляр и вызвал
blueString.setColor("Red");
У вас внезапно и, вероятно, неожиданно появится «красная» струна, когда вы захотите «синюю». Из-за этого неизменяемые объекты почти всегда предпочтительнее при передаче экземпляров объектов. Когда у вас есть случай, когда изменяемые объекты действительно необходимы, вы обычно должны защищать объект, передавая копии только из вашего конкретного поля управления.
Напомним, что в Java java.lang.String является неизменяемым объектом (не могу может быть изменен после его создания), а java.lang.StringBuilder - изменяемый объект, поскольку его можно изменить без создания нового экземпляра.
Вы должны сделать поля доступными только для чтения. Прямо сейчас ваш класс неизменен по соглашению. Для будущих разработчиков нет никаких указаний на то, что неизменяемость является преднамеренной. Если вы сделаете поля только для чтения, это поможет прояснить ваши намерения будущему разработчику.
@JaredPar - На самом деле класс вообще не является неизменяемым ... это пример изменяемого класса, чтобы продемонстрировать, почему это может быть проблемой.
@JaredPar - О, это совершенно нормально :) Я собирался немного переписать его, чтобы было понятнее, но Дуглас уже хорошо написан и кажется фаворитом, поэтому я просто оставлю свой в качестве другого примера; но кто-то на самом деле отредактировал его, чтобы сделать свойства окончательными, что мне показалось забавным :)
Мне очень нравится объяснение от SCJP Sun Certified Programmer for Java 5 Study Guide.
To make Java more memory efficient, the JVM sets aside a special area of memory called the "String constant pool." When the compiler encounters a String literal, it checks the pool to see if an identical String already exists. If a match is found, the reference to the new literal is directed to the existing String, and no new String literal object is created.
Он должен иметь возможность делать это с любыми идентичными неизменяемыми объектами, но я полагаю, что это займет слишком много времени выполнения.
Неизменяемый объект - это объект, который нельзя изменить после его создания. Типичный пример - строковые литералы.
В языке программирования D, который становится все более популярным, есть понятие «неизменяемость» через ключевое слово «инвариант». Посмотрите статью об этом доктора Добба - http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29. Это прекрасно объясняет проблему.
Я действительно считаю, что с D 2.020 ключевое слово было изменено с неизменного на неизменяемое. Я не вижу в этом смысла, но там сказано: «Сейчас реализовано неизменное». digitalmars.com/d/2.0/changelog.html#new2_020
Состояние неизменяемых объектов не может быть изменено после создания.
Есть три основные причины использовать неизменяемые объекты всякий раз, когда вы можете, и все они помогут уменьшить количество ошибок, которые вы вводите в свой код:
Есть также некоторые другие оптимизации, которые вы могли бы сделать в коде, когда знаете, что состояние объекта неизменяемо - например, кэширование вычисленного хэша - но это оптимизации и, следовательно, не так уж и интересны.
На самом деле String не является неизменным, если вы используете определение из Википедии, предложенное выше.
Состояние строки меняет конструкцию поста. Взгляните на метод hashcode (). String кэширует значение хэш-кода в локальном поле, но не вычисляет его до первого вызова hashcode (). Эта ленивая оценка хэш-кода помещает String в интересное положение как неизменяемый объект, состояние которого изменяется, но его нельзя наблюдать без использования отражения.
Так что, возможно, определение неизменяемого должно быть объектом, изменение которого невозможно наблюдать.
Если состояние неизменяемого объекта изменяется после его создания, но никто не может его увидеть (без отражения), остается ли объект неизменным?
Хорошая идея - объект, который нельзя увидеть как изменившийся, а также невозможно изменить его извне. Частное поле для hashCode () - это внутреннее изменение, которое не является существенным для внешнего видимого состояния объекта.
На самом деле следует заметить, что может изменился, если вы используете отражение. См. Больше на сайте Sedgewick's Строки изменяемы, если вы разрешаете отражение.
Неизменяемый объект - это объект, внутренние поля которого (или, по крайней мере, все внутренние поля, влияющие на его внешнее поведение) не могут быть изменены.
У неизменяемых строк есть много преимуществ:
Представление: Выполните следующую операцию:
String substring = fullstring.substring(x,y);
Базовый C для метода substring (), вероятно, выглядит примерно так:
// Assume string is stored like this:
struct String { char* characters; unsigned int length; };
// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
struct String* out = malloc(sizeof(struct String));
out->characters = in->characters + begin;
out->length = end - begin;
return out;
}
Обратите внимание, что ни один из символов не должен копироваться! Если бы объект String был изменяемым (символы могли измениться позже), вам пришлось бы скопировать все символы, иначе изменения символов в подстроке будут отражены в другой строке позже.
Параллелизм: Если внутренняя структура неизменяемого объекта действительна, она всегда будет действительной. Нет никаких шансов, что разные потоки могут создать недопустимое состояние внутри этого объекта. Следовательно, неизменяемые объекты - это Поток Безопасный.
Вывоз мусора: Сборщику мусора намного проще принимать логические решения относительно неизменяемых объектов.
Однако у неизменяемости есть и обратные стороны:
Представление: Подождите, я думал, вы сказали, что производительность - это преимущество неизменности! Иногда бывает, но не всегда. Возьмите следующий код:
foo = foo.substring(0,4) + "a" + foo.substring(5); // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder
В обеих строках четвертый символ заменяется буквой «а». Второй фрагмент кода не только читабельнее, но и быстрее. Посмотрите, как вам нужно сделать базовый код для foo. Подстроки просты, но теперь, поскольку уже есть символ в пятом месте и что-то еще может ссылаться на foo, вы не можете просто изменить его; вам нужно скопировать всю строку (конечно, некоторые из этих функций абстрагируются в функции в реальном базовом C, но суть здесь в том, чтобы показать код, который выполняется в одном месте).
struct String* concatenate(struct String* first, struct String* second)
{
struct String* new = malloc(sizeof(struct String));
new->length = first->length + second->length;
new->characters = malloc(new->length);
int i;
for(i = 0; i < first->length; i++)
new->characters[i] = first->characters[i];
for(; i - first->length < second->length; i++)
new->characters[i] = second->characters[i - first->length];
return new;
}
// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));
Обратите внимание, что объединение называется дважды, что означает, что вся строка должна быть пропущена по циклу! Сравните это с кодом C для операции bar:
bar->characters[4] = 'a';
Операция с изменяемой строкой, очевидно, намного быстрее.
В заключение: В большинстве случаев вам нужна неизменяемая строка. Но если вам нужно много добавлять и вставлять в строку, вам нужна изменчивость для скорости. Если вам нужны преимущества безопасности параллелизма и сборки мусора, ключевым моментом является сохранение ваших изменяемых объектов локальными по отношению к методу:
// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
StringBuilder mutable;
boolean first = true;
for(int i = 0; i < strings.length; i++)
{
if (!first) first = false;
else mutable.append(separator);
mutable.append(strings[i]);
}
return mutable.toString();
}
Поскольку объект mutable является локальной ссылкой, вам не нужно беспокоиться о безопасности параллелизма (только один поток когда-либо касается его). И поскольку на него больше нигде нет ссылок, он выделяется только в стеке, поэтому он освобождается, как только вызов функции завершается (вам не нужно беспокоиться о сборке мусора). И вы получаете все преимущества в производительности как изменчивости, так и неизменяемости.
Отличное чтение! только одно, я думаю, это должно быть, если (во-первых), а не если (! во-первых)
Необходимо не то, чтобы поля были неизменяемыми, а скорее, чтобы определенное наблюдаемое состояние объекта было неизменным; объект, содержащий ссылку на другой объект как средство инкапсуляции состояния, содержащегося в нем, может быть неизменным только в том случае, если все инкапсулированные аспекты состояния, которые он предоставляет внешнему миру, также неизменны. Обратите внимание на то, что поля неизменяемых типов не являются ни обязательными, ни достаточными. Что важно, так это видимое состояние.
Passing pointers because Java is pass-by-reference Разве java не является «передачей по значению»?
@CristianGutu, да, вы правы, JAVA - это "передача по значению", а не "передача по ССЫЛКЕ"
Ссылка передается как значение !!
Строка s1 = «Старая строка»;
//s1 variable, refers to string in memory
reference | MEMORY |
variables | |
[s1] --------------->| "Old String" |
Строка s2 = s1;
//s2 refers to same string as s1
| |
[s1] --------------->| "Old String" |
[s2] ------------------------^
s1 = «Новая строка»;
//s1 deletes reference to old string and points to the newly created one
[s1] -----|--------->| "New String" |
| | |
|~~~~~~~~~X| "Old String" |
[s2] ------------------------^
The original string 'in memory' didn't change, but the reference variable was changed so that it refers to the new string. And if we didn't have s2, "Old String" would still be in the memory but we'll not be able to access it...
Возможно, это немного поздно, но чтобы понять, что такое неизменяемый объект, рассмотрим следующий пример из нового API даты и времени Java 8 (java.time). Как вы, наверное, знаете, все объекты даты из Java 8 - это неизменный, поэтому в следующем примере
LocalDate date = LocalDate.of(2014, 3, 18);
date.plusYears(2);
System.out.println(date);
Выход:
2014-03-18
Это печатает тот же год, что и начальная дата, потому что plusYears(2) возвращает новый объект, поэтому старая дата остается неизменной, потому что это неизменяемый объект. После создания вы не можете вносить в него дальнейшие изменения, и переменная даты по-прежнему указывает на него.
Итак, этот пример кода должен захватывать и использовать новый объект, созданный и возвращенный этим вызовом plusYears.
LocalDate date = LocalDate.of(2014, 3, 18);
LocalDate dateAfterTwoYears = date.plusYears(2);
date.toString()… 2014-03-18
dateAfterTwoYears.toString()… 2016-03-18
String s1 = "Hi";
String s2=s1;
s1 = "Bye";
System.out.println(s2); //Hi (if String was mutable output would be: Bye)
System.out.println(s1); //Bye
s1 = "Hi": был создан объект s1 со значением «Hi» в нем.
s2=s1: объект s2 создается со ссылкой на объект s1.
s1 = "Bye": значение предыдущего объекта s1 не изменяется, потому что s1 имеет тип String, а тип String является неизменяемым типом, вместо этого компилятор создает новый объект String со значением «Bye» и ссылкой на s1. здесь, когда мы печатаем значение s2, результатом будет «Hi», а не «Bye», потому что s2 ссылался на предыдущий объект s1, который имел значение «Hi».
не могли бы вы добавить небольшое пояснение?
Неизменяемые объекты
Объект считается неизменным, если его состояние не может измениться после создания. Максимальное использование неизменяемых объектов широко признано разумной стратегией создания простого и надежного кода.
Неизменяемые объекты особенно полезны в параллельных приложениях. Поскольку они не могут изменить состояние, они не могут быть повреждены вмешательством потоков или наблюдаться в несогласованном состоянии.
Программисты часто неохотно используют неизменяемые объекты, потому что они беспокоятся о стоимости создания нового объекта, а не о его обновлении на месте. Влияние создания объекта часто переоценивается и может быть компенсировано некоторыми эффективностями, связанными с неизменяемыми объектами. К ним относятся снижение накладных расходов из-за сборки мусора и устранение кода, необходимого для защиты изменяемых объектов от повреждения.
Следующие подразделы принимают класс, экземпляры которого являются изменяемыми, и наследуют от него класс с неизменяемыми экземплярами. При этом они дают общие правила для такого преобразования и демонстрируют некоторые преимущества неизменяемых объектов.
Неизменяемые просто означают неизменяемые или неизменяемые. После создания строкового объекта его данные или состояние не могут быть изменены
Рассмотрим нижеприведенный пример.
class Testimmutablestring{
public static void main(String args[]){
String s = "Future";
s.concat(" World");//concat() method appends the string at the end
System.out.println(s);//will print Future because strings are immutable objects
}
}
Давайте рассмотрим схему ниже,
На этой диаграмме вы можете увидеть новый объект, созданный как «Мир будущего». Но не меняем "Будущее" .Because String is immutable. s, до сих пор относятся к «Будущему». Если вам нужно вызвать в «Мир будущего»,
String s = "Future";
s=s.concat(" World");
System.out.println(s);//print Future World
Почему строковые объекты неизменяемы в Java?
Because Java uses the concept of string literal. Suppose there are 5 reference variables, all refers to one object "Future".If one reference variable changes the value of the object, it will be affected to all the reference variables. That is why string objects are immutable in java.
Поскольку принятый ответ отвечает не на все вопросы. Я вынужден дать ответ через 11 лет и 6 месяцев.
Can somebody clarify what is meant by immutable?
Надеюсь, вы имели в виду неизменный объект (потому что мы могли подумать о неизменная ссылка).
Объект - это неизменный: если однажды они были созданы, они всегда представляют одно и то же значение (не имеют метода, изменяющего значение).
Why is a
Stringimmutable?
Соблюдайте приведенное выше определение, которое можно проверить, просмотрев исходный код Sting.java.
What are the advantages/disadvantages of the immutable objects? immutable types are :
безопаснее от ошибок.
легче понять.
и многое другое готово к переменам.
Why should a mutable object such as StringBuilder be preferred over String and vice-verse?
Сужение вопроса Зачем нам в программировании нужен изменяемый StringBuilder? Обычно его используют для объединения большого количества строк, например:
String s = "";
for (int i = 0; i < n; ++i) {
s = s + n;
}
При использовании неизменяемых строк создается множество временных копий - первое число строки («0») фактически копируется n раз в процессе построения последней строки, второе число копируется n-1 раз, и поэтому на. На самом деле просто копирование занимает O (n2) времени, хотя мы объединили только n элементов.
StringBuilder разработан, чтобы минимизировать это копирование. Он использует простую, но продуманную внутреннюю структуру данных, чтобы избежать копирования до самого конца, когда вы запрашиваете последнюю строку с помощью вызова toString ():
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
sb.append(String.valueOf(n));
}
String s = sb.toString();
Хорошая производительность - одна из причин, по которой мы используем изменяемые объекты. Другой - удобный совместный доступ: две части вашей программы могут более удобно взаимодействовать, разделяя общую изменяемую структуру данных.
Больше можно найти здесь: https://web.mit.edu/6.005/www/fa15/classes/09-immutability/#useful_immutable_types
Понимаете, это был не такой уж и глупый вопрос. Рад, что ты спросил!