- 在思考这个问题之前首先要明白JAVA一个类中的数据成员有多少种:
JAVA一个类中数据成员只有两种,分别是:基本数据类型和对象。基本数据类型就不用多说了,只有8种;其他的都是对象,JAVA class字节码文件在内存中是一个静态对象、类实例是对象,比如String对象。你可能会觉得奇怪,数组呢?特别是基本数据类型的数组呢?在这里,你要明白,JAVA中数组也是对象,基本类型数组是一个由JVM创建的对象,其他数组,比如Array类等,本身就是类,肯定是对象。
- 作用域,在JAVA中没有作用域的概念,一般描述方法的作用范围是左花括号“{”与右花口号之间“}”。作用域在C++中是一个重要的概念,是类、对象、变量的影响范围和访问权限的描述。比如类中方法的作用域:在方法中可以访问类的所有有权限访问的变量和其他方法,但是不能访问其他方法内的变量;另外类的变量能被该方法内访问,但是却不能访问其他方法内的变量。
- 明白JAVA一个类中的数据成员有多少种有什么用呢?
目的是知道JAVA参数传递的种类,通过上面可以知道,JAVA方法参数只能传递两种数据成员:基本数据类型和对象。
(如果学过C++可以思考一下C++函数参数传递的数据成员有几种?这样方便区分概念,不思考清楚,很容易被C++弄混自己对JAVA的理解的。我个人理解C++函数参数传递有4种:基本数据类型、对象、指针、地址)
- 要理解标题,还要弄清楚两个概念:按值传递是什么?引用传递是什么?
- 按值传递:函数方法的参数即形参是实际参数的一个拷贝
- 引用传递:指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。(来源百度百科)
(在C++中可能会有更加复杂的引用关系,不过不是本文讨论内容)
- JAVA方法的参数是按值传递还是引用传递?
通过上面我们知道JAVA一个类中只有两种数据成员:基本数据类型和对象。基本数据类型不用讨论了,在JAVA肯定是按值传递(当然在栈内存中的实际情况更加复杂,这里只谈表象。C++可以把基本数据类型任意传,按值、引用、指针和地址,全套服务都可以)
那么我们只需要讨论传对象了。首先,我们需要明白对象在内存的创建位置,和创建模式。JAVA对象创建有两种方式,一种是在堆内存中创建,另一个是在栈内存中创建。堆中创建的对象是个完整的对象,对象中包含所有应该有的数据。而栈中创建,比较小(可能仅仅是该对象类型的一个变量名内存空间,即只能储存对象的地址,和其他一些少量数据(猜的,不对别打我= = JAVA那坑爹设计,根本获取不了具体堆和栈的地址和大小。听说通过外部工具可以,也麻烦得要死。为什么JAVA不允许调试模式查看或者获取内存地址和大小呢)。
- 堆对象内存:比较大,包含所有该对象应该有的数据。
- 栈对象内存:比较小,其他不确定。但是只要明白,一般不能确定大小的对象(可变大小对象,比如可变长度的数组,类对象)都是在堆中创建的就可以了。new操作就是在堆中创建内存的。
以 String str=new String("Hello")为例,这句语句实际上会创建两个对象,一个堆对象,一个栈对象。堆中的String对象变量名不是str,实际名字只有JVM才知道,就假设以堆的内存地址作为名字吧。而栈中的String对象名才叫做str
传递到方法中void setString(String str2)后的内存变化
str2是setString方法的形参,str是外部实参,由上面的作用域分析知道setString的str2变量必须拷贝实参str的内容到形参str2上才能访问,str的内容是堆地址0xFF01,所以方法就可以通过形参访问堆中的Hello字符串。
从这里就可以看出JAVA方法的参数是按值传递的,不是吗?。不过可能还不太相信是吧。再想想按值传递的概念吧,按值传递:函数的形参参数即形参是实参的一个拷贝。假设堆中的Hello字符串内存名字为它的地址,那么这里的实参是什么?是0xFF01吗?错!!是栈中的str内存!!形参拷贝的是栈中str的内容,而str内存的内容是0xFF01,即堆内存地址,那么形参的内容就是堆的内存0xFF01。想得快得人应该会立刻想到,既然str2内存中的内容是堆内存0xFF01,那不就是引用咯?是个蛋蛋,这里问题是JAVA方法的参数是按值传递还是引用传递,是传递过程中的实参和形参之间的问题,关实参、形参内存中的内容个屁事……即与实参、形参内容无关!!
那为什么有些书会说是引用传递?比如JAVA编程思想中文版第4版就这样说。因为这些书说的是最终效果,而不是过程。最终效果的确是引用传递,这样才能让方法中对对象属性进行修改响应到实际内存中。说成引用传递也是为了让新人理解方法为什么不用返回值,就可以通过参数传递进来的数据进行更改外面的数据。