1.
数据类型
Primitive types 基本数据类型(8种)
1.
Object types 对象数据类型(reference types 引用数据类型)
Short、int 、long、float、double、boolean、char、byte
如:String、BigInteger
只有值,没有ID(无法与其他值区分),不能赋值为null;
immutable
有值,也有ID;
部分mutable,部分immutable
在栈中分配内存,代价低
在堆中分配内存,代价高
静态类型检查&动态类型检查
静态类型检查
Java是一种静态类型语言
在编译阶段进行类型检查
静态检查:可在编译阶段发现错误,避免了将错误 带入到运行阶段,可提高程序正确性/健壮性
关于“类型”的检查,不考虑值 动态检查:关于“值”的检查
语法错误, 类名/函数名错误,参数数目错误,参数类型错误, 返回值类型错误
动态类型检查
在动态类型语言(如Python)中,这种检查被推迟到运行时(程序运行时)
在运行阶段进行类型检查
非法的参数值. 非法的返回值,越界,空指针
一种语言可以提供三种自动检查:
Static checking静态检查:在程序运行之前自动发现错误。
Dynamic checking动态检查:执行代码时自动发现错误
No checking无检查:语言根本不能帮助您找到错误。你必须自己小心,否则你会得到错误的答案
静态检查 >> 动态动态
无检查
Mutability and Immutability
改变一个变量、改变一个变量的值,二者有何区别?
改变一个变量:将该变量指向另一个存储空间
改变一个变量的值:将该变量
当前指向的存储空间中写入一个新的值。
Immutability 不变性
不变性:重要设计原则
不变数据类型:一旦被创建,其值不能改变
如果是引用类型,也可以是不变的:一旦确定其指向的对象,不能再被改变指向其他对象
要使引用不可变,请使用关键字final声明它
编译器进行静态类型检查时,如判断final变量首次赋值后发生了改 变,会提示错误
尽量使用final变 量作为方法的输入参数、作为局部变量。
final类无法派生子类
final变量无法改变值/引用
final方法
无法被子类重写
不变对象:一旦被创建,始终指向同一个值/引用
可变对象:拥有方法可以修改自己的值/引用
字符串是不可变类型的一个示例。
不可变对象:用双线
椭圆
StringBuilder是可变类型的一个示例
可变类型:修改值
使用 不可变类型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收)
不可变类型更“安全”,
在其他质量指标上表现更好
可变类型最
少化拷贝以提高效率
使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据
如果有多个引用(别名),使用可变类型就非常不安全
防御式拷贝
不可变类型
不需要防御式拷贝
Snapshot diagrams(快照图)
快照图显示程序在运行时的内部状态—其堆栈(正在进行的方法及其局部变量)和堆(当前存在的对象)。用于描述程序运行时的内部状态
便于程序员之间的交流,便于刻画各类变量随时间变化,便于解释设计思路
Primitive values 基本类型的值
Object values 对象类型的值
可变引用:单线箭头
不可变的引用:用双线箭头
引用是不可变的,但指向的值却可以是可变的
可变的引用,也可指向不可的值
例子:针对可变值的不可变 引用
针对不可变值的可变引用
、
Designing Specification 设计规约
1.
作用
1.
规约可以隔离"变化",无需通知客户端
2.
规约可以提高代码效率
3.
规约扮演"防火墙"角色
4.
解耦,不需要了解具体实现
2.
内容:只讲"能做什么",而不讲"怎么实现"
Behavior
equivalence 行为等价性
是否可以相互替换
1.
站在客户端的视角看行为等价性,不同的行为,对用户来说(根据用户需求)可能等价!
2.
根据规约判断行为等价,两个方法符合同一个规约,则等价
规约的结构:
1.
Pre-condition
2.
Post-condition
3.
Exceptional behavior 异常行为,如果违背了前置条件,会发生什么 (throw an exception)
前置条件:对客户端的约束,在使用方法时必须满足的条件
后置条件:对开发者的约束,方法结束时必须满足的条件
契约:如果前置条件满足了,后置条件必须满足
前置条件不满足,则方法可做任何事情。
. 除非在后置条件里声明过,否则方法内部不应该改变输入参数
尽量不设计mutating的spec,否则容易引发bugs。
尽量避免使用mutable对象。
避免使用可变的全局变量。
规约的强度与替换
Spec变强:更放松的前置条件(前置条件更弱)+更严格的后置条件(后置条件你更强),
两条件同时变强或变弱则无法比较。
若规约强度S2>=S1,则可以用S2替换S1。
越强的规约,意味着implementor的自由度和责任越重,而client的 责任越轻。
Diagraming specification
规约定义一个区域,该区域包含所有可能的实现方式。
空间中的每个点表示一种方法的实现。
对于某个具体实现,若满足规约,则落在其区域内。
更强的规约表达为更小的区域。
1.
Quality of specification 规约质量
1.
内聚性:spec描述的功能应单一、简单、易理解
2.
运行结果信息丰富(可能的改变,以及返回值等),不能让客户端产生理解上的歧义
3.
足够强(如postcondition中充分阐述各种情况)
4.
适当弱(太强的规约,在很多特殊情况下难以达到)
5.
在规约里使用抽象类型(在java中,经常使用interface,如Map、List,而不是HashMap、ArrayList),可以给方法的实现体和客户端更大的自由度
6.
使用前置条件和后置条件?
客户端不喜欢太强的pre-condition,不满足precondition的输入会导致失败
So:不限定太强的precondition,而在postcondition中抛出异常:输入不合法,
fail fast,避免fail大规模扩散
是否使用前置条件取决于:
1.
check(检查参数合法性)的代价
2.
方法的使用范围:
1.
如果只在类内部使用(private),则可以不使用precondition,在使用该方法的各个位置进行check
2.
如果在其他地方使用(public),则必须使用precondition,若client不满足则抛出异常