软件构造复习笔记(3)——第三章

一、数据类型与类型检验

1、数据类型

(1)基本数据类型:int,long,boolean,double。char,float

  • 只有值,没有ID
  • immutable 不可变的
  • 在栈中分配内存

(2)对象数据类型:String,Integer,classes,arrays,enums(枚举)

  • 既有值,也有ID
  • 一些mutable,一些 immutable
  • 在堆中分配内存

2、类型检查

(1)静态类型检查:编译阶段进行

  • 例子:语法错误,类名、函数名错误,参数数目错误,参数类型,返回值类型错误
  • 关于“类型”的检查,不考虑值

(2)动态类型检查:运行阶段进行

  • 非法的参数值,非法的返回值,越界,空指针
  • 关于“值”的检查

静态类型检查优于动态类型检查

3、可变数据类型和不可变数据类型

(1)不可变数据类型:一旦被创建,其值不改变

  • 如果是引用类型,也可以是不变的:一旦确定指向的对象,不能再改变。eg:final Person a = new Person(“Rose”)
  • 例子:String s = “a”;s = s.concat("b”);s指向新的地址和值
    在这里插入图片描述

【note】final:
final变量无法改变值、引用
final类无法派生子类
final方法无法被子类重写

(2)可变数据类型:StringBuilder,Date

  • 可变对象拥有方法可以修改自己的值/引用
  • 例子:StringBuilder sb = new StringBuilder(“a”);sb.append(“b”);sb仍然指向原地址,但是值改变了
    在这里插入图片描述

(3)可变数据类型和不可变数据类型区别

  • 不可变数据类型使用时,频繁修改产生大量临时拷贝。垃圾回收,降低性能。不可变更安全
    可变数据类型较少拷贝,提高效率,有更好性能,适合于多模块之间共享数据

(4)安全使用不可变数据类型

  • 防御式拷贝,返回全新对象eg:return new Date(groundhogAnswer.getTime());
  • 使用局部变量,不会涉及共享
  • 只是用一个引用

4、Snapshot图

用于描述程序运行时的内部状态
在这里插入图片描述

  • 引用是不可变的,但指向的值可以变化。可变的引用也可以指向不可变的值。
    如下图,不可变引用sb可以改变指向的值,但是引用不可改变
    在这里插入图片描述

5、复杂数据类型

(1)Array数组

  • int[]

(2)List列表

  • Snapshot图:圆角矩形表示
    在这里插入图片描述

(3)Set集合

(4)Map
在这里插入图片描述

(5)迭代

  • 显示迭代
    for(int i=0;i<n;i++){
    cities.get(i);
    }
  • 隐式迭代
    for(interger i : cities){
    System.out.println(i);
    }
  • 迭代器
    List<String> lst = …;
    Iterator iter = lst.iterator();
    while(iter.hasNext()){
    String t = iter.next();
    System.out.println(t);
    }
  • 迭代时容易出现的错误
    使用remove时,修改原列表中的值
    如以下出现错误:
    List<String> lst = …;
    Iterator iter = lst.iterator();
    while(iter.hasNext()){
    String t = iter.next();
    if(t.startsWith(“6.”)){
    list.remove(t);
    }
    }

首先,remove 第一个。此时lst为{“6.005”,“6.813”}。lst.get(0)=“6.005”。迭代器指向“1”,然而,lst.get(1)=“6.813”
在这里插入图片描述
迭代器指向“1”,remove “6.813”。剩余一个“6.005”
在这里插入图片描述
采用下述:
while(iter.hasNext()){
String t = iter.next();
if(t.startsWith(“6.”)){
iter.remove();
}
}

6、不可变数据类型例子

(1)基本数据类型都是不可变的
(2)复杂数据类型中不可变的:List,Set,Map
但是 ArrayList,HashMap可变
(3)包装
Collections.unmodifiableList
Collections.unmodifiableSet
Collections.unmodifiableMa

  • 这种包装,编译阶段无法据此进行静态类型检查

二、设计规约

1.规约作用

可以隔离变化,无需通知客户端。防火墙角色

2、行为等价性

两个行为是否可以相互替换
根据规约判断行为是否等价,如果符合规约,则等价
站在客户端角度

3、spec构成

(1)前置条件precondition:关键词requires
(2)后置条件postcondition:关键词effects
(3)契约:如果前置条件满足,则后置条件必须满足;前置条件不满足,则方法可以做任何事情
(4)规约:

  • 静态类型声明是一种规约
  • 方法前的注释也是规约
    /**
    *@param 参数,前置条件
    *@return 返回值,后置条件
    *@throws 异常
    **/

4、设计spec

(1)规约强度:
前置条件更弱,后置条件更强的规约强度大
(2)规约分类

  • 确定的规约:给定一个输入,输出唯一
  • 欠定的规约:一个输入可以有多个输出
  • 非确定的规约:同一个输入,多次执行得到不同输出
    在这里插入图片描述
  • 操作式规约:伪代码
  • 声明式规约:没有内部描述,只有初始和终止状态。内部细节在内部注释中
    【声明式规约更有价值】

三、ADT

1、opertations

(1)creator构造器:构造一个新的同类型对象 。 t*->T
(2)producer生产器:从本对象生成一个新的同类型对象,T+,t*->T
(4)observer观察器:返回一个其他类型的对象,T+,t*->t
(5)mutator变值器:改变对象属性的方法,T+,t*->T|t|void

2、表示独立性

客户使用ADT时无需考虑内部如何实现,ADT内部表示变化不影响外部的spec和客户端

3、测试

(1)测试creator,productor,mutator:调用observer观察结果是否满足spec
(2)observer:调用creator,productor,mutator等产生或改变对象,看结果是否正确

4、RI,AF

(1)invariants保持不变量

  • 不变量:在任何时候都为true
  • 保持程序的“正确性”,容易发现错误

(2) rep exposure 表示泄露:影响不变形和辨识独立性

  • 使用防御式拷贝来避免
  • 使用immutable彻底避免泄露

(3)Rep invariant 和 Abstraction function

  • R:表示空间 ,开发者看到的 。 A:抽象空间,客户端看到和使用的
  • abstraction function:抽象函数 。AF: R->A。满射,未必单射
    // AF(属性1,属性2,…) = 客户端看到的抽象空间
  • Rep invariant:表示不变性。
    某个具体表示是否合法
    所有表示值的一个子集,包含了所有合法值
    一个条件,描述什么是合法的表示值

(3)checkRep():随时检查RI是否满足

  • 在所有可能改变rep的方法内检查:creator,productor,mutator
  • observer方法可以不用,但最好检查

(4)有益的可变性beneficial mutator:对immutable的ADT来说,在A空间的abstract value不可变,但是内部表示的R空间的取值可以变化。但不是immutable的类中可以随便出现mutator!!取决于AF
如下图的AF,此时两个属性可以改变(约分),只要A不变
在这里插入图片描述

5、撰写

精确记录AF:解释每个R值
精确RI: 所有属性什么时候有效,即多个属性之间的关系
//Abstraction Function:
//…
//Rep invariant:
//…
//Safety from rep exposure:
//…

【区分】
spec规约:客户端看到的
rep:表示空间,只有程序员看到
A:抽象空间,客户可见

四、OOP

1、Interface接口

  • 确定ADT规约
  • 接口之间可以继承和扩展
  • 一个类可以实现多个接口
  • 一个接口可以有多个实现类
  • 实现类要实现接口所有方法,但可以增加新方法
    public interface Person{
    定义方法…
    }

【注】无法保证所有实现类中的方法全部是接口中定义的,因此客户端需要知道某个具体类的名字

  • default方法:在接口中使用default修饰方法,实现类中可以直接使用
    default int method(int a){…}

2、继承inheritance

子类 extends 父类

  • 严格继承:子类只能添加新方法,无法重写父类中的方法(父类方法final修饰)
    可重写继承:子类可以重写

(1)Override:重写函数完全相同的函数返回值,函数名,参数,运行时决定执行哪个方法

  • 父类中被重写的函数不为空,子类可重写也可不重写
  • 父类函数为空,所有子类必须重写
  • super.方法名,调用父类函数

(2)abstract 抽象类

  • 某些方法所有子类型都有,但是彼此有差别,在父类型中使用抽象方法
    public abstract class Person{
    int x,y;
    public void Method1(int a){…}

    public abstract void draw();
    }

3、多态,子类型、重载

(1)多态分类:

  • 特殊多态:功能重载(方法名相同,参数类型不同)
  • 参数化多态
  • 子类型多态

(2)overload重载

  • 多个方法具有同样名字,但是参数和返回值不同
  • 编译时决定使用哪个方法
  • 参数类型一定不同,返回值可以相同也可以不同
  • overload也可以发生在父类和子类之间

【overload和override】重载overload在编译时决定使用哪个方法,重写override在运行时决定使用哪个方法

(3)泛型编程

  • 使用<>来表示
    List<Integer> ints = new ArrayList<>();
    public interface List<E>
    public class Pair<E>{
    private final E first;

    }
  • 通配符?
    List<?> list = new ArrayList<String>();
    List<? extends Animal> : Animal的所有子类
    List<? super Animal> :Animal的所有父类

(4)子类型多态:不同类型的对象可以统一处理不用区分

五、ADT/OOP中的等价性

1、不可变ADT中的等价性

判断方法:
(1)使用AF判断,映射到同一个A,则等价
(2)使用observer方法,返回结果相同

  • 不可变类型要重写equals和hadCode

2、==和equals

(1)==是引用等价性,指向同一地址

  • 基本数据类型
  • 判断两个对象时使用==,判断ID内存空间是否相同

(2)equals是对象等价性

  • 对象类型
  • 在object中缺省的equals()是判断引用等价性,相当与==
    自定义ADT时,要重写equals方法

(3)hasCode():哈希表中的值

  • 等价的对象必须映射到同一哈希表中
  • 重写equals()后紧跟重写hasCode()除非保证ADT不会放在Hash类型的集合类中

(4)clone

  • x.clone() != x
  • x.clone().getClass() == x.getClass() // 获得一个实例的类型类。
  • x.clone().equals(x)

(5)Integer 使用==和equals

  • Integer 中-128~127 可以用==,其他判断相等要用equals
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

3、可变ADT中等价性

(1)观察等价性:不改变状态时,连个对象是都看起来一致

  • 有时候观察等价性会导致bug
    在这里插入图片描述

(2)行为等价性:调用任何方法展示出一致结果

  • 对可变类型,无需重写equals和hasCode
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值