关于Java的值传递和引用传递问题

关于Java参数传递中值传递和引用传递问题

先说说一些基本概念:

基本类型: 编程语言中内置的最小粒度的数据类型。Java中八大基本类型:byte、short、int、long、float、double、char、boolean。

引用类型: 引用也叫句柄,引用类型,是编程语言中定义的在句柄中存放着实际内容所在地址的地址值的一种数据形式。它主要包括:类接口数组。

值传递: 在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。

引用传递:”引用”也就是指向真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,形参和实参指向同一块内存地址,对形参的操作会影响的真实内容。

一般很多人这样认为:Java在进行参数传递时,

1、参数为基本类型,本质为值传递,即对参数的任何操作都不会对原数据有影响。
2、参数为引用类型(实例化new出来的对象)时,本质为引用传递,参数地址指向原数据地址,即任何操作都是在操作原数据。

上述第1点无可非议,但对于第2点引用类型是引用传递这种说法,却是存在一定争议的。

从如下代码可以看出,对于引用类型A,Test1测试类展示的似乎是引用传递,在change方法上修改形参,main方法中的实参也被同步修改;而Test2测试类展示的却似乎不是如此,swap方法中对形参的修改并未对main方法中的实参产生影响。何为?

class A {
	private int a;
	
	public A() {}
	public A(int a) {
		this.a = a;
	}
	
	public void set(int a) {
		this.a = a;
	}
	
	public int get() {
		return this.a;
	}
}

public class Test1 {
    public static void main(String[] args) {
        A a = new A(1);
        A b = new A(2);
        change(a, b);
        System.out.println(a.get() + "  " + b.get());//10  20
    }

    public static void change(A a, A b) {
     	a.set(10);
     	b.set(20);
    }
}

public class Test2 {
    public static void main(String[] args) {
        A a = new A(1);
        A b = new A(2);
        swap(a, b);
        System.out.println(a.get() + "  " + b.get());//1  2
    }
    
    public static void swap(A a, A b) {
        A temp = a;
        a = b;
        b = temp;
    } 
}

解析:对于Test2的swap方法,其本质如下图所示,swap方法的两个形参其实是main方法中两个实参的引用拷贝,当发生交换时交换的其实是swap方法中两个形参的引用指向,而main方法中两个实参的引用指向并没有发生改变。对于Test1的change方法,因为它未曾改动change方法中两个形参的引用指向,所以它对形参的任何操作都是在操作原数据。

image-20210919000253516

由此可得出以下结论:

在Java的所有参数传递中,无论是基本类型和是引用类型,在实参传入形参时,都是值传递(或者说是副本传递,也就是说传递的都是一个副本,而不是内容本身)。

Java所谓引用传递其实是传递了引用拷贝,既不是引用本身,更不是对象,所以本质也是值传递。

所以:

  1. 对于基本类型的参数来说,传递的是值的拷贝。
  2. 对于引用类型的参数来说,传递的是引用本身的拷贝。

只是在传递过程中:

1. 如果是对基本数据类型的数据进行操作,由于原始内容和副本都是存储实际值,并且是在不同的栈区,因此形参的操作,不影响原始内容。

2. 如果是对引用类型的数据进行操作,分两种情况,一种是形参和实参保持指向同一个对象地址,则形参的操作,会影响实参指向的对象的内容。一种是形参被改动指向新的对象地址(如重新赋值引用),则形参的操作,不会影响实参指向的对象的内容。

所以,对于引用类型而言,如果想在参数传递后方法中任何对形参的操作都是在操作原数据,那么请不要在方法内部去改变外部传入对象的引用。

注:在《Thinking in Java》一书中有如下描述

This brings up the terminology issue, which always seems good for an argument. The term is “pass by value,” and the meaning depends on how you perceive the operation of the program. The general meaning is that you get a local copy of whatever you’re passing, but the real question is how you think about what you’re passing. When it comes to the meaning of “pass by value,” there are two fairly distinct camps:

1.Java passes everything by value. When you’re passing primitives into a method, you get a distinct copy of the primitive. When you’re passing a handle into a method, you get a copy of the handle. Ergo, everything is pass by value. Of course, the assumption is that you’re always thinking (and caring) that handles are being passed, but it seems like the Java design has gone a long way toward allowing you to ignore (most of the time) that you’re working with a handle. That is, it seems to allow you to think of the handle as “the object,” since it implicitly dereferences it whenever you make a method call.

2.Java passes primitives by value (no argument there), but objects are passed by reference. This is the world view that the handle is an alias for the object, so you don’t think about passing handles, but instead say “I’m passing the object.” Since you don’t get a local copy of the object when you pass it into a method, objects are clearly not passed by value. There appears to be some support for this view within Sun, since one of the “reserved but not implemented” keywords is byvalue. (There’s no knowing, however, whether that keyword will ever see the light of day.)

Having given both camps a good airing and after saying “It depends on how you think of a handle,” I will attempt to sidestep the issue for the rest of the book. In the end, it isn’t that important – what is important is that you understand that passing a handle allows the caller’s object to be changed unexpectedly.

对于该问题的结论,是值传递还是引用传递并不重要,重要的是要理解”对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的”。

文本引用以及参考链接:
这一次,彻底解决Java的值传递和引用传递
一个绝对害了不少人的Java技术问题!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值