从本章开始,介绍本课程的重点:
- 软件构造的理论基础——ADT
- 软件构造的技术基础——OOP
一、编程语言中的数据类型
以本课程使用的java语言为例,数据类型是一组值,以及可以对这些值执行的操作。变量是用特定数据类型定义,可存储满足类型约束的值。java语言的数据类型分为两种,基本数据类型和对象数据类型。
其中,对象数据类型形成层次结构。比如extends(继承)关系,它允许一个类(子类)继承另一个类(父类)的属性和方法。
二、静态(Static)与动态(Dynamic)数据类型检查
所谓检查,检查的是”型“与”值“的匹配性。
java是一种静态类型语言,因此,在编译阶段就会进行类型检查,也就是说,当输入的类型与数值不匹配时,编译器就会报错。而动态类型语言,只有运行时才会报错。所以说,静态类型检查是在运行之前bug就会被检查出来的,避免了将错误带入到运行阶段。而动态则只能在运行阶段。
三、可变与不可变
“赋值”的本质:在内存特定区域开辟一段空间,写入特定值,将该空间与变量(引用)关联到一起。
那么,改变一个变量,与改变一个变量的值又什么区别呢。在java语言中,改变一个变量指的是将一个变量指向另一个存储空间,改变一个变量的值,指的是将该变量当前指向的存储空间中写入一个新的值。
String a = “abc”; a = “def”;
//改变一个变量
Date a = new Date(2024, 1, 1); a.setMonth(2);
//未改变变量,向变量中写入新值
数据类型的immutability(不变性)是十分重要的,不变数据类型一旦被创建,其值就不能再改变。比如String类型,一旦某个存储空间内部的值被定义,就永远无法改变。于是表面上看上去的”更改其值“操作,实际上是给新的值又找了一块存储空间,只是此时这个String变量指向的地址变为了这个新的存储空间。
除了值的不变性,java语言还提供了引用的不变性。被引用的变量一旦被定义为final,那么其指向的内存对象就被确定,该指向关系也不能再被改变。如果编译器无法确定final变量不会改变,就提示错误,这也是静态类型检查的一部分。
所以,尽量使用final变量作为方法的输入参数或作为局部变量。这样可以有效的规避数据暴露后被改变的风险。
总的来说,可变性与不变性能分成两个维度进行总结:
- 不变对象:一旦被创建,其指向的值不能被修改
- 可变对象:拥有方法可以修改其指向的值
这是数据类型本身决定的。
- 不变引用:一旦被创建,变量和内存区域之间的关联关系无法修改
- 可变引用:变量和内存区域之间的关联关系可以修改
四、Snapshot
用于描述程序运行时的内部状态,刻画各变量随时间变化。
基本类型值通常由箭头直接指向,一个箭头表示一个引用。
对象类型值是按其类型标记的圆。当我们想显示更多细节时,我们会在里面写字段名,箭头指向它们的值。有关更多详细信息,字段可以包括其声明的类型。
五、复杂数据类型
Interator(迭代器)
是一个可变的类型。有两个方法。
(1)next(),返回集中的下一个数。这是一个可变的方法。
(2)hasNext()测试是否遍历到集合中最后一个数。
List、Map、Set
不多赘述其用法。在java语言中,他们都是可变的。
六、实用的不可变类型
我们知道,以上三中集都是可变的。那么,如何使用不可变得它们?Collections具有用于获取这些可变集合的不可变的方法:
不可变的包装:
此外,还可以用不可变的方法使这些集不可变。比如使用List.of,Map.of,Set.of方法创建新集。
使用List.copyOf也可以得到一个可变List在当前值的情况下的一个不可变的copy。