В данном случае в качестве аргумента передаётся примитивное целочисленное значение переменной a
(локальной для метода main
), то есть при вызове метода test1
в стек записывается копия целого числа (аргумента метода), внутри метода эта копия считывается из стека и все модификации этой переменной внутри метода остаются незамеченными снаружи.
Если бы передавалась переменная-объект типа Integer
, результат был бы такой же, так как класс Integer
является неизменяемым (immutable), то есть его внутреннее состояние нельзя изменить и разным числам 1, 2, .. соответствуют разные экземпляры.
static void test2(Integer a) {
a++;
System.out.println("test2: a=" + a);
}
Integer a = 10;
test2(a);
System.out.println("main: a=" + a);
test2: a=11
main: a=10
То есть здесь в метод через стек передаётся копия ссылки на Integer(1)
, вследствие инкремента локальной переменной a
внутри метода присваивается другая ссылка на Integer(2)
, и это изменение никак не затрагивает "внешнюю" локальную переменную a
.
Чтобы добиться изменения внутри некоего экземпляра класса после вызова метода, необходимо этот класс реализовать соответствующим образом, то есть реализовать класс с полем для хранения состояния и методами для изменения этого состояния:
static class Foo {
private int x;
Foo(int x) {this.x = x;}
public void inc() {this.x++;}
public void dec() {this.x--;}
public void setX(int x) { this.x = x;}
public void add(int y) {this.x += y;}
// и т.д.
public int getX() {return this.x;}
@Override public String toString() {
return "Foo: x=" + x;
}
}
Тогда при передаче ссылки на экземпляр такого класса и вызове методов-мутаторов состояния внутри некоего метода, эти изменения состояния будут видимы снаружи.
static void test3(Foo f) {
f.inc();
System.out.println("test3: f=" + f);
}
Foo f = new Foo(10);
test3(f);
System.out.println("main: f=" + f);
Результат:
test3: f=Foo: x=11
main: f=Foo: x=11