c#中对于参数的传递,有二种处理方式,默认情况下:值类型的参数,按值传递(即:方法体内的参数是原值的副本);引用类型的参数,"加ref关键字后“,按引用传递(即:方法体内的参数,是对象的指针引用,在方法体内修改了对象的属性,方法调用完成后,这种变化也会保持下去).
java虽然也是OO语言,但是这一点有很大不同,不管是“值”类型的简单参数(比如:int),还是“引用”类型的对象参数(比如:Object),参数永远是按值传递(参数永远是原值的副本)。只不过,对于int型的简单参数,比如:5,副本也是5,相当于在内存又开辟一块空间,存储5这个值,但这二个5完全不相关联。而对于对象参数(比如:Object),参数副本应理解成对象指针引用“地址值”的副本,比如:原Object对象在内存中的指针地址为OX0001,则参数为OX0001的另一个副本,因为这二个地址值相同,所以在大多数情况下,方法体内,通过指针地址定位到的对象是同一个,即:方法体内修改对象的属性,该对象就被永远修改了,即使方法调用完成,这种修改的结果也会永远保存下去。这给很多java初学者造成java中参数有按引用传递的错觉。
看下面的示例:
先定义一个测试用的Person类
1 package com.cnblogs.yjmyzz.test; 2 3 public class Person { 4 5 public Person(){} 6 7 private String name; 8 9 public String getName() { 10 return name; 11 } 12 13 public void setName(String name) { 14 this.name = name; 15 } 16 17 public String toString() { 18 return "name:" + name; 19 } 20 21 }
然后来一个单元测试:
1 @Test 2 public void testPerson1() { 3 4 Person p1 = new Person(); 5 p1.setName("aaa"); 6 System.out.println(p1); // aaa 7 System.out.println(); 8 9 changePerson1(p1);// xxx 10 System.out.println(p1);// xxx 11 12 } 13 14 void changePerson1(Person p) { 15 p.setName("xxx"); 16 System.out.println(p); 17 System.out.println(); 18 }
运行结果:
name:aaa
name:xxx
name:xxx
如上图所示,这种情况很好理解,p1,p因为值相同,所以根据这个地址值,寻址找到的对象是同一个,改p.name值与改p1.name没什么分别
再来一个版本:
1 @Test 2 public void testPerson2() throws InstantiationException, 3 IllegalAccessException { 4 5 Person p1 = new Person(); 6 p1.setName("aaa"); 7 System.out.println(p1); // aaa 8 System.out.println(); 9 10 changePerson2(p1);// yyy 11 System.out.println(p1);// xxx 12 } 13 14 15 16 void changePerson2(Person p) throws InstantiationException, 17 IllegalAccessException { 18 Person newPerson = Person.class.newInstance(); 19 newPerson.setName("yyy"); 20 21 p = newPerson; 22 23 System.out.println(p); 24 System.out.println(); 25 }
运行结果:
name:aaa
name:yyy
name:aaa
如上图,刚开始P1,P值相同,但是在ChangePerson2内部,又new了一个Person的新实例:newPerson,修改newPerson.Name,并不会影响P1和P,在最后,将newPerson赋值给p,注意:p只是p1的地址值副本,所以虽然p与newPerson最终指向一样了,但是并不会影响原值p1,所以方法调用完成后,p1.name还是原来的值。