ADT(Abstract Data Type) 抽象数据类型
OOP(Object-Oriented Programming) 面向对象的程序设计
数据类型
基本数据类型 | 对象数据类型 |
int, long, byte, short, char, float, double, boolean | Classes, interfaces, arrays, enums, annotations |
只有值,没有ID(与其他值无法区分) | 既有值也有ID |
不可变 | 可变 |
在栈中分配内存 | 在堆中分配内存 |
代价低 | 代价昂贵 |
基本数据类型的声明方式:
int a = 0;
对象数据类型的声明方式:
List = new ArrayList<>();
将基本类型包装为对象类型:Integer,Double,Boolean,Long,Character,Float等,通常在定义集合类型的时候使用,除此之外一般要避免使用这些类型。
类型检查
静态类型检查:在编写代码时进行检查。能检查的错误有语法错误、类名/函数名错误、参数数目错误、参数类型错误、返回值类型错误等。是关于“类型”的检查。
动态类型检查:在代码运行时进行检查。能检查的错误有非法参数值、非法返回值、越界、空指针等。是关于“值”的检查。
无检查:从不检查。
可变和不可变
改变一个变量:将该变量指向另一个值的存储空间。
改变一个变量的值:将该变量当前指向的值的存储空间中写入一个新的值。
不变对象:一旦被创建,始终指向同一个值/引用。
可变对象:拥有方法可以修改自己的值/引用。
例如,String是不可变的,将"a"变成"ab",实际上是创建一个新的内容为"ab"的String;而StringBuilder是可变的,将"a"变成"ab",则是直接改变其值。
两者各有优缺点,使用可变类型,可获得更好的性能,但安全性欠佳;不可变类型在修改时会产生大量垃圾,可它更加安全。
Sanpshot Diagram 规则
1.基本类型的值
2.对象类型的值
List
Set
Map
3.可变类型用单椭圆,不可变类型用双椭圆
4.可变引用(可以指向不可变的值)用单线箭头,不可变引用(可以指向可变的值)用双线箭头
使用snapshot diagram,可以帮助我们更好地理解变量改变的过程,分析代码中出现的错误。
规约(Specification)
规约起到防火墙的作用,只讲这个代码能做什么,而不涉及具体的实现方法,精确的规约有助于区分责任。
前置条件:对客户端的约束,使用这个方法时需要满足的条件。
后置条件:对开发者的约束,方法结束时需要满足的条件。
如果前置条件满足了,后置条件必须满足;如果前置条件不满足,则后置条件不一定会满足。
更强的spec指更弱的前置条件和更强的后置条件。
行为等价性:两个方法实现的过程不同,但它们遵循的规约相同,则说这两个方法是行为等价的。
spec具体写法如下:
/**
* 这里写这个方法的功能。
* @param attr1 参数attr1需要满足的条件
* @param attr2 参数attr2需要满足的条件
* ……
* @return ret 返回值满足的条件
*/
ReturnType method(AttrType1 attr1, AttrType2 attr2, ……) {......}
ADT
ADT是由操作定义的,与其内部如何实现无关。
可变类型的对象提供了可改变其内部数据的操作。不变数据类型的操作不改变其内部值,而是构造新的对象。
ADT操作的四种类型:Creator(构造器)、Producer(生产器)、Observer(观察器)、Mutator(变值器)
Creator(构造器):构造这个类的对象。
Producer(生产器):利用这个类的对象,通过某些操作创建一个新的这个类的对象。
Observer(观察器):观察对象的属性等。
Mutator(变值器):改变对象的值。不变对象没有mutator。
表示独立性 RI representation independent
client使用ADT时无需考虑其内部如何实 现,ADT内部表示的变化不应影响外部spec和客户端。除非ADT的操作指明了具体的pre和post-condition,否则不能改变ADT的内部表示——spec规定了client和implementer之间的契约。
不变量 Invariants
在任何时候总是true。由ADT 来负责其不变量,与client端的任何行为无关。
表示泄露 representation exposure
不仅影响不变性,也影响了表示独立性:无法在不影响客户 端的情况下改变其内部表示。使用immutable的类型,彻底避免表示泄露。
RI、AF、Safety from rep exposure
表示空间R:ADT实现者关注的空间。
抽象空间A:用户关注的空间。
AF : R → A
RI : R → boolean 某个具体的“表示”是否是“合法的”,也可看作所有表示值的一个子集,包含了所有合法的表示值,也可看作一个条件,描述了什么是“合法”的表示值。
选择某种特定的表示方式R,进而指定某个子集是“合法”的(RI),并为该子集中的每个值做出“解释”(AF)——即如何映射到抽象空间中的值。
即使是同样的R、同样的RI,也 可能有不同的AF,即“解释不同”。
//Rep Invariant:
// 所有合法的表示值/一个描述合法表示值的条件
//Abstraction Function:
// 从R到A的映射
//Safety from rep exposure
// 描述是如何防止表示泄露的
在ADT中使用checkRep()来随时检查RI是否满足。
OOP
类 Class
下面为一个简单的类的例子:
public class Person {
//类成员变量
private String name;
private String sex;
private int age;
//类方法
Person(String name, String sex, int age) { //Creator
this.name = name;
this.sex = sex;
this.age = age;
}
int howOld() { //Observer
return this.age;
}
void setAge(int age) { //Mutator
this.age = age;
}
......
}
接口 interface
定义一个ADT。接口中只有方法的声明,一个接口可以有多种实现类,接口之间可以继承。
下面为一个接口InterName和这个接口的实现类ClassName的例子:
public interface InterName {
ReturnType method1();
ReturnType method2(AttrType attr, ...);
......
}
public class ClassName implements InterName {
Type1 var1;
Type2 var2;
public InterName(Type1 var1, Type2 var2) {
this.var1 = var1;
this.var2 = var2;
}
ReturnType method1() {......}
ReturnType method2(AttrType attr, ...) {......}
......
}
Overriding(覆盖/重写)
根据子类中不同的需要,重写父类中的方法。
Overloading(重载)
重载:多个方法具有同样的名字,但有不同的参数列表或返回类型,方便根据具体情况调用。
重载规则:必须有不同的参数列表,返回值可相同可不同,访问修饰符可相同可不同,可以有新的异常,可以再一个类内重载,也可以在子类中重载。
equals()、hashCode()和toString()
在自定义的类中,需要重写这三个方法来保证结果正确。(toString()一般是根据程序员自己定义的格式写的,没有标准)
==和equals()的区别:==判断两个对象是否指向内存中同一段空间,而缺省的equals()判断两个对象是否指向同一个引用,但我们使用equals()时通常是为了判断两个对象是否相同,所以就需要重写。
对可变类型,实现行为等价性即可,所以无需重写这两个函数,直接继承Object即可。
一个例子:
public class Person {
private String name;
private String sex;
private int age;
@Override
public boolean equals(Object o) {
if(!(o instanceof Person)) {
return false;
}
Person pe = (Person) o;
return pe.name.equals(this.name) && pe.sex.equals(this.sex) && pe.age.equals(this.age);
}
@Override
public int hashCode() {
return 31 * this.name.hashCode() + 31 * this.sex.hashCode() + age;
}
@Override
public String toString() {
return "[name]"+this.name+"[sex]"+sex+"[age]"+age;
}
......
}
泛型接口
其定义中包含类型变量(例如public interface Set