直接上例子
1.方法中,局部变量为基本类型
public class BaseChange {
static void change(int a) {
a += 5;
}
public static void main(String[] args) {
int a = 1;
change(a);
System.out.println(a);
}
}
得到的结果是 1。
2.方法中,局部变量为数组
static void changeArr(int[] arr) {
for (int i = 0; i < arr.length; i++) {
arr[i] += 100;
}
}
public static void main(String[] args) {
int[] arr = { 1, 2, 3, 4 };
changeArr(arr);
System.out.println(Arrays.toString(arr));
}
得到的结果是:
[101, 102, 103, 104]
3.方法中,局部变量为String
private static void change(String s) {
s += "123";
}
public static void main(String[] args) {
String s = "abc";
change(s);
System.out.println(s);
}
得到的结果是:
abc
4.方法中,为自定义对象
Foo
public class Foo {
private int a;
private int b;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
@Override
public String toString() {
return "Foo [a=" + a + ", b=" + b + "]";
}
}
测试类:
static void changeFoo(Foo foo) {
foo.setA(100);
foo.setB(200);
}
public static void main(String[] args) {
System.out.println("--change object-");
Foo foo = new Foo();
System.out.println(foo);
changeFoo(foo);
System.out.println(foo);
}
得到的结果是:
--change object-
Foo [a=0, b=0]
Foo [a=100, b=200]
通过上面4个例子,可以得出以下结论:
1.当方法中的参数为int等基本类型时候,方法结束,值不变;
2.当方法中参数为String的时候,方法结束,值也不变;
3.当方法中参数为对象(自定义对象和数组),方法技术,值发生了变化。
方法以栈帧的形式被虚拟机加载到虚拟栈里面,每个线程单独的管理自己的虚拟栈,所以,多个线程调用同一个方法,是相互不干扰的(前提不对全局的数据修改);
方法结束的时候,栈帧退出虚拟机栈。此时,所有的方法中的入参的声明周期就结束了。
然后来回顾上面4段代码:
第一段代码:
1.当main中调用 int a=1的时候,基本变量a入虚拟栈
2.当调用方法change的时候,change方法栈帧入栈,局部变量a在栈帧上操作a+=5
3.当change方法结束的时候,栈帧退栈,打印a的值,a仍然是1。
因为java中,基本类型就是存储在虚拟栈中的。当然如果是全局变量,或者静态变量,至少可以肯定的是,不是存储在线程私有的虚拟栈上面的。
第二段代码和第四段代码
把他们放在一起讲,是笔者粗糙的认为,数组也是对象,所以直接放在一起解释。对于对象,虚拟机栈中存储的是该对象的应用,对象在堆上分配内存。
1.首先,方法进入前,变量引用入虚拟机栈,实际内容入堆,引用指向内容,且内容被初始化x:(当然,虚拟机实际的存储模型里面,还有一个类的信息,但是,数据确实是在堆里修改的)
2.当调用change方法的时候,引用作为参数传输到栈帧中,这时,两个引用同时都指向对象,然后,方法中进行操作,绿色框框中的引用将对象的值改为M
3.方法结束,栈帧退栈
因此,最后打印的结果,发生了变化,感觉是方法里面把值修改了。
再来看第三段代码:
我们先假设,String的值分配在堆上,但实际上(String有可能被分配在方法代,书上说的)。
String是一种特殊的对象,在堆上的表现是不可动态扩展的,就是如果 a="1"执行了a+="2"的操作,那么,堆上会产生两个String对象,一个是"1",一个是"2"。(java在编译的过程中,高版本的或许会将a="1",a+="2"的这种代码进行优化,不过,并不妨碍我们分析String作为参数传递到方法中).
ok,现在来进行分析代码:
1.首先String s="abc",此时,虚拟栈上会有一个s的变量引用,堆上有一个“abc”的对象,s的变量引用指向堆上的"abc"
2.调用change方法,栈帧入栈,参数栈,蓝色的引用作为参数传递给绿色的引用,这是,他们指向同一个对象:
3.方法中,执行s+="123",前面有说到,String比较特殊,他不会像其他对象一样,在内部进行修改,而是新分配一块内存。这是可以看到,方法中的引用和蓝色的引用,已经指向的不是同一个对象了。
4.方法执行完毕,栈帧退栈:
所以,最终输出的结果,仍然是"abc"。