Что подразумевается под неизменным?

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

  1. Может кто-нибудь уточнить, что подразумевается под неизменным?
  2. Почему String неизменна?
  3. Каковы преимущества/недостатки неизменяемых объектов?
  4. Почему изменяемый объект, такой как StringBuilder должен быть предпочтительнее, чем String и наоборот?

Хороший пример (на Java) будет очень полезен.

Ответ 1

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

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

например.

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.

Также остерегайтесь различных видов неизменности, которые вы можете найти: Эрик Липперт написал статью статью в блоге об этом. В принципе, у вас могут быть объекты, интерфейс которых неизменен, но за кулисами фактическое mutables private state (и, следовательно, нельзя безопасно обмениваться между потоками).

Ответ 2

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

Существует много преимуществ неизменяемых строк:

Производительность: Выполните следующую операцию:

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

Concurrency: Если внутренняя структура неизменяемого объекта действительна, она всегда будет действительна. Нет никаких шансов, что разные потоки могут создать недопустимое состояние внутри этого объекта. Следовательно, неизменяемыми объектами являются Thread Safe.

Сбор мусора:. Сборщику мусора гораздо легче принимать логические решения о неизменяемых объектах.

Однако есть и минусы неизменности:

Производительность: Подождите, я думал, вы сказали, что производительность - это потенциал неизменности! Ну, это иногда, но не всегда. Возьмите следующий код:

foo = foo.substring(0,4) + "a" + foo.substring(5);  // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder

Две строки заменяют четвертый символ буквой "a". Не только вторая часть кода более читаема, но и быстрее. Посмотрите, как вам придется делать базовый код для 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));

Обратите внимание, что concatenate получает вызов дважды, что означает, что вся строка должна быть зациклирована! Сравните это с кодом C для операции bar:

bar->characters[4] = 'a';

Операция с изменяемой строкой, очевидно, намного быстрее.

В заключении: В большинстве случаев вам нужна неизменяемая строка. Но если вам нужно сделать много добавления и вставки в строку, вам нужна изменчивость для скорости. Если вы хотите, чтобы с ним были полезны функции безопасности и сбора мусора concurrency, ключ заключается в том, чтобы сохранить измененные объекты локально в методе:

// 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 является локальной ссылкой, вам не нужно беспокоиться о безопасности concurrency (только один поток когда-либо затрагивает его). И поскольку он не упоминается нигде, он выделяется только в стеке, поэтому он освобождается, как только вызов функции завершен (вам не нужно беспокоиться о сборке мусора). И вы получаете все преимущества производительности как изменчивости, так и неизменности.

Ответ 3

На самом деле String не является неизменным, если вы используете определение википедии, предложенное выше.

Строковое состояние меняет постконструкцию. Взгляните на метод hashcode(). String кэширует значение hashcode в локальном поле, но не вычисляет его до первого вызова hashcode(). Эта ленивая оценка hashcode помещает String в интересную позицию в качестве неизменяемого объекта, состояние которого изменяется, но нельзя заметить, что оно изменилось без использования отражения.

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

Если состояние изменяется в неизменяемом объекте после его создания, но никто не может его видеть (без отражения), объект остается неизменным?

Ответ 4

Неизменяемые объекты - это объекты, которые не могут быть изменены программно. Они особенно хороши для многопоточных сред или других сред, в которых более чем один процесс способен изменять (мутировать) значения в объекте.

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

new ColoredString("Blue", "This is a blue string!");

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

blueString.setColor("Red");

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

Чтобы воспроизвести, в Java, java.lang.String - неизменяемый объект (его нельзя изменить после его создания), а java.lang.StringBuilder - изменяемый объект, потому что его можно изменить без создания нового экземпляра.

Ответ 5

  • В больших приложениях его общий для строковых литералов занимает большие бит памяти. Таким образом, чтобы эффективно обрабатывать память, JVM выделяет область, называемую "String constant pool". (Обратите внимание, что в памяти даже не привязанная строка содержит вокруг char [], int для его длину и другую для хэш-кода. Для числа, наоборот, требуется не более восьми мгновенных байтов)
  • Когда complier попадает в строковый литерал, он проверяет пул, чтобы увидеть, существует ли уже идентичный литерал. И если кто-то найден, ссылка на новый литерал направлена ​​на существующую строку, и новый "объект литерала струны" не создается (существующая строка просто получает дополнительную ссылку).
  • Следовательно: Изменчивость String сохраняет память...
  • Но когда какая-либо переменная меняет значение, на самом деле - это только их ссылка, которая изменилась, а не значение в памяти (следовательно, это не повлияет на другие переменные, ссылающиеся на нее), как показано ниже.

String 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]   ------------------------^

Исходная строка "в памяти" не изменилась, но ссылочная переменная была изменена так, чтобы она ссылалась на новую строку. И если бы у нас не было s2, "Old String" все еще была бы в памяти, но мы не сможем получить к нему доступ...

Ответ 6

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

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

Чтобы сохранить изменения, вы должны сделать что-то вроде этого   foo = foo.sustring(3);

Неизменяемый vs mutable может быть забавным, когда вы работаете с коллекциями. Подумайте, что произойдет, если вы используете изменяемый объект в качестве ключа для карты, а затем измените значение (подсказка: подумайте о equals и hashCode).

Ответ 7

java.time

Это может быть немного поздно, но для того, чтобы понять, что является неизменяемым объектом, рассмотрим следующий пример из нового Java 8 Date and Time API (java.time). Поскольку вы, вероятно, знаете, что все объекты даты из Java 8 являются неизменяемыми, поэтому в следующем примере

LocalDate date = LocalDate.of(2014, 3, 18); 
date.plusYears(2);
System.out.println(date);

Вывод:

2014-03-18

Это печатает тот же год, что и начальная дата, потому что plusYears(2) возвращает новый объект, поэтому старая дата все еще остается неизменной, потому что это неизменный объект. После создания вы не можете его изменить, и переменная date все еще указывает на нее.

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

LocalDate date = LocalDate.of(2014, 3, 18); 
LocalDate dateAfterTwoYears = date.plusYears(2);

date.toString()... 2014-03-18

dateAfterTwoYears.toString()... 2016-03-18

Ответ 8

Мне очень нравится объяснение из SCJP Sun Certified Programmer для Java 5 Учебное пособие.

Чтобы повысить эффективность использования памяти Java, JVM выделяет специальную область памяти, которая называется "Строка константных пулов". Когда компилятор встречает литерал String, он проверяет пул, чтобы увидеть, существует ли уже идентичная строка. Если совпадение найдено, ссылка на новый литерал направляется на существующую строку, и новый объект литерала строки не создается.

Ответ 9

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

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

  • Гораздо проще рассуждать о том, как работает ваша программа, когда вы знаете, что состояние объекта нельзя изменить другим способом.
  • Неизменяемые объекты автоматически потокобезопасны (при условии, что они публикуются безопасно), поэтому никогда не будет причиной этих трудноподдающихся многопотоковых ошибок
  • У неизменяемых объектов всегда будет один и тот же код хэша, поэтому они могут использоваться как ключи в HashMap (или аналогичном). Если бы хэш-код элемента в хеш-таблице изменился, запись таблицы была бы эффективно потеряна, так как попытки найти ее в таблице оказались бы в неправильном месте. Это основная причина, по которой объекты String неизменяемы - они часто используются в качестве ключей HashMap.

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

Ответ 10

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

Ответ 11

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" .

Ответ 12

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

String s1 = "  abc  ";
String s2 = s1.trim();

В приведенном выше коде строка s1 не изменилась, другой объект (s2) был создан с помощью s1.

Ответ 13

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

Рассмотрим приведенный ниже пример,

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  
  }  
 }  

Давайте разберемся с диаграммой ниже,

enter image description here

На этой диаграмме вы можете увидеть новый объект, созданный как "Мир будущего". Но не меняйте "Будущее". Because String is immutable. s, все еще ссылаются на "Будущее". Если вам нужно позвонить "Мир будущего",

String s="Future";  
s=s.concat(" World");  
System.out.println(s);//print Future World

Почему строковые объекты неизменяемы в Java?

Потому что Java использует концепцию строкового литерала. Предположим, есть 5 ссылочных переменных, все ссылаются на один объект "Будущее". Если одна ссылочная переменная изменит значение объекта, это повлияет на все ссылочные переменные. Вот почему строковые объекты неизменны в Java.

Ответ 14

После инстанцирования нельзя изменить. Рассмотрим класс, в котором экземпляр может использоваться как ключ для хэш-таблицы или аналогичного. Ознакомьтесь с передовыми методами Java.

Ответ 15

Что такое неизменяемый объект?

В соответствии с Java Reference Documentation, "Объект считается неизменным, если его состояние не может измениться после его построения" Просто неизменяемым классом является класс, свойства которого не могут быть изменены после создания. Это означает, что при создании все данные экземпляра предоставляются и остаются неизменными до уничтожения объектов.

Почему неизменяемые объекты?

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

Обеспокоенность

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

Как работает это приложение String?

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

Ссылка: http://www.devdummy.com/2017/09/immutable-objects-in-java.html

Ответ 16

Неизменяемые объекты

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

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

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

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

Источник

Ответ 17

оракул документы говорит

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

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

Мне нравится эта фраза из поста

Неизменяемые объекты облегчают параллельное программирование

Ответ 18

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

Язык программирования D, который становится все более популярным, имеет понятие "неизменности" через ключевое слово "инвариант". Проверьте эту статью Dr.Dobb об этом - http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29. Это прекрасно объясняет проблему.