值传递:在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容
引用传递:就是指向真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,形参和实参指向同一块内存地址,对形参的操作会影响的真实内容
对于基本数据类型是值传递,对于引用数据类型是引用传递。
在基本数据类型中,变量和值都存储在栈中,用一个方法修改变量时,只是修改了变量的副本,而变量本身没有被改变。
引用数据类型中,变量存在栈中,值存在堆中,变量通过指针指向值。用一个方法修改变量时,形参和实参的地址是相同的。所以改变形参的内容,实参也被改变。
当然也可以说java就是值传递,只不过对于引用数据类型来说,复制的是地址值。
例题一:
public static void main(String[] args) {
Numtransport numtransport = new Numtransport();
int i = 10;
numtransport.fermin(i);
System.out.println(i);
}
void fermin(int i){
i++;
}
输出:
10
因为基本数据类型是值传递的,方法体的变量存储的是副本,无法影响到main方法的i值。所以输出是10
例题二:
public static void main(String[] args) {
Referencetransport referencetransport = new Referencetransport();
User hollis = new User();
hollis.setName("Hollis");
hollis.setGender("Male");
referencetransport.pass(hollis);
System.out.println("print in main , user is " +hollis.getName()+ ","+hollis.getGender());
}
public void pass(User user) {
user.setName("hollischuang");
System.out.println("print in pass , user is " +user.getName()+","+ user.getGender());
}
输出:
print in pass , user is hollischuang,Male
print in main , user is hollischuang,Male
对于引用数据类型user,传递的是地址值。所以name属性被改变。
例题三:
public static void main(String[] args) {
Referencetransport referencetransport = new Referencetransport();
User hollis = new User();
hollis.setName("Hollis");
hollis.setGender("Male");
referencetransport.pass(hollis);
System.out.println("print in main , user is " +hollis.getName()+ ","+hollis.getGender());
}
public void pass(User user) {
user = new User();
user.setName("hollischuang");
user.setGender("Male");
System.out.println("print in pass , user is " +user.getName()+","+ user.getGender());
}
输出:
print in pass , user is hollischuang,Male
print in main , user is Hollis,Male
因为方法pass中新建了一个变量。这使得原变量没有改变。pass中输出的是新建变量user的属性值。
需要特别注意String类型。这是一个引用类型,却在很多方面上体现出值传递的特征。
例题四:
public static void main(String[] args) {
Stringtransport pt = new Stringtransport();
String name = "Hollis";
pt.pass(name);
System.out.println("print in main , name is " + name);
}
public void pass(String name) {
name = "hollischuang";
System.out.println("print in pass , name is " + name);
}
输出:
print in pass , name is hollischuang
print in main , name is Hollis
显然在方法pass中,name指向了字符串“hollischuang”,但是主方法的name却并没有发生改变。这是因为String是final类型的。
终极一题:
class Value{
public int i=15;
}
public class Test{
public static void main(String argv[]){
Test t=new Test( );
t.first( );
}
public void first( ){
int i=5;
Value v=new Value( );
v.i=25;
second(v,i);
System.out.println(v.i);
}
public void second(Value v,int i){
i = 0;
v.i = 20;
Value val = new Value( );
v = val;
System.out.println(v.i+" "+i);
}
}
这道题的输出结果会是什么?
答案是15 0 20
首先,要分清楚属性和与属性重名的局部变量。v.i输出属性值,i输出局部变量值。其次,在second方法中,新定义了一个val变量,并将val赋给second中的变量v,此时v和val的属性值i同为初始值15。而second中的局部变量 i 为0。second结束后,返回继续执行first方法。因为v.i在second中被修改为20,所以此时输出20。为什么不是15呢?因为15是v指向second方法中new Value()这个对象的属性值。而first方法中的v对象的属性值是被修改成了20