Неизменяемое целое число

102

Я знаю, что это, вероятно, очень глупо, но во многих местах утверждается, что класс Integer в Java неизменен, но следующий код:

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

Выполняется без каких-либо проблем, давая (ожидаемый) результат 6. Таким образом, фактически изменилось значение a. Разве это не значит, что Integer изменчив? Вторичный вопрос и небольшой оффтоп: «Неизменяемые классы не нуждаются в конструкторах копирования». Кто-нибудь хочет объяснить, почему?

К. Стефф
источник
12
Класс неизменяем, но автобокс создает забавные вещи: stackoverflow.com/questions/3085332/…
wkl 06
Спасибо, бокс был ключевым словом, которое мне нужно было погуглить :)
Стефф
7
Вы путаете неизменяемое с конечным или постоянным значением.
Code Enthusiastic

Ответы:

95

Неизменяемость не означает, что aникогда не может равняться другому значению. Например, Stringтоже неизменяемо, но я все еще могу это сделать:

String str = "hello";
// str equals "hello"
str = str + "world";
// now str equals "helloworld"

strне был изменен, strтеперь это полностью новый экземпляр объекта, как и ваш Integer. Таким образом, значение aне мутировать, но он был заменен совершенно новым объектом, то есть new Integer(6).

Трэвис Уэбб
источник
14
«Это потому, что str теперь является полностью новым экземпляром объекта». Или, скорее, str (варибал) указывает на новый объект. Сам объект не является изменяемым, но поскольку переменная не является окончательной, она может указывать на другой объект.
Sandman
Да, он указывает на другой объект, который был создан в результате +=операции.
Трэвис Уэбб
11
Строго говоря, это не обязательно должен быть новый объект. Бокс использует Integer.valueOf(int)и этот метод поддерживает кеш Integerобъектов. Таким образом, результат +=на Integerпеременной может быть объектом , который существовал ранее (или это может быть даже тот же объект ... в случае a += 0).
Stephen C
1
Почему JavaDoc для String явно говорит о своей неизменности, а JavaDoc для Integer - нет? Эта разница, почему я читаю этот Вопрос ...
cellepo
52

aявляется «ссылкой» на какое-то целое число (3), ваше сокращение на a+=bсамом деле означает следующее:

a = new Integer(3 + 3)

Так что нет, целые числа неизменяемы, но указывающие на них переменные - *.

* Возможно наличие неизменяемых переменных, они обозначаются ключевым словом final, что означает, что ссылка не может изменяться.

final Integer a = 3;
final Integer b = 3;
a += b; // compile error, the variable `a` is immutable, too.
Марк Эллиот
источник
20

Вы можете определить, что объект изменился, используя System.identityHashCode()(лучший способ - использовать простой, ==но не так очевидно, что ссылка, а не значение изменилась)

Integer a = 3;
System.out.println("before a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));
a += 3;
System.out.println("after a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));

отпечатки

before a +=3; a=3 id=70f9f9d8
after a +=3; a=6 id=2b820dda

Вы можете видеть, что базовый «id» объекта, на который aссылается, изменился.

Питер Лоури
источник
1
System.identityHashCode () - очень хороший совет. Спасибо за это.
Ad Infinitum
11

На начальный вопрос:

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

Целое число неизменяемо, поэтому то, что произошло выше, означает, что «a» изменилось на новую ссылку значения 6. Начальное значение 3 осталось без ссылки в памяти (оно не было изменено), поэтому оно может быть обработано сборщиком мусора.

Если это происходит со строкой, она будет храниться в пуле (в пространстве PermGen) дольше, чем целые числа, поскольку она ожидает иметь ссылки.

Харша
источник
8

Да Целое число неизменяемо.

A - это ссылка, которая указывает на объект. Когда вы запускаете + = 3, это переназначает A для ссылки на новый объект Integer с другим значением.

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

О разнице между объектами и ссылками читайте здесь .

Энди
источник
Просто и легко сказано языком непрофессионала, со всеми остальными сложными объяснениями :)
Рошан Фернандо
5

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

Чтобы понять это для себя, выполните целочисленное присваивание в цикле (с целым числом, объявленным вне цикла) и посмотрите на живые объекты в памяти.

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

uncaught_exceptions
источник
2

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

Причина в том, что редко возникает необходимость в копировании (или даже в любой точке копирования) экземпляра неизменяемого класса. Копия объекта должна быть «такой же, как» оригинал, и если она такая же, создавать ее не нужно.

Однако есть некоторые основные предположения:

  • Предполагается, что ваше приложение не придает никакого значения идентификатору объекта экземпляров класса.

  • Предполагается, что класс перегружен equals и hashCodeчто копия экземпляра будет «такой же, как» оригинал ... в соответствии с этими методами.

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

Стивен С
источник
1

Вот как я понимаю неизменное

int a=3;    
int b=a;
b=b+5;
System.out.println(a); //this returns 3
System.out.println(b); //this returns 8

Если бы int мог изменяться, "a" напечатало бы 8, но это не так, потому что оно неизменяемо, поэтому оно равно 3. Ваш пример - это просто новое присвоение.

Ндхети
источник
0

Я могу прояснить, что Integer (и другие его кредо, такие как Float, Short и т. Д.) Неизменны с помощью простого образца кода:

Образец кода

public class Test{
    public static void main(String... args){
        Integer i = 100;
        StringBuilder sb = new StringBuilder("Hi");
        Test c = new Test();
        c.doInteger(i);
        c.doStringBuilder(sb);
        System.out.println(sb.append(i)); //Expected result if Integer is mutable is Hi there 1000
    }

    private void doInteger(Integer i){
        i=1000;
    }

    private void doStringBuilder(StringBuilder sb){
        sb.append(" there");
    }

}

Фактический результат

Результатом будет он Hi There 100 вместо ожидаемого результата (в случае, если оба объекта sb и i являются изменяемыми объектами) Hi There 1000

Это показывает, что объект, созданный i в main, не изменяется, тогда как sb изменяется.

Итак, StringBuilder продемонстрировал изменчивое поведение, но не Integer.

Итак, целое число неизменяемо. Следовательно, доказано

Другой код без целого числа:

public class Test{
    public static void main(String... args){
        Integer i = 100;
        Test c = new Test();
        c.doInteger(i);
        System.out.println(i); //Expected result is 1000 in case Integer is mutable
    }

    private void doInteger(Integer i){
        i=1000;
    }


}
Ашутош Нигам
источник
Вы делаете две разные вещи - пытаетесь переназначить целое число и вызываете метод в построителе строк. Если вы это сделаете, private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }то sbне изменится.
MT0
Я добавил StringBuilder (который является изменяемым), чтобы просто сопоставить Integer с другим изменяемым объектом. Если хотите, можете удалить весь код, связанный с StringBuilder, и просто распечатать i, чтобы увидеть 100.
Ашутош Нигам,
Это не доказывает неизменяемость - все, что вы делаете, - это повторно хешируете этот пример, демонстрируя, что Java использует передачу по значению (и что значения, передаваемые для объектов, являются указателями).
MT0
Попробуйтеprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0
@ MT0 Когда вы переходите по значению, StringBuilder все еще указывает на тот же объект, но Integer передает новую копию, а не ссылку на тот же объект. Если вы распечатываете в doInteger, вы показываете копию, которой обладает функция, а не основная функция. Мы хотим увидеть, является ли объект, на который указывает i в main, таким же или нет. Надеюсь, это проясняет концепцию :) Также неизменной версией StringBuilder является String. Дайте мне знать, если вы хотите, чтобы я поделился с ним образцом.
Ашутош Нигам
-1
public static void main(String[] args) {
    // TODO Auto-generated method stub

    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

    Integer i=1000;
    Integer i2=i;

    i=5000;

    System.out.println(i2); // 1000
    System.out.println(i); // 5000

    int j=1000;
    int j2=j;

    j=5000;

    System.out.println(j2); // 1000
    System.out.println(j); //  5000


    char c='a';
    char b=c;

    c='d';

    System.out.println(c); // d
    System.out.println(b); // a
}

Выход:

Привет-пока 1000 5000 1000 5000 д а

Итак, char является изменяемым, String Integer и int неизменяемы.

özgür arslan
источник
1
Этот ответ не дает никакой информации от других.
Джулио Каччин