首先讲一下代码快照图,在软件多维视图中属于运行时视图、时刻视图、代码视图。它实际上是表示某一时刻代码中各变量的实际情况。
代码快照图用箭头指向引用,而实际上关于引用,可以简单通俗的理解如下:对于语句new Hero(),代表创建了一个Hero对象但是也仅仅是创建了一个对象,没有办法访问它为了访问这个对象,会使用引用来代表这个对象:Hero h = new Hero(),h这个变量是Hero类型,又叫做引用,=的意思指的h这个引用代表右侧创建的对象“代表” 。在面向对象里,又叫做“指向”。
引用有多个,但是对象只有一个。对象就像 "房产", 引用就像"房产证",房产证的复印件可以有多张,但是真正的"房产" 只有这么一处。在这个例子里,所有引用都指向了同一个对象。
对象值是一个用其类型标记的椭圆。当我们想显示更多细节时,我们在其中写上字段名称,并用箭头指出它们的值,这些字段可以包括其声明的类型。可变对象用单圈表示,不可变对象用双圈表示。那么什么是可变不可变对象呢?首先我们针对数据类型来说:
不可变数据类型: 当该数据类型的对应变量的值发生了改变,那么它对应的内存地址也会发生改变,对于这种数据类型,就称不可变数据类型。其中基本数据类型都是不可变数据类型,例如int,如果一个int类型的数据发生改变,那么它指向了内存中的另一个地址,但是需要注意的是java缓存了所有-128-127的值。
可变数据类型 :当该数据类型的对应变量的值发生了改变,那么它对应的内存地址不发生改变,对于这种数据类型,就称可变数据类型,当可变数据类型改变时它实际上是更改了内存中的内容
比如我们熟知的String和StringBuilder,前者是不可变数据类型,后者是可变数据类型。当我们改变他们的值时,String实际上会再创建一个新的对象,原来的对象则会被垃圾处理器回收。而后者则会在原有的基础上进行修改。
将可变与不可变的范围扩大到所有对象,即讨论可变与不可变。我们如果想创建一个自己的不可变类的话,需要做到以下几点:所有成员都是private final不提供对成员的改变方法,确保所有的方法不会被重载。其实这个final也不是必要的,此外如果我们想提供修改方法,那可以使用防御式拷贝,即创建一个新的修改后的对象传给调用者。还有一件很重要的事就是我们不能对外泄露内部引用,否则外部仍然能修改内部值,这一点也是可以通过防御式拷贝来避免的。
这里还要特别说明一点,也是我经常容易弄错的一个知识点,就是用final修饰修饰对象引用时,并不是让对象不可变,这一点和基本数据类型不太一样,基本数据类型用final修饰后就不可以修改,而对象则是引用无法修改,这个怎么解释呢?
其实很好理解,因为对象数据类型里面保存的是引用而不是真实值!换种说法来说,引用对象的地址不能变,但是它里面的内容可以变。比如用final修饰的StringBuilder,如果改变他的值是可以的。
package hetest; public class fianltest { public static void main(String args[]) { final StringBuilder s=new StringBuilder("a"); s.append("b"); System.out.print(s); } }
上面这一段代码运行之后就会输出结果ab,但是如果换成String就会报错。具体原因上面也说了,就是final修饰对象的引用不可变,但是String是不可变对象,一旦改变他的值引用就能改变,所以加上final也间接的使得对象的值不可改变。