4

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

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导致未经检查的异常!

线程“主”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();
    }
}

那么,什么给了?

4

3 回答 3

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

该类具有默认的访问说明符,这使得它在该包之外不可见。

您需要将其公开。

更新

以下是反编译的样子:

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

这当然不会编译。

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

于 2012-11-15T23:41:10.757 回答
1

不完全是答案,但在导入静态函数时需要考虑其他一些问题。

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

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

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

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

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

于 2012-11-16T00:33:45.457 回答
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, 因为foo对 Util 可见。

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

于 2012-11-15T23:55:47.633 回答