使用静态导入时未选中的异常,怎么来了



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

com/example/util/BaseUtil.java:

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

com/example/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/example/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/example/UtilTest.java

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

那么,什么给了呢?

/*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();
  }
}

这当然不会编译。

看起来像编译器中的一个洞(可能是由于静态导入)。

不完全是答案,而是导入静态函数时要考虑的其他事项。

当您使用静态函数/常量时,它们有时会被内联编译。这取决于您使用的编译器。我不记得是哪些。

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

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

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

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

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

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在内的所有方法,因为foo对Util是可见的。

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

相关内容

  • 没有找到相关文章

最新更新