参数传递中的值传递
当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递?
**答:是值传递。**Java 语言的参数传递只有值传递。当一个实例对象作为参数被传递到方法中时,参数的值就是该对象的引用的一个副本。指向同一个对象,对象的内容可以在被调用的方法内改变,但对象的引用(不是引用的副本) 是永远不会改变的。
但是String和Integer这些类型的内容被调用的方法改变,原对象的值不会改变。因为String是不变类,任何的操作都不会影响已经创建好的值。而Integer和其他包装类也有这个特点。看下面这三个例子,可以更好地理解参数传递中的值传递。
例1:普通的类
package demo;
public class Cat {
private String name;
private int age;
private String gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public static void main(String[] args) {
Cat cat = new Cat();
cat.setName("小白");
cat.setAge(18);
cat.setGender("母");
System.out.println(cat.name);
cat.change(cat);
System.out.println(cat.name);
Cat cat1 = cat;
cat1.setName("小美");
System.out.println(cat1.name);
System.out.println(cat.name);
}
public void change(Cat cat) {
cat.name = "小花";
}
}
//输出的值为
小白
小花
小美
小美
使用change方法可以修改cat对象的值,因为该方法将cat对象的引用地址传给了参数cat,然后参数cat修改其对应的堆空间中的值,而调用change方法的cat对象指向的也是这个堆空间,所以此修改会影响到原对象。
例2:string类
public class StrTest {
public static void main(String[] args) {
String str = new String("laowang");
changeString(str);
System.out.println(str);
}
public static void changeSting(String str) {
str.replace("x","d");
}}
//输出的值为
laowang
使用change方法,复制一份str对象的引用给参数str,然后str使用replace方法进行一些修改,但string类对象是不可修改的,所以此番修改会重新创建一个string对象,修改是在新创建的对象上进行,不影响原对象。这个例子很好地说明string的不可变性
要警惕另一种情况,例子如下:
public class StrTest {
public static void main(String[] args) {
String str = new String("laowang");
changeString(str);
System.out.println(str);
}
public static void changeSting(String str) {
str="xiaowang"
}}
//结果如下:
laowang
虽然结果一样,但原因并不是string的不可变性。是因为调用方法时,计算参数string的值(可以理解为“laowang”这个字符串对象在内存中的地址),拷贝给参数str,参数str虽然拿到了原来laowang字符串对象的引用(即str的值等于string的值,也即hello的地址),但是立马给这个参数重新赋值,也就是创建了一个新的字符串,把这个字符串的地址赋给了参数str,这个时候str参数已经跟“laowang”字符串没有任何关系了,str不再指向"laowang",改指向xiaowang了,但是这个过程中laowang自身以及laowang在内存中的地址没有任何变化.
//还有另外一种情况
//示例1:对象类型
public class PassReferByValue
{
String a = "123";
public static void test(PassReferByValue test)
{
test.a = "abc";
}
public static void main(String[] args)
{
PassReferByValue test = new PassReferByValue();
System.out.println("对象的数据的初始值是:" + test.a); //123
test(test);
System.out.println("通过调用test方法修改对象的数据的值之后:" + test.a); //abc
}
}
在方法中使用类实例改变全局变量的值,由于该全局变量的String类型,为“123”,该对象不会被改变,但其会被覆盖,当使用类实例.属性名访问时,访问到的是修改后的值。因为该值在方法之外也会生效。这个和string类型的不可变性无关。
例3:Integer类和stringBuilder类
import java.util.Iterator;
/**
* Created by lili on 15/9/24.
*/
public class TestNew {
public static void main(String args[]){
Integer i1 = 10;
Integer i2 = 20;
System.out.println(i1 + " " + i2);
change(i1,i2);
System.out.println(i1 + " " + i2);
StringBuilder sb1 = new StringBuilder("sb1");
StringBuilder sb2 = new StringBuilder("sb2");
System.out.println(sb1 + " " + sb2);
change(sb1,sb2);
System.out.println(sb1 + " " + sb2);
}
public static void change(Integer i1, Integer i2){
i1 = 100;
i2 = 200;
}
public static void change(StringBuilder i1, StringBuilder i2){
i1.append("sbsbsb1");
i2.append("sbsbsb2");
}
}
//输出结果为以下所示,可以知道只有stringBuilder才为引用传递。Integer也是不可变类。
10 20
10 20
sb1 sb2
sb1sbsbsb1 sb2sbsbsb2
下面再来看一个数组的例子
public static void main(String[] args) {
int[] arr = {2, 3, 4, 8};
change(arr);
System.out.println(arr[2]);
}
private static void change(int[] arr) {
for (int i = 0; i < arr.length; i++) {
if (i % 2 == 0) {
arr[i] *= i;
}
}
}
输出的值是多少呢?
数组作为参数传递到change方法中,传递的为引用地址,方法的操作可以对原数组进行修改,故数组里面的值会变化,最后输出的值为8.