储存器的值与引用

声明一个变量

我们使用 var 来声明一个变量,就好像从柜子里拿出了一个试管放在了实验台上;

我们给变量规定了一个类型,就好像在试管上贴上了标签;

那么放入的试剂就必须是标签上标记了的——否则可能导致中毒或者爆炸。

同样的,如果我们试图给一个储存器放入一个错误的数据类型,那么编译器就会报错——没错总有办法能够骗过编译器——反正我不会教你这个方法,那样就会导致程序崩溃啦。

所以说,试管有容量,我们声明的类型也有大小限制,超限也会报错——一般够用的。

这就是为什么会有个术语叫做“溢出”。

 

如果你对化学不感兴趣

那好,我们换一个栗子:

我们都知道有一些动物是吃草的,而有一些动物则只吃这些吃草的动物?

那么我这里有狼和羊,狼要吃羊,而羊要吃草。

你给狼吃草或者给羊吃狼都是不对的………………

好了,我们还是下一个话题吧。

 

对象不是值

现在,我们知道了储存器是什么,我们再来说说“放”到储存器里的另外一种东西——对象。

在接触了面向对象编程之后,我们就必须面对一个新的概念——什么是对象,当然,这个问题我们在上一节课已经讨论过了,这节课我们重点来讨论如何把大象放进冰箱的问题。

当我们这样声明:

难道不是把实例放进了储存器里吗?  ——还真不是。

实际上还真的没有能储存对象的储存器,就像我们说的,也没有能放进大象的家用冰箱。

悄悄告诉你,对象其实放在“堆”上……没错,就是垃圾堆。

好了,这里我们要请出除了 nil 这个特殊的值以外的另一个特殊的值了:

引用

其实引用就是指针。只不过它把指针给包装了——具体包装成了什么我们不清楚——恐怕只有苹果的攻城狮能告诉你了。总之,我们把这个值叫做引用。

不同于 C 语言的指针,虽然引用也是一个指向堆里边某个对象的指针,但它的的确确是不能被直接编辑的。

电视机和遥控器

我谈起这两个东西你可能又觉得古老……正是因为它古老,所以我觉得可能所有人都应该是接触过的。

当你生成了一个对象,那我们就说你买了一个电视机;那么对应放到你手里的,自然就是遥控器啦。所以我们说,不论你的生成的对象实例是多么复杂,多么庞大,它的遥控器永远是那个样子——顶多多了几个按钮(方法)而已,真正的实例其实是放在堆上的。

说到堆

我们再来说一说储存器里的引用与堆上对象实例的对应关系。

一般我们声明一个储存器,然后赋值给它一个对象,那么这个储存器就存了一个引用,然后对象的实例被扔在了堆上。那么,这时候我们的遥控器和电视机是一对一对应的。

就像这样,我们从同一个类生成了两个实例,两个实例被放在了堆上,而我们手中的变量则存了它们的引用——就像是门牌号。

那如果我要把引用复制一份给别人呢?

没错!那么另外一个变量也就指向了堆上相同的对象!

现在其实 objA 和 objC 是同一个对象的引用呢!

现在我这么做:

哈!都成了同一个对象!……等等,那 objB 中原来的对象哪里去了?这样岂不是再也没办法访问到它了吗??

没错,这个时候 objB 所引用的原来的那个对象就孤零零的存在于堆上——没关系,它早就被 GC(垃圾收集器)给带走了(别问我带到了哪里)。

做个栗子吧!

其实,就是上节课的课后题啦~

运行结果是24而不再是10.

 

课后题

我们声明一个叫做 HeapQuiz 的类,这个类只包含一个属性就是 ID,我们用它来区分实例,现在我们生成了5个实例,然后把它们的引用打乱,现在,你能说出这五个引用对应的实例的 ID 吗?

 

“储存器的值与引用”的一个回复

发表评论

电子邮件地址不会被公开。 必填项已用*标注