`
ruilin521314
  • 浏览: 874924 次
文章分类
社区版块
存档分类
最新评论

静态最终域初始化时抛异常的问题

 
阅读更多

原文链接:http://supermmx.org/blog/20090319_exception_in_initialization_of_static_final_field

有时候需要一些预先创建好的对象以便别的类直接使用,这些对象通常都是静态最终常量(static final),通常都是这样创建的:

  1. package org.supermmx.example.misc;
  2. public class StaticFinalException {
  3. public static final Test TEST_1 = new Test("value1");
  4. }
  5. class Test {
  6. public Test(String value) {
  7. }
  8. }

但如果在构造函数中声明有异常抛出的话,会怎么样呢?

  1. package org.supermmx.example.misc;
  2. public class StaticFinalException {
  3. public static final Test TEST_1 = new Test("value1");
  4. }
  5. class Test {
  6. public Test(String value) throws Exception {
  7. }
  8. }

编译的结果如下:

  1. StaticFinalException.java:4: 未报告的异常 java.lang.Exception;必须对其进行捕捉或声明以便抛出
  2. public static final Test TEST_1 = new Test("value1");
  3. ^
  4. 1 错误

这种异常是必须要抓住的,显然不能直接放在赋值语句中,所以首先想到是不是可以放到静态初始化中(static intialization)去:

  1. package org.supermmx.example.misc;
  2. public class StaticFinalException {
  3. public static final Test TEST_1;
  4. static {
  5. try {
  6. TEST_1 = new Test("value1");
  7. } catch (Exception e) {
  8. }
  9. }
  10. }
  11. ...

结果是这样:

  1. StaticFinalException.java:3: 可能尚未初始化变量 TEST_1
  2. public class StaticFinalException {
  3. ^
  4. 1 错误

这个结果也是预料之中的,如果抛出异常的话,TEST_1 就有可能没有赋值。那么就在 catch 中设为 null

  1. package org.supermmx.example.misc;
  2. public class StaticFinalException {
  3. public static final Test TEST_1;
  4. static {
  5. try {
  6. TEST_1 = new Test("value1");
  7. } catch (Exception e) {
  8. TEST_1 = null;
  9. }
  10. }
  11. }
  12. class Test {
  13. public Test(String value) throws Exception {
  14. }
  15. }

结果如下:

  1. StaticFinalException.java:10: 可能已指定变量 TEST_1
  2. TEST_1 = null;
  3. ^
  4. 1 错误

对于 final 变量来说,它只能赋值一次,引用 Java 语言规范 4.12.4 final Variables

A final variable may only be assigned to once. It is a compile time error if a final variable is assigned to unless it is definitely unassigned (§16) immediately prior to the assignment.

也就是说,在一次赋值之前,这个变量必须是明确未赋值的。否则就出现上述的编译错误。但上述代码如果抛了异常以后,TEST_1 是应该没有赋值的,因为只有一条语句,为什么还会报可能已经赋值的错呢?就必须看一下在这个时候 TEST_1 的赋值状态,在 16.2.15 try Statements 小节:

# V is definitely unassigned before a catch block iff all of the following conditions hold:
* V is definitely unassigned after the try block.
...

只有满足一系列的条件时,V 在 catch 块之前才是明确未赋值的,其中一个就是 V 在 try 块之后必须是明确未赋值,try 块里面是一条赋值语句,在 16 小节的引言中:

In all, there are four possibilities for a variable V after a statement or expression has been executed:
...
* V is not definitely assigned and is not definitely unassigned.(The rules cannot prove whether or not an assignment to V has occurred.)
...

也就是说流程分析在无法判断对 V 的赋值是否发生了,那么 V 既不是明确赋值,也不是明确未赋值。

那在我们的例子中,对于 TEST_1 的赋值是有可能抛出异常的,不一定正常执行结束,所以它不是明确赋值,也不是明确未赋值。所以在 try 块之后、catch 块之前,它不是明确未赋值,所以编译会报错。

不过还是有解决方法,如下:

  1. package org.supermmx.example.misc;
  2. public class StaticFinalException {
  3. public static final Test TEST_1 = initTest1();
  4. private static Test initTest1() {
  5. try {
  6. return new Test("value1");
  7. } catch (Exception e) {
  8. }
  9. return null;
  10. }
  11. }
  12. class Test {
  13. public Test(String value) throws Exception {
  14. }
  15. }

不过这样有个不好的地方,就是如果有太多变量的话,看起来不好看,写起来也很费劲,代码重复也很多。

当然最简单的还是不抛异常或者抛的是运行时异常:

  1. package org.supermmx.example.misc;
  2. public class StaticFinalException {
  3. public static final Test TEST_1 = new Test("value1");
  4. }
  5. class Test {
  6. public Test(String value) throws RuntimeException {
  7. }
  8. }

当然有违初衷,如果在其他需要处理的地方没抓住异常,就可能导致错误的结果。

开始一直想不通,编译的时候知道只有这么一条语句,不管怎么看 TEST_1 都只会赋值一次,怎么还会报错呢?但看 16 章中给出的例子:

  1. void unflow(boolean flag) {
  2. final int k;
  3. if (flag) {
  4. k = 3;
  5. System.out.println(k);
  6. }
  7. if (!flag) {
  8. k = 4; // k is not "definitely unassigned" before here
  9. System.out.println(k);
  10. }
  11. }

就明白了,明确赋值分析是逐行静态分析的,只要不是常量,并不考虑上下文的真正语义。

分享到:
评论

相关推荐

    javaSE代码实例

    7.5.3 静态最终成员变量 119 7.6 小结 121 第8章 继承——多态的支柱 122 8.1 继承概述 122 8.1.1 类之间的关系 122 8.1.2 面向对象中的继承性 124 8.2 类的继承 125 8.3 成员变量的继承与隐藏 126...

    Visual C++ 2005入门经典--源代码及课后练习答案

    7.4.4 在构造函数中使用初始化列表 320 7.5 类的私有成员 320 7.5.1 访问私有类成员 323 7.5.2 类的友元函数 324 7.5.3 默认复制构造函数 326 7.6 this指针 328 7.7 类的const对象 331 7.7.1 类的...

    Visual C++ 2010入门经典(第5版)--源代码及课后练习答案

    7.4.4 在构造函数中使用初始化列表 316 7.4.5 声明显式的构造函数 317 7.5 类的私有成员 318 7.5.1 访问私有类成员 320 7.5.2 类的友元函数 321 7.5.3 默认复制构造函数 323 7.6 this指针 325 7.7 类的const...

    软件工程-理论与实践(许家珆)习题答案

    RCP法与RSP法的主要区别是前者采用循环渐进的开发方式,原型将成为最终的产品,而后者将被废弃。(√) 三、简答题 1. 软件产品的特性是什么? 答: ● 软件是一种逻辑产品,具有无形性;  ● 软件产品的生产...

    C#微软培训资料

    11.3 静态和非静态的方法.129 11.4 方法的重载.130 11.5 操作符重载.134 11.6 小 结.137 第十二章 域 和 属 性 .139 12.1 域 .139 12.2 属 性 .143 12.3 小 结 .146 第十三章 事件和索引指示器 .148 ...

    PHP和MySQL Web开发第4版pdf以及源码

    2.4.4 解决打开文件时可能遇到的问题 2.5 写文件 2.5.1 fwrite()的参数 2.5.2 文件格式 2.6 关闭文件 2.7 读文件 2.7.1 以只读模式打开文件:fopen() 2.7.2 知道何时读完文件:feof() 2.7.3 每次读取一行...

    PHP和MySQL WEB开发(第4版)

    2.4.4 解决打开文件时可能遇到的问题 2.5 写文件 2.5.1 fwrite()的参数 2.5.2 文件格式 2.6 关闭文件 2.7 读文件 2.7.1 以只读模式打开文件:fopen() 2.7.2 知道何时读完文件:feof() 2.7.3 每次读取一行数据:fgets...

    PHP和MySQL Web开发第4版

    2.4.4 解决打开文件时可能遇到的问题 2.5 写文件 2.5.1 fwrite()的参数 2.5.2 文件格式 2.6 关闭文件 2.7 读文件 2.7.1 以只读模式打开文件:fopen() 2.7.2 知道何时读完文件:feof() 2.7.3 每次读取一行...

    C语言精典版本C程序设计语言

    5.8 指针数组的初始化 5.9 指针与多维数组 5.10 命令行变元 5.11 指向函数的指针 5.12 复杂说明 第6章 结构 6.1 结构的基本知识 6.2 结构与函数 6.3 结构数组 6.4 结构指针 6.5 自引用结构 6.6 查找表 ...

    仓库管理系统课程设计UML.doc

    针对这一情况,为了减轻仓库管理员和操作员的工作负担,此系统在满足仓库的 基本管理功能基础上发挥信息系统的智能化。 根据要求可将系统分为四个模块 (1)用户登录模块 普通操作员和管理人员登录此系统,执行仓库...

    语言程序设计课后习题答案

    2-11 在一个for循环中,可以初始化多个变量吗?如何实现? 解: 在for循环设置条件的第一个";"前,用,分隔不同的赋值表达式。 例如: for (x = 0, y = 10; x ; x++, y++) 2-12 执行完下列语句后,n的值为多少? ...

    《数据结构 1800题》

    《数据结构 1800题》 第一章 绪论 一、选择题 1. 算法的计算量的大小...9. 当你为解决某一问题而选择数据结构时,应从哪些方面考虑?【西安电子北京科技大学 2000】 10. 若将数据结构定义为一个二元组(D,R),...

    JAVA程序设计教程

    1.2.2 结构化程序设计 .............................................................................................5 1.2.3 面向对象程序设计 ...........................................................

Global site tag (gtag.js) - Google Analytics