Java参数是传值还是传引用

 

前言

对于Java参数是传值还是传引用这个问题,大家总是众说纷纭,在《Thinking in Java》中是这么解释的:When you’re passing primitives into a method, you get a distinct copy of the primitive. When you’re passing a reference into a method, you get a copy of the reference.(对于基本类型变量,Java是传值的副本;对于所有的引用类型变量,如String等,Java都是传引用的副本)这里我们首先需要明确下什么是基本类型变量,什么是引用类型变量。

  • 基本类型变量
    • 整型:byte,short,int,long
    • 浮点型:float,double
    • 字符型:char
    • 布尔型:boolean
  • 引用类型变量
    • 数组
    • 接口

代码说明

接下来将三个代码示例对Java参数是传值还是传引用进行详细的说明。

示例代码1:

public class Test1 {
    public static void main(String[] args) {
        int a = 1;
        System.out.println("Before test(int) : a = " + a);
        test(a);
        System.out.println("After test(int) : a = " + a);
    }
    public static void test(int a) {
        a++;
        System.out.println("In test(int) : a = " + a);
    }
}

运行结果 :

Before test(int) : a = 1

In test(int) : a = 2

After test(int) : a = 1

不难看出,虽然在test(int)方法中改变了传进来的参数值,但这个对参数的源变量本身(即对main(String[])方法中的a变量)并没有影响,说明在传递基本类型变量时,实际上是将参数值作为一个副本传进方法函数的,在调用的方法函数中,不管怎么改变这个值,其结果都只是改变了副本的值,而不会影响到源值。

流程:

  1. 主函数进栈,a初始化。
  2. 调用test(int)方法,test(int)方法进栈,将main(String[])中a的,复制一份给test(int a)中的a。
  3. test(int)方法中对a的值进行a++自增运算。
  4. test(int)方法运行完毕,a的值加1。
  5. test(int)方法弹栈。
  6. 主函数弹栈。

示例代码2:

public class Test2 {
public static void main(String[] args) {
        StringBuffer string = new StringBuffer("Hello");
        System.out.println("Before test(StringBuffer) : string = " + string);
        test(string);
        System.out.println("After test(StringBuffer) : string = " + string);
    }
    public static void test(StringBuffer str) {
        str.append(", world!");
        System.out.println("In test(StringBuffer) : str = " + str);
    }
}

 运行结果:

扫描二维码关注公众号,回复: 3956572 查看本文章

Before test(StringBuffer) : string = Hello

In test(StringBuffer) : str = Hello, world!

After test(StringBuffer) : string = Hello, world!

 在main(String[])中调用了test(StringBuffer)方法,并将string作为参数进行传递。这里的string是一个对象引用,Java实际上将它的副本传进方法函数的,这个函数里面的引用副本指向的是对象的地址,通过引用副本找到地址并修改地址中的值,也就改变了对象。

流程:

  1. 主函数进栈,StringBuffer string初始化。
  2. 调用test方法,test( )进栈,将string指向的地址值,复制一份给str。
  3. test方法中,根据地址值,找到堆中的StringBuffer对象,并将它的值改为“Hello,world!”。
  4. test方法运行完毕,StringBuffer的值已经改变。
  5. test方法弹栈。
  6. 主函数弹栈。

示例代码3:

public class Test3 {    
    public static void main(String[] args) {
        String string = "Hello";
        System.out.println("Before test(StringBuffer) : string = " + string);
        test(string);
        System.out.println("After test(StringBuffer) : string = " + string);
    }
    public static void test(String str) {
        str = "Hello, world!";
        System.out.println("In test(StringBuffer) : str = " + str);
    }
}

运行结果:

Before test(StringBuffer) : string = Hello

In test(StringBuffer) : str = Hello, world!

After test(StringBuffer) : string = Hello

也许大家会觉得奇怪,类比示例代码2,同样是将引用类型作为参数传进方法函数中,为什么示例代码2能够改变对象的值,而示例代码3中却不行?首先,我们需要清楚的一点,在String的API中有这么一句话:their values cannot be changed after they are created,即String类是final类型的,String的值在创建之后不能被更改。所以在执行str = "Hello, world!";时,其过程为:首先系统会自动生成一个新的String对象,并把这个新对象的值设为“Hello,world!”,然后把这个对象的引用赋给str。既然对象都是新的,那么就与原来的“Hello”没有任何关系。因此,当函数结束,引用副本str的作用消失,原来内存地址上的内容并没有任何的改变,打印结果仍是Hello。而在示例代码2中,str.append(", world!");就不同了,StringBuffer是产生一块内存空间,相关的增删改都在其中运行,所以添加一句“,world!”仍然是在同一块内存地址上进行,str所指向的引用并没有改变。 

流程:

  1. 主函数进栈,string初始化。
  2. 调用test方法,test( )进栈,将string指向的地址值,复制一份给str。
  3. test方法中,重现创建了一个String对象”Hello,world!”,并将str指向了新的地址值。
  4. test方法运行完毕,str所指向的地址值已经改变,但string指向的地址值不变。
  5. test方法弹栈。
  6. 主函数弹栈。

 小结

对于Java参数是传值还是传引用这个问题,对于基本类型变量,Java是传值的副本;对于引用类型变量,即所有的对象型变量,Java都是传引用的副本。

PS:上述如果有任何理解错误的地方,欢迎大家批评指出。

猜你喜欢

转载自blog.csdn.net/qq_22408539/article/details/82945990