【HIT】哈工大2021春软件构造复习(4)

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也会修改。

在这里插入图片描述

可变类型

优势:如果使用不可变类型型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收),而可变类型最少化拷贝以提高效率。使用可变数据类型,可获得更好的性能,也适合于在多个模块之间共享数据。

风险

例一

在这里插入图片描述

因为之前调用了sumAbsolute方法导致其值被改变了,所以sum方法也就得不到想要的结果了。所以可以看出,传递可变对象容易产生潜在错误,且难以跟踪。

如何修改?

可以通过防御式拷贝,给客户端返回一个全新的Date对象:

在这里插入图片描述

但是大部分时候该拷贝不会被客户端修改, 可能造成大量的内存浪费。这个时候如果使用不可变类型, 则节省了频繁复制的代价。

例二

在这里插入图片描述

groundhogAnswer作为缓存结果,避免多次重复计算。

Date是可变类型,可以用java.time包里的其他不变类代替,如:LocalDateTime,Instant等。

例三:防御性拷贝

在这里插入图片描述

通过构造函数,修改了内部private变量。

在这里插入图片描述

可以看到,由于Date是可变类,get到以后仍然修改了private的值,或者来自构造元素的变化也修改了private的值,所以可变类的使用很容易造成风险。

结论:想要安全地使用可变类型,需要我们使用局部变量,这样不会涉及共享;或是在只有一个引用的情况下。如果有多个引用(别名),那么使用可变类型就很不安全。

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:List list = new ArrayList();

操作包括:在这里插入图片描述

注意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。但是这种“不可变”是在运行阶段获得的,编译阶段无法据此进行静态检查。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值