深入理解java方法的按值调用
在编程中,在方法调用之间复制数据的过程称为按值调用。在java中,我们不需要指定要传递的实际参数要使用按值调用,因为它是自动发生的实际上也是唯一的选择。不管传递给方法的实际参数是什么类型,相关的形式参数都会得到该数据的一份拷贝,这就是按值调用的工作原理。
有如下代码:
public class Test {
public void changeNum(int a){
a=3;
System.out.println("方法内a=3");
}
public void changeString(String b){
b="s";
System.out.println("方法内b="+b);
}
public void changeStringBuffer1(StringBuffer c){
c.append("111");
System.out.println("方法内c="+c);
}
public void changeStringBuffer2(StringBuffer d){
d=new StringBuffer("1");
System.out.println("方法内d="+d);
}
public static void main(String[] args) {
Test t=new Test();
int a=1;
String b="b";
StringBuffer c=new StringBuffer("c");
StringBuffer d=new StringBuffer("d");
t.changeNum(a);
System.out.println("main中a="+a);
t.changeString(b);
System.out.println("main中b="+b);
t.changeStringBuffer1(c);
System.out.println("main中c="+c);
t.changeStringBuffer2(d);
System.out.println("main中d="+d);
}
}
运行程序后,结果为如下图:
为了弄清这种结果:
int num = 10;
String str = "hello";
二:明白赋值运算符(=)的作用
int num = 20;
String str = "java";
对于引用类型 str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象不会被改变(重要)。
如上图所示,"hello" 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器回收)
三:调用方法时发生了什么?
- public class TempTest {
- private void test1(A a){
- a.age = 20;
- System.out.println("test1方法中的age="+a.age);
- }
- public static void main(String[] args) {
- TempTest t = new TempTest();
- A a = new A();
- a.age = 10;
- t.test1(a);
- System.out.println(”main方法中的age=”+a.age);
- }
- }
- class A{
- public int age = 0;
- }
运行结果如下:
- test1方法中的age=20
- main方法中的age=20
用上面的例子来进行分析:
(1):运行开始,运行第8行,创建了一个A的实例,内存分配示意如下:
(2):运行第9行,是修改A实例里面的age的值,运行后内存分配示意如下:
(3):运行第10行,是把main方法中的变量a指向对象的内存空间地址,传递给test1方法中的a变量。请注意:这两个a变量是完全不同的,不要被名称相同所蒙蔽。
内存分配示意如下:
形成的新的内存示意图如下:
也就是说:是两个变量都指向同一个空间。
(4):运行第3行,为test1方法中的变量a指向的A实例的age进行赋值,完成后形成的新的内存示意图如下:
此时A实例的age值的变化是由test1方法引起的
(5):运行第4行,根据此时的内存示意图,输出test1方法中的age=20
(6):运行第11行,根据此时的内存示意图,输出main方法中的age=20
6:对上述例子的改变
能不能让test1方法里面的修改不影响到main方法里面呢?
方法是在test1方法里面新new一个实例就可以了(相当于重新赋值,指向新的对象,而非对原对象的内容进行更改)。改变成下面的例子,其中第3行为新加的:
- public class TempTest {
- private void test1(A a){
- a = new A();//新加的一行
- a.age = 20;
- System.out.println("test1方法中的age="+a.age);
- }
- public static void main(String[] args) {
- TempTest t = new TempTest();
- A a = new A();
- a.age = 10;
- t.test1(a);
- System.out.println(”main方法中的age=”+a.age);
- }
- }
- class A{
- public int age = 0;
- }
运行结果为:
- test1方法中的age=20
- main方法中的age=10
为什么这次的运行结果和前面的例子不一样呢,还是使用内存示意图来理解一下
(1):运行开始,运行第9行,创建了一个A的实例,内存分配示意如下:
(2):运行第10行,是修改A实例里面的age的值,运行后内存分配示意如下:
(3):运行第11行,是把main方法中的变量a所指向的对象内存空间地址,传递给test1方法中的a变量。请注意:这两个a变量是完全不同的,不要被名称相同所蒙蔽。
内存分配示意如下:
传递完成后形成的新的内存示意图如下:
也就是说:是两个变量都指向同一个空间。
(4):运行第3行,为test1方法中的变量a重新生成了新的A实例的,完成后形成的新的内存示意图如下:
(5):运行第4行,为test1方法中的变量a指向的新的A实例的age进行赋值,完成后形成的新的内存示意图如下:
注意:这个时候test1方法中的变量a的age被改变,而main方法中的是没有改变的。
(6):运行第5行,根据此时的内存示意图,输出test1方法中的age=20
(7):运行第12行,根据此时的内存示意图,输出main方法中的age=10