类和实例的初始化循环问题
2016-06-22 00:30
232 查看
转载自:http://imu2008.blog.51cto.com/3844842/1605937
如下所示代码:
结果说明:
输出结果是-1970,如果将1和2调换位置,则输出结果为当前年减去1970的差--45。
代码分析:
该程序所遇到的问题是由类初始化顺序中的循环而引起的。类的初始化是由虚拟机对其 main 方法的调用而触发的。 首先,其静态域被设置为缺省值,其中 INSTANCE 域被设置为 null,CURRENT_YEAR 被设置为 0,overtime被设置为0。接下来,静态域初始器按照其出现的顺序执行初始化。
静态域INSTANCE的值是通过调用构造器而计算出来的。这个构造器会用一个涉及静态域 CURRENT_YEAR 的表达式来初始化 overtime。通常,读取一个静态域是会引起一个类被初始化的事件之一,但是我们已经在初始化类了,递归的初始化尝试会直接被忽略掉。在静态域被初始化之前,存在着读取它的值的可能,而此时该静态域包含的还只是其所属类型的缺省值。因此,CURRENT_YEAR
的值仍旧是其缺省值 0,这就是为什么结果是-1970了。
由类初始化中的循环所引发的问题是难以诊断的,但是一旦被诊断到,通常是很容易订正的。要想订正一个类初始化循环,需要重新对静态域的初始器进行排序,使得每一个初始器都出现在任何依赖于它的初始器之前。将2和1调换位置,静态域CURRENT_YEAR会先被初始化,再初始INSTANCE
的时候,CURRENT_YEAR已经有正确的值了,所以输出结果正确。
某些通用的设计模式本质上就是初始化循环的, 特别是本题展示的单例模式( Singleton)和服务提供者框架( Service Provider Framework)。类型安全的枚举模式(Typesafe Enum
pattern)也会引起类初始化的循环。
总之,要当心类初始化循环。最简单的循环只涉及到一个单一的类,但是它们也可能涉及多个类。
类初始化循环也并非总是坏事,但是它们可能会导致在静态域被初始化之前就调用构造器。静态域, 甚至是 final 类型的静态域,可能会在它们被初始化之前,被读走其缺省值。
再看如下的代码片段:
上述代码片段的输出为1--null。这个代码片段和第一个代码片段不同的是:字符串f是实例域,不是静态域。在一个实例域被赋值之前,存在着取用其值的可能,而此时它包含的仍旧是其所属类型的缺省值,这一点和静态域(类域)是相同的。在这两个示例中,都会产生初始化循环,第一个是类的初始化循环,第二个是实例初始化循环。但是,需要注意的不同是,循环的类初始化是无法避免的灾难,但是循环的实例初始化总是可以且总是应该避免的。
无论何时,只要一个构造器调用了一个已经被其子类覆写了的方法,那么该问题就会出现,因为以这种方式被调用的方法总在实例被初始化之前执行。要想避免这个问题,就千万不要在构造器中调用可覆写的方法 直接调用或间接调用都不行。这项禁令应该扩展至实例初始器和伪构造器(readObject 与 clone,这些方法之所以被称为伪构造器,是因为它们可以在不调用构造器的情况下创建对象)。总之,在任何情况下,你都务必要记住:不要在构造器中调用可覆
写的方法。在实例初始化中产生的循环将是致命的。
如下所示代码:
输出结果是-1970,如果将1和2调换位置,则输出结果为当前年减去1970的差--45。
代码分析:
该程序所遇到的问题是由类初始化顺序中的循环而引起的。类的初始化是由虚拟机对其 main 方法的调用而触发的。 首先,其静态域被设置为缺省值,其中 INSTANCE 域被设置为 null,CURRENT_YEAR 被设置为 0,overtime被设置为0。接下来,静态域初始器按照其出现的顺序执行初始化。
静态域INSTANCE的值是通过调用构造器而计算出来的。这个构造器会用一个涉及静态域 CURRENT_YEAR 的表达式来初始化 overtime。通常,读取一个静态域是会引起一个类被初始化的事件之一,但是我们已经在初始化类了,递归的初始化尝试会直接被忽略掉。在静态域被初始化之前,存在着读取它的值的可能,而此时该静态域包含的还只是其所属类型的缺省值。因此,CURRENT_YEAR
的值仍旧是其缺省值 0,这就是为什么结果是-1970了。
由类初始化中的循环所引发的问题是难以诊断的,但是一旦被诊断到,通常是很容易订正的。要想订正一个类初始化循环,需要重新对静态域的初始器进行排序,使得每一个初始器都出现在任何依赖于它的初始器之前。将2和1调换位置,静态域CURRENT_YEAR会先被初始化,再初始INSTANCE
的时候,CURRENT_YEAR已经有正确的值了,所以输出结果正确。
某些通用的设计模式本质上就是初始化循环的, 特别是本题展示的单例模式( Singleton)和服务提供者框架( Service Provider Framework)。类型安全的枚举模式(Typesafe Enum
pattern)也会引起类初始化的循环。
总之,要当心类初始化循环。最简单的循环只涉及到一个单一的类,但是它们也可能涉及多个类。
类初始化循环也并非总是坏事,但是它们可能会导致在静态域被初始化之前就调用构造器。静态域, 甚至是 final 类型的静态域,可能会在它们被初始化之前,被读走其缺省值。
再看如下的代码片段:
无论何时,只要一个构造器调用了一个已经被其子类覆写了的方法,那么该问题就会出现,因为以这种方式被调用的方法总在实例被初始化之前执行。要想避免这个问题,就千万不要在构造器中调用可覆写的方法 直接调用或间接调用都不行。这项禁令应该扩展至实例初始器和伪构造器(readObject 与 clone,这些方法之所以被称为伪构造器,是因为它们可以在不调用构造器的情况下创建对象)。总之,在任何情况下,你都务必要记住:不要在构造器中调用可覆
写的方法。在实例初始化中产生的循环将是致命的。
相关文章推荐
- swfUpload 上传图片
- win10 导航已取消 解决 亲身验证 !
- 112-Rotate Image
- mysql 创建远程连接用户
- 构建高并发高可用的电商平台架构实践
- Android Scroll(四)----ViewDragHelper
- 记一次面经
- HDU 2036 (平面几何 多边形面积)
- 用EF框架操作数据库
- Android开发笔记之Jni编程
- 求1+2+…+n(要求不能使用乘除法、for、while、if、else、switch、case等关键字)
- 关键字、标识符与变量
- [Photon项目解析] 维京人 MainMenuVik
- 求一个整数是否是回文(正着念和反着念都一样)
- HDU 1086 (平面几何 线段求交)
- 用CXF编写基于spring的web service 并添加cxf日志拦截器和自定义拦截器
- 树中两个结点的最低公共祖先(超全解&&拓展)
- Volley 请求返回数据中文出现乱码
- Java反射机制调用private类型的构造方法
- 浅谈C语言中的强符号、弱符号、强引用和弱引用