老生常谈--Java值传递和引用传递
起因
前两天面试被问到了这个问题,虽然之前老早就了解过这个问题,但是并没有深入了解,所以面试的时候一下子慌了,菜是原罪,今天菜鸡来补补基础知识。
其实这个问题一直是被讨论的,常见的三种说法就是,1,Java 值传递引用传递都有,2,只有值传递,3只有引用传递,今天查了很多资料,我发现这个问题并不是随随便便就能说清楚。
先说传参
方法的参数可以分为实参和形参,实参是指被调用时传入的实际的值,在方法调用前就已经初始化完毕。而形参是指方法中用来“承接”实参的参数,它是在这个方法里有效,即作用域。方法执行结束之后即被销毁。
OK,接下来说所谓的值和引用。
Java数据类型
1,基本数据类型,这种是Java中最小粒度的数据类型,包括:byte,short,int,long,float,double,char,boolean。
2,引用类型:是编程语言中存放实际内容所在地址的一种数据类型。一般是类,接口,数组。
在这里不得不说一下内存区域划分:
在Java文件经过编译器之后,转化成为class文件,之后JVM将class文件通过类加载加载到运行时数据区来存储程序运行时需要用到的数据和相关信息。
运行时数据区
1,虚拟机栈,它是线程私有的,所以是线程隔离的,栈里面存储的是栈帧,每一个栈帧表示被调用的一个方法,方法调用的过程对应着栈帧从入栈到出栈的过程,栈帧中存储的是方法运行需要的信息,它是用来支持方法调用和方法执行的数据结构:
(1),局部变量表(方法中的局部变量,变量为基本类型时,直接存储值,为引用类型时,存储指向具体对象的引用)
(2),方法出口地址(方法执行完返回的地址)
(3),操作数栈(JVM被称为基于栈的执行引擎)
(4),指向常量池的引用
(5),一些附加信息。
2,堆,是所有线程共享的,用来存储对象和数组,在JVM中,只有一个堆。
3,方法区:是一块所有线程共享的内存逻辑区域,在JVM这个中也只有一个方法区,用来存储一些线程可共享的内容,同时它也是线程安全的。方法区存储的信息有:
类的全路径名,类的直接超类的全限定名,类的访问修饰符,类的类型,类的直接接口的全限定名的有序列表,常量池等。
4,本地方法栈:本地方法栈类似于虚拟机栈,也是线程私有,不同的是,虚拟机栈是为Java方法服务,而本地方法栈是为native方法服务。
5,程序计数器,也是线程私有,字节码解释器工作就是通过改变程序计数器里的值来选取下一条执行的字节码指令,分支循环等都需要它来实现。
数据是如何存储的?
JVM 在运行过程中,涉及内存分配有:堆,栈,静态方法区,常量区。内存分配的策略有:堆式,栈式,静态。
基本数据类型的存储:
1,基本数据类型的局部变量以及他的值都是存储在栈上,也就是虚拟机栈的栈帧中。
如:int a = 30;事实上这个过程分为两步,第一步是创建一个age变量, 第二步是先查找栈中是否有30这个值,如果有,则直接将age指向它,没有则开辟一块内存来存储30.
再将age指向它。对它再赋值也是相同的道理,如age = 50,先查找栈中是否有50,有则指向,没有则开辟一块空间存50,再将age指向它。所以足以见之,基本数据类型的赋值是不改变自身数据的,而是像上面那样先查找栈中是否存在,有则指向它,没有则开辟一块内存来装下新的值。
2,基本数据类型的成员变量
与上面的不同的是,它存放在堆里,因为它跟随着一个实例对象,跟实例对象的生命周期相同。
3,基本数据类型的静态变量,与上面都不同,静态变量和它的值都存储在运行时常量区,它是跟随类加载的,随类的消失而消失。
引用数据类型的存储:
引用数据类型分为两部分,一部分是变量,存在栈中,用来存储实际内容的地址,同时在堆中有一块内存来存实际内容。
所谓值传递和引用
值传递
在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时的形参接收到的实际上是实参的副本,在方法体内的任何操作,都是对副本的操作,对实参不影响。如:
public class Main {
public static void main(String[] args) {
int a = 10;
function(a);
System.out.println(a);
}
private static void function(int a){
a = 50;
}
}
输出结果:10
结合之前的铺垫,首先JVM会将其中的main()压入虚拟机栈,用一个栈帧存储,即为当前栈帧,栈帧存储着方法的局部变量表,操作栈,方法出口地址等。之后执行到function方法时,JVM也创建为它创建一个
栈帧,用来存放function的相关信息,因此a的值是在function的栈帧中,而它的值是从实参复制得来的,而再方法体内部进行赋值的时候,也不会去改变main所在的栈帧中的a的值,而是在当前的栈帧中,对值进行修改。而栈帧之间是隔离的,所以不会对实参产生影响。
引用传递
引用传递和值传递的不同点在于,调用方法的时候,实参的地址通过方法传给形参,所以对实参的操作会影响本身的内容,但是但是,这里面还需要分情况。如:
public class Main {
public static void main(String[] args) {
StringBuilder a = new StringBuilder("10");
function(a);
System.out.println(a);
}
private static void function(StringBuilder str){
str.append("20");
}
}
输出:1020
public class Main {
public static void main(String[] args) {
StringBuilder a = new StringBuilder("10");
function(a);
System.out.println(a);
}
private static void function(StringBuilder str){
str = new StringBuilder("20");
}
}
输出:10
这是为何呢,之前有说,变量是存储在栈,实际内容存储在堆,而造成这里不同的原因就是,Java中的赋值的操作(=)都不是将自身的内容进行改变,而是先检查有没有已经存在的并且等于它的值,有则指向
它,没有则另外创建一个,再指向它,所以在第二段代码里,str=new StringBuilder("20")并没有修改原有的自身的内容而是指向了一个新对象,也就是形参的str和实参a只想了不同的地址,自然对形参没有影响。而在第一段代码中,str.append();是对指向的内存进行操作,而指向的内存也就是实参引用所指向的内存,故有影响。
所以我觉得这里可以做个总结,那就是Java之间并没有引用传递,无论基本数据类型还是引用类型,传的都是值,都是实参的副本,但是不同的是,基本数据类型是实参内容的复制品,而引用类型也是,但是赋值的内容是地址罢了,即实参和形参指向一个同一块内存(对象),形参变化是否会改变实参取决于操作是否是对指向的这块内存进行直接修改。如果是赋值操作这种,那么形参操作对实参就没有影响。
老生常谈--Java值传递和引用传递的更多相关文章
- 死磕面试系列,Java到底是值传递还是引用传递?
Java到底是值传递还是引用传递? 这虽然是一个老生常谈的问题,但是对于没有深入研究过这块,或者Java基础不牢的同学,还是很难回答得让人满意. 可能很多同学能够很轻松的背出JVM.分布式事务.高并发 ...
- Java中引用类型变量,对象,值类型,值传递,引用传递 区别与定义
一.Java中什么叫做引用类型变量?引用:就是按内存地址查询 比如:String s = new String();这个其实是在栈内存里分配一块内存空间为s,在堆内存里new了一个Stri ...
- java中值传递和引用传递
最近工作中使用到了值传递和引用传递,但是有点懵,现在看了下面的文章后清晰多了.一下是文章(网摘) 1:按值传递是什么 指的是在方法调用时,传递的参数是按值的拷贝传递.示例如下: public clas ...
- Java中的值传递和引用传递
这几天一直再纠结这个问题,今天看了这篇文章有点思路了,这跟C++里函数参数为引用.指针还是有很大区别. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里 ...
- java的值传递和引用传递
昨天博主在对于值传递和引用传递这里栽了一个大坑啊,导致一下午时间都浪费在这里,我们先说下值传递和引用传递java官方解释: 值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对 ...
- java中方法的参数传递机制(值传递还是引用传递)
看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递.Java 编程语言只有值传递参 ...
- java 对象传递 是 值传递 还是 引用传递?
这个问题说实话我感觉没有太大的意义. 按第一印象和c++的一些思想去理解的话对象传递是引用传递,因为传递过去的对象的值能被改变. 但是又有很多人,不知道从哪里扣出来一句,java中只有值传递,没有引用 ...
- java参数传递时到底是值传递还是引用传递
java参数传递时到底是值传递还是引用传递(baidu搜集) 问”,很多人的BLOG里都引用这些面试题,最近因为工作内容比较枯燥,也来看看这些试题以调节一下口味,其中有一道题让我很费解. 原题是:当一 ...
- Java面向对象-方法的值传递和引用传递
Java面向对象-方法的值传递和引用传递 0 发布时间:『 2016-08-21 14:21』 博客类别:Java核心基础 阅读(197) 评论(0) Java面向对象-方法的值传递和引用传递 方 ...
随机推荐
- 0级搭建类003-CentOS Linux安装 (CentOS 7)公开
项目文档引子系列是根据项目原型,制作的测试实验文档,目的是为了提升项目过程中的实际动手能力,打造精品文档AskScuti. 项目文档引子系列目前不对外发布,仅作为博客记录.如学员在实际工作过程中需提前 ...
- archlinux install.txt
++++++ 注意事项+++ +++++++++++++++++++++++++++ 强烈建议新手移步 Arch Wiki > 新手指南 经验者请参阅 Arch Wiki > 安装指南 若 ...
- Cow Contest POJ - 3660 floyd传递闭包
#include<iostream> #include<cstring> using namespace std; ,INF=0x3f3f3f3f; int f[N][N]; ...
- layer弹出层右上角的关闭按钮怎么没有显示
问题描述:layer弹出层右上角的关闭按钮怎么没有显示,但鼠标移上去又可以点击 解决方式: 这是因为样式中需要一个图标,你的项目中缺少.解决如下:1.下载图标:http://www-x-zi-han- ...
- [USACO08JAN] 手机网络 - 树形dp
经典问题系列 覆盖半径\(1\)的最小点覆盖集 \(f[i][0]\) 表示不在此处建信号塔,但\(i\)及其子树都有信号 \(f[i][1]\) 表示在此处建信号塔,但\(i\)及其子树都有信号 \ ...
- PP: Deep r -th Root of Rank Supervised Joint Binary Embedding for Multivariate Time Series Retrieval
from: Dacheng Tao 悉尼大学 PROBLEM: time series retrieval: given the current multivariate time series se ...
- mysql远程链接(可以在服务器上配置然后在本地连接远程服务器)
ps:如果一下的连接不成功原因:一定要关闭windows防火墙或者linux的防火墙 1.在服务器端授权(黄色标记的地方第一个是用户名,第二个的意思是可以远程连接,第三个是密码) GRANT ALL ...
- navicat连接mysql查询结果中文都是?号(C#)
记录解决方法,方便以后查看. 有几个地方需要注意: 1.连接mysql数据库的字符串最后加上Charset=utf8; 2.mysql中character_set_XX设置都为utf8,使用show ...
- 高效完成R代码
为什么R有时候运行慢? 参考https://www.cnblogs.com/qiaoyihang/p/7779144.html 一.为什么R程序有时候会很慢? 1.计算性能的三个限制条件 cpu ra ...
- AcWing 1012. 友好城市
#include<iostream> #include<algorithm> using namespace std ; typedef pair<int,int> ...