`

不纠结!Java是按值传递的!

阅读更多
在Java中,所有的方法参数,都是"按值传递".
有那么一种说法,Java中基本类型是按值传递,对象是按引用传递.这个说法其实是不确切的,确切的说法是
Java中基本类型将值作为参数,按值传递.对象将引用作为参数,按值传递.
所谓按值传递,就是传递的是一个副本.对象引用的副本,也算是按值传递!

现在来解释下Java方法传递引用时的情况.
比如说有一个引用 A a = new A();
将a当作参数传递的时候,传递的不是a指向的对象,也不是a引用本身,而是a的一个副本,并且这个副本和a是一模一样.  这里的一模一样就是a和副本指向同一个对象.
那么当a的副本进入方法后,对这个副本的修改同样会影响a.
但是如果进入方法后将a的副本指向其它对象的时候,那么和a是一点关系都没有了.

看一段代码和运行结果:
public class A {
	int a = 0;
}

public class Test {

	public static void main(String[] args) {
		A a = new A();
		A a2 = new A();
		System.out.println("改变前的a:" + a);
		System.out.println("改变前的a2:" + a2);
		System.out.println("-----------------");
		doSomething(a, a2);
		System.out.println("-----------------");
		System.out.println("改变后的a:" + a);
		System.out.println("改变后的a2:" + a2);
		System.out.println("-----------------");
		System.out.println(a.a);
		System.out.println(a2.a);
	}

	public static void doSomething(A a, A a2) {
		System.out.println("方法内前的a:" + a);
		a.a = 2;
		a = a2;
		System.out.println("-----------------");
		System.out.println("方法内的a:" + a);
		System.out.println("方法内的a2:" + a2);
	}
}


结果:
①改变前的a:A@18a992f
②改变前的a2:A@4f1d0d
-----------------
③方法内前的a:A@18a992f  
-----------------
④方法内的a:A@4f1d0d
⑤方法内的a2:A@4f1d0d
-----------------
⑥改变后的a:A@18a992f
⑦改变后的a2:A@4f1d0d
-----------------
⑧2
⑨0


解释:

③说明,方法内的a为方法外a的一个引用副本。这个时候,方法内的a和方法外的a是指向同一个对象(A@18a992f)。

④说明,在方法内,改变了a的副本的引用,指向了a2所指向的这个对象(A@4f1d0d),并没有改变方法外a的引用。

⑧说明,在方法内a=a2前,对a的操作实际上就是对方法外a的操作,因为他们指向的是同一个对象(A@18a992f)。

图例:



黑线是在方法内
红线是刚刚进入方法
蓝线是a=a2操作完成后的效果

所以当a=a2操作后,对a的任何操作都不发改变方法外的a的值。
但是如果是按引用传递,则a=a2操作后,方法外的值也就跟着改变了!

所以,java是按值传递的!!
  • 大小: 17.8 KB
分享到:
评论
3 楼 shuangwhywhy 2013-01-16  
pmh905001 写道
如何证明这个副本是内存中真实存在的?这个副本就是作者凭空想象的东西。如果很深调用栈,那岂不是要创建很多副本?那这些副本又放在哪儿?这个像c语言的指针,传递的是一个地址的值而已。


我提醒你,C 语言本身就是按值传递的,因为它根本不支持按引用传递,无论你用什么方法模拟,你用指针,它依然是在函数调用时拷贝出一个相同指针来。
2 楼 jwx0925 2012-03-06  
pmh905001 写道
如何证明这个副本是内存中真实存在的?这个副本就是作者凭空想象的东西。如果很深调用栈,那岂不是要创建很多副本?那这些副本又放在哪儿?这个像c语言的指针,传递的是一个地址的值而已。



如果是基本对象,传递的是副本。

如果是对象,传递的是对象的引用的副本。引用的副本,实际上也是值。

理解原理,不必纠结“按值”或者“按引用”
1 楼 pmh905001 2012-03-05  
如何证明这个副本是内存中真实存在的?这个副本就是作者凭空想象的东西。如果很深调用栈,那岂不是要创建很多副本?那这些副本又放在哪儿?这个像c语言的指针,传递的是一个地址的值而已。

相关推荐

    面向Java开发者的函数式编程

    - **函数作为一等公民**:在函数式编程中,函数不仅可以被定义,还可以像其他值一样被传递、返回或存储。这种能力极大地增强了程序的灵活性和表达力。 - **Lambda表达式与闭包**:Lambda表达式允许我们定义匿名函数...

    2016 Java 基础 面试题

    - **GET**:主要用于获取数据,数据会被附加在URL后面,安全性较低,不适合传递敏感信息;GET请求长度有限制。 - **POST**:主要用于发送数据,数据放在HTTP消息的正文中,没有数据量限制,相对安全。 #### 4. **...

    深入浅出JNA

    JNA极大地简化了Java调用原生函数的过程,使得Java开发人员可以更加专注于应用程序的核心逻辑,而不是纠结于底层细节。通过本文的介绍,相信读者已经掌握了如何使用JNA进行跨平台的原生函数调用。在实际项目中,合理...

    使用EL和JSTL显示查询结果

    在JavaWeb开发中,EL(Expression Language)和JSTL(JavaServer Pages Standard Tag Library)是两种非常重要的技术,它们极大地简化了视图层的编程,使得开发者可以从Servlet中传递数据到前端页面,并以优雅的方式...

    java-refactoring-project:应用程序开发实践的最终项目,RIT 的一个课程

    我们的重构集中在几个关键领域:使添加新类型的输入和输出文件变得容易,将 EdgeConvertGUI 的纠结“上帝类”分解为更小、更有凝聚力的部分,并创建一个中央中介来存储状态程序和类之间传递方法调用以减少耦合。...

    Android-Flow一个Android包装器来简化启动一个Activity

    Flow库的核心思想是通过构建一个Activity的导航模型,将启动流程和数据传递过程进行抽象和封装,从而让开发者能够更专注于业务逻辑,而不是纠结于繁琐的Intent构建和生命周期管理。Flow的使用可以显著提高代码的...

    Filter过滤器的概念和使用

    在Java Web开发中,`Filter`是一种功能强大的机制,它能够拦截客户端请求,并在请求到达目标资源(如Servlet)之前或之后进行处理。这为开发者提供了极大的灵活性来添加诸如编码处理、登录验证、权限检查等功能。 #...

    Android多Fragment的MVP实现

    在Fragment中定义一个接口,该接口作为Presenter的回调,用于传递数据和事件。 ```java public interface MyFragmentView { void showData(String data); void updateUI(); } ``` **4. 在Fragment中实现View接口...

    Android webview和JS交互

    不过,需要注意的是,不同版本的Android系统可能会有差异,比如早期版本可能不支持`addJavascriptInterface`的安全特性,所以在实际开发中,应尽量使用API Level 17及以上版本的`@JavascriptInterface`注解,并对低...

    Android BroadcastReceiver

    Intent不仅用于启动活动(Activity)、服务(Service),还充当消息传递的载体,允许组件之间进行通信。BroadcastReceiver允许你在后台接收并响应这些意图,即使应用没有在前台运行。 一、Intent概述 Intent是...

    Application聊天室

    3. **消息序列化与反序列化**:在客户端和服务器之间传递的消息需要被序列化为字节流以便在网络上传输,然后在另一端进行反序列化恢复成原本的数据结构。JSON或XML是常见的序列化格式,它们易于解析且跨平台。 4. *...

    RealTimeComm-开源

    RealTimeComm是一个开源的通信框架,专为实现XML、文本或Java对象的消息传递而设计。这个框架的核心功能在于管理客户端的连接接收,以及高效地处理消息的发送与接收,为构建各种实时通信应用提供了稳固的基础架构。 ...

Global site tag (gtag.js) - Google Analytics