我们知道在Java中类型可分为两大类:基本类型与引用类型。
引用类型,是指除了基本类型之外的所有类型。所有的类型在内存中都会分配一定的存储空间(形参在使用的时候也会分配存储空间,方法调用完成之后,这块存储空间自动消失), 基本类型只有一块存储空间(分配在stack中), 而引用类型有两块存储空间(一块在stack中,一块在heap中),在函数调用时Java是传值还是传引用,改变一个变量的值,会不会影响其他变量,这个估计很多人至今都很糊涂,下面用图形与代码来解释:
在上图中引用类型在传参时不是在heap中再分配一块内存来存变量c 所指向的A(),而是让a 指向同一个A 的实例,这就与C++ 中的指针一样,先声明指针变量a,b,c,d 在传参的时候让a 指向c所指向的内存,让 d 指向 b 所指向的内存。很明显Java中的引用与C++中的指针在原理上是相类似的,但Java中没有指针,只有引用。
一、基本类型是按值传递的
当Java 方法的参数是基本类型的时候,是按值传递的。我们上代码说明:
public static void main (String[] args) {
int a = 99;
int num = 100;
Integer NUM = 101;
change0(num);
change0(a);
change0(NUM);
System.out.println(num);
System.out.println(NUM);
System.out.println(a);
}
public static void change0(int a) {
a = 1;
System.out.println(a);
}
public static void change0(Integer a) {
a = 12;
}
运行结果:
1
100
101
99
我们能够看出,虽然在change0 (a) 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对 main(String[]) 方法里的 a 变量没有影响。那说明,参数类型是基本类型的时候,是按值传递的。以参数形式传递基本类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。
下面再用代码看一下:
public class Loop {
public String name;
}
Loop a = new Loop();
Loop b = a;
a.name = "123";
b.name = "345";
//a给name赋值123,后来b使name变为了345,a也会受影响改变值
System.out.println(a.name);
//a = null;使得a指向断开,不影响b,若改为a。name则b受影响
a = null;
System.out.println(b.name);
//前面使得a = null;断开了a的指向,使a在堆中没有初始空间了,无法存放123,会报错
a.name = "123";
System.out.println(a.name);
运行结果:
二、引用类型
首先我们要知道,引用类型的出现是为了节省内存,当我们使用引用类型时,一定要给定一个空间,
即需要new一个对象。
Java 是传值还是传引用,问题主要出在对象的传递上,因为 Java 中基本类型没有引用。既然争论中提到了引用这个东西,为了搞清楚这个问题,我们必须要知道引用是什么。
简单的说,引用其实就像是一个对象的名字或者别名,一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,我们不会直接是访问对象在内存中的数据,而是通过引用去访问。引用也是一种数据类型,我们可以把它想象为类似 C++ 语言中指针的东西,它指示了对象在内存中的地址——只不过我们不能够观察到这个地址究竟是什么。
如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间(栈)来保存。但是它们的值是相同的,都指示同一个对象在内存(堆)的中位置。比如:
String a="This is a Text!";
String b=a;
通过上面的代码和图形示例不难看出,a 和 b 是不同的两个引用,我们使用了两个定义语句来定义它们。但它们的值是一样的,都指向同一个对象 “This is a Text!”。但要注意String 对象的值本身是不可更改的 (像 b = “World”; b = a; 这种情况不是改变了 “World” 这一对象的值,而是改变了它的引用 b 的值使之指向了另一个 String 对象 a)。
如图,开始b 的值为所指向的“word1”,然后 b=a; 使 b 指向了蓝线所指向的”word“。
这里我描述了两个要点:
(1) 引用是一种数据类型(保存在栈中),保存了对象在内存(堆)中的地址,这种类型即不是我们平时所说的基本数据类型也不是类实例(对象);
(2) 不同的引用可能指向同一个对象,换句话说,一个对象可以有多个引用,即该类类型的变量。
三、对象是如何传递的呢
随着学习的深入,你也许会对对象的传递方式产生疑问,即对象究竟是“按值传递”还是“按引用传递”?
下面我们看代码:
public static void main(String[] args) {
Te aa = new Te();
aa.name = "100";
Te bb = new Te();
bb.name = "55";
change2(aa,bb);
System.out.println(aa.name);//bbb qqqq
System.out.println(bb.name);// bbb qqqq
}
public static void change2(Te a,Te aa) {
Te temp = new Te();
temp.name = a.name;
a.name = aa.name;
aa.name = temp.name;
}
public class Te {
public String name;
}
结果:
这里我们能够看到change2(Te a,Te aa)中确实实现了交换,所以它是“按引用传递”的!
我们一定要搞清楚引用传递,否则在日后会出现很多bug的。