我知道有人会说,Java只有值传递而没有引用传递,但是如果把引用地址也看成一个值的话,是可以这么说的。
对于两个传递的定义是什么呢?
第一,值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量。(一般发生在基本数据类型当中)
第二,引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。 所以对该副本进行操作时,会同时改变原对象。(发生在引用类型当中)
很多时候,传递都是发生在往方法里传参的情况,这个时候,你是可以将方法的形参当成一个变量,实际上,形参就是局部变量。
我会根据几道例题,来说明他们之间的区别
这是一道面试题
public static void main(String[] args) {
MyClass myClass = new MyClass();
change(myClass);
System.out.println(myClass.val);
}
private static void change(MyClass myClass) {
myClass = new MyClass();
myClass.val = 2;
}
public static class MyClass{
int val = 1;
}
为了方便理解,画图时,形参我改为myClass2了
以上可以大致可以清楚的看见引用变量指向对象的改变,但是为什么注释掉第三步会影响结果?
看下图
一开始的第三步的作用是,重写new一个对象,所以myClass2被重新赋值地址值,并不是原来myClass传给它的对象地址值。
可以看见,因为没有了第三步,所以这两个引用都是指向同一堆上的对象,所以任意一个引用只要对该对象进行数据域的操作,都会影响到数据域的改变。
myClass.val=2; //重新对变量val赋值
这便是引用传递。
再来看一道引用传递的面试题。
以下代码结果是什么?
public class foo {
public static void main(String sgf[]) {
StringBuffer a=new StringBuffer(“A”);
StringBuffer b=new StringBuffer(“B”);
operate(a,b);
System.out.println(a+”.”+b);
}
static void operate(StringBuffer x,StringBuffer y) {
x.append(y);
y=x;
}
}
看一下图
还是我刚才的那句话,你把形参看作是一个引用变量,引用传参的实际就是地址值的赋值。
x.append(y);
上面这个操作对应着是第二步骤,是对同一个对象的数据域的操作。
y=x;
这是再一次的引用传递,将x的地址赋值给了y,导致原来 y 放弃指向了 B字符 所在对象,而指向了 AB字符 所在的对象。
注意,很多新手误以为,x=y 就是b=a,这是编程,不是数学。
这里只是将 x 的地址赋值给了 y,与 b=a 没有一毛钱的关系, b 和 a 的存储的地址未发生改变。
再来看一道值传递的例题
public static void change(int i, int j) {
int temp = i;
i =j;
j =temp;
}
public static void main(String[] args) {
int a = 3;
int b = 4;
change(a,b);
System.out.println(a);
System.out.println(b);
}
change方法是平常将数值互换的一种方法,但是前提必须是本值,而不是传递的值。
在main方法中调用了change方法,在将a和b的值传递过去,这时就要注意了,这是值传递,传递的只是一个副本,与原值无关。
也就是,无论change方法中如何操作这两个形参a和b,输出的始终是
3
4
到这里,其实还没有结束,因为,你要注意有一个特殊的数据类型,就是String。
先来说String到底特殊在那一方面:
第一,当new出新对象时,会产生两个对象,第一是在字符串常量区,第二是在堆上(new 出对象)
第二,String是不可改变的,任何试图改变String的行为都会产生新对象,所以无论是用”+”还是用toLowerCase()等方法,都会产生新对象。
第三,String是引用类型。
虽然我说的有一丁点的偏题,但这些是对String传递时前的铺垫。
进入正题,看下面这道题,会输出什么?
public static void change(String s) {
s = “123”;
}
public static void main(String args[]) {
String s = “abc”;
change(s);
System.out.println(s);
}
看下面的图
为什么第二步和第三步会不一样?
String不是引用吗?
引用传递不是说,操作对象的数据域应该会影响到指向同一对象的引用啊?
但是,看我上面说的第二句话,String是不可改变的,任何试图改变String的行为都会产生新对象。
所以,实际上到第三步,两个S的指向已经是不同对象了,并没有给S重新赋值为“ 123”
再来看一题,
public class Example {
String str = "abc";
char[] ch = { 'a', 'b', 'c' };
public static void main(String args[]) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.println(ex.str);
System.out.println(ex.ch);
}
public void change(String str, char ch[]) {
*** // ch = new char[]{'a','b','c'};
str = "change";
ch[0] = 'c';
}
}
输出的结果为:
abc
cba
str变量不用说,是上面一道题一样的,两个str,
形参 str 指向了”change”,
成员变量 str 指向了”abc”。
变量ch和引用传递是一样的,都是操作的为同一对象,如果有* * *的一行不注释的话,结果就为
abc
abc
因为change的形参变量ch被重新赋值了地址值()。
ch = new char[]{'a','b','c'};
它操作的不再是成员变量 char[] ch 指向的对象了。