【Java】 参数的传递:值传递与引用传递讨论

 

内容稍多,可直接看第4点的讨论结果java

前言windows

在涉及到传递参数给方法时,容易出现一些参数传递错误的问题,这就涉及到了参数的传递问题,必须搞清楚:参数是如何传递到方法中的?通常来讲,参数的传递能够分为两种:值传递和引用传递。数组

所谓值传递,就是方法中的形参得到的是实参的值,而引用传递,就是说方法中的形参得到的是实参的引用(地址)。app

参数的传递其实相似于一个赋值操做,因此接下来,先讨论值和地址的问题,再讨论赋值操做,最后才进行参数传递的讨论。iphone

1.基本概念ide

  首先要搞清楚一个概念,即:变量中储存的内容是什么。ui

  变量类型分为基本数据类型和引用类型:spa

    基本数据类型的变量直接存储的是变量的值,如int i=10;  i中存储的为变量的值。.net

    引用类型的变量存储的则是实际对象的地址,如Dog myDog = new Dog();  myDog中存储的仅仅是地址(也能够称为引用)。3d

 

2.赋值操做

  须要明确赋值操做的含义。

  赋值操做“=”包含两个意思:1.放弃了原有的值或引用;2.获得了 = 右侧变量的值或引用。

    基本类型:= 操做表明完整复制了变量的值。例如:int a=b;  a仅获取了b的值,二者在此以后并没有任何关系。

    引用类型:= 操做表明复制了变量的引用。 例如:Dog aDog=bDog;  aDog复制了bDog的引用,两个变量都指向同一个Dog对象。

  一个简单的例子:

public class Test {
    public static void main(String[] args)  {
    	int x=10;
    	int y=x;
    	x=20;
    	System.out.println("x:"+x);
    	System.out.println("y:"+y);
    	Dog aDog=new Dog();
    	aDog.name="阿黄";
    	aDog.age=1;
    	Dog bDog=aDog;
    	aDog.name="旺财";//改变aDog的名字
    	bDog.age=2;//改变bDog的年龄
    	System.out.println("aDog:"+aDog.name+","+aDog.age);
    	System.out.println("bDog:"+bDog.name+","+bDog.age);
    }
}

class Dog {
	String name;
	int age;
}

  

x:20
y:10
aDog:旺财,2
bDog:旺财,2
输出结果

  由结果能够看出,对于基本数据类型,在y=x后,y仅仅是得到了x的值,二者再也不有关系了,所以输出结果不一样;

  对于引用类型,在bDog=aDog后,bDog获取了aDog的地址,两个变量都是指的同一条狗。虽然分别变化了aDog的名字和bDog的年龄,但其实变的都是同一个对象,因此输出结果相同。

 

3.方法中的参数传递讨论

  方法中参数传递的本质即为赋值操做。

/*第一个例子:基本类型*/
void foo(int value) {
    value = 100;
}
foo(num); // num 没有被改变

  例一分析:参数传递,至关于执行了value=num,value得到了num的值,这以后value与num无关,因此num没有改变。

 

/*第二个例子:没有提供改变自身方法的引用类型*/
void foo(String text) {
    text = "windows";
}
foo(str); // str 也没有被改变

  例二分析:参数传递后,text与str都存储了相同的地址,指向同一个对象;但以后text从新进行了赋值操做text= "windows",text放弃了原有的引用而指向了新的对象(见上述赋值操做的两重意思),而str仍为原有的引用,因此str没有改变。

 

/*第三个例子:提供了改变自身方法的引用类型*/
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
    builder.append("4");
}
foo(sb); // sb 被改变了,变成了"iphone4"。

  例三分析:参数传递后,sb与builder指向同一个对象,builder.append()仅对该对象进行了增长操做,sb和builder仍指向同一对象,因此sb变为了"iphone4"。

  参数传递后:

  执行builder.append("4")后:

 

/*第四个例子:提供了改变自身方法的引用类型,可是不使用,而是使用赋值运算符。*/
StringBuilder sb = new StringBuilder("iphone");
void foo(StringBuilder builder) {
    builder = new StringBuilder("ipad");
}
foo(sb); // sb 没有被改变,仍是 "iphone"。  

  例四分析:参数传递后,sb与builder指向同一个对象,但以后builder从新进行了赋值操做builder = new StringBuilder("ipad"),builder指向了一个新的对象,而sb仍为指向原来的对象,因此sb没有改变。

  参数传递后:

  执行builder = new StringBuilder("ipad")后:

 

4.值传递与引用传递讨论结果

  在上述讨论中,能够知道,参数传递至关于赋值操做,

    对于基本数据类型,方法中的形参获取了实参的值,便可以理解为值传递

    对于引用类型,方法中的形参获取的是实参的引用,二者指向同一对象,便可以理解为引用传递

  值传递中,形参的改变不影响实参;引用传递中,形参让对象改变,至关于让实参也发生了改变。

  也有人认为,Java中的参数传递只有值传递,我以为应该是把“引用传递”认为是引用的地址值的传递,因此统称为值传递。(知识还很浅薄,只是我的理解)

 

5.再来两个例子

  基本数据类型比较简单,再也不赘述。引用类型的例子再来两个,读者可自行分析

public class Test {
	private void test1(A a) {
		a.age = 20;
		System.out.println("test1方法中的age=" + a.age);
	}
	public static void main(String[] args) {
		Test t = new Test();
		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
test1结果

 

public class Test {
	private void test2(A a) {
		a = new A();// 新加的一行
		a.age = 20;
		System.out.println("test2方法中的age=" + a.age);
	}
	public static void main(String[] args) {
		Test t = new Test();
		A a = new A();
		a.age = 10;
		t.test2(a);
		System.out.println("main方法中的age=" + a.age);
	}
}
class A {
	public int age = 0;
}

  

test2方法中的age=20
main方法中的age=10
test2结果

  

  提示:test2中新加的一行使形参从新指向了一个新的对象,因此以后操做就和主程序中的实参无关了。

 

 6.String类型和数组的参数传递

  String类型和数组的参数传递比较容易让人困惑,后面又看到相关的文章,这里再讨论一下

public class Test {
	public void arrayPassTest(String s, String[] ss) {
		s = "bad";
		ss[0] = "bbb";
	}
	public static void main(String[] args) {
		String s1 = new String("good");
		String[] ss1 = { "aaa" }; // string数组,只有一个元素
		Test test = new Test();
		test.arrayPassTest(s1, ss1);
		System.out.println(s1 + ss1[0]);
	}
}

  

goodbbb 
输出结果

对于String类型,

  不少人将String类型的参数传递理解为值传递(相似于基本数据类型),但其实String 类型的传递与其余引用类型相同,仍然是引用传递,也便是地址传递。

  既然是引用传递,那为何s1不随s改变呢?在方法arrayPassTest()中,s="bad"其实至关因而s=new String("bad"),s指向了一个新的String对象,与实参s1再也不是同一个对象了,因此s1不变。(明白new String(),将String类型和其余引用类型同样理解就能够了。)

对于数组,

  数组也是一种引用类型,ss和ss1指的是同一个数组对象,ss[0]="bbb"只是将该数组对象中的第一个元素改变了,并无新的数组对象产生。ss和ss1指向的数组对象始终都是同一个,因此ss1发生了改变。

 

7.参考来源

  《Head First Java》

  Java 究竟是值传递仍是引用传递?

  主要参考的是这两个回答:12

  【Java基础】Java:按值传递仍是按引用传递详细解说

  java之传递String类型的参数

本文内容是根据参考内容 结合本身的我的理解写的,若有错误,请多多指正。

相关文章
相关标签/搜索