软件构造第4章知识总结

4 数据类型和类型检验
4.1 数据类型基本知识,静态/动态类型检查
Java中的数据类型分为基本数据类型和对象数据类型两种。按照约定,基本类型以小写字母开头,对象类型以大写字母开头。
Java的内存管理机制:
栈:存储局部变量,包括方法中定义的变量、for循环内部定义的变量等。只有加载函数才能定义局部变量,变量有自己的作用域,离开作用域后变量会被释放。
堆:存储数组和对象,由new关键字创建的都在堆中。堆中存放的是实体(对象),用于封装多个数据(对象的属性)。堆中的对象不会随时释放,但如果没有指针指向某个对象,它会被垃圾回收机制回收。
垃圾回收机制:Java有独特的垃圾回收机制,可以回收不再被引用的对象。
对象类型形成继承层次结构,最顶层的父类是Object类。
基本数据类型可以包装成对象类型(Boxed Primitives),如Boolean、Integer、Short、Long、Character、Float、Double等,常用于定义容器类型(如List、Map、Set)的元素。一般情况下,基本类型和对应的包装类型可以自动转换,但会降低性能。
运算符包括+、-、*、/、()等,可用于基本类型和对象类型。字符串可以通过+号连接。某些静态方法(如Math库)也可以执行运算。
4.2 静态和动态检查  
静态类型语言:变量类型在编译时已知,编译器可以推导表达式类型,在编译阶段进行类型检查。
动态类型语言:变量类型在编译时未知或不需要知道,在运行时进行类型检查。
一种语言可提供3种检查:
静态检查:编译前进行,检查语法错误、类名/函数名错误、参数数目/类型错误、返回值类型错误等。动态语言会检查除类型外的其他语法错误。
动态检查:运行时进行,检查非法参数值、返回值、越界、空指针等。
无检查:不进行检查。
静态检查优于动态检查,动态检查优于无检查。有些问题静态检查和动态检查都无法发现,如整数除法截断、整数溢出、浮点数的特殊值(NaN、正/负无穷)等。
4.3 易变性和不变性
改变一个变量是指将其指向另一个存储空间;改变一个变量的值是指修改其当前指向的存储空间的内容。
4.3.1 不变性(重要设计原则)
数据类型一旦创建,其值不能改变;引用类型确定指向的对象后,不能再指向其他对象。
Java中使用final关键字标记:
final类无法派生子类
final变量无法改变值/引用 
final方法无法被子类重写
编译器在静态类型检查时,如果发现final变量在首次赋值后被改变,会报错。
应尽量使用final变量作为方法的输入参数和局部变量。
4.3.2 可变性
不变对象:一旦创建,始终指向同一个值/引用。
可变对象:拥有可以修改自身值/引用的方法。
例如,String是不可变类型,StringBuilder是可变类型。
4.3.3 易变性与不变性的区别
当只有一个引用指向对象时,二者没有区别;当有多个引用时,则有差异。
不可变类型频繁修改会产生大量临时拷贝,性能较差,但更"安全",在其他质量指标上表现更好。
可变类型最小化拷贝,提高效率,性能更高,适合在多个模块间共享数据。但可变性使得难以理解程序行为,更难满足方法的规约。
需要根据具体情况选择使用。
4.3.4 危险示例
传递可变对象是潜在的错误源泉,一旦被无意中改变,这种错误非常难于跟踪和发现。
返回可变值也有风险,因为外部可以修改该值,导致内部引用该值的指针的值也发生变化,可能在其他地方出错。
4.3.5 如何改进代码
进行防御式拷贝,但可能造成大量内存浪费。
使用不可变类型,防止被修改,不需要防御式拷贝。
对于一个类,如果某些方法使得内部参数被改变,调用者就可以轻易破坏封装,造成危害。这种情况下需要防御性拷贝,即不把原本类中的对象提供给调用者,而是创建一个相同的新对象返回,这样修改该参数就不会影响类中的参数。
别名使可变类型具有风险。如果在一个方法中完全局部地使用可变对象,并且只对该对象进行一次引用,那么可以使用可变对象。如果有多个引用(别名),使用可变类型就会变得不安全。
4.4 代码级、运行时和时刻视图的快照图
快照图可以表示程序在运行时的内部状态,包括堆栈(正在进行的方法及其局部变量)和堆(当前存在的对象),有助于理解程序的运行状况。
快照图的优点:
便于程序员之间交流
便于刻画各类变量随时间变化
便于解释设计思路 
便于为后续课程中更丰富的设计符号铺平道路
基本数据类型在快照图中用裸常量表示,对象数据类型用标有类型的圆表示,内部写明字段名,用箭头指向它们的值。不可变对象用双线椭圆表示,不可变引用用双线箭头表示。
4.5 复杂数据类型:数组和集合
数组是一种固定长度的类型为T的序列,分为定长数组和可变数组。定长数组是普通数组,变长数组用List接口表示。
数组的遍历可以使用for循环或增强for循环。
List有两个实现类:ArrayList(数组)和LinkedList(链表)。
Set是一种无序、不可重复的集合,常用方法有contains、containsAll、removeAll等。
Map是一种字典数据类型,将两组对象匹配(映射),常用方法有put、get、containsKey、remove等。
Java容器分为三层:接口(模型)、模板和具体实现。根据需求选择合适的容器模型,再选择符合性能要求的实现类或自己实现。
List、Set、Map都是容器接口,需要具体的实现类,如ArrayList、LinkedList、HashSet、HashMap等,但需要指定元素类型,不可混用。
迭代器是一个对象,用于遍历元素并逐个返回。增强for循环实际上调用了被遍历对象的迭代器。迭代器有两个方法:next()返回下一个元素,hasNext()判断是否到达末尾。
在使用迭代器遍历集合时,不可以使用集合的方法增删元素,否则会破坏迭代器结构而抛出ConcurrentModificationException异常。应该使用迭代器自身的remove()方法删除next()获取的元素。
4.6 有用的不可变数据类型
基本类型及其包装类型都不可变,日期类型Date是可变的。
Java集合类型(列表、集合、映射)的常见实现(ArrayList、HashMap)都是可变的,这是必须的,因为需要进行元素的增删改等操作。但如果需要在方法之间传递整个集合,可以使用防御式拷贝(缺点是浪费空间),或者使用不可修改的包装器。
Collections实用工具类提供了获取可变集合的不可修改视图的方法,这种包装器的结果是不可变的,试图修改会抛出异常,但这种"不可变"是在运行时获得的,编译期无法进行静态检查。
不可修改的包装器通过截获所有修改集合的操作并抛出UnsupportedOperationException,剥夺了修改集合的能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值