我在使用继承的静态方法的静态导入时遇到了一些奇怪的行为:
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是可见的。
让我感到困惑的是为什么任何版本都能正确编译?