java到底有没有引用传递?


前言

我们都知道参数传递分为:值传递引用传递
那么Java中有没有引用传递呢?答案是No!Java只有值传递!


涉及知识点

你知道吗?
并不是传参的时候进行值传递,就是所谓 “值传递”
也不是传引用地址就是 “引用传递”

分享一篇讲的很形象的博文【点我】

(1)先看看什么是值传递和引用传递?

【 值传递 】
(1)实参将值复制了一份,传给了形参。也就是值拷贝
(2)注意!复制地址也叫做值拷贝! 引用传递和传递引用是不一样的,后者是将引用的副本值进行拷贝传递,算是值传递!

【 引用传递 】
(1)实参将地址值无需复制,直接交给形参,这样形参就可以直接对引用指向的区域进行操作。


(2)值传递和引用传递最大的区别是什么?

最大的区别就是值传递的时候,有无进行值的复制拷贝
哪怕传的是地址,只要是进行复制拷贝的方式,而不是直接将地址交给形参,那么就是值拷贝。
传地址,那叫做传递引用副本值,而不叫引用传递!


我们从一个简单的案例说起

(0) 看案例之前需要知道

1.方法对应的是一个个栈帧,当前方法就是栈顶的栈帧
2.方法中的局部变量,处于局部变量表
3.局部变量如果存储的是:
(1)8大基本类型的话,值存在局部变量表中。
(2)String以及基本类型的包装类型,比较特殊,下面分析。
(3)数组、对象之类的引用类型的话,对象本身都存在堆中,引用地址存在局部变量表中。


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

(1) 基本类型的参数传递

下面这个例子输出什么?肯定是1啦~

public class Test {
    public static void main(String[] args) {
        int a = 1;
        change(a);
        System.out.println(a);
    }
    public static void change(int a){
        a = 2;
    }
}

【但是原理是什么???我们来一步步分析吧!】

(1)首先我们得知道:我们知道方法内的局部变量是存储在栈帧中的局部变量表中的。

(2)Main方法中创建了int类型的变量a并初始化为1,此时Main方法的栈帧由于是当前方法,所以处于栈顶位置。变量a则存在了该栈帧的局部变量表中。

(3)Main方法调用change(a)方法,并将实参a变量传入。

(4)实参a将值传递给方法中的形参。其实就是在main栈帧上再添加一个change方法对应的栈帧,将main的实参进行一个值拷贝,并将值保存到当前方法栈帧的局部变量表的形参中。

(5)其实这里是两个不同的int类型变量,当前栈顶change对应的栈帧中的变量值更改,如果没有逃逸,那么不会影响到其他的栈帧中的变量


(2) String以及基本类型的包装类型参数传递

String存在运行时数据区的哪个位置?

这个问题需要分很多情况进行讨论

(1)如果jdk1.6及再之前,那么字符串的字面量(也算是String对象)存储的位置是方法区中的字符串常量池StringTable;如果是New出来的String对象,String对象存在堆中。

(2)如果jdk1.7及以后,StringTable由于方法区空间有限以及Full GC频率低等原因,放置堆中,此时字面量存在堆中的StringTable字符串常量池中,通过New出来的String对象依然存在堆中,并且他们可以通过intern()方法进行一些内存优化…

【详情看我的另一篇文章 从JVM的角度看看String


基本类型的包装类型存在运行时数据区的哪个位置?

JDK1.7版本虽然将StringTable字符串常量池移入堆中,但其他常量池依旧在方法区(1.8后叫做元空间)

另外关于Java的包装类的池化:Java的池化技术

Java中基本类型的包装类的大部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean

以上5种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据(Boolean特殊一点对应的只有true 和 false),但是超出此范围仍然会去创建新的对象。 两种浮点数类型的包装类Float,Double并没有实现常量池技术。

在这里插入图片描述


那么String以及基本类型的包装类型参数传递方式是?

当然也还是值传递

(1)在常量池中push一个127。
(2)栈帧中的局部变量表中存一个变量a,值是对常量池中127的引用。
(3)调用change函数,该函数对应栈帧中存着形参的变量x。
(4)实参a拷贝127的引用给参数x 至少到现在,他们指向的还是一个引用地址
(5)在change方法中又往常量池中存了个新的常量 100,并将该引用地址传给参数x。从这里开始,只要x不逃逸change方法,那么就不会影响main栈帧中的a变量

在这里插入图片描述


(3) 引用类型参数传递

逐步分析

(1)开辟内存空间,在堆中new了一个User对象,初始化,并将这个User对象的地址返回给user变量进行存储。
(2)对这个对象的id进行set值 100
(3)在Main方法中将user变量所存的对象地址值传给change方法的形参进行接收,这个时候,这两个user变量指向的是一个堆中的User对象
(4)在change中更改这个User的id值。

change中创建变量(形参)动作,以及set值操作,并不会导致新的User对象生成,所以他们操作的是一个对象,因此main中输出user的id的时候id发生了改变!
在这里插入图片描述


总结知识点

(1)引用传递和值传递的区别
(2)Java为什么只有值传递
(3)包装类除了Float和Double其他都有池化技术提供复用支持


—————END—————

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值