软件构造--Chapter4总结

数据类型与类型检验

Data type in programming languages

数据类型,即一组值以及可以对其执行的操作,例如boolean,它是在true或者false两个中的值,支持逻辑运算操作;String,它是形如“hello”等字符组成的串,可以进行拼接、复制等操作。
变量,即用特定数据类型定义,用于存储满足类型约束的值,例如

String s1 = "Hello,HIT!"

其中s1就是变量,是字符串类型,表示值“Hello,HIT!”
在Java中有几种基本的数据类型,通常用小写字母表示,如int,long,boolean,double,char;除此之外,Java还有一些对象数据类型,通常用开头大写的单词表示,如String,Integer,Double,Boolean等。

基本数据类型对象数据类型
int,long,byte,short,char,float,double,booleanClasses,interfaces,arrays,enums,annotations
只有值,没有ID既有ID,也有值
不可变的有一些可变,有一些不可变
在栈中分配内存在堆中分配
无法实现表达统一表达与泛型的统一
代价较低代价昂贵

在Java中,所有的类的最初的父类是Object,有三种基本的操作,分别是equals(),hashCode()和toString(),这三个方法是通用的。
另外,Java也将基本类型包装为对象类型,通常定义容器类型的时候需要使用它们(需要传入对象类型),但是要避免多地使用,因为它们会降低性能。

Static vs. dynamic data type checking

Java是一种静态类型的编程语言,所有变量的类型在编译时是已知的,编译器可以推导对应的表达式类型;对应的Python是动态类型的编程语言,在运行阶段进行类型检查。

静态检查

静态检查意味着在编译时找到bug,避免将错误带入运行阶段,可提高程序的正确性/健壮性。常见的一些错误包括语法错误、类名/函数名错误(Math.sine(2)–>Math.sin(2))、参数数目错误、参数类型错误、返回值类型错误等。静态检查是关于“类型”的检查,不考虑具体的值。

动态检查

动态检查是在运行时找到bug,常见的错误有非法的参数值(除数为0)、非法的返回值、越界、空指针等。动态检查是关于“值”的检查。
下面举几个常见的错误例子:

int n = 5;
if(n){
	n = n + 1;
}

这里n是int类型,if中需要boolean类型,无法将int类型转换为boolean类型,属于静态检查发现的错误。

int big = 200000;
big = big * big;

结果为1345294336,运算结果过大,会产生溢出,是关于值的错误,属于动态检查发现的错误。

int sum = 0;
int n = 0;
int average = sum/n;

这个不是错误,但是会引发问题,运行时会抛出异常:Exception in thread “main” java.lang.ArithmeticException。

double sum = 7;
double n = 0;
double average = sum/n;

这个不是错误,但是会引发问题,运行时由于除数是0,商过大,会中断CPU运行。

Mutability and Imuutability

Assignment,即赋值,最常见的赋值就是’=',例如:

String s ="Hello,world!"

改变一个变量,是将该变量指向另一个存储空间;改变一个变量的值,是将该变量当前指向的存储空间写入一个新的值。程序是需要变化的,但是变化很可能产生问题,要尽可能避免变化。
不变性,一个重要的设计原则。对应的,不变数据类型,一旦被创建,其值不能被改变。

关键字final

为了使参数不变,可以用关键字final修饰。
在Java中,用final修饰的变量在首次赋值后发生改变,编译器在静态类型检查时会提示错误。
关键字final代表了程序员的一种“设计决策”。final类无法派生子类、final变量无法改变值/引用、final方法也无法被子类重写。

不变对象

Immutable type,即不变对象,一旦被创建,始终指向同一个值/引用。
String就是一个不变对象,一个特定的String对象总是指向一个字符串。

String s = "a";
s = s.concat("b");//s+="b"

在第一行s初始化的时候s是指向"a"的,在运行完第二行的函数后,s并不指向原来的"a",而是另外指向一个新的"ab"。因此此过程有两个串。
当有多个引用指向一个对象的时候,例如:

String t = s;
t = t + "c";

原来的s指向的"ab"还会存在,t会新指向"abc",接着上述过程,共有三个串。
在使用不变类型时,对其频繁修改会产生大量的临时拷贝,需要进行回收。

可变对象

Mutable type,即可变对象,拥有方法可以修改自己的值/引用。
StringBuilder就是一个可变对象,拥有修改的方法。

SringBuilder sb = new StringBuilder("a");
sb.append("b");

在这两行运行过程中,sb变量一直指向同一个地方,此过程只有一个串。
当多个引用指向一个对象的时候,例如:

StringBuilder tb = sb;
tb.append("c");

在这个过程中,tb变量和sb变量始终指向同一个地方终都指向"abc",此过程只有一个串。
使用可变类型,可以最少化拷贝,提高效率。

即使使用可变类型有不少好处,但是一些场景还是要不可变类型,因为不可变类型更加“安全”,在其他质量指标上表现更好。
传递可变对象可能在不经意间改变,此错误难以发现;
返回可变对象也可能发生问题,可以通过防御式拷贝,给客户端返回全新的对象,大部分该拷贝不会被客户端修改,可能造成大量内存浪费。如果返回的是不可变类型,则不需要防御式拷贝。在实际使用中,也要减少多个引用(别名)的使用,尽量使用局部变量,不会涉及共享。

Snapshot diagram as a code-level,run-time,and moment view

Snapshot diagrams,即快照图,用于描述程序运行时的内部状态。快照图可以便于程序员之间交流、刻画各类变量随时加变化、解释设计思路。
基本类型的值,用一个指向的箭头指向值进行表示,如下图所示:
在这里插入图片描述
对象类型的值,用一个指向的箭头指向一个圈,圈内有对象名,以及他们的值,如下图所示:
在这里插入图片描述
不可变对象使用双线椭圆进行表示,可变对象使用单线椭圆进行表示,如下图所示:

String s = "a";
s = s + "b";
StringBuilder sb = new StringBuilder("a");
sb.append("b");

在这里插入图片描述
不可变的引用常常用关键词final修饰,引用是不可变的,但是指向的值是可变的,在快照图中需要用双线箭头表示;可变的引用,也可以指向不可变的值,如下图所示:
在这里插入图片描述
下面有两个具体的例子:
针对可变值的不可变引用:

final StringBuilder sb = new StringBuilder("abc");
sb.append("d");
sb = new StringBuilder("e");

在编译阶段会出错:The final variable sb cannot be assigned
sb的值为"abcd"。
在这里插入图片描述
针对不可变的可变引用:

String s1 = new String("abc");
List<String> list = new ArrayList<>();
list.add(s1);
s1 = s1.concat("d");
System.out.println(list.get(0));
String s2 = s1.concat("e");
list.set(0,s2);
System.out.println(list.get(0));

输出为:
abc
abcde
在这里插入图片描述
list(0)存储的是添加前的s1,s1经过concat函数,指向另一个串,但是list(0)还是原串。

Complex data types:Arrays and Collections

Array,固定长度的数组,在声明时就要确定好数组的长度。

int[] array = new int[100];

List,变长的数组,是一种接口,成员必须是对象类型,在声明时无需确定数组的长度。

List<Integer> list = new ArrayList<Integer>();

Set,无序的、元素为对象类型、元素个数为0或者更多的集合,是一种抽象接口。

Set<Integer> numbers = new HashSet<>();

在这里插入图片描述
Map,类似于字典的键值对,是一种抽象接口。

Map<String,Turtle> turtles = new HashMap<>();

在这里插入图片描述
当添加一个item时,编译器执行静态检查,确保只添加合适类型的item。因此,取出来的值也都是指定类型的。

Iterator

Iterator,迭代器,一个对象,遍历一组元素并逐个返回元素。
迭代器有两种方法,next()返回下一个元素,是可变方法;hasNext()是测试是否到达一组数据的结束的。

for(String str:list){
	System.out.println(str);
}
Iterator iter = list.iterator();
while(iter.hasNext()){
	String str = iter.next();
	System.out.println(str);
}

Useful immutable types

基本类型及其封装对象类型都是不可变的。Java的包装器得到的结果是不可变的,但是这种“不可变”是在运行阶段获得的,编译阶段无法据此进行静态检查。
不可修改的包装主要有两个用途:使集合在构建后保持不变;允许某些客户端以只读方式访问您的数据结构。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

深海质粒ABCC9

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值