Java中到底是值传递还是引用传递?
Java中到底是值传递还是引用传递?
我们先回顾一下基本概念
实参和形参
参数在编程语言中是执行程序需要的数据,这个数据一般保存在变量中。在Java中定义一个方法时,可以定义一些参数,
举个例子:
public class Example {
public static void main(String[] args) {
	String myName = "hawk";
	sayYourName(myName);// 实际参数是myName
	}
public static void sayYourName(String name) {// 形式参数是name
		System.out.println(name);
	}
}
上面的代码中定义一个名为sayYourName的方法,如果想要执行这个方法,那么你需要传入一个String类型的变量给这个方法,定义这个方法时声明的String类型的name就是形式参数,而在这个方法执行时传入的myName就是实际参数。
小结
实际参数是调用有参方法的时候真正传递的内容,而形式参数是用于接收实参内容的参数。
传参的几种方式
按值传递(call by value)
按值传递,就是指在调用函数时,将实参对应的值做一个拷贝指向函数对应的形参。在函数内改变形参对应的值并不会影响外部实参的值
按引用传递 (call by reference)
按引用传递,是指在调用函数时,传递给函数的是实参的地址即引用,而不是实参的拷贝。在函数内部参数的值,对外部的实参是可见的。
按共享传递 (call by sharing)
按共享传递,是指在调用函数时,传递给函数的是实参的地址的拷贝(如果实参在栈中,则直接拷贝该值)。在函数内部对参数进行操作时,需要先拷贝的地址寻找到具体的值,再进行操作。如果该值在栈中,那么因为是直接拷贝的值,所以函数内部对参数进行操作不会对外部变量产生影响。如果原来拷贝的是原值在堆中的地址,那么需要先根据该地址找到堆中对应的位置,再进行操作。因为传递的是地址的拷贝所以函数内对值的操作对外部变量是可见的。
按共享传递可以理解为按值传递的一个特例,这里的值是对象的引用地址,而不是具体对象。
上面描述的传参方式实际上是函数调用时参数的求值策略(Evaluation Strategy),这个其实比较好理解,比如我们调用上面的sayYourName()方法
我们可以这样:sayYourName("hawk");
可以这样:sayYourName("ha"+"wk");
可以这样:sayYourName("hawk"+x.subString(2));// 此处的x是一个变量,进行截取后和"hawk"合并
但是所有这些实参的形式,都统称为表达式(Expression)。求值(Evaluation)即是指对这些表达式的简化并求解其值的过程。
求值策略(值传递和引用传递)的关注的点在于,这些表达式在调用函数的过程中,求值的时机、值的形式的选取等问题。求值的时机,可以是在函数调用前,也可以是在函数调用后,由被调用者自己求值。这里所谓调用后求值,可以理解为Lazy Load或On Demand的一种求值方式。
而且,除了按值传递和按引用传递,还有一些其它的求值策略。这些求值策略的划分依据是:求值的时机(调用前还是调用中)和值本身的传递方式。详见下表:
| 求值策略 | 求值时间 | 传值方式 | 
| 按值传递( pass by value ) | 调用前 | 值的结果(只是结果的副本) | 
| 按引用传递( pass by reference ) | 调用前 | 原值(原始对象,无副本) | 
| 按名传递( pass by name ) | 调用后(用到才求值) | 与值无关的一个名 | 
按值传递和按引用传递在行为表现上的差异如下:
| 按值传递 | 按引用传递 | |
| 本质区别 | 创建副本 | 不创建副本 | 
| 表现结果 | 函数中无法改变原始对象 | 函数中可以改变原始对象 | 
**这里面的改变,是指把一个变量指向另一个对象,而不是指仅仅改变属性或者成员什么的**
只要是按值传递,不管传递的参数类型是值类型还是引用类型,都会在调用栈上面创建一个实参的副本,区别只是如果传递的参数类型是值类型,该副本就是实际参数的值的复制,而对于引用类型来说,引用类型的实例(即对象)是保存在堆中的,在栈上只有一个该实例的引用(一般情况下是该实例在堆中的内存地址),此时,实际参数的副本并不是该对象,而是引用的副本。
所以,综上所述,对于Java的函数调用方式最准确的描述是:参数藉由值传递方式,传递的值是个引用。(句中两个“值”不是一个意思,第一个值是evaluation result,第二个值是value content)
举例
说到Java是值传递还是引用传递,一般都会举下面三个例子,我们来一一说明一下:
第一个例子,基本类型参数传值:
public class Example {
	public static void main(String[] args) {
		int i = 10;
		changeValue(i);
		System.out.println(i);
	}
	public static void changeValue(int a ) {
		a = 20;
	}
}
上面的代码解释如下:
定义了一个int类型的变量i,并赋值为10
调用changeValue方法,传入i,changeValue方法将传入的变量赋值为20
输出i
结果是:10
changValue方法内部对形参a的修改并没有影响到实参i
第一个结论:Java中基本类型的传值方式是按值传递
第二个例子,引用类型传值:
public class Example {
	public static void main(String[] args) {
		String i = "hawk";
		changString(i);
		System.out.println(i);
	}
	public static void changString(String a ) {
		a = "HAWK";
	}
}
上面的代码解释如下:
定义一个String类型的变量i,赋值为"hawk"
调用changeString方法,将传入的变量赋值为"HAWK"
输出i
结果: hawk
在Java中String类型虽然可以直接赋值,但是是引用类型,因为String类型的具体值保存在堆中,而不是栈上
既然String是引用类型, 上面的例子又说明String是值传递的  那么我们是不是就可以得出:
Java中引用类型也是值传递的,这样的结论呢?
我们来看第三个例子:
public class Example {
	int i = 20;
	public static void main(String[] args) {
		Example ex = new Example();
		changValue(ex);
		System.out.println(ex.i);
	}
	public static void changValue(Example e ) {
		e.i = 30;
	}
}
上面的代码解释如下:
实例化一个Example对象ex,ex中只有一个属性i,初始值为20
调用changValue方法,将ex中的属性i的值赋值为30
输出ex中属性i的值
结果是:30
这样的结果就让很多人产生了困惑,String类型是引用类型,对象也是引用类型,为什么第二个例子不能改变i的值,第三个例子却改变了ex中i的值呢?Java中引用类型的参数传递到底是引用传递还是值传递呢?
其实,第二个例子和第三个例子我们所关注的点已经是错误的了,自然无法得出正确的结论
我们回顾一下引用传递和值传递的本质区别和行为表现上的区别
| 按值传递 | 按引用传递 | |
| 本质区别 | 创建副本 | 不创建副本 | 
| 表现结果 | 函数中无法改变原始对象 | 函数中可以改变原始对象 | 
注意到了吗?我们应该关注的是**原始对象的变化**
> **Java中,引用类型的原始对象(即原始值)是保存在堆中的,变量中存储的是原始值的引用(Java中存储的是该对象在堆中的内存地址),**
> **该引用指向堆中的对象,所以我们关注的关键点应该是对象的引用有没有发生变化,而不是原始对象中的内容有没有发生变化**
重要依据:如果可以改变对象的引用,就说明是引用传递,如果不能改变对象的引用就说明是值传递
下面我们多举一个例子:
public class Example {
	int i = 20;
	public static void main(String[] args) {
		Example ex = new Example();
		changValue(ex);
		System.out.println(ex.i);
	}
	public static void changValue(Example e ) {
		e = new Example();
		e.i = 50;
	}
}
这个例子和上面第三个例子很相似,区别在于:
changValue方法中将一个新的原始对象的引用赋值给了形参e
如果Java是按引用传递的话,e = new Example();就是修改了实参ex的引用,即就是改变了原始对象
如果Java是按值传递的话,实参ex不会有任何变化
结果:20
这个结果说明:changValue方法并没有修改到ex的引用,也就是说,e只是ex的副本,对e的引用进行的所有操作都不会影响到ex,所以我们
从上述代码运行的结果也可以证明Java中是只有值传递的
参考
为什么说Java中只有值传递
按值传递、按引用传递、按共享传递
为什么 Java 只有值传递,但 C# 既有值传递,又有引用传递,这种语言设计有哪些好处?
Java中到底是值传递还是引用传递?的更多相关文章
- Java 到底是值传递还是引用传递?
		
关于这个问题,引发过很多广泛的讨论,看来很多程序员对于这个问题的理解都不尽相同,甚至很多人理解的是错误的.还有的人可能知道Java中的参数传递是值传递,但是说不出来为什么. 在开始深入讲解之前,有必要 ...
 - JAVA方法中参数到底是值传递还是引用传递
		
当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递? 答:是值传递.Java 语言的参数传递只有值传递.当一个实例对象作为参数被传递到方法中时,参 ...
 - 【Java思考】Java 中的实参与形参之间的传递到底是值传递还是引用传递呢?
		
科普: 值传递(pass by value)是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数. 引用传递(pass by reference)是指在 ...
 - Java中的值传递和引用传递
		
这几天一直再纠结这个问题,今天看了这篇文章有点思路了,这跟C++里函数参数为引用.指针还是有很大区别. 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里 ...
 - java中方法的参数传递机制(值传递还是引用传递)
		
看到一个java面试题: 问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递.Java 编程语言只有值传递参 ...
 - java参数传递时到底是值传递还是引用传递
		
java参数传递时到底是值传递还是引用传递(baidu搜集) 问”,很多人的BLOG里都引用这些面试题,最近因为工作内容比较枯燥,也来看看这些试题以调节一下口味,其中有一道题让我很费解. 原题是:当一 ...
 - Java中传参的值传递和引用传递问题(转)
		
今天遇到了一个java程序,需要用参数来返回值(虽然最后用另一种方法实现了),在网上看到这样一篇文章,很受启发. 本文章来自于http://hi.baidu.com/xzhilie/blog/item ...
 - Java调用函数传递参数到底是值传递还是引用传递
		
今天翻看微信上有关Java技术的公众号时,看到了一篇关于Java中值传递的问题,文章讨论了在Java中调用函数进行传参的时候到底是值传递还是引用传递这个面试时会问到的问题.之前也接触过类似的问题,但只 ...
 - 188W+程序员关注过的问题:Java到底是值传递还是引用传递?
		
在逛 Stack Overflow 的时候,发现了一些访问量像阿尔卑斯山一样高的问题,比如说这个:Java 到底是值传递还是引用传递?访问量足足有 188万+,这不得了啊!说明有很多很多的程序员被这个 ...
 
随机推荐
- 1 下载abp 以及 遇到的包管理问题
			
我选择的是ef 多页面 不适用系统的module zero模块 项目名为Blog_Solution 遇到一个问题是就是 Castle.LoggingFacility.MsLogging 版本问题 我 ...
 - WPF的消息机制(一)- 让应用程序动起来
			
原文:WPF的消息机制(一)- 让应用程序动起来 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/powertoolsteam/article/det ...
 - C# WPF 实现鼠标固定在指定范围内运动
			
原文:C# WPF 实现鼠标固定在指定范围内运动 一.背景: 需要实现带有三个屏幕,三个屏幕分别显示窗体,但鼠标只能在主窗体中运动,不能移动到其他的两个附屏中. 二.实现: 具体实现使用的是u ...
 - ios 拿到第一响应者的当前视图
			
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; UIView *firstResponder = [keyWi ...
 - UWP 应用中的Back button(TitleBar) 的处理
			
后退按钮是一项系统提供的 UI 提示,可以在后退堆栈或用户导航历史记录中支持向后导航. 用起来其实也是很简单的,只需要在App.xaml.cs 中修改(添加)如下红色代码,便可实现.. sealed ...
 - WPF图片放大后模糊的解决方法
			
原文:WPF图片放大后模糊的解决方法 WPF中显示图片的方式很多,可以用Image控件来显示图像,或者直接设置一个控件的Background.图片的放大也很简单,直接设置显示图片的控件的Width和H ...
 - Debian7离线升级bash漏洞—然后修复方法
			
### 昨天还说的传说要又一次出补丁,今天就都出来了.基本操作一致就是測试结果不一样.继续修复 Debian7 wheezy版本号的bash漏洞,例如以下操作: 1.測试是否须要升级 # env x= ...
 - opencart源码解析之 index.php
			
//访问index.php,安全过滤.加载配置文件.核心启动文件.函数库.类库 //转载请注明: http://blog.csdn.net/dabao1989/article/details/2122 ...
 - typescript Json Convert
			
关键代码 this.data={}; // json string this.dataStr=JSON.stringify(this.data); // json object this.conver ...
 - 搭建本地yum源和局域网yum源
			
搭建本地yum源和局域网yum源 由于很多客户环境是专网,不允许连网,无法使用网上的各种yum源,来回拷贝rpm包安装麻烦,还得解决依赖问题.所以想着搭建个本地/局域网YUM源,方便安装软件. 1 ...