之前用的c/c++比较多,在c/c++中对于传参类型,无外乎就是传值、传引用、传指针这几种。但在java中,由于没有指针类型,其传参的方式也发生了相应的变化。根据网上的信息,按我之前的理解,java中传参方式应该主要有两种:传值和传引用
一 传值
java中的传值即传递的是原变量的一个副本。基本的数据类型,如:char,int,double等类型的变量向函数传递的参数,都是变量的副本都属于传值。函数内部不论进行任何操作,都不影响变量本身,影响的只是变量的副本。
例如:
public class Test { public static void main(String[] args){ int a = 1, b = 2; fun(a,b); System.out.println("a="+a); } static void fun(int m , int n) { m = m + n; } }
运行结果为:a = 1. a的值并没有发生变化, 说明此处的传参是传的值。
二 传引用
从表面上来看,java中的对象类型都是传递的引用,而不是传递的值。首先,来看一个例子(此处以LinkedList对象为例):
public class Test { public static void main(String[] args){ List<String> str_1 = new LinkedList<String>(); str_1.add("111"); fun(str_1); System.out.println("str_1 ="+str_1); } static void fun(List s) { s.add("222"); } }
运行结果为: str_1 =[111, 222] . 从这个例子来看的话,java中兑现的传参应该是传的引用。那么,再看下面的一个例子:
public class Test { public static void main(String[] args){ List<String> str_1 = new LinkedList<String>(); List<String> str_2 = new LinkedList<String>(); str_1.add("111"); str_2.add("333"); fun(str_1,str_2); System.out.println("str_1 ="+str_1); System.out.println("str_2 ="+str_2); } static void fun(List s,List m) { List<String> str = new LinkedList<String>(); str = s; s = m; m = str; } }
运行结果为:str_1 =[111] , str_2 =[333] .我们发现str_1与str_2的值并没有发生交换。 如果该对象传的是引用的话,那它们两个的值应该发生了变化才对。
然而,java中对象作为实参,传递的实际上还是值,即java中的传参传递的都是值!为什么会这么说?我会从jvm的构成的角度来解释一下自己对这个问题的理解。接下来我们先看一下JVM的结构:
三 JVM的结构及相应的解释
JVM的结构图如下:
从Jvm的结构图上可以看出来,Jvm在实现的时候将属于它的内存分为五部分,其中程序代码(严格的说应当是字节码)和基本的数据类型是放在java栈的栈帧中,而对象是从堆中分配的,堆这个东西我认为可以理解成“对象池”。程序和程序中需要用到的对象放在两个相对独立的区域中,那么程序怎么使用对象呢?实际上是程序中真正使用对象的地方其实只是声明了一个对象的引用,也就是把堆中分配了的相应对象的地址放到引用中,当然引用也是放在栈内存中,栈和堆之间就是通过一个一个的引用来联系的。至于引用,我们可以把它看做是一个指针常量,说白了,就是一个数值,这个数值所表达的是引用对象的地址。所以,不管是基本类型变量(int,float,double等)还是对象,相应的内存地址中存放的都是一个数(无符号整数,整数,浮点数等)。当方法调用时候,方法的参数会占内存中开辟一块新的区域,同时把要传递的基本类型,或者引用类型名称复制到这块内存中,结果是,基本类型(存放在占内存中的)复制之后连同其数值也复制到了这块内存中,而对象只是复制了引用名,实际还要联系到原对象所在的堆区域中。之所以说java中传递的都是值,是因为传递得是内存地址中的数,当然,这个值对于基本类型和对象类型来说意义是不一样的,对于基本类型这个数就是其值本身,传递值的结果就是,改变新的变量的值不影响旧的变量的值;而对于对象来说这个数是它的地址,传递这个值就相当于传递了真实对象的引用,传递了引用或者说是地址的结果就是变化会全局可见。所以说java中的方法都是按值调用的!只不过基本类型和对象类型的"值"的在具体的机制上作用不同。
所以在第二个例子中,fun方法中,两个list s和m交换,实际只是str_1与str_2在栈中对应的引用值进行了交换,而引用与相应堆区域的联系并没有改变。因此,str_1与str_2的值并没有发生变化。
综上,java中不论是基本类型还是对象,方法调用时,传递的都是值!
1.对于基本类型的变量,相当于操作其副本。
2.对于对象,相当于复制了一个引用,该引用指向的还是原变量的内存区域。