目录链接
从本篇起,我会较简洁的提炼重点知识点内容而非泛泛地了解知识点。这次笔记我们主要学习第三部分。
一、数据类型与类型检验
1.1 数据类型
数据类型的概念如下:
数据类型:一组值以及可以对其执行的操作。
变量:用特定数据类型定义,可存储满足类型约束的值。
1.1.1 基本数据类型
基本数据类型内容如下:
– int (for integers like 5 and -200, but limited to the range ± 2^31, or roughly ± 2 billion)
– long (for larger integers up to ± 2^63)
– boolean (for true or false)
– double (for floating-point numbers, which represent a subset of the real numbers)
– char (for single characters like ‘A’ and ‘$’ )
1.1.2 对象数据类型
对象数据类型内容如下:
– String represents a sequence of characters.
– BigInteger represents an integer of arbitrary size.
不属于基本数据类型的所有类型都是对象数据类型。
可以看出,对象数据类型首字母大写,而基本数据类型首字母是小写。
Object类型是所有对象类型的父类,所有类若没有继承默认以Object为父类
1.2 类型检查
首先我们要了解类型转换,相关内容与在计算机系统学习的内容相同,分为显示类型转化和显示类型转化等,因此需要对这些进行检查。
静态类型语言:在编译阶段进行类型检查,所有变量的类型在编译期就已经确定。
动态类型语言:在运行阶段进行类型检查,变量的类型在运行期才能确定。
java属于静态类型语言,同时具有静态检查和动态检查。
静态检查
- 检查是否有语法错误。
- 名称错误。
- 参数个数错误。
- 参数类型错误。
- 返回值类型错误。
动态检查
- 非法参数:比如x/y中y为0就是不合法。
- 不可描述的返回值:即该返回值不能作为代表出现在该类型中。
- 超出范围的索引值:如数组越界等。
- 调用空对象的方法。
静态检查针对类型与特定值无关的错误,动态检查针对由特定值引起的错误。
1.2.1可变性和不可变性
1.2.1.1 变与不变
改变可以分成两种:
改变一个变量:将该变量指向另一个值的存储空间。
改变一个变量的值:将该变量当前指向的值的存储空间中写入一个新的值。
同时,不变性还可分成以下两种:
不变数据类型(immutable type):一旦被创建,其值不能改变
引用不变(immutable references):一旦确定其指向的对象,不能再被改变(但其值是可能变化的)
1.2.1.2 final
引用不变性可以利用final关键字产生。
一个类,无论它是可变的或是不变的,都可加上final关键字,使其具有如下特质:
final 类无法派生子类
方法和变量也可以加final:
final 方法无法被子类重写
final 变量无法改变其引用(而非值)
1.2.2可变类型(mutable)和不可变类型(immutable)
不可变类型
一旦被创建,就始终代表(对外表现出)同样的值。
可变类型
拥有方法可以修改自己的值。
不可变类型安全,而可变类型比较方便。
1.2.3 程序快照图
属于程序的运行时视图、代码级视图、瞬间视图。
基本类型
在箭头末端填写值。
对象类型
在箭头末端画一个椭圆,在其中写上对象的类名,更详细的话再包含内部属性。
可变类型对象的椭圆使用单线,而不可变类型的椭圆使用双线。
引用
可变引用使用单线,不可变引用使用双线。
这里需要注意,即使是immutable的数据也可变引用,而mutable的数据上了final就是不可变引用的。这两点需要分开看待。
1.2.4 容器
经典的容器包括数组、map、set、list等。数组长度确定,而其他长度可变。典型的程序快照如下:
迭代器:可变数据类型,有方法next返回下一个类型,hasnext返回是否有下一个类型。
1.3 总结
基本数据类型不可变,包括大数类型BigInteger等;
一般容器都可变。如果希望容器不可变,可使用包装器 Collections.unmodifiableList 等获取一个不可变的包装对象;
程序快照图非常重要。画法如下:
可变 | 不可变 | |
---|---|---|
基本类型 | 箭头末端 | |
对象类型 | 单椭圆 | 双椭圆 |
引用(final) | 单箭头 | 双箭头 |
此外,变量名也要画出,但不用画圆圈。
二、设计规约(spec)
类中包含方法,参数,返回值等,方法中的主函数可以作为客户端;对于非参数和返回值的类型检测在在静态类型检查阶段完成。针对这些内容我们需要设计规约。
2.1 规约
规约是客户端与实现者之间签订的“契约”,客户端的输入应当满足前置条件,实现者编写的程序应当给出满足后置条件的结果。
规约描述了方法的功能以及接口(“能做什么”),不需要依赖(也不应该透露)方法的具体实现。
Spec给“供需双方”都确定了责任,在调用的时候双方都要遵守。同时spec不能涉及内部变量私有域。
2.2 行为等价性
去判断行为等价性,问题的关键就在于两种实现方法能否相互替代。
两种行为等价的方法可以相互替代,主要看用户需求。
2.3规约内容
前置条件(Precondition):
规约的前置条件是对客户端的约束,即客户端在使用这个方法的时候必须要满足的要求。
后置条件(Postcondition):
规约的后置条件是对开发者的约束,即开发人员在完成方法后需要满足的条件。
异常行为(Exceptional behavior):
规约中做的规定,如果违反前置条件那么方法会怎么做,返回什么值。
同时,如果客户端没有满足前置条件,那么方法可以做任意事情。
2.4 java中的规约
java中的规约包括注释、@param、@return、@throws。其中@param属于前置条件,@return、@throws属于后置条件。
2.5 规约的强弱
java中的规约是分强弱的,较强的规约有以下的特征:
前置条件较弱,后置条件较强
也就是给用户的自由度更大了,因此用户会比较需要强的规约,同时会使得方法更难实现。
注意,抛出的异常更少和返回的内容更详细时会加强后置条件。
2.5.1 规约图
规约图是规约强弱的表示,越小的圈代表了越强的规约(能让它实现的函数越少)。强规约的函数无法用弱规约的函数替代。
2.6 其他的分类方法
按照确定性分类
确定性规约(Deterministic):给定前置条件,其输出是唯一的、明确的。
欠定的规约(Underdetermined):同一个输入可以有多个输出,但是一旦使用具体的方法实现了这个规约,那么这个返回值也将会被确定。
非确定的规约(Not deterministic):同一个输入,多次执行可能得到多个结果,比如涉及到随机数的方法。
按照陈述性分类
操作式规约:有具体的实现细节,如伪代码
声明式规约:没有内部实现的描述,只有对输入输出的规定
声明式规约更有价值,更能应对变化。但是操作式规约能够方便开发。