相信对很多初学java的人来说这部分内容不太容易明白,最近又看了一些java视频,有点收获,打算总结一下。java明确说明取消了指针,因为指针往往是再带来方便的同时也是导致代码不安全的根源,而且还会使得程序变得非常复杂和难以理解。java放弃指针只是在java语言中没有明确的指针定义,实质上,每一个new语句返回的都是一个指针的引用。只不过在大多数时候java不用关心如何操作这个“指针”,更不像在操作C++的指针那样胆战心惊,唯一要多注意的是在给函数传递对象的时候。所以,初学java的人不必花费太多精力去了解,但是对于一个真正的java程序员来说这个问题是基础。废话少说,直接进入正题:
java中是传值还是传引用呢 ?
关于这个问题,就像光到底是波还是粒子的问题一样众说纷纭。但是我们首先要搞清楚一点:不管java的参数类型是什么,一律传递参数的副本(引用thinking in Java 中的一句话:When you're passing primitives into a method, you get a distinct copy of the primitive.When you're passing a reference into a method, you get a copy of the reference)。
java中的变量分为以下两类:
(1)基本类型变量(int,long,double,boolean,float, byte,char),对于这些变量,java传递的是值的副本(与c++相同),即把自己赋值一份传递,即使自己的副本变了,自己也不会改变;
(2)对象型变量。java传递的是引用的副本(复制指向地址的指针),而不是自己实际值的副本。为啥这么做呢?原因很简单,因为对象类型是放在堆里边的(new一个对象的时候实际上是再堆中开辟出了一块内存空间存放对象,在栈中存放指向对象的指针),所以访问位于堆中的对象相对于基本类型数据来说比较慢;另一方面是因为对象类型本身就比较大,如果采用复制对象值的方法,不仅浪费内存而且速度比较慢。
在thinking in Java 中解释是这样的:不管是基本类型还是对象类型,都是传值。这种说法是把引用的副本也看作了一种“值”,也能够说的过去,但是本人还是更赞成将按值传递和按引用传递分开比较好理解。
注意:java中String类型也是对象型变量,所以它传递的是引用的副本。String是一个非可变类,其传值还是传引用显得没什么区别。
理论说完了,下面看几个例子:
num1:传基本类型
public class Test { public static void test(boolean test) { test = !test; System.out.println("In test(boolean):test = " + test); } public static void main(String args[]) { boolean test = true; System.out.println("Before test(boolean):test = " + test); test(test); System.out.println("After test(boolean):test = " + test); } }
运行结果:
Before test(boolean):test = true
In test(boolean):test = false //传进去的是boolean类型的副本,副本改变了但是原值不改变
After test(boolean):test = true
num2:传对象类型
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! //通过引用副本找到地址,然后修改里边的值,即修改了对象。
num3:关于传String
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); } }
一眼看去输出结果应该是:World
但是实际的运行结果却是:Hello,String不是按引用传递的吗?难道出问题了?
翻看了一些java教程,原因如下:
执行str = “World”;这句的时候,系统会自动生成一个新的String对象,并把这个新对象的值设为“World”,然后将这个对像的引用赋值给str,str是原来String的副本,改变的是副本的值,那么就与原来的值没有关系了。当test函数执行完毕,str随之释放,原来的内存地址上内容没发生任何变化。因此输出hello,num2中str.append(", World!");就不一样了,StringBuffer是产生一块内存空间,相关的增、删、改操作都在其中进行,所以此句是在同一段内存上进行的,即修改了原值,str所指向的引用并没有变。