`

传了值还是传了引用(转载)

    博客分类:
  • java
 
阅读更多

JAVA中的传递都是值传递吗?有没有引用传递呢?

在回答这两个问题前,让我们首先来看一段代码:

public class ParamTest {
	// 初始值为0
	protected int num = 0;
	// 为方法参数重新赋值
	public void change(int i) {
		i = 5;
	}
	// 为方法参数重新赋值
	public void change(ParamTest t) {
		ParamTest tmp = new ParamTest();
		tmp.num = 9;
		t = tmp;
	}
	// 改变方法参数的值
	public void add(int i) {
		i += 10;
	}
	// 改变方法参数属性的值
	public void add(ParamTest pt) {
		pt.num += 20;
	}
	public static void main(String[] args) {
		ParamTest t = new ParamTest();
		System.out.println("参数--基本类型");
		System.out.println("原有的值:" + t.num);
		// 为基本类型参数重新赋值
		t.change(t.num);
		System.out.println("赋值之后:" + t.num);
		// 为引用型参数重新赋值
		t.change(t);
		System.out.println("运算之后:" + t.num);
		System.out.println();
		t = new ParamTest();
		System.out.println("参数--引用类型");
		System.out.println("原有的值:" + t.num);
		// 改变基本类型参数的值
		t.add(t.num);
		System.out.println("赋引用后:" + t.num);
		// 改变引用类型参数所指向对象的属性值
		t.add(t);
		System.out.println("改属性后:" + t.num);
	}
}

 

这段代码的运行结果如下:

参数--基本类型

原有的值:0

赋值之后:0

运算之后:0

 

参数--引用类型

原有的值:0

赋引用后:0

改属性后:20

 

从上面这个直观的结果中我们很容易得出如下结论:

1. 对于基本类型,在方法体内对方法参数进行重新赋值,并不会改变原有变量的值。

2. 对于引用类型,在方法体内对方法参数进行重新赋予引用,并不会改变原有变量所持有的引用。

3. 方法体内对参数进行运算,不影响原有变量的值。

4. 方法体内对参数所指向对象的属性进行运算,将改变原有变量所指向对象的属性值。

 

上面总结出来的不过是我们所看到的表面现象。那么,为什么会出现这样的现象呢?这就要说到值传递和引用传递的概念了。这个问题向来是颇有争议的。

 

大家都知道,在JAVA中变量有以下两种:

1. 基本类型变量,包括charbyteshortintlongfloatdoubleboolean

2. 引用类型变量,包括类、接口、数组(基本类型数组和对象数组)。

 

当基本类型的变量被当作参数传递给方法时,JAVA虚拟机所做的工作是把这个值拷贝了一份,然后把拷贝后的值传递到了方法的内部。因此在上面的例子中,我们回头来看看这个方法:

// 为方法参数重新赋值
public void change(int i) {
	i = 5;
}

 

在这个方法被调用时,变量iParamTest型对象t的属性num具有相同的值,却是两个不同变量。变量i是由JAVA虚拟机创建的作用域在change(int i)方法内的局部变量,在这个方法执行完毕后,它的生命周期就结束了。在JAVA虚拟机中,它们是以类似如下的方式存储的:

 

很明显,在基本类型被作为参数传递给方法时,是值传递,在整个过程中根本没有牵扯到引用这个概念。这也是大家所公认的。对于布尔型变量当然也是如此,请看下面的例子:

public class BooleanTest {
	// 布尔型值
	boolean bool = true;
	// 为布尔型参数重新赋值
	public void change(boolean b) {
		b = false;
	}
	// 对布尔型参数进行运算
	public void calculate(boolean b) {
		b = b && false;
		// 为了方便对比,将运算结果输出
		System.out.println("b运算后的值:" + b);
	}
	public static void main(String[] args) {
		BooleanTest t = new BooleanTest();
		System.out.println("参数--布尔型");
		System.out.println("原有的值:" + t.bool);
		// 为布尔型参数重新赋值
		t.change(t.bool);
		System.out.println("赋值之后:" + t.bool);
		// 改变布尔型参数的值
		t.calculate(t.bool);
		System.out.println("运算之后:" + t.bool);
	}
}

 

输出结果如下:

参数--布尔型

原有的值:true

赋值之后:true

b运算后的值:false

运算之后:true

 

那么当引用型变量被当作参数传递给方法时,JAVA虚拟机又是怎样处理的呢?同样,它会拷贝一份这个变量所持有的引用,然后把它传递给JAVA虚拟机为方法创建的局部变量,从而这两个变量指向了同一个对象。在篇首所举的示例中,ParamTest类型变量t和局部变量ptJAVA虚拟机中是以如下的方式存储的:

 

有一种说法是当一个对象或引用类型变量被当作参数传递时,也是值传递,这个值就是对象的引用,因此JAVA中只有值传递,没有引用传递。还有一种说法是引用可以看作是对象的别名,当对象被当作参数传递给方法时,传递的是对象的引用,因此是引用传递。这两种观点各有支持者,但是前一种观点被绝大多数人所接受,其中有《Core Java》一书的作者,以及JAVA的创造者James Gosling,而《Thinking in Java》一书的作者Bruce Eckel则站在了中立的立场上。

我个人认为值传递中的值指的是基本类型的数值,即使对于布尔型,虽然它的表现形式为truefalse,但是在栈中,它仍然是以数值形式保存的,即0表示false,其它数值表示true。而引用是我们用来操作对象的工具,它包含了对象在堆中保存地址的信息。即使在被作为参数传递给方法时,实际上传递的是它的拷贝,但那仍是引用。因此,用引用传递来区别与值传递,概念上更加清晰。

 

最后我们得出如下的结论:

1. 基本类型和基本类型变量被当作参数传递给方法时,是值传递。在方法实体中,无法给原变量重新赋值,也无法改变它的值。

2. 对象和引用型变量被当作参数传递给方法时,在方法实体中,无法给原变量重新赋值,但是可以改变它所指向对象的属性。至于到底它是值传递还是引用传递,这并不重要,重要的是我们要清楚当一个引用被作为参数传递给一个方法时,在这个方法体内会发生什么。

 

转载自:http://zangweiren.iteye.com/category/34977

  • 大小: 13.8 KB
  • 大小: 27.2 KB
分享到:
评论

相关推荐

    【重磅、更新、百万级观测值】1991-2021上市公司专利引用数据合集,海量数据!

    【原创整理匹配,严禁任何团队和个人转载获利!非法转载必究!】 压缩包内为全网年份最新、公司数量最多、...测值,包括被引用专利号、申请日、引用年度、公司类型、发明类型、各年被引用(剔除自 引用)次数,各年

    【重磅、更新、海量观测值!】1991-2022上市公司专利引用数据合集(发明-实用新型专利)

    【原创整理,严禁任何团队和个人转载获利,转载必究!】 上 市公司专利引用相关研究是近年来研究的热点方向之一,上市公司专利引用可以用于金融学 和企业经济等领域的研究,并在各类顶级刊物中频繁出现。附件内为...

    Java语言基础的理解与笔记

    主要简述Java基础,包括概述,基础语法,流程控制...注:本文档是基于参考许多视频文章引用和自己理解写成,版权归于作者本人,未经允许不可以直接转载,如有冒犯他们版权请通知作者及时修改,谢谢各位朋友!!!^.^

    arcgis工具

    转载ESRI论坛Lucy1114帖子说明: 12. 导出Shape格式为其他软件识别的打印格式如JEPG等格式 FILE/EXPORT MAP 然后选择相应的图片格式,此时也可设置答应的分辨率 pdi 13. 建立注记层 方法一.carvert to ...

    网站后台管理系统框架.rar

    请不要修改该控件的GUID值,因为该值是上传进度的唯一标识,如果固定,那么多用户同时上传时将会出现错误 然后布置1到N个的HtmlUpFile控件,这里要注意的HtmlUpFile控件必须加Runat="Server" 您可以为该控件SubmitID指定...

    perl 程序开发教程

    Perl 5 教程 by flamephoenix ... 本教程欢迎下载、转载和链接,转载必须保留本说明并保持文章完整。本教程不允许以任何形式用于商业用途。 对本教程有何疑问、更正或建议请告诉我,谢谢。 flamephoenix

    SQL Server 中 EXEC 与 SP_EXECUTESQL 的区别.doc

    SQL Server 中 EXEC 与 SP_EXECUTESQL 的区别 MSSQL为我们提供了两种动态...本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.

    Ajax 模仿google百度提示输入框 v1.8

    转载请注明出处 此小程序是模仿Google(sgguest)输入提示框所编写 亦可用于用户注册邮箱时的提示 此程序使用方便简单、易用灵活 且不限Asp、php、net 更新说明: 1.支持xhtml 2.兼容IE6,IE7,FF 3.支持input显示与...

    javascript常用对象梳理

    JS中的常用对象[转载]web 技术 2010-06-05 15:00:30 阅读3 评论0 字号:大中小 订阅 [removed] Window For JavaScript 熟练window对象的open、close、alert、confirm、prompt、setTimeout、clearTimeout、...

    windows用户称拦截api

    Windows用户层下拦截api的原理与实现(附源码) (2008-03-29 16:15:07)转载▼ 标签: computer 杂谈 声明:本页所发布的技术文章及其附件,供自由技术传播,拒绝商业使用。本页文章及其附件的所有权归属本文作者...

    leetcode超时用例数-Basic:基本的

    leetcode超时用例数    |    :books: Docsify :light_bulb: 关于 :books: ...修饰引用,指向常量的引用(reference to const),用于形参类型,即避免了拷贝,又避免了函数对值的修改; 修饰成员

    leetcode超时用例数-Computer:电脑

    leetcode超时用例数 ...常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改; 修饰成员函数,说明该成员函数内不能修改成员变量。 使用 const 使用 // 类 class A { private: const

    leetcode2sumc-Interview::books:C/C++面试基础知识总结

    leetcode 2 sum c :blue_book: ...常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改; 修饰成员函数,说明该成员函数内不能修改成员变量。 使用 const 使用 // 类 class A { private: co

    leetcode2sumc-NaughtyBear_notes:NaughtyBear笔记+搬运+总结

    leetcode 2 sum c NaughtyBearNotes NaughtyBear 笔记 搬运+总结 :open_book: ...常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改; 修饰成员函数,说明该成员函数内不能修改成员变量。

    leetcode超时用例数-interview:面试

    leetcode超时用例数 ...常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改; 修饰成员函数,说明该成员函数内不能修改成员变量。 使用 const 使用 // 类 class A { private: const int a;

    IIS服务器SSI(rewrite重写)插件的帮助资料

    相较之下,“a*a”匹配器不能告知哪个分支被采取(第一个 a还是第二个a)因此必须尝试两种。 Regex可以检测出这种“病态”的正则表达式并终止和它们的匹配。这将使ISAPI_Rewrite的规则失效。当一个规则失效时,ISAPI...

    飞飞Ajax模仿google提示输入框源码 v1.7

    当输入框失去焦点时,提示框会自动隐藏******************************************************************文件使用说明:在需要使用的页面首先要引用2个js文件此二文件必须放于调用函数之前*********************...

    springmybatis

    MyBatis ...-- 返回list 的select 语句,注意 resultMap 的值是指向前面定义好的 --> select * from user where userName like #{userName} 在 IUserOperation 接口中增加方法:public List...

    Linux_unix_shell第一部分编程(共两部分)

    1.8.1 如何计算umask值 10 1.8.2 常用的umask值 11 1.9 符号链接 12 1.9.1 使用软链接来保存文件的多个映像 12 1.9.2 符号链接举例 12 1.10 小结 13 第2章 使用find和xargs 14 2.1 find命令选项 14 2.1.1 使用name...

    基于SIP开发软件电话的一些资源(转自YOUTOO)

    (CopyLeft by Meineson | www.mbstudio.cn,原创文章,欢迎转载,但请保留出处说明!) 本文档最新版本及文中提到的相关源码及VC6工程文件请在本站找,嘿嘿~~ (首页的SkyDriver公开文件夹中,可能需要用代理...

Global site tag (gtag.js) - Google Analytics