Chapter 3: Data Type and Type Checking(数据类型与类型检验)
本章重点:
3.1 Data type in programming languages
类型(type):一组值以及可以对这些值执行的操作。
变量(variables):用特定数据类型定义,可存储满足类型约束的值。
基本数据类型(primitve types)以及对象数据类型(object types):通常前者小写开始,后者大写开始。
二者比较:
基本数据类型只有值没有ID也就代表,可以按照值来区分基本数据类型,而对于对象数据类型,即使值相同也未必相等。
对象类型形成层次结构:
object类是所有类的父类;一个类是它所有父类的一个实例
3.2 Static vs. dynamic data type checking
静态类型语言(statically-typed language):Java就是一个静态类型语言,会在编译时进行类型检查,所有变量类型须已知,编译器可以判断所有表达式类型。
静态类型检查发生在程序运行前,优点是可在编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性。
静态类型检查可以检查出以下错误: 语法错误、类名/函数名错误、参数数目错误、参数类型错误、返回值类型错误。
动态类型语言(dynamically-typed language):例如Python,在运行阶段进行类型检查,变量类型在运行过程中可以变化。
动态类型检查发生在在代码运行过程中,可以检查出以下错误:非法的参数值、非法的返回值、越界、空指针等。
总体来说,静态检查是关于“类型”的检查,不考虑值;而动态检查是关于“值”的检查。
静态类型检查好于动态好于无检查(no checking)
3.3 Mutability & Immutability
我们用“=”对一个变量赋值。对于改变一个变量和改变一个变量的值,二者有何区别?
改变一个变量的实质是将该变量指向另一个值的存储空间,也就是改变变量的指向;而改变一个变量的值则是将该变量当前指向的值的存储空间中写入一个新的值。
1. 不变性
不变性是Java的重要设计原则,不变数据类型一旦被创建,其值不能改变。
使用final关键字作为方法输入参数或局部变量。final类无法派生子类或是被子类重写,也无法改变值(引用)。通过final关键字实现的不可变引用可以被静态检查检测出。
2. 可变类和不可变类举例
String类是一个不可变类,总是表示相同的值。如下图所示,如果想修改一个String类变量,就必须新建一个String变量。
StringBuilder是一个可变类,含有删改、删除其某个字符的方法。
3. 总结
对于可变类和不可变类来说,当只有一个引用时,二者使用区别不大,但是当有多个引用时,二者区别就很大。如下图所示,当s和t指向同一个String类变量,那么当修改t时,t会指向新的变量而s不变;当sb和tb指向同一个StringBuilder类变量时,tb修改则sb也会修改。
可变类型
优势:如果使用不可变类型型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收),而可变类型最少化拷贝以提高效率。使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据。
3.4 Snapshot diagram(关系快照图)
关系快照图(Snapshot diagrams):用于描述程序运行时的内部状态,例如栈(包括方法和变量)、堆(存在的对象),便于程序员之间交流、刻画各类变量随时间变化和解释设计思路。
如下图所示,画关系快照图时,对于基本类型的值,直接用箭头指向就可以了,对于对象类型要先圈出来再用箭头指向
如果final修饰可变类型,那么实际上引用时不可变的但是指向的值是可变的。同样,可变的引用也可指向不可变的0值
3.5 Complex data types: Arrays and Collections(复杂数据类型)
1. Array类
Array:int [] a = new int[100];
2. List类
注意List是一个接口,其成员必须是对象数据类型。
3. Set类
Set:集合类似于数学中的集合概念,没有重复元素且无序。
4. Map类
Map:Map集合类提供了一个更通用的元素存储方法。Map集合类用于存储元素对(也就是key和val),其中每个key映射到一个val。
Map也是一个抽象接口,常用的有HashMap(key不能重复)。
5. 总结
List、Set和Map作为接口,都定义了类型的工作方式,但不提供实现代码,所以用户可以自行选择不同实现。例如:ArrayList、LinkedList;HashSet;HashMap等。
6. 迭代器(Iterator)
可以直接作用于for循环的对象统称为可迭代对象(Iterable)。我们可以使用isinstance()来判断一个对象是否是Iterable对象。而可以被next()函数调用并不断返回下一个值的对象称为迭代器(Iterator)。
迭代器有两个方法,next()方法返回下一个元素,hasNext()可以检查迭代是否结束。
3.6 Useful immutable types
基本类型及其封装对象类型都是不可变的。java集合类(List、Set、Map)的常用实现都是可变的(ArrayList、HashMap等)。
Immutable Wrappers:但是Collections实用类中存在获取这些可变集合的不可修改视图的方法,也就是Collections.unmodifiableList,Collections.unmodifiableSet和Collections.unmodifiableMap。但是这种“不可变”是在运行阶段获得的,编译阶段无法据此进行静态检查。