Java中是值传递还是引用传递?

答案:

Java中只有值传递。

针对值类型,传递的是实参的值,对于引用类型,传递的是引用值所存储的地址。

基本概念

1.形参与实参

形式参数:在定义函数名和函数体时使用的参数,目的是用来接收调用该函数是传入的参数。
实际参数:在调用有参函数是, 主调函数被调函数之间有 数据传递关系。在主调函数中调用一个函数时,函数名后面括号中的参数,称为实际的参数。
  • 两者区别:

实参是调用有参方法的时候,真正传递的内容。而形参,是用于接收实参内容的参数。

举例如下:

public static void main(String[] args) {
   pt.sout("Hollis");//实际参数为 Hollis
}

public static void sout(String name) { //形式参数为 name
   System.out.println(name);
}

2.值传递与引用传递

上面提到了,当我们调用一个有参函数的时候,会把实际参数传递给形式参数。但是,在程序语言中,这个传递过程中传递的两种情况,即值传递和引用传递。两者的定义如下:

值传递:是指在调用一个有参函数时,会把实际参数复制一份传递到函数中。这样在函数中如果对参数进行修改,将不会影响到实际参数

引用传递:是指在调用一个有参函数是,直接把实际参数的地址传递到函数中,那么,如果在函数中对参数所进行的修改,将影响到实际参数

从上面的表述中,可知值传递与引用传递的区别主要有两个:第一,传递的是否是实际参数的地址。第二,函数中对参数进行的修改,是否会影响到实际的参数。

下面,来看一段Java代码:

public static void main(String[] args) {
   ParamTest pt = new ParamTest();

   User hollis = new User();
   hollis.setName("Hollis");
   hollis.setGender("Male");
   pt.pass(hollis);
   System.out.println("print in main , user is " + hollis);
}

public void pass(User user) {
   user.setName("hollischuang");
   System.out.println("print in pass , user is " + user);
}

最终输出的结果为:

print in pass, userisUser{name='hollischuang', gender='Male'}
print in main , userisUser{name='hollischuang', gender='Male'}

在上面的代码中,由于在函数中对参数进行了修改,影响了实际的user.name的值,所以,很多人认为是进行了引用传递。但是,这种说法是错误的。因为在引用传递的定义中,引用传递的定义是:将实际参数的地址传递到函数中。函数的对参数的修改将影响实际参数的值,是引用传递的结果。并不能根据函数对实际参数的修改,产生了影响就说它是引用传递。之所以在上面那段代码中会产生效果,这和Java语言中一种特殊的类型有关:引用类型。

3.Java中的值类型与引用类型

值类型就是基本数据类型:包括byte、short、int、long、float、double、char、boolean。其中byte、short、int、long是整型。float、double是浮点型,char是字符型,boolean是布尔型。

引用类型引用其实就像是一个对象的名字或者别名 (alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,我们不会直接是访问对象在内存中的数据,而是通过引用去访问。

比如:

String a="This is a Text!";
String b=a;

在Java 虚拟机中对应的是存储状态为:

通过上面的代码和图形示例不难看出,a 和 b 是不同的两个引用,我们使用了两个定义语句来定义它们。但它们的值是一样的,都指向同一个对象 "This is a Text!"。

引用具有以下两个要点:

(1) 引用是一种数据类型(保存在stack中),保存了对象在内存(heap,堆空间)中的地址,这种类型即不是我们平时所说的简单数据类型也不是类实例(对象);

(2) 不同的引用可能指向同一个对象,换句话说,一个对象可以有多个引用,即该类类型的变量。

如下图所示:

在上图中,

当函数形参为值类型时,在传递参数的过程是将变量c和变量d的值,复制给形参a和b.

当函数形参为引用类型时,由于引用类型变量存储的是对象在对堆中的地址。所以,实参 c,d分别指向堆中的地址。在进行参数传递是,c将其指向的地址,复制给对应的形参a,d将其所指向的地址,复制给对应的形参b.这个过成中,传递参数仍然为“复制”的方式。

综上所述,因为Java中只有值传递的方式。由于Java语言中,只有基本类型变量和应用变量这两种,而没有指针类型。所以,会导致参数传递过程对值传递还是引用传递的误解。

Java中只有值传递。针对值类型,传递的是实参的值,对于引用类型,传递的是引用值所存储的地址。


引用类型在List.addAII()中的应用.

在实际的开发过程中,经常会涉及到两个链表的合并。

比如,将list1.addAll(list2),这里的作用就是将list2将入到表list1中。

由于list1.addAll(list2),采用的是值传递。在这个过程中,本质上是将list2中的所有元素的引用复制到list1中。所以,当进行链表合并是,需要注意:

若list2中存储的是不可变元素类型,则将list1.add(list2)之后,修改list2的值,不会影响list1的值。

如果list2存储的是可变类型元素,如Class类型,List、Map等类型是,修改list2的值,会印象list1中的值。

代码演示:list2中存储的是不可变元素类型

 // list2 存储不可变元素的情况:
    public static void list1AddList2Invariable(){
        List<String> list1 = new ArrayList<String>();
        List<String> list2 = new ArrayList<String>();

        list2.add("hello");
        list2.add("world");
        list2.add("MeiTuan");

        list1.addAll(list2);
        System.out.println("list2修改之前");
        System.out.println("list1---"+list1);
        System.out.println("list2---"+list2);
        
        list2.set(2,"Alibaba");//修改list2中元素的引用
        // 修改list2中元素的引用  list.set(index,value) 与 list.get(index).add()
        System.out.println("list2修改之后");
        System.out.println("list1---"+list1);
        System.out.println("list2---"+list2);
    }

输出结果为:

list2的修改,并没有印象list1中存储的值。这是因为list2中出处的是不可变类型。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值