软件构造:3-1 Data Type and Type Checking

数据类型与类型检验

  1. 静态/动态类型检验
  2. 可变/不可变的数据类型
  3. 可变数据的危险性
  4. 不可变数据的优越性
  5. 用Snapshot图理解数据类型
  6. 用集合类表达复杂数据类型
  7. Null的危害

Java 中的数据类型

在这里插入图片描述
注意几个小问题:

  1. object在初始化之后都是有ID的,而基本数据类型没有;
  2. class不都是mutable的!常见的immutable的class的有很多,比如将基本类型包装的对象类型:String BigInteger BigDecimal Boolean Integer Short Long Character Float Double等;

静态/动态类型检查

静态类型语言与动态类型语言的区别主要在于类型检查的时间:
静态类型检查在编译阶段进行检查,动态类型检查在运行阶段进行检查。
静态类型检查的部分bug可以自动在程序运行前被找到,而动态类型检查的bug只能在代码执行时被找到。
静态类型检查>>动态类型检查>>不检查

静态类型检查可以检查的错误:

  1. 语法错误
  2. 类名/函数名错误
  3. 参数数目错误
  4. 参数类型错误
  5. 返回值类型错误
    动态类型检查可以检查的错误:
  6. 非法的参数值(比如除零异常(稍微注意除零报的是异常),区别参数类型错误)
  7. 非法的返回值(注意区别返回值类型错误)
  8. 越界
  9. 空指针错误

静态类型检查是关于类型的,检测出的问题是不依赖于值(除非final关键字的赋值问题)的;而动态类型检查考虑值的检查

引入一下强类型语言与弱类型语言:

强类型:偏向于不容忍隐式类型转换。
弱类型:偏向于容忍隐式类型转换。
静态类型:编译的时候就知道每一个变量的类型,因为类型错误而不能做的事情是语法错误。
动态类型:编译的时候不知道每一个变量的类型,因为类型错误而不能做的事情是运行时错误。譬如说你不能对一个数字a写a[10]当数组用。

借用网络上的一张图描述强弱类型语言以及静态/动态类型检查的划分:

可变与不可变

主要内容PPT上描述很详细,只记录自己认为重要的点,嘿嘿
final方法也会被子类继承,但是无法被子类重写;

在性能上,不可变类型表现的较差,对其频繁的修改会产生大量的临时拷贝
可变类型可最少化拷贝以提高效率,也可以获得更好的性能,同时适用于在各个模块之间共享数据

防御式拷贝是针对可变类型的,不可变类型节省频繁复制的代价,与上面相反

程序快照 (作为代码层面运行时视图)

程序快照提供了一个将改变变量和改变变量的值可视化的方法
基本类型的值和对象类型的值
不可变对象用双线椭圆

不可变的引用用双线箭头

对于对象来说,final修饰的对象的引用是不可变的,但是指向的值是可变的;对于可变的引用,也可以指向不可变得值

迭代器暗中破坏

        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        for (String s : list)
            if (s.equals("2"))
                list.remove(s);
        for (String s : list)
            System.out.println(s);

尝试此段代码会报如下错误:

Exception in thread "main" java.util.ConcurrentModificationException
	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:939)
	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:893)
	at Plan.main(Plan.java:29)

查看原因:
迭代器内置的modcount与ExpectedModCount不匹配导致报错,modCount在ArrayList中定义为记录改变了ArrayList的size(即增减操作)的次数;
当ArrayList调用iterator()方法,初始化iterator时,将list的modCount值赋给expectedModCount,此时此刻,如果后续expectedModCount和modCount的值不变,或者同步改变保持相等,iterator.next()是不会抛并发修改异常的。既然抛了,它们肯定没有同步修改,直接跟进list.remove()到ArrayList.remove()方法中查看,源码如下:

跟进fastRemove()源码如下:

System.arraycopy()效果如下图:

回到刚才说的list.remove(),方法中modCount自增了,而expectedModCount是没有修改的,因此,在下一次iterator调用next()时,将抛出ConcurrentModificationException。
问题似乎已经弄明白了,我们再看看iterator.remove()方法为什么是OK的。如下是其源码:

首先调用外部类ArrayList的remove()方法,即前面图片中所示,其中修改了modCount。而此处,同时将expectedModCount置为modCount的值,它俩的值再次同步了,所以,iterator的remove()是OK的。
关于可能其它的地方也修改了modCount或expectedModCount的值,在上面代码中,已经确认,除了本文所述的地方存在修改以及list.add()修改了modCount,从迭代开始,是没有其它修改的。
结论:
在使用ArrayList过程中,当使用iterator迭代获取元素并需要移除元素时,需使用iterator的remove()方法移除元素。如果使用list.remove(),将在iterator下一次调用next()时抛出ConcurrentModificationException。如果中途调用list.add(),同样会修改modCount,导致抛出异常。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值