软件构造(3)- 数据类型与类型检查

一.编程语言中的数据类型
基本类型对象类型
int,long…类(classes),接口(interfaces),数组(arrays),枚举(enums),注解(annotations)
不可变的有些可变有些不可变
On stack,exist only when in use(栈中分配内存)On heap,garbage collected(堆中分配内存)
代价低代价昂贵

类的根是Object,所有类(除Object)均有父类。
当extends省略时默认继承自Object

包装类型
将基本类型包装为对象类型:Boolean, Integer, Short, Long, Character, Float, Double
通常是在定义集合类型的时候使用它们 eg:Map<Integer,Double> map = new HashMap()
Language does autoboxing and auto-unboxing 一般可以自动转换

Overloading operations 重载:同样的操作名可用于不同的数据类型

二.静态和动态类型检查

类型转换中可能出现的问题:

int a = 2; // a = 2 
double a = 2; // a = 2.0 (Implicit) 
int a = (int) 18.7; // a = 18 
double a = (double)2/3; // a = 0.6666…
int a = 18.7; // ERROR 
String a = 1; // ERROR
double a = 2/3; // a = 0.0

静态类型语言:所有变量的类型在编译阶段被确定,编译器能够推测出表达式的类型。在编译阶段进行类型检查。bug会在程序运行前被找出。eg:Java

动态类型语言:在运行阶段进行类型检查。bug会在程序执行时找出。eg:Python

静态类型检查:可在编译阶段发现错误,避免了将错误带入到运行阶段,可提高程序正确性/健壮性。
可检测出的类型:语法错误,类名/函数名错误, like Math.sine(2),参数数目错误, like Math.sin(30, 20),参数类型错误, like Math.sin(“30”),返回值类型错误, like return “30”; from a function that’s declared to return an int。

动态类型检查:非法的参数值(除0错误),非法的返回值错误,越界,空指针。
可检测出的类型:非法的参数值:除0错误; 非法的返回值 ; 越界:数组越界,集合越界 ; 空指针。

静态检查:关于“类型”的检查,不考虑值。
动态检查:关于“值”的检查
基本数据类型有时不是真正的数字:
1. 整数除法:5/2 会返回一个被截断的整数2
2. 整数溢出:int big = 200000*200000; 会返回一个错误的数值,并会躲过动态和静态类型检查
3. 浮点数溢出:Not a Number

int sum = 0;
int n = 0;
int average = sum / n;
System.out.println(average);//报错:除0错误

    double sum = 7;
    double n = 0;
    double average = sum / n;
    System.out.println(average);//输出Infinity

    double probability = 1 / 5;
    System.out.println(probability);//输出0.0

	int a = 5; // (1)
    if (a > 10) { // (2)
      int b = 2; // (3)
    } else { // (4)
      int b = 4; // (5)
    } // (6)
    b *= 3; //静态类型检查错误(b cannot be resolved to a variable)
三,可变性与不可变性

改变一个变量:将该变量指向另一个值的存储空间。
改变一个变量的值:将该变量当前指向的值的存储空间中写入一个新的值。

如果是引用类型,也可以是不变的:一旦确定其指向的对象,不能再被改变。
可用关键字final修饰,使类型不可变。

final int n = 5;
final Person a = new Person(“Ross”);

如果编译器无法确定final变量不会改变,就提示错误,这也是静态类型检查的一部分。

** Note: **

  1. final 类无法派生子类
  2. final变量无法改变值/引用
  3. final方法无法被子类重写

不变对象:一旦被创建,始终指向同一个值/引用
可变对象:拥有方法可以修改自己的值/引用
eg:

  1. string是不可变类型:一旦被创建,值就不可变。若要在String后添加,必须创建新的String对象。
    在这里插入图片描述
  2. StringBuilder是可变类型:内部存在修改方法
    在这里插入图片描述
    当存在多个引用对像时会存在差异
    在这里插入图片描述
    由此,可以看出对于不可变类型的频繁修改会产生大量垃圾,可变类型可获得更佳性能,也适合在多个模块间共享数据。但是,不可变类型更“安全”,在其他质量指标上表现更好。

传递可变的值出现的问题:
计算集合中绝对值的和时将所有元素都改为其绝对值,直接求和时无法求出正确结果。
在这里插入图片描述
返回可变的值出现的问题:
Data数据类型是可变的,在PartyPlanning中通过set函数将全局变量中的信息进行了改变
在这里插入图片描述
解决方案:
通过防御式拷贝,给客户端返回一个全新的Date对象return new Date(groundhogAnswer.getTime());
但是,大部分时候该拷贝不会被客户端修改,可能造成大量的内存浪费,最好采用不可变类型,避免了客户端修改问题,也不会出现防御式拷贝产生的内存浪费。

安全的使用可变类型:局部变量,不会涉及共享;只有一个引用,如果有多个引用(别名),使用可变类型就非常不安全

四.Snapshot diagrams快照图

基本类型:
在这里插入图片描述
对象类型:
在这里插入图片描述
不可变对象:用双线椭圆
在这里插入图片描述

不可变的引用:用双线箭头
在这里插入图片描述
引用是不可变的,但指向的值却可以是可变的
可变的引用,也可指向不可变的值

final StringBuilder sb = new StringBuilder("abc");
sb.append("d");//输出abcd
sb = new StringBuilder("e");//出错!final的对象引用不可变
System.out.println(sb);

一个可变的引用(list)指向不可变的值(string):
在这里插入图片描述
四,Java迭代器

迭代器就是提供一种方法对一个容器对象中的各个元素进行访问,而又不暴露该对象容器的内部细节。

两种形式:

List<String> list = new Arraylist<>();
for (String str : list) {
	System.out.println(str);
}
List<String> list = new Arraylist<>();
Iterator iter = list.iterator();
while(iter.hasnext() ) {
	String str = iter.next();
	System.out.println(str);
	if(str.startsWith("6.")){
		iter.remove();//正确的移除方法
		list.remove(str);//错误的移除方法,但不会报错,可能会移除失败
	}
}

区别:
第一种形式不可在遍历过程中改变其中的元素,否则会报错。
第二种形式如需要在遍历过程中修改其中元素,需要采用正确方式,否则虽不会报错但会产生错误结果。
应使用iterator中的remove()方法,不能直接从集合类中移除。

参考资料:
https://www.cnblogs.com/zyuze/p/7726582.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值