当我运行下面的源代码时,我在shell窗口中看到的运行时错误如下:
Exception in thread "main" java.lang.IllegalAccessError: tried to access method
pkgs.test.B.mStaPro()V from class pkgs.main.Main3
at pkgs.main.Main3.m6(Main3.java:919)
at pkgs.main.Main3.main(Main3.java:9)
Run Code Online (Sandbox Code Playgroud)
在上面的mStaPro()之后看到字母V是什么意思?
这是我的源代码,全部编译:
class Main3,包main:
package pkgs.main;
import pkgs.test.B;
class Main3 {
static public void main(String args[]) {
new Main3().m6();
}
void m6() {
B.mStaPro();
}
}
Run Code Online (Sandbox Code Playgroud)
A类,包主:
package pkgs.main;
public class A {
static protected void mStaPro() { System.out.println("A mStaPro()"); }
}
Run Code Online (Sandbox Code Playgroud)
B级,包装测试:
package pkgs.test;
import pkgs.main.A;
public class B extends A {
// Note: if this line below is commented out, then the runtime exception
// mentioned in this post's title is not seen.
static protected void mStaPro() { System.out.println("B mStaPro()"); }
}
Run Code Online (Sandbox Code Playgroud)
以下是基于shell的编译和运行批处理文件的内容:
REM For compilation:
javac -Xlint -sourcepath ..\src -d ..\cls ..\src\pkgs\main\Main3.java
REM For running:
java -cp ..\cls pkgs.main.Main3
Run Code Online (Sandbox Code Playgroud)
请注意我在B课中发表的评论.非常感谢任何评论.
编辑:
我尝试使用Apache Ant构建我的源代码,但获得的结果是相同的:
run:
[java] Exception in thread "main" java.lang.IllegalAccessError: tried to access method pkgs.test.B.mStaPro()V from class pkgs.main.Main3
[java] at pkgs.main.Main3.m6(Main3.java:11)
[java] at pkgs.main.Main3.main(Main3.java:7)
[java] Java Result: 1
main:
BUILD SUCCESSFUL
Run Code Online (Sandbox Code Playgroud)
请注意,在本文顶部的第一个异常错误消息中,我在源代码中注释了很多代码,因此该错误消息和上面的Ant错误消息之间的行号不同.
我尝试过的另一件事是将JDK从版本1.7.0_40升级到版本1.7.0_55.
编辑2:
这是我的Apache Ant build.xml文件.它与Apache Ant网站上提供的教程build.xml文件几乎完全相同:
<project name="Main3 test" basedir="." default="main">
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="main-class" value="pkgs.main.Main3"/>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}"
includeantruntime="false" debug="true"
debuglevel="lines,vars,source" />
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar"
basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
</target>
<target name="clean-build" depends="clean,jar"/>
<target name="main" depends="clean,run"/>
</project>
Run Code Online (Sandbox Code Playgroud)
Java类通常在三个阶段验证(粗略地说):
大多数编程错误都是由Java编译器捕获的.然而,Java编译器是由你的欺骗方法遮蔽(隐藏)中的pkgs.main.A#mStaPro
该方法pkgs.test.B#mStaPro
的方法.这是发生的事情:
当您编译Main
的类,要调用的static
方法mStaPro
上B
.问题是,您是在调用pkgs.main.A
类中定义的阴影方法还是在类中定义的隐藏方法pkgs.test.B
?在这个问题中,Java编译器和Java运行时得出了不同的结论,这就是为什么Java编译器在Java运行时拒绝它时批准你的代码的原因:
mStaPro
定义的方法A
.该A
班住在同一个包Main
,使得它的protected
方法被认为是可见的Main
类.mStaPro
定义的方法B
.此方法也是如此,protected
但它在pkgs.test
包中定义.因此,mStaPro
不得Main
在不同的包中生活.为了通知您有关此非法代码的信息,Java运行时会抛出IllegalAccessError
您遇到的内容.让我们在这里更进一步.如果查看生成的Java字节代码,该Main#m
方法编译如下:
invokestatic #5 // Method pkgs/test/B.mStaPro:()V
return
Run Code Online (Sandbox Code Playgroud)
什么类似于您的Java源代码:
B.mStaPro()
return // a void return statement is implicit in Java source code
Run Code Online (Sandbox Code Playgroud)
此编译结果与B
类是否定义方法mStaPro
无关.这就是您遇到异常的原因.在这种情况下B
类定义的方法mStaPro
,将invokestatic
调用绑定到B#mStaPro
方法(什么是非法的).否则,调用绑定到A#mStaPro
(什么是合法的).
要解决此问题,您应该命名实际的目标类,A.mStaPro()
而不是调用方法B
.但是,我必须诚实地说,我发现Java编译器的行为与直觉相反.一种static
方法是静态的而不是动态的.因此,Java编译器应该在编译时静态绑定B.mStaPro()
to 的调用pkgs/main/A.mStaPro:()V
.实际上,没有这种假设,编译就不会成功.更好的是,当遇到无法访问B
的目标A
并且代码无法成功运行时,它应该只是产生编译错误.
最后,这种行为未成年人暗示可能会在Oracle的给予教程上static
调用其中提到此行为:
被调用的隐藏静态方法的版本取决于它是从超类还是从子类调用的.
然而,由于我上面提到的原因,这还不够好.但是,我多年来一直使用Java,因为阴影不是推荐的做法,我从来没有遇到过这个问题.像这样,你真的找到了一个边缘情况.
信息:我将其发布到Java编译器邮件列表中.得到答案后,我会将信息添加到此帖子中.