作为一个有着 8 年 Java 编程经验的 IT 老兵,说起来很惭愧,我被 Java 当中的四五个名词一直困扰着:对象、引用、堆、栈、堆栈(栈可同堆栈,因此是四个名词,也是五个名词)。每次我看到这几个名词,都隐隐约约觉得自己在被一只无形的大口慢慢地吞噬,只剩下满地的衣服碎屑(为什么不是骨头,因为骨头也好吃)。
记得中学的课本上,有一篇名为《狂人日记》课文;那时候根本理解不了鲁迅写这篇文章要表达的中心思想,只觉得满篇的“吃人”令人心情压抑;老师在讲台上慷慨激昂的讲,大多数的同学同我一样,在课本面前“痴痴”的发呆。
十几年后,再读《狂人日记》,恍然如梦:
鲁迅先生以狂人的口吻,再现了动乱时期下中国人的精神状态,视角新颖,文笔细腻又不乏辛辣之味。
当时的中国,混乱成了主色调。以清廷和孔教为主的封建旧思想还在潜移默化地影响着人们的思想,与此同时以革命和新思潮为主的现代思想已经开始了对大众灵魂的洗涤和冲击。
最近,和沉默王二技术交流群(120926808)的群友们交流后,Java 中那四五个会吃人的名词:对象、引用、堆、栈、堆栈,似乎在脑海中也清晰了起来,尽管疑惑有时候仍然会在阴云密布时跑出来——正鉴于此,这篇文章恰好做一下归纳。
一、对象和引用
在 Java 中,尽管一切都可以看做是对象,但计算机操作的并非对象本身,而是对象的引用。 这话乍眼一看,似懂非懂。究竟什么是对象,什么又是引用呢?
先来看对象的定义:按照通俗的说法,每个对象都是某个类(class)的一个实例(instance)。那么,实例化的过程怎么描述呢?来看代码(类是 String):
new String("我是对象张三");
new String("我是对象李四");
在 Java 中,实例化指的就是通过关键字“new”来创建对象的过程。以上代码在运行时就会创建两个对象——“我是对象张三"和"我是对象李四”;现在,该怎么操作他们呢?
去过公园的同学应该会见过几个大爷,他们很有一番本领——个个都能把风筝飞得老高老高,徒留我们眼馋的份!风筝飞那么高,没办法直接用手拽着飞啊,全要靠一根长长的看不见的结实的绳子来牵引!操作 Java 对象也是这个理,得有一根绳——也就是接下来要介绍的“引用”(我们肉眼也常常看不见它)。
String zhangsan, lisi;
zhangsan = new String("我是对象张三");
lisi = new String("我是对象李四");
这三行代码该怎么理解呢?
先来看第一行代码:String zhangsan, lisi;——声明了两个变量 zhangsan 和 lisi,他们的类型为 String。
①、歧义:zhangsan 和 lisi 此时被称为引用。
你也许听过这样一句古文:“神之于形,犹利之于刀;未闻刀没而利存,岂容形亡而神在?”这是无神论者范缜(zhen)的名言,大致的意思就是:灵魂对于肉体来说,就像刀刃对于刀身;从没听说过刀身都没了刀刃还存在,那么怎么可能允许肉体死亡了而灵魂还在呢?
“引用”之于对象,就好比刀刃之于刀身,对象还没有创建,又怎么存在对象的“引用”呢?
如果 zhangsan 和 lisi 此时不能被称为“引用”,那么他们是什么呢?答案很简单,就是变量啊!(鄙人理解)
②、误解:zhangsan 和 lisi 此时的默认值为 null。
应该说 zhangsan 和 lisi 此时的值为 undefined——借用 JavaScript 的关键字;也就是未定义;或者应该是一个新的关键字 uninitialized——未初始化。但不管是 undefined 还是 uninitialized,都与 null 不同。
既然没有初始化,zhangsan 和 lisi 此时就不能被使用。假如强行使用的话,编译器就会报错,提醒 zhangsan 和 lisi 还没有出生(初始化);见下图。
如果把 zhangsan 和 lisi 初始化为 null,编译器是认可的(见下图);由此可见,zhangsan 和 lisi 此时的默认值不为 null。
再来看第二行代码:zhangsan = new String("我是对象张三");——创建“我是对象张三"的 String 类对象,并将其赋值给 zhangsan 这个变量。
此时,zhangsan 就是"我是对象张三"的引用;“=”操作符赋予了 zhangsan 这样神圣的权利。
第三行代码 lisi = new String("我是对象李四");和第二行代码 zhangsan = new String("我是对象张三");同理。
现在,我可以下这样一个结论了——对象是通过 new 关键字创建的;引用是依赖于对象的;= 操作符把对象赋值给了引用。
我们再来看这样一段代码:
String zhangsan, lisi; zhangsan = new String("我是对象张三"); lisi = new String("我是对象李四"); zhangsan = lisi;
当 zhangsan = lisi; 执行过后,zhangsan 就不再是"我是对象张三"的引用了;zhangsan 和 lisi 指向了同一个对象(“我是对象李四”);因此,你知道 System.out.println(zhangsan == lisi); 打印的是 false 还是 true 了吗?