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

感谢原文作者:http://supermmx.org/blog/20090319_exception_in_initialization_of_static_final_field

 

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

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

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

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

编译的结果如下:

  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.  
  3. public class StaticFinalException {
  4.     public static final Test TEST_1 ;
  5.  
  6.     static {
  7.         try {
  8.             TEST_1 = new Test ( "value1" ) ;
  9.         } catch ( Exception e ) {
  10.         }
  11.     }
  12. }
  13. ...

结果是这样:

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

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

 

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

结果如下:

  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.  
  3. public class StaticFinalException {
  4.     public static final Test TEST_1 = initTest1 ( ) ;
  5.  
  6.     private static Test initTest1 ( ) {
  7.         try {
  8.             return new Test ( "value1" ) ;
  9.         } catch ( Exception e ) {
  10.         }
  11.         return null ;
  12.     }
  13. }
  14.  
  15. class Test {
  16.     public Test ( String value ) throws Exception {
  17.     }
  18. }

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

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

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

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

开始一直想不通,编译的时候知道只有这么一条语句,不管怎么看 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. }

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值