从java.util.ConcurrentModificationException看静态代码块中的坑

最近学习时编写demo代码出现异常,发现代码中的一个坑,再次记录一下,先贴代码:

public class ComponentScanner {
    
    public static void main(String[] args) throws Exception  {
        
    }
    
    static{
        try {
            System.out.println("ComponentScanner.enclosing_method(1)");
            loadComponent();
            System.out.println("ComponentScanner.enclosing_method(2)");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 項目中的所有class文件都加載到集合中
     * @return
     * @throws Exception
     */
    private static List scan() throws Exception{
        List result = new ArrayList<>();
        List list = FileScanner.scan();
        int count = 0;
        for (String str : list) 
        {
            str = str.replace("\\", ".");
            str = str.substring(0, str.length() - ".class".length());
//            System.out.println(str);
            Class c = Class.forName(str);
            result.add(c);
            count+=1;
        }
        System.out.println("scan执行:"+count);
        return result;
    }
    
    /**
     * 扫描项目中的所有类
     * @return 类上带有@component注解的类的class对象的集合
     * @throws Exception 
     */
    public static List loadComponent() throws Exception
    {
        System.out.println("ComponentScanner.loadComponent()");
        List result = new ArrayList<>();
        List source = scan();
        
        for (Class c : source) 
        {
            Component a = (Component) c.getAnnotation(Component.class);
            if (a != null) 
            {
                result.add(c);
            }
        }  
         return result;
    }
}
第二段代码:

public class ObjectFactoryAnno {  

    public static void main(String[] args) throws Exception {
        
    }
    
    private static Map objects = new HashMap();
    
    static{
        
        try {
            System.out.println("ObjectFactoryAnno.enclosing_method(1)");
            ComponentScanner.loadComponent();
            System.out.println("ObjectFactoryAnno.enclosing_method(2)");
            ioc();
    
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化异常");
        }
    }
    
    /**
     * 将项目中所有带有@Component注解的类 创建对象,放到objects中
     * @throws Exception
     */
    private static void ioc() throws Exception{
        
        System.out.println("ObjectFactoryAnno.ioc()");
        
        List components = ComponentScanner.loadComponent();
        
        for (Class c : components) 
        {   
            Object obj = c.newInstance();
            objects.put(c, obj);
        }
    }
}
其中两个类都有一个空的main函数,执行ObjectFactoryAnno,输出结果:

从java.util.ConcurrentModificationException看静态代码块中的坑_第1张图片

 而执行ComponentScanner,再来看看程序运行的结果:

从java.util.ConcurrentModificationException看静态代码块中的坑_第2张图片

 为什么会出现这个结果呢?解释前顺便提一下导致类加载的方式

1. 创建对象
2. 调用类中的静态方法或者 静态属性
3. 执行main 方法
4. Class.forName("类的包名.类名")

每个类会且仅会加载一次,而对象所在的类被加载会使 1. 静态代码块会被执行  2. 静态属性会被初始化

回到代码我们知道当执行ComponentScanner的loadComponent()时,调用了scan()方法,依次执行了ComponentScanner.loadComponent()—>ComponentScanner.scan()—>FileScanner.scan(),list中存放模块中所有被加载到的类的class文件,在foreach循环遍历中执行Class.forname ("ObjectFactoryAnno.class")时,ObjectFactoryAnno类被classLoader加载(与此同时,list中的部分类已经被迭代到,一部分没有进行迭代),然后在ObjectFactoryAnno的static代码块中依次执行了ComponentScanner.loadComponent()—>ComponentScanner.scan()—>FileScanner.scan(),此时继续进行和之前一样的迭代操作,list被重新赋值

从java.util.ConcurrentModificationException看静态代码块中的坑_第3张图片

 从java.util.ConcurrentModificationException看静态代码块中的坑_第4张图片

 

由于list是static属性,并且所有操作在static块中执行因此没有被回收,和之前迭代了一半的情况冲突,于是抛出java.util.ConcurrentModificationException异常,关于这个异常的报错机制,有一篇写的很详细的文章这里给出链接: java.util.ConcurrentModificationException详解 - 简书。

另一方面,执行ObjectFactoryAnno的static块时,ClassLoader会先加载ObjectFactoryAnno类static块中的内容,当执行ComponentScanner.loadComponent()时,加载ComponentScanner类,再执行ComponentScanner的static块中的内容,此时再去调用scan方法,遍历 list时是第一次使用foreach进行迭代,Class.forName()也不会再去重复加载之前加载过的类,所以没有报错。

 

你可能感兴趣的:(java,java)