JAVA 参数传递

网络上有太多关于JAVA参数传递是传值还是传引用的讨论,其实大多是概念不清,混淆视听。从程序运行的角度来看,参数传递,只有传值,从不传递其它的东西。只不过值的内容有可能是数据,也有可能是一个内存地址。

  Java中的数据类型有两大类,即基本类型(primitive types), 共有8种,包括int, short, long, byte, float, double, boolean, char,存在于栈(Stack)中。另一种暂称为对象类型,包括Integer, String, Double等相应基本数据类型的包装类,以及其他所有JAVA自带和用户自定义的类,这些类数据全部存在于堆中(Heap),如下图所示。

   对象类型的数据不同于基本类型的数据,我们所定义的对象变量并不是对象实例本身,而是对象的一个引用(reference)地址,该内存地址用来定位该对象实例在HEAP中的位置。对象实例本身和对象的引用分别保存在HEAP和STACK中,对象引用和对象实例之间的关系好比遥控器和电视机之间的关系,在房间走动的时候里,你只需拿着遥控器就可以控制电视机,而不必带着电视机。而且,即使没有电视机,遥控器也可以独立存在,也就是说你可以定义一个对象类型的变量,但是可以暂时不和一个对象实例关联。多个对象引用也可以指向同一个对象实例。

 

          Heap(堆)      Stack(栈)
 JVM中的功能           内存数据区                   内存指令区
 存储数据 对象实例 基本数据类型, 指令代码,常量,对象的引用地址

 

下面我们来看看几个例子,您就会更加明白。

例子1:

public class Test {
public static void changeValue(int i) {
i=2;
System.out.println("during test: i = " + i);
}
public static void main(String[] args) {
int i = 1;
System.out.println("Before test: i = " + i);
changeValue(i);
System.out.println("After test: i = " + i);
}
}
  运行结果:
Before test: i = 1
during test: i = 2
After test: i = 1

不难看出,虽然在 changeValue(int i)方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响。其内部原理是,main方法里的变量和changeValue方法的参数是两个不同的变量,以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法的,那么在方法里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。

例子2:

public class Test {
public static void test(StringBuffer str) {
str.append(", World!");
}
public static void main(String[] args) {
StringBuffer string = new StringBuffer("Hello");
test(string);
System.out.println(string);
}
}
  运行结果:
Hello, World!

   在这个例子里,似乎变量string被“改变”了。但其实改变的并不是string变量本身,也就是说string保存的内存地址并没有被改变,改变的是它所指向的对象实例。内部原理是这样的,在main方法里定义了一个对象引用string,并且把它和一个对象实例关联new StringBuffer。方法调用的时候,string所保存的对象实例的内存地址传递给了test方法的对象引用参数str,这时就有两个对象引用变量指向同一个对象实例。这两个对象引用都可以对该对象实例进行操作,操作结果都有效,因此在test方法执行完之后,对象实例的内容已经被改变了,这个时候再通过main方法里的string引用去查看对象实例的内容,看到的就是改变之后的内容。

例子3:

public class Test {
public static void test(String str) {
str = "World";
}
public static void main(String[] args) {
String string = "Hello";
test(string);
System.out.println(string);
}
}

运行结果:

Hello。

这个结果和上面结果矛盾吗?一点也不矛盾。在这个例子中,参数传递过程和上个例子完全一样,但是在test方法里并不是对原来指向的对象实例进行操作,而是把str指向了另外一个对象实例,当然对原来的对象实例毫无影响。   


总结:对于例1(基本数据类型),大家应该比较容易理解,因为Java调用方法的参数传递方式为“传值”,如method(O o),Java调用method(a),a传给o,即o的值等于a,但是明确一点,o与a是分割且独立的,可以理解o为a的一份拷贝,那么在方法method里面对o进行的任何操作,不会对a产生影响。就像电脑E盘下有一份word文档E:/B.doc,现在你要对B里面的内容进行修改,但是B.doc同样也是要使用的,那么就拷贝一份B.doc到F盘下名为F:/C.doc,那么B.doc和C.doc里面的内容完全相同,只是位置不同,B.doc在E盘下,C.doc在F盘下,他俩是相互分割且独立的,操作互不影响;

对于例2和例3(对象/引用数据类型),其实和例1的原理是一样的,只是有点绕。这里要求大家回顾并理解Java中“对象”的概念和“如何操纵对象”,Java编程思想(第四版)第二章(一切都是对象)2.1节(用引用操纵对象)中第一段第一句话:“每种编程语言都有自己的操纵内存中元素的方式。”这句话貌似没有提到对象,但是提到了“内存中元素”,不管是基本数据类型还是对象/引用数据类型,都是数据,数据在Java虚拟机里都是分配在内存中(关于数据如何分配,请看《深入理解Java虚拟机》一书),所以这句话中”内存中元素“我们可以理解为”对象“。第二段第三句话:”尽管一切都看作对象,但操纵的标识符实际上是对象的一个“引用”(reference)。”从这两句话,我们可以这样理解:Java中的对象(数据)都保存在内存中,但是其实是通过直接操纵对象的一个引用来间接操纵对象。这里有个关键词“引用”,下面这张图片是Java编程思想(第四版)中对“引用”一词的解释,长篇大论。


我先不理会书中的解释,给出自己形象的理解,可以把“引用”理解为电脑中的“快捷方式”,C盘深层目录下有一份word文档名为C:/Object/test/B.doc,但是我想用它,每次都要点开“我的电脑”-->C盘,然后再点到B.doc所在的目录,感觉狠繁琐,就把它生成一个快捷方式到桌面,右键快捷方式-->属性-->快捷方式栏目中有“目标”:C:/Object/test/B.doc这一项,这个目标就是说B.doc文件在这个快捷方式指向的位置C:/Object/test/B.doc,以后就可以直接操作快捷方式来间接操作B.doc了,是不是狠方便!

上面对“对象”、“如何操纵对象”和“引用”做了一个详细的解释,现在我们言归正传,仍然以method(O o)为例,Java调用method(A),A为对象引用(快捷方式),对象实例(目标地址)为***,A传给o,o也是快捷方式,这里传的到底是什么?疑问我肯定地告诉你,是“值”,是对象实例的地址。那么对于例2,是不是就狠好理解了?是不是狠嗨皮!大笑但是这才是开始,例2和例3看着不是一样的么,为何结果不一样,你是不是又想哭了?快哭了注意例2中test方法体中操作对象的是这句:str.append(", World!");例3中的是:str = "World";结果不一样的关键就在于这两句话,现在我们应该都知道str是String对象“Hello”的快捷方式了吧!由“点”操作符的意义可知例2中str.append(", World!");中的append方法是String对象自身的方法,可以理解为word文档的“编辑”功能,可知是对str自身进行操作,也可以理解为对word文档内容进行修改;而例3中str = "World";的“=”操作符是赋值操作符,如果你看了《Java编程思想(第四版)》中第三章(操作符)3.4节(赋值)的内容,第一段第一句话是:“赋值使用操作符”=“。它的意思是‘取右边的值(即右值),把它复制给左边(即左值)’”这是对操作符的理解,但是这后面还有一段话,如下图:



你是不是又觉得例3错了呢?纠结不抓狂,其实,上图对赋值操作符操纵对象的解释是完全正确的,而例3并没有违背以上解释,只是Java自带的特殊类String,关于String类的一系列特点和在Java中的特殊之处,在此就不赘述了,我只说结果,new操作符大家应该不陌生了吧,《Java编程思想(第四版)》中第二章(一切都是对象)2.2节(必须由你创建对象)第一段第一句:“一旦创建了一个引用,就希望它能与一个新的对象相关联。通常用new操作符来实现这一目的。new关键字的意思是‘给我一个新对象。’”这句话中“给我一个新对象”表明new操作符给引用一个新的内存地址值,而String str =“****”等同于String str = new String(“****”),例3中str = "World";意思就是说将新对象“World”的内存地址和str引用相关联,即例3中的快捷方式str指向新对象“World”的内存地址(目标位置),而例3中最后打印的是快捷方式string仍然指向原来的对象“Hello”,最后打印结果当然是“Hello”。到这里,我想例3的结果大家也认为是对的了吧!是不是又兴奋了一把大笑,终于把所有的问题都给Over了。

总结之前的内容是拷贝的,总结之后是本菜鸟写的,其实看似简单,但是涵盖了很多对Java基础知识的理解!写得不好,勿喷~

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值