关于Java中值传递和引用传递的一点思考

 曾经在面试中被问到一个问题:”Java中传递方式是值传递还是引用传递?”其实我个人觉得不必过于纠结于这种字面上的意义,而应该从本质上去理解Java在数据传递过程中所发生的变化。
 众所周知,Java的数据类型分为基本类型和引用类型,其中基本类型就是int/double/char…,而引用类型则是指类、接口、String类型。两者的主要区别如下:

int num = 10;
Student stu = new Student();

这里写图片描述
 如图所示,num是基本类型,因此值直接保存在变量中,而stu是引用类型,变量中保存的是实际Student对象的地址,即所谓的“引用”,引用指向实际对象,实际对象中保存着内容。参数的传递本质上就是赋值(=)操作。
 对于基本类型num,进行赋值操作时会直接覆盖变量的值。
 对于引用类型stu,进行赋值操作实际上是改变变量中所保存的地址值,而实际对象是不会被改变的。
 我们可以通过一些例子来分析Java的参数传递,主要以传递的变量是基本类型和引用类型进行区分:
1、传递参数为基本类型时:

public class ParamTest {

    public static void main(String[] args){
        int num = 10;
        changeNum(num);
        System.out.println(num);
    }

    public static void changeNum(int temp){
        temp = temp * 2;
    }
}

输出结果:10
这里写图片描述
 从图中看出,temp的值改变为了原先的2倍,而num的值没有变化,说明以基本类型作为参数传递时,实际上传递的只是一份数值的拷贝,传入函数以后就跟原来的值没有关系了,在函数中改变这个数的值也不会影响原来变量的值。

2、传递参数为引用类型时:
 当传递的参数类型为引用类型时,是我们比较容易判断出错的情况。其实我们只要牢记一点就好了,就是传递的引用类型并不是对象本身,而是指向对象的地址,我们可以通过绘制内存指向图来分析判断。下面将通过三段代码来进行举例说明。

public class ParamTest {

    public static void main(String[] args){
        Student a = new Student();
        a.score = 80;
        changeScore(a);
        System.out.println(a.score);
    }

    public static void changeScore(Student stu){
        stu.score = 90;
    }

}

class Student{
    public int score;
}

输出结果为:90
这里写图片描述

 由图可以明白,stu与a指向同一个Student对象,对stu指向对象进行操作,也是对a指向的同一个对象进行操作,因此输出分数发生了变化

public class ParamTest {

    public static void main(String[] args){
        Student a = new Student();
        a.score = 80;
        changeScore(a);
        System.out.println(a.score);
    }

    public static void changeScore(Student stu){
        stu = new Student(); //增加的一行代码
        stu.score = 90;
    }

}

class Student{
    public int score;
}

输出结果为:80
这里写图片描述

 这个例子与上一个例子仅增加了一行代码,而输出则发生了变化,由图可以很清晰看出,在执行了stu = new Student()以后,stu地址发生了变化,指向了一个新的Student对象,因此对stu指向的对象进行操作时,并不影响a指向的对象的score。

public class ParamTest {

    public static void main(String[] args){
        Student a = new Student();
        Student b = new Student();
        a.score = 80;
        b.score = 100;
        System.out.println("交换前:");
        System.out.println("a的分数:"+ a.score + "---b的分数: "+ b.score);
        changeScore(a,b);
        System.out.println("交换后:");
        System.out.println("a的分数:"+ a.score + "---b的分数: "+ b.score);
    }

    public static void changeScore(Student x,Student y){
        Student stu = x;
        x = y;
        y = stu;
        System.out.println("交换后:");
        System.out.println("x的分数:"+ x.score + "---y的分数: "+ y.score);
    }

}

class Student{
    public int score;
}

输出结果为:
交换前:
a的分数:80—b的分数: 100
交换后:
x的分数:100—y的分数: 80
交换后:
a的分数:80—b的分数: 100
这里写图片描述

 从图中可以发现,在执行交换函数后,仅仅是x和y的地址发生了交换,并不影响原先a和b指向的对象。

3、String不可变类:
  此外还有一种比较特殊情况需要注意,就是String类型对象,虽然其也是引用类型,下面先看一个例子。

public class ParamTest {

    public static void main(String[] args){
        String s = new String("XZ");
        StringBuilder sb = new StringBuilder("XZ");
        change(s,sb);
        System.out.println(s);
        System.out.println(sb);
    }

    public static void change(String str,StringBuilder sb){
        str = "XZ Premium";
        sb.append("Premium");
    }
}

输出结果为:
XZ
XZPremium
其中一点让人比较奇怪的是String作为引用类型,为什么在子函数对其进行操作,并没有改变原先String对象的值。接下来我们看下内存指向图。
这里写图片描述
这里写图片描述

  由于String是final修饰的不可变类,因此对String进行的操作都会重新分配一块内存生成一个新的String对象,并不影响原先的String对象。

4、总结:
经过以上的分析,关于Java中的值传递和引用传递可以得出以下结论:
(1) 基本数据类型传值,对形参的修改不会影响实参;
(2) 引用类型传引用,形参和实参指向同一个内存地址(同一个对象),所以对参数的修改会影响到实际的对象;
(3) String, Integer, Double等immutable的类型特殊处理,可以理解为传值,最后的操作不会修改实参对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值