前言
考虑如下代码:
//: xin/greglee/demo/Demo.java
public class Demo {
public static void main(String[] args) {
// test1
int a = 6;
int b = a;
b = 77;
System.out.println(a);
// test2
Node node1 = new Node(6);
Node node2 = node1;
node2.data = 77;
System.out.println(node1.data);
}
}
class Node {
int data;
public Node(int data) {
this.data = data;
}
}/*output:
6
77
*///:~
test1与test2进行了相似的过程,但为何得到的结果却截然相反呢?希望在阅读本文后,读者能够找到问题的答案。
JVM中的堆和栈
堆(heap)
用于存放所有的Java对象。
栈(stack)
用于存放基本类型的数据和对象的引用。
变量的声明与使用
基本类型
Java中的基本类型有8种:boolean、char、byte、short、int、long、float、double(注意:String是对象,不是基本类型)。
声明一个基本类型的变量时,会在栈中为该变量分配一块内存,当对其初始化和赋值时,可以简单的理解为将值存储在这块内存中。
注:事实并非如此,这样描述是为了简化理解,但这样理解通常不会得到错误的结果,如果读者感兴趣,可自行查阅JVM相关资料。
对象
声明
声明一个对象类型的变量时,同样会在栈中为该变量分配一块内存,不同的是:这块内存用来存储一个对象的引用。
赋值
当使用new关键字创建一个对象,会在堆中分配空间来存储此对象,通过赋值号 “=” 可以将new关键字创建出的对象的引用存储在相应对象类型的变量中。
当使用赋值号将一个对象类型的变量赋值给另一个变量时,由于该变量存储的是一个对象的引用,因此实际上是将引用赋值给了另一个变量。此时,可以简单的描述为:这两个变量引用了同一个对象。
调用
当使用英文句点(.)通过一个对象类型的变量调用其引用对象的成员时,首先通过该变量存储的引用找到其引用的对象,然后再从此对象中找到相应的成员。
tips:关于对象的引用和对象的关系,可以理解为遥控器和电视机的关系:遥控器是引用,引用的是电视机,你可以通过控制遥控器来控制电视机。
回到问题
对于test1,使用了两个基本类型的变量a、b,两个变量各有一块内存存储它的值。因此改变b的值并不会影响a的值,故输出6。
对于test2,使用了两个对象类型的变量node1、node2,两个变量各有一块内存来存储一个对象的引用。
Node node1 = new Node(6) 创建了一个Node对象,将其data值初始化为6,并将该对象的引用赋值给node1。
Node node2 = node1 将node1存储的引用赋值给了node2,也就是说,node2和node1引用了同一个对象。
node2.data = 77 通过node2存储的引用找到其引用的对象、访问该对象的成员data,并将77赋值给该对象的成员data。
System.out.println(node1.data) 输出node1引用的对象的成员data的值,由于node1和node2引用的是同一个对象,而上面已经通过node2将该对象的成员data的值赋值为77,因此输出77。
网友评论