JAVA - 值传递与不存在的引用传递

概念

pass-by-value(按值传递):方法调用时,传递参数会在内存中开辟新的空间来存储参数,实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个copy此时内存中存在两个相同的变量,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值离开函数体,不影响参数本身

pass-by-reference(引用传递):引用传递,则方法接收的是调用者提供的实际参数的地址每次传递变量时,直接传递栈地址,而不做复制方法体内和外指向同一个内存空间,里面改外面也会变

背景知识

基本数据类型值就直接保存在变量中;对引用类型,变量中保存的只是实际对象的地址一般称这种变量为"引用",引用指向实际对象,实际对象中保存着内容


JAVA中的值传递与引用传递

JAVA中只存在值传递

JAVA程序设计语言总是采用值调用。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容

* 对基本类型byte,short,int,long,float,double,char,boolean,char)的函数参数传递是值传递,一个方法是不可能修改一个基本数据类型的参数

public class Test {
	
	private static int x=10;  
    
    public static void updateValue(int value){  
        value = 3 * value;  
    }  
      
    public static void main(String[] args) {  
        System.out.println("调用前x的值:"+x);  
        updateValue(x);  
        System.out.println("调用后x的值:"+x);  
    }
}

运行结果

调用前x的值:10

调用后x的值:10

执行过程:


分析:

value被初始化为x值的一个拷贝(也就是10);value被乘以3后等于30,但注意此时x的值仍为10;这个方法结束后,参数变量value不再使用,被回收

* 对于非基本类型(数组,类,接口)的函数参数传递传递的是对象引用的拷贝,即传递的是引用变量内存储的对象内容的地址值,所以还是按值传递

对于引用类型的变量而言,它们的“内容”存放在托管堆里,而在线程栈里存放的是“内容”的“地址”。如对象在托管堆中的地址是x0001,那么栈地址0001的内存空间中存放的就是这个对象的内存地址x0001

:当传递方法参数类型为引用数据类型时,一个方法不修改引用变量本身,但可以修改一个引用数据类型的参数所指向对象的值

public class Test {	
	private static User user=null;  
    public static void updateUser(User student){  
        student.setName("Lishen");  
        student.setAge(18);  
    }  
            
    public static void main(String[] args) {  
        user = new User("zhangsan",26);  
        System.out.println("调用前user的值:"+user.toString());  
        updateUser(user);  
        System.out.println("调用后user的值:"+user.toString());  
    } 
}

class User {  
    private String name;  
    private int age;
    
    public User(String name, int age) {  
        this.name=name;  
        this.age=age;  
    }  
    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;  
}  

    @Override
    public String toString() {
    	return "Name: " + name + "    Age: " + age;
    }
}

运行结果

调用前user的值:Name: zhangsan    Age: 26

调用后user的值:Name: Lishen    Age: 18

执行过程:

分析:

student变量被初始化为引用user值的拷贝;调用student变量的set方法作用在这个引用的对象上,user和student同时引用的User对象内部值被修改;方法结束后,student变量不再使用,被释放,而user还是没有变,依然指向User对象

public class Test {	
	private static User user=null;  
    private static User stu=null;  
      
    public static void swap(User x,User y){  
        User temp =x;  
        x=y;  
        y=temp;  
    }        
      
    public static void main(String[] args) {  
        user = new User("user",26);  
        stu = new User("stu",18);  
        System.out.println("调用前user的值:"+user.toString());  
        System.out.println("调用前stu的值:"+stu.toString());  
        swap(user,stu);  
        System.out.println("调用后user的值:"+user.toString());  
        System.out.println("调用后stu的值:"+stu.toString());  
    }  
}

运行结果

调用前user的值:Name: user    Age: 26

调用前stu的值:Name: stu    Age: 18

调用后user的值:Name: user    Age: 26

调用后stu的值:Name: stu    Age: 18

执行过程:

分析:

swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝的值而已,最终在方法结束后x,y将被丢弃,而原来的变量user和stu仍然引用这个方法调用之前所引用的对象。这个过程也充分说明了JAVA对对象采用的不是引用调用,而是对象引用进行的是值传递,一个方法是不可能修改参数本身的。

* 不可变类String\Integer\Double等

不可变类因为没有提供自身修改的函数,创建后,一旦更改等于是重新开了一块内存地址新生成一个对象

public static void change(String string2){
        string2="zhangsan";
    }
    
    public static void main(String[] args) {
        String string1=new String("abcd");
        System.out.println(string1);
        change(string1);
        System.out.println(string1);
    }

运行结果:

abcd

abcd

分析:

调用change函数后,两个字符串指向同一个Heap上的地址string2修改内容后指向了新地址,但是string1仍然指向原地址


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章