Є цілим незмінним


101

Я знаю, що це, мабуть, дуже дурно, але багато де стверджують, що клас Integer у Java незмінний, проте такий код:

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

Виконується без будь-яких проблем, даючи (очікуваний) результат 6. Настільки ефективно значення а змінилося. Хіба це не означає, що Integer можна змінювати? Вторинне питання і трохи поза темою: "Незмінні класи не потребують конструкторів копіювання". Хтось хоче пояснити, чому?


12
Клас незмінний, але Autoboxing робить фанки речі: stackoverflow.com/questions/3085332 / ...
WKL

Дякую, бокс був ключовим словом, яке мені потрібно було
загуглити

7
Ви плутаєте незмінне з кінцевим або постійним значенням.
Код ентузіазму

Відповіді:


95

Незмінна не означає, що aніколи не може дорівнювати іншому значенню. Наприклад, Stringвін також незмінний, але я все одно можу зробити це:

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

strне було змінено, швидше strце вже зовсім нещодавно створений об’єкт, як і ваш Integer. Отже, значення aне мутувало, але воно було замінено на абсолютно новий об'єкт, тобто new Integer(6).


14
Msgstr "Це тому, що str тепер є повністю новим екземпляром об'єкта". Вірніше, str (a varibale) вказує на новий об'єкт. Сам об'єкт не можна змінювати, але оскільки змінна не є остаточною, вона може вказувати на інший об'єкт.
Сендмен

Так, він вказує на інший об'єкт, який був створений в результаті +=операції.
Тревіс Вебб,

11
Строго кажучи, це не повинен бути новий об'єкт. Бокс використовує Integer.valueOf(int)і цей метод підтримує кеш Integerоб'єктів. Таким чином, результат +=на Integerзмінної може бути об'єктом , який існував раніше (або це може бути навіть той же об'єкт ... в разі a += 0).
Стівен С

1
Чому JavaDoc for String прямо говорить про свою незмінність, а JavaDoc для Integer - ні? Ось ця різниця, чому я читаю це Питання ...
cellepo

51

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.

19

Ви можете визначити, що об’єкт змінився, використовуючи 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

Ви бачите, що основний "ідентифікатор" об'єкта, на який 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 для посилання на новий цілий об'єкт з іншим значенням.

Ви ніколи не модифікували оригінальний об'єкт, скоріше вказали посилання на інший об'єкт.

Про різницю між об’єктами та посиланнями читайте тут .


Просто і легко сказано непрофесійною мовою, з усіма іншими складними поясненнями :)
Рошан Фернандо

5

Імутація не означає, що ви не можете змінити значення змінної. Це просто означає, що будь-яке нове призначення створює новий об’єкт (призначає йому нове місце в пам’яті), а потім йому присвоюється значення.

Щоб зрозуміти це для себе, виконайте присвоєння цілого числа у циклі (із цілим числом, оголошеним за межами циклу) і перегляньте об'єкти, що живуть у пам'яті.

Причиною того, що конструктор копій не потрібен для незмінних об'єктів, є простий здоровий глузд. Оскільки кожне призначення створює новий об’єкт, мова технічно вже створює копію, тому вам не потрібно створювати іншу копію.


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");
    }

}

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

Результат приходить до he Hi There 100 замість очікуваного результату (у випадку, якщо і sb, і я є змінними об'єктами) 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 d a

Отже, char є змінним, String Integer та int незмінні.


1
Ця відповідь не дає жодної інформації від інших.
Джуліо Каччін,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.