重看ADT 第三章总结
在贯穿我们整个Lab1-lab4,ADT一直都是我们必须要直面的一个问题。恰逢复习第三章,所以拿出来重新讲一讲。
首先需要明确java数据类型
Java有一些基本数据类型,包括int、long、boolean、double、char
还有对象数据类型,例如String、BigInteger
注意基本数据类型的第一个字母小写,对象数据类型第一个字母大写
基本数据类型只有值没有ID,不可变,在栈中分配内存,代价低 (不存在线程安全问题)
基本数据类型既有ID也有值,在堆中分配内存,代价昂贵 (因此,很有可能会出现线程安全问题,每一个线程都能访问堆,请务必注意这一点)
具体变化可以查看下图:

Java中有将基本类型包装为对象类型的机制,例如int变为Integer。通常是在定义容器类型的时候使用它们,因为容器类型操作的元素要求是对象类型,所以需要包装。一般情况下尽量避免使用,因为会降低性能。这种转换一般是自动的。
Java是静态类型语言,也就是说在编译的时候已经知道了所有变量的类型,其中没有显式地标识的数据类型也能够推理出,比如int+int结果为int。这里我认为,可能学了C的情况下对于这里是比较容易好理解的,但是也要小心,我自己在利用java语言编写某些计算方法实验时,就因为类型转化出了点问题。在编译阶段进行类型检查
上面也提到了静态检查和动态检查,其对比在实验中体现的也十分明显,这里再总结一下:
静态检查:可再编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性,能够检查出的错误包括语法错误、类名/函数名错误、参数数目错误、参数类型错误、返回值类型错误。 这里我们实验都采用的eclipse,一般的错误eclipse都会报给你。 这里再稍微多嘴一句,lab4推荐的spotbugs还是挺不错的,能找到挺多错误的(虽然有的错误并不属于静态检查范围,但是都是在运行阶段前就能找到的错误,姑且将他们放在一起讲)
静态检查:可再编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性,能够检查出的错误包括语法错误、类名/函数名错误、参数数目错误、参数类型错误、返回值类型错误
关于重载和重构,在实验中也讲了许多了,这里再强调一下:同名方法可以用于不同参数类型或者顺序,这是重载。
可变与不可变
改变一个变量:将该变量指向另一个值的存储空间
改变一个变量的值:将该变量当前指向的值的存储空间中写入一个新的值
不变性质是重要的设计原则。不变数据类型:一旦被创建,其值不能改变。如果是引用类型,也可以是不变的:一旦确定其指向的对象,不能再被改变指向其他对象。
变化是罪恶,为什么罪恶?变化使得我们程序员难以控制状态,但需不需要变化?必须!程序不能没有变化。
那么我们想要改变一个不变数据类型该怎么办呢?很遗憾,new或者提供的构造器创建一个新的数据类型,讲变量指向该数据类型的空间。
可以用final修饰来表明是不变的。如果编译器无法确定final变量不会被改变也会提示错误。 final表示变量只能指向某一处,不可改变指向
所以,尽量使用final变量作为方法的输入参数、作为局部变量
另:final类无法派生子类,final方法无法被子类重写
使用不可变类型,对其频繁修改会产生大量的临时拷贝,需要垃圾回收。可变类型就是为了减少这种拷贝来提高效率的,也适用于在多个模块之间共享数据。相比之下,不可变类型更安全,在其他质量指标上表现更好。可变性使得难以理解程序正在做什么,更难满足方法的规约。 因此在你能解决问题的基础上,如何选用实际还是要根据自身需求的。我个人会更加倾向于不可变类型,因为其更加安全,即便在之后可能在rep exposure上犯了一点错误,因为不可变数据类型,能够将损失尽可能减少。
快照图画法:
快照图已经明确这次考试要考,所以还需要再仔细复习一下
不可变数据类型:

因为字符串是不可变的,因此改变时会创造一个新的字符串,只是改变变量指向而已。
可变数据类型:

StringBuilder是可变的,因此只需要改变其内部表示即可
不可变数据类型和可变数据类型在引用上会具有差异:

基本数据类型:

对象数据类型:

不可变数据类型:双线椭圆如下图所示:

不可变的引用:双线箭头:

id 不可变 age可变
有关集合的部分,常见的几种类型以及某些用法在此不提,关于集合的snapshot如下图:

方法:
一个说明文件中应该包含的有:类的继承关系和应用的接口、子类(对于接口来说就是应用这个接口的类)、关于这个类的描述、构造函数描述、这个类所有可以调用的方法、具体对于构造函数和每个方法的描述(包括功能描述、参数、返回值、异常等)代码本身就蕴含着设计决策,但是远远不够。写出假设只为了防止自己记不住,也防止别人不懂代码中蕴含的设计决策给编译器读,而注释形式的设计决策给自己和别人读
行为等价性,就是说两个方法是否可以相互替换,要站在客户端的视角看。例如某两个方法对于规约要求的输入都返回相同的值但是对错误输入的处理方式不同,那么这两个方法是行为等价的。
前置条件:对客户端的约束,在使用方法时必须满足的条件
后置条件:对开发者的约束,方法结束时必须满足的条件
契约:如果前置条件满足了,后置条件必须满足;如果前置条件不满足,则方法可做任何事情
从几次实验的角度来说,规约是十分有必要的,这其中在开发Lab3的时候最为明显,在Lab3开发ADT与客户端时,有一种一人分饰两角的感觉,而我在开发客户端调用自己写的ADT时,都会感到容易给忘记,更何况其他人呢。因此一个好的规约,是对自己负责,也是对他人负责。
除非在后置条件里声明过,否则方法内部不应该改变输入参数。应尽量遵循此规则,尽量不设计可变的规约,否则容易引发bug。程序员之间应该达成的默契:除非规约必须如此,否则不应该修改输入参数。尽量避免使用可变的对象,程序中可能有很多变量指向同一个可变对象(别名),无法强迫类的实现体和客户端不保存可变变量的别名。
因此在lab4中,flight那个问题上,我个人认为我犯了一个错误,其规约后置条件并没有声明可以改变参数,我实际上是改变了传递的参数,讲飞机进行了分配,虽然方法在仅调用一次的情况下,这个方法并不会有任何问题,但因为我改变了参数,实际上是一个巨大的错误,并不符合规约了。
但是造成这个问题,我自己是能够理解的,最主要问题是胆子太小,当时想到了这个问题,但是不敢去flight写一个new方法,其实写一个new方法就可以让参数不被改变了。唉,这方面的问题还是要多加注意,下次不能再犯了。
浙公网安备 33010602011771号