这道Java基础题真的有坑!我求求你,认真思考后再回答。 (2)

简介: 这道Java基础题真的有坑!我求求你,认真思考后再回答。 (2)

三.层层揭秘,为什么发生了异常呢?


我们现在明白为什么阿里强制要求不要在foreach循环里面进行元素的remove/add操作,因为会发生异常了。


但是开发手册里面并没有告诉你,为什么会发生异常。需要我们自己层层深入,积极探索。


3.1 第一层:异常信息解读


所以这一小节我们就一起探索,为什么会发生异常。我们再解析一下程序的运行结果,如下:


image.png


正如上图里面异常信息的体现,异常是在代码的第21行触发的。而代码的第21行,是一个foreach循环。foreach循环是Java的语法糖,我们可以从编译后的class文件中看出,如下图所示:


image.png


请注意图中的第26行代码:


list.remove(item)  (这句话很关键!!!)


很关键,很重要,后面会讲到。


这也解释了,异常信息里面的这一个问题:


image.png


好了,到这一步,我们把异常信息都解读完毕了。


3.2 第二层:抛出异常的条件解读


我再看看真实抛出异常的那一个方法:


image.png


很简单,很清晰的四行代码。抛出异常的条件是:


modCount !=expectedModCount


所以,我们需要解开的下两层面纱就是下面两大点:


第一:什么是modCount?它是干啥的?什么时候发生变化?


第二:什么是expectedModCount?它是干啥的?什么时候发生变化?


3.3 第三层:什么是modCount?它是干啥的?什么时候发生变化?


先来第一个:什么是modCount?



image.png


modCount上的注释很长,我只截取了最后一段。在这一段中,提到了两个关键点。


1.modCount这个字段位于java.util.AbstractList抽象类中。


2.modCount的注释中提到了"fail-fast"机制。


3.如果子类希望提供"fail-fast"机制,需要在add(int,E)方法和remove(int)方法中对这个字段进行处理。


4.从第三点我们知道了,在提供了"fail-fast"机制的容器中(比如ArrayList),除了文中示例的remove(Obj)方法会导致ConcurrentModificationException异常,add及其相关方法也会导致异常。


知道了什么是modCount。那modCount是干啥的呢?


在提供了"fail-fast"机制的集合中,modCount的作用是记录了该集合在使用过程中被修改的次数。


证据就在源码里面,如下:


这是java.util.ArrayList#add(int, E)方法的源码截图:


image.png


注:这里不讨论手动设置为null是否对GC有帮助,我个人认为,在这里有这一行代码并没有坏处。在实际开发过程中,一般不需要考虑到这点。


同时,上面的源码截图也回答了这一层的最后一个问题:它什么时候被修改?


拿ArrayList来说,当调用add相关和remove相关方法时,会触发modCount++操作,从而被修改。


好了,通过上面的分析,我们知道了什么是modCount和modCount是干啥的。准备进入第四层。


3.4 第四层:什么是expectedModCount?它是干啥的?什么时候发生变化?

接下来:什么是expectedModCount?


image.png


expectedModCount是ArrayList中一个名叫Itr内部类的成员变量。


第二问:expectedModCount它是干啥的:


它代表的含义是在这个迭代器中,预期的修改次数


第三问:expectedModCount什么时候发生变化?


情况一:从上图中也可以看出当Itr初始化的时候,会对expectedModCount字段赋初始值,其值等于modCount。


情况二:如下图所示,调用Itr的remove方法后会再次把modCount的值赋给expectedModCount。


换句话说就是:调用迭代器的remove会维护expectedModCount=modCount。(这句话很关键!!!)


image.png


好了分析到了这里,我们知道了下面这个六连击:


1.什么是modCount?

2.modCount是干啥的?

3.modCount什么时候发生变化?

4.什么是expectedModCount?

5.expectedModCount是干啥的?

6.expectedModCount什么时候发生变化?


目录
相关文章
|
7月前
|
存储 缓存 Java
最新Java基础系列课程--Day10-IO流文件处理
最新Java基础系列课程--Day10-IO流文件处理
|
7月前
|
存储 Java
最新Java基础系列课程--Day10-IO流文件处理(一)
最新Java基础系列课程--Day10-IO流文件处理
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
83 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
6月前
|
Java
【Java基础】输入输出流(IO流)
Java基础、输入输出流、IO流、流的概念、输入输出流的类层次结构图、使用 InputStream 和 OutputStream流类、使用 Reader 和 Writer 流类
174 2
|
3月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
6月前
|
安全 Java
|
6月前
|
搜索推荐 算法 Java
【Java基础】 几种简单的算法排序
几种简单的JAVA算法排序
54 4
|
6月前
|
存储 缓存 Java
Java基础17-读懂Java IO流和常见面试题(二)
Java基础17-读懂Java IO流和常见面试题(二)
42 0
|
6月前
|
存储 Java Unix
Java基础17-读懂Java IO流和常见面试题(一)
Java基础16-读懂Java IO流和常见面试题(一)
77 0
|
7月前
|
Java
Java基础教程(12)-Java中的IO流
【4月更文挑战第12天】Java IO涉及输入输出,包括从外部读取数据到内存(如文件、网络)和从内存输出到外部。流是信息传输的抽象,分为字节流和字符流。字节流处理二进制数据,如InputStream和OutputStream,而字符流处理Unicode字符,如Reader和Writer。File对象用于文件和目录操作,Path对象简化了路径处理。ZipInputStream和ZipOutputStream则用于读写zip文件。