使用静态导入时未经检查的异常,怎么来的?

时间:2012-11-15 23:29:23

标签: java static-methods unchecked-exception

使用静态导入的继承静态方法时,我遇到了一些奇怪的行为:

COM /示例/ UTIL / BaseUtil.java:

package com.example.util;

/*default*/ class BaseUtil {
    public static final void foo(){ System.out.println("foo"); }
}

COM /示例/ UTIL / Util.java:

package com.example.util;

public final class Util extends BaseUtil{
    public static void bar(){ System.out.println("bar"); }
    //foo() will be inherited
}

COM /示例/ UtilTest.java

package com.example;

import static com.example.util.Util.bar;
import static com.example.util.Util.foo;

public class UtilTest {
    public static void main(String[] args) {
        bar();
        foo();
    }
}

运行UtilTest会导致未经检查的异常!

  

线程“main”中的异常java.lang.IllegalAccessError:尝试从类com.example.UtilTest访问类com.example.util.BaseUtil

    at com.example.UtilTest.main(UtilTest.java:15)

但是,如果我通过Util引用方法(没有静态导入),一切都按预期工作:

COM /示例/ UtilTest.java

package com.example;

import com.example.util.Util;

public class UtilTest {
    public static void main(String[] args) {
        Util.bar();
        Util.foo();
    }
}

那么,是什么给出了?

3 个答案:

答案 0 :(得分:5)

/*default*/ class BaseUtil { //only visible within the package com/example/util

该类具有defualt访问说明符,使其从该包外部不可见。

你需要公开它。

<强>更新

以下是反编译的样子:

public class com.example.UtilTest extends java.lang.Object{
public com.example.UtilTest();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   invokestatic    #16; //Method com/example/util/Util.bar:()V
   3:   invokestatic    #21; //Method com/example/util/BaseUtil.foo:()V
   6:   return

}

以下是我使用JD GUI获得的内容

package com.example;

import com.example.util.BaseUtil;
import com.example.util.Util;

public class UtilTest
{
  public static void main(String[] args)
  {
    Util.bar();
    BaseUtil.foo();
  }
}

当然不会编译。

在这里看起来像编译器中的漏洞(可能是由于静态导入)。

答案 1 :(得分:1)

不完全是答案,但在导入静态函数时还需要考虑其他事项。

当您使用静态函数/常量时,它们有时可以在线编译。这取决于您使用的编译器。我不记得我的头顶是哪一个。

当您从外部库导入静态变量/函数,然后在运行时升级该库时,这是一个问题,您的代码仍将具有OLD静态函数。

我的建议是完全避免使用静态函数,而是使用单例对象。 使用诸如spring之类的框架在运行时将单例注入到类中。

最好将此对象设为final,并使用构造函数进行设置。

这也使得测试变得更容易,因为你可以模拟单例。

答案 2 :(得分:0)

您的代码的第一个版本有效地编译为

package com.example;

public class UtilTest {
    public static void main(String[] args) {
        com.example.util.Util.bar();
        com.example.util.BaseUtil.foo();
    }
}

并且由于BaseUtil具有包范围,因此您无法从其他包中调用它并且会出现异常。

您的代码的第二个版本有效地编译为

package com.example;

public class UtilTest {
    public static void main(String[] args) {
        com.example.util.Util.bar();
        com.example.util.Util.foo();
    }
}

由于Util是公共类,因此您可以访问包括foo在内的所有方法,因为[{1}}对于Util来说是可见的。

令我困惑的是为什么任何版本都能正确编译?