在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式:
A. 是按值传递的?
B. 按引用传递的?
C. 部分按值部分按引用?
此处暂不宣布正确答案,我们通过一个简单的例子让大家自己找答案:
1. 先定义一个类型Value
public static class Value {
private String value = "value";
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
2. 写两个函数newValue和modifyValue:newValue会将入参指向一个新的对象,modifyValue会调用入参的setValue方法修改对象的value值。
public static void newValue(Value value) {
value = new Value();
value.setValue("new value");
System.out.println("In newValue, HashCode = " + value.hashCode() + ", value = " + value.getValue());
}
public static void modifyValue(Value value) {
value.setValue("new value");
System.out.println("In modifyValue, HashCode = " + value.hashCode() + ", value = " + value.getValue());
}
3. 简单的测试代码
public static void main(String[] args) {
Value value1 = new Value();
System.out.println("Before modify, HashCode = " + value1.hashCode() + ", value = " + value1.getValue());
// 将value1指向新的Value对象
newValue(value1);
System.out.println("After modify, HashCode = " + value1.hashCode() + ", value = " + value1.getValue() + "\n");
Value value2 = new Value();
System.out.println("Before modify, HashCode = " + value2.hashCode() + ", value = " + value2.getValue());
// 使用object的set方法,修改对象的内部值
modifyValue(value2);
System.out.println("After modify, HashCode = " + value2.hashCode() + ", value = " + value2.getValue());
}
4. 执行结果日志:
Before modify, HashCode = 12677476, value = value
In newValue, HashCode = 33263331, value = new value
After modify, HashCode = 12677476, value = value
Before modify, HashCode = 6413875, value = value
In modifyValue, HashCode = 6413875, value = new value
After modify, HashCode = 6413875, value = new value
5. 结果分析:
上述代码这是非常常见的一种编程模式:在外围定义|保存|获取一个值或对象,将这个对象作为参数传入一个方法,在方法中修改对象的属性、行为。但两个方法newValue和modifyValue的修改方式不一样,在方法调用之后,该对象在外围看来也有很大的差别!如何理解这种差异呢?先温故一下按值传递、按引用传递的概念:
* 按值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。
* 按引用传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本。因此,如果函数修改了该参数,参数的原始值(函数块之外的调用代码中)也随之改变。
正确的答案:A——Java函数是按值传递参数的!
分析一下日志:
* 第一段日志输出,value1参数在newValue方法内部被改为指向新对象,并输出了新对象的hashCode和value值,但跳出newValue方法域之后,在main方法中的value1没有发生任何变化,这符合按值传递的定义和特点;如果是按引用传递,value1在调用newValue(Value value)方法之后,应该是发生变化的。
* 第二段日志输出,value2在modifyValue方法内部进行了setValue操作,hashCode不变而value被修改,离开modifyValue方法域之后,在main方法中value2确实发生了变更。使用过C++的人容易将这种现象理解为:按引用传递函数参数!因为这跟C++中的按引用传递像极了!但这里恰恰是最容易陷入误区的地方!
两段日志的不同现象背后所隐藏的是原理是:Java语言是按值传递参数,按引用传递对象的;Java中所操作的对象其实都是操作对象的引用,object本身保存在“堆”中,而对象的“引用“保存在寄存器或“栈”中。
伪代码描述一下newValue方法和modifyValue方法的不同之处:
newValue{
Value_ref2 = value_ref1; // 按值传入引用value_ref1,得到value_ref1的副本
value_obj2 = new Value(); // value_obj2被创建、初始化在“堆“中
value_ref2 -> value_obj2; // value_ref2 指向value_obj2
value_ref2 ->value_obj2.setValue(“xxx”); // value_obj2 的value被修改
printValueObj2(); // 此处打印的是obj2的值
}
modifyValue{
Value_ref2 = value_ref1; // 按值传入引用value_ref1,得到value_ref1的副本
value_ref2 ->value_obj1.setValue(“xxx”); // value_obj1 的value被修改
printValueObj1(); // 此处打印的是obj1的值
}
够清楚了吧!value1_ref1在作为参数传入函数的时候,首先被复制了一份副本value1_ref2供函数域使用,此时这两个ref都是指向同一个value_obj; newObject函数中的代码[ value = new Value(); ] 其实是将value1_ref1指向了一个新的对象value_obj2;在这之后的set操作都是对新对象的操作;modifyValue函数是通过set方法直接操作value_obj1,这是跟newValue函数的不同之处。
如果还是不太明白,请先确定是否已经理解 “引用”、“对象”的概念,可以Google、百度相应的文章学习一下:)
分享到:
相关推荐
主要介绍了 java 中函数的参数传递详细介绍的相关资料,需要的朋友可以参考下
描述:为了解决java与C结构通信过程中结构体解析问题。 主要功能:能友好的用java处理任何发送的C结构体对象,并且能发送java对象转换成C结构体接收的二进制。 功能说明 1、基于spring框架开发 2、对于结构体定义...
5.2.2 类方法的参数传递 81 5.2.3 类方法的返回值 83 5.2.4 值传递与引用传递 84 5.3 类变量和变量作用域 86 5.3.1 Java的类变量 86 5.3.2 Java的变量作用域 87 5.4 递归程序设计 89 5.4.1 递归方法...
在本篇文章中小编给大家整理了关于java8传函数方法和相关知识点,需要的朋友们学习下。
因为函数是弱类型的,没有方法去声明它所期望的参数类型,并且给任何函数传递任何类型的值都是合法的。 1.Js函数可以传入不同的参数,如 function writeNString(strMsg){ [removed](strMsg + " "); } 2.Js函数...
在JavaScript中,回调函数具体的定义为:函数A作为参数(函数引用)传递到另一个函数B中,并且这个函数B执行函数A。我们就说函数A叫做回调函数。如果没有名称(函数表达式),就叫做匿名回调函数。因此callback 不一定...
经过几天的奋战,终于让我搞懂了JNA的解析以及指针在结构体中的应用!!!下了很多的资料,里面包含c++的.h头文件,给需要帮助的人看看!! 里面包含代码以及解析过程。
那个可变参数的就是个数组,你传多少个参数都被放到那个数组里面。这样方便了程序员,因为如果不确定要传的参数的个数的话,我们要写带1个参数的,带... 该进后的这个方法,我们只要写一个函数就好,可以传任意个参数。
invokeByObject.java 对象实参传递示例程序 invokeByValue.java 传值调用示例程序 invokeMethod.java 同一个类中调用方法示例 invokeOther.java 类的外部调用方法示例 invokeStaticMethod.java 调用静态方法...
C 语言中有一种数据类型叫做指针,于是将一个数据作为参数传递给某个函数的时候,就有两种方式:传值,或是传指针,它们的区别,可以用一个简单的例子说明: /* 例 5 */ /** * @(#) test.c * @author fancy */...
Java方法的参数列是资讯传递的机制,可以从外面将资讯送入程序的黑盒子,参数列是方法的使用介面. 一个方法如果拥有参数列,在呼叫方法时,传入不同的参数就可以产生不同的执行结果. 4-3-2 类别方法的参数传递-范例 例如...
Python唯一支持的参数传递方式是『共享传参』(call by sharing) 多数面向对象语言都采用这一模式,包括Ruby、Smalltalk和Java(Java的引用类型是这样,基本类型按值传递) 共享传参是指函数的各个形式参数获得实参...
第一章 流与文件 1.1 流 1.1.1 读入和写出字节 1.1.2 完整的流家族 1.1.3 组合流过滤器 1.2 文本输入与输出 1.2.1 如何写出文本输出 1.2.2 如何读入文本输入 ...12.10.3 以本地方法方式实现注册表访问函数
因为function实际上是一种对象,它可以“存储在变量中,通过参数传递给(别一个)函数(function),在函数内部创建,从函数中返回结果值”。 因为function是内置对象,我们可以将它作为参数传递给另一个函数,延迟到...
本书的主要内容如下: Java 8实战目录第一部分 基础知识 第1章 为什么要关心Java 8 2 第2章 通过行为参数化传递代码 20 第3章 Lambda表达式 34 第二部分 函数式数据处理 第4章 引入流 68 第5章 使用流 82 ...
参数说明 callback: 要对每个数组元素执行的回调函数。 thisObject : 在执行回调函数时定义的this对象。 ...如参数 thisObject 被传递进来,它将被当做回调函数(callback)内部的 this 对象,如
Java基本类型的都是值传递,对象使用的都是引用传递 Q0043 java相关概念 "static:静态,无需实例化,可直接引用,全局只有一份copy,修饰变量和方法 final:最终的,不可继承、不可修改,修饰变量、方法、类 ...
5.2.2 方法的参数传递机制 116 5.2.3 形参长度可变的方法 120 5.2.4 递归方法 121 5.2.5 方法重载 123 学生提问:为什么方法的返回值类型不能用于区分重载的方法? 124 5.3 成员变量和局部变量 124 5.3.1 成员...
每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些对象成员变量的拷贝,线程对所有对象成员变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。...