文章目录
前言
我们都知道参数传递分为:值传递,引用传递
那么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—————