`
focus2008
  • 浏览: 27351 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

Java语言中函数参数的传递机制初探

    博客分类:
  • java
阅读更多
先上一段代码,无码无真相 ^-^

import java.util.ArrayList;
import java.util.Date;

public class JavaReference
{
	public static void change(Date date)
	{
		date = null;
	}
	
	public static void changeList(ArrayList<Integer> lst)
	{
		lst.add(3);
		lst = null;
	}
	
	public static void main(String[] args)
	{
		Date d = new Date();		
		System.out.println(d);		
		JavaReference.change(d);		
		System.out.println(d);	// 这里会打印  null 吗?
		
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		System.out.println(list.size());
		
		JavaReference.changeList(list);		
		System.out.println(list.size());	// 这里会报空指针错误码?还是会打印 3 呢?
	}
}

上面的 d 会打印null吗?
最后的打印语句会报空指针吗?

继续看之前,请您在心中该处答案,并且分析为什么结果是这样的。

===================================华丽的分割线======================================

打印结果为:
Wed Dec 03 13:35:21 CST 2014
Wed Dec 03 13:35:21 CST 2014
2
3

您答对了吗?

为什么结果是这样的呢?
原因分析:
    有人说Java中没有指针,但是java中有处处是指针。指针的本质是什么呢?学过C/C++的应该不陌生,指针是一个变量,他自己的内存空间在栈上分配,既然指针是一个变量,那么这个变量的值是什么呢?指针这个变量专门用于保存 地址值 ,而这个地址值,在Java中是一个堆上的对象的地址。在C/C++中指针也会指向栈上的变量或者对象。但是在Java中指针或者说引用只能指向堆上的某个对象。

在Java中函数参数的传递,是值传递呢?还是引用传递呢?

没有异议:绝对是值传递!

有了上面的基础,再来分析上面的结果:
JavaReference.change(d);
change函数将 d 这个保存了 new Date() 语句所产生的对象的地址传给了函数change,在C/C++中,如果要在change函数中修改 d 所指向的对象,必须如下操作:
*d = null;

这里的含义是: *d  表示取得 d 所指向的对象,就是得到d所指向的对象,然后再来修改d所指向的那个对象。

但是在java中不能这样操作,那么java中change函数中的
date = null

是什么意思呢?
是这样的:change(d) 将 new Date() 对象的地址传递给了 change函数的参数 date, 此时date是change函数局部变量,然后再将null赋值给这个局部变量,那么实际参数 d 的值变了吗?没有!d 还是指向 new Date() 所产生的那个对象。
所以 System.out.println(d); 不会打印null,他会自动调用 d.toString()

注意注意:d.toString() 这个调用很神奇!!!! 他为什么神奇,他哪里神奇呢?
我们知道 d 保存了指向 new Date() 的那个对象,那么 d 是如何访问 new Date() 对象的方法的呢?语法我们都知道: d.toString(),但是其中的暗含了下面的过程:
1)先获得 d 指向或者说引用的对象,即那个 new Date() 产生的对象;
2)然后调用他的 toString()方法; 

在C/C++中相当于: d->toString(); 或者: (*d).toString();
看到这里,您应该明白了,因为java中没有 (*d) 这样的操作符来访问d所指向的对象,所以java中直接省略了(*d) 或者,将 (*d) 暗含在 d.toString()中了。

所以java 中的 d.toString() 等价于C/C++中的 (*d).toString().

明白了这一点就一切真相大白了。

JavaReference.changeList(list);

调用,在changeList中,将list所指向的 new ArrayList<Integer>() 对象的地址传递给了changeList的局部变量lst ,然后 lst.add(3) 相当于C/C++中的 (*lst).add(3);即先获得lst这个局部变量中保存的地址所指向的对象,也就是指向 new ArrayList<Integer>() ,然后调用这个对象 的 add(3)方法。所以list.size() == 3

那么 局部变量 lst = nul; 是什么意思呢?他只是将 局部变量 lst 的值赋值为null,他没有改变实际参数 list.

lst = null 不等价于C/C++中的 (*lst) = null;

原因是这里没有使用调用操作符 “.”,他就是普通的局部变量的赋值操作。

调用操作符 “.”为什么会相当于C/C++中的 (*lst).add(3)呢?即局部变量 lst 调用函数 add(3) 时,为什么先要 访问 lst 所指向的对象 new ArrayList<Integer>() ??

废话,不先访问他指向的对象,怎么去调用他的方法呢???

所以到这里:最重要的是,java中对象参数的传递时基于值的传递,传递的是实际参数所引用的那个对象的地址。指针,或者说引用在调用函数时,会自动先去访问它的地址所指向的那个对象,也就是它说引用的那个对象,然后调用该对象的方法。所以就修改了该对象。所以最后的list.size() == 3;





分享到:
评论

相关推荐

    eclipse 下实现java JNI 初探

    JNI接口定义了如何在Java和本地代码之间传递参数、执行方法调用以及返回结果。 在Eclipse中开始JNI开发,你需要做以下几步: 1. **创建Java项目**:首先,我们需要一个Java项目,其中包含至少一个Java类,该类声明...

    cocos2d-x初探学习笔记(3)

    ### cocos2d-x初探学习笔记(3)—— ...通过以上内容,我们可以看到`cocos2d-x`中的`CCAction`不仅提供了丰富的动作类型供开发者选择,还支持灵活的动作组合以及消息传递机制,极大地提高了游戏开发的效率和灵活性。

    基于Android的应用软件开发实例初探.doc

    2.1.2 Scala:Scala是一种多范式编程语言,它集成了面向对象和函数式编程的特点。虽然使用Scala开发Android应用相对较少,但它提供了强大的语言特性和表达能力。 2.1.3 Java:Java是Android开发最常用的语言,...

    C语言入门经典(第4版)--源代码及课后练习答案

    8.3 按值传递机制 304 8.4 函数声明 305 8.5 指针用作参数和返回值 307 8.5.1 常量参数 310 8.5.2 从函数中返回指针值 318 8.5.3 在函数中递增指针 322 8.6 小结 322 8.7 习题 323 第9章 函数再探 325 9.1 ...

    javascript 自定义事件初探

    为了传递额外的参数,代码展示了使用`createFunction`函数来包装事件处理器,使其能接收任意数量的参数。`createFunction`通过`arguments`对象收集额外参数,并在事件触发时使用`apply`方法将它们传递给实际的事件...

    Beginning Python Using Python 2.6 and Python 3.1 - James W. Payne.pdf )

    介绍函数的概念,包括如何定义函数、传递参数、返回值,以及局部变量与全局变量的区别。函数是模块化编程的核心,有助于提高代码的复用性和可维护性。 #### 第6章:类与对象 探索面向对象编程(OOP)的基本原理,...

    Ioc注入讲解

    - **构造函数参数**:指定构造函数所需的参数。 - **属性值**:用于通过setter方法注入的属性值。 这些配置信息通常通过配置文件(如XML文件)或者注解的形式来定义。 ##### 2.3 注入参数 注入参数是指容器为Bean...

    first:2015 年 IT 学校练习

    了解函数的定义、参数传递和返回值是编程的基础。 4. **类与对象**:Java是面向对象的语言,类是对象的蓝图,而对象则是类的实例。理解封装、继承和多态是理解面向对象编程的关键。 5. **数组**:数组是存储相同...

    Spring开发指南

    1. **接口注入**:通过将依赖对象作为参数传递给目标对象的方法,实现依赖的注入。 - **优点**:易于测试。 - **缺点**:可能会导致方法参数过多,降低代码的可读性。 2. **设值注入**:通过setter方法将依赖对象...

    Spring经典资料

    3. **Type3 构造子注入**:依赖通过构造函数参数传递,这种方式确保了依赖在对象创建时即被初始化,从而避免了潜在的null引用问题。虽然可能使得构造函数变得冗长,但在高并发或多线程环境中,它能提供更好的线程...

    HelloWorldAgent.rar_Agent_agent jade

    5. **通信机制**:JADE提供了丰富的消息传递机制,例如,Agent之间可以通过`ACLMessage`进行通信。在`HelloWorldAgent`中,你可以发送或接收`ACLMessage`,处理与其他Agent的交互。 6. **调试与监控**:JADE提供了...

    spring中文教程

    2. **构造子注入**:通过类的构造函数传递依赖对象,这种方法确保了Bean的不可变性,增强了组件的安全性和稳定性。 3. **设值注入**:通过setter方法注入依赖,这是Spring中最常用的依赖注入方式,因为其灵活性高,...

    C语言高级编程技巧与数据结构.md

    这种特性使得在C语言中可以实现更加灵活的编程方式,比如可以将函数作为参数传递给其他函数,或者在运行时根据条件动态选择调用不同的函数。 ```c int add(int a, int b) { return a + b; } int (*operation)...

    Spring 开发指南

    - **构造子注入**:在构造函数中声明依赖,由Spring在实例化Bean时传入。 - **设值注入**:通过setter方法将依赖注入到Bean中。 每种注入方式都有其适用场景和优缺点,选择合适的注入方式对于提高代码质量和可维护...

    spring 帮助文档

    构建器注入(constructor injection)是指通过构造函数传递依赖项。这种方式能够确保对象在创建时就有所有必要的依赖项,提高了代码的稳定性和可测试性。 **2.2.3 属性注入** 属性注入(property injection)是通过...

    spring 開發指南

    - **Type3 构造子注入**:通过构造函数传递依赖,确保了对象的不可变性和依赖的一致性,适合于依赖不可变或者必不可少的情况。 - **几种依赖注入模式的对比总结**:每种依赖注入模式都有其适用场景,选择哪一种取...

    Ioc容器的个人理解介绍

    构造器注入是在创建对象时通过构造函数传递依赖;setter注入则是通过调用setter方法;接口注入是通过实现特定接口来接收依赖。 - **容器指令**:容器通过一系列的指令来指导如何创建和管理对象,例如bean的定义、...

    C语言文件操作、多线程编程和网络编程.md

    下面是一个简单的示例,演示了如何创建一个线程并传递参数给它: ```c #include #include void* printMessage(void* message) { printf("%s\n", (char*)message); return NULL; } int main() { ...

Global site tag (gtag.js) - Google Analytics