Неизменяемое целое число, заключенное в другой класс (вызов по значению) - PullRequest
0 голосов
/ 02 мая 2018

Я задавался вопросом о вызове по значению в Java и попробовал какой-то код.

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        Integer integer = 4;
        System.out.println(integer);
        test.change(integer);
        System.out.println(integer);
    }

    public void change(Integer integer) {
        integer++;
    }
}

Так как java является вызовом по значению, я блуждаю, как:

4
5

Но это напечатано

4
4

А потом я узнал, что целые числа являются неизменяемыми, поэтому мой метод «change» создал новое Integer со значением 5, в то время как «integer» в main все еще ссылался на 4.

Тогда я написал следующий класс:

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        MyInteger myInteger = new MyInteger();
        myInteger.x = 4;
        System.out.println(myInteger.x);
        test.change(myInteger);
        System.out.println(myInteger.x);
    }
    public void change(MyInteger myInteger) {
        myInteger.x++;
    }
} 
class MyInteger {
    Integer x;
}

А теперь вывод был, как я и ожидал сначала

4
5

А вот и мой тезис:

Метод change(Integer integer) изменяет объект myInteger путем создания нового неизменяемого Integer вместо Integer x, а аргумент myInteger в этом методе указывает на один и тот же экземпляр MyInteger все время. Я прав?

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Давайте сделаем небольшой анализ и покопаемся в байт-коде обоих подходов.

public class Main {

    public static void main(String[] args) {
        Main test = new Main();
        Integer integer = 4;
        System.out.println(integer);
        test.change(integer);
        System.out.println(integer);
    }

    public void change(Integer integer) {
        integer++;
    }
}

Байт-код для change метода:

public change(Ljava/lang/Integer;)V
   L0
    LINENUMBER 14 L0
    ALOAD 1
    ASTORE 2
    ALOAD 1
    INVOKEVIRTUAL java/lang/Integer.intValue ()I // gets value of wrapped int
    ICONST_1 // load 1 into stack
    IADD // add 1 to your value
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; // return
    DUP
    ASTORE 1
    ASTORE 3
    ALOAD 2
    POP

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

Кто-то может подумать, что вывод для следующего кода будет 4, 5:

public class Main {
    public static void main(String[] args) {
        Main test = new Main();
        Integer integer = 4;
        System.out.println(integer);
        Integer integerNew = test.change(integer);
        System.out.println(integerNew);
    }

    public Integer change(Integer integer) {
        return integer++;
    }
}

Вывод 4, 4.

Почему? Потому что это постинкремент . Вы увеличили новый Integer и вернули старый. Анализ байт-кода подтверждает это:

public change(Ljava/lang/Integer;)Ljava/lang/Integer;
   L0
    LINENUMBER 14 L0
    ALOAD 1
    ASTORE 2 // store the copy of the first Integer (4)
    ALOAD 1 // 
    INVOKEVIRTUAL java/lang/Integer.intValue ()I // get value of first
    ICONST_1 // load 1
    IADD // add 1
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; // get modified value Integer (5)
    DUP
    ASTORE 1 // save it
    ASTORE 3
    ALOAD 2 // load the copy Integer (4)
    ARETURN // return it

Итак, мы видим, что значение Integer было увеличено и ... и снова потеряно, потому что мы вернули значение в состоянии до того, как оно было увеличено.

Предварительное увеличение - это то, что мы ищем:

public Integer change(Integer integer) {
    return ++integer;
}

И байт-код:

public change(Ljava/lang/Integer;)Ljava/lang/Integer;
   L0
    LINENUMBER 14 L0
    ALOAD 1 // Integer (4)
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ICONST_1 // load 1
    IADD // becomes Integer (5)
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    DUP
    ASTORE 1 // save it
    ARETURN // return Integer (5)

Значение было увеличено до того, как оно было возвращено, и мы можем принять его.

Анализ последнего случая:

public class Main {

    public static void main(String[] args) {
        Main test = new Main();
        MyInteger myInteger = new MyInteger();
        myInteger.x = 4;
        System.out.println(myInteger.x);
        test.change(myInteger);
        System.out.println(myInteger.x);
    }

    public void change(MyInteger integer) {
        integer.x++;
    }
}

class MyInteger {
    Integer x;
}

и его байт-код:

public change(Lcom/test/MyInteger;)V
   L0
    LINENUMBER 15 L0
    ALOAD 1
    ASTORE 2
    ALOAD 2 
    GETFIELD com/test/MyInteger.x : Ljava/lang/Integer; // get Integer (4)
    ASTORE 3 // store copy of Integer value (4)
    ALOAD 2
    ALOAD 2
    GETFIELD com/test/MyInteger.x : Ljava/lang/Integer; // get Integer (4)
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ICONST_1 
    IADD // add 1
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; 
    DUP_X1
    PUTFIELD com/test/MyInteger.x : Ljava/lang/Integer; // PUT Integer (5) BACK!
    ASTORE 4 
    ALOAD 3
    POP

Как видите, начиная с строки PUTFIELD экземпляр MyInteger начинает содержать ссылку на Integer со значением 5.

Надеюсь, что это поможет вам понять более подробно о теме.

0 голосов
/ 02 мая 2018

Вы абсолютно правы. myInteger.x++ фактически изменяет значение, потому что Integer x переменная экземпляра является общей.

Infact myInteger.x++ также создает новый неизменный Integer экземпляр. Но вы можете получить доступ к этому новому экземпляру через переменную test, так что вы никогда не потеряете его изменения.

Как правило большого пальца, чтобы избежать путаницы Я бы сказал:

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

В вашем случае: myInteger.x, x является подобъектом myInteger. Именно поэтому вы могли видеть изменения, которые вы делаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...