Java Applet - 无法继承 final class
Java Applet - Cannot inherit from final class
我们有一个 java 小程序,它在大多数客户端环境中工作正常,主要是 Windows 7,但最近我们被要求也支持 Ubuntu 客户端。
问题是当小程序在 Ubuntu 客户端(运行 Firefox 和本机安装的 "IcedTEA" Java VM 1.7.0_75) 我们得到这个异常:
java.lang.VerifyError: Cannot inherit from final class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access0(URLClassLoader.java:71)
at java.net.URLClassLoader.run(URLClassLoader.java:361)
at java.net.URLClassLoader.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.access01(JNLPClassLoader.java:103)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.run(JNLPClassLoader.java:1636)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.run(JNLPClassLoader.java:1634)
at java.security.AccessController.doPrivileged(Native Method)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.findClass(JNLPClassLoader.java:1633)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.loadClassExt(JNLPClassLoader.java:1670)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.loadClass(JNLPClassLoader.java:1471)
at com.renosci.Nlx.chartapplet.NlxBrowserJsEngine.<init>(NlxBrowserJsEngine.java:46)
at com.renosci.Nlx.chartapplet.UtilityApplet.init(UtilityApplet.java:87)
at sun.applet.AppletPanel.run(AppletPanel.java:436)
at java.lang.Thread.run(Thread.java:745)
我们在 Windows 下没有得到这个异常(诚然,不同的 JVM 构建和 Windows 我们正在使用
Oracle 提供的 VM 而不是此 IcedTea 版本)。
我理解异常的含义 - 并且快速 google 搜索发现多个 SO 问题,主要归结为构建路径与 class 不同的建议路径,这样在编译时基 class 不是最终的,但在运行时 classloader 发现它是最终的。
但是,我不明白怎么会这样:
引发错误的 class 是从作为我们代码库一部分的抽象基础 class 扩展而来的,
根据我们的版本控制系统,从来没有最终版本
我正在测试的目标机器上不可能有这个 class 的任何其他版本 - 事实上我为这个测试创建了一个新的 VM 并获得了问题马上
我假设 class 它在调用 loadClass 之前立即抱怨 NlxBrowserJsEngine,运行 - 对吗?这个 class 的基数 class 是它认为最终的那个吗?
为了完整性 - 这里是 class 的声明(这是异常抱怨的第 46 行)和它仅有的两个字段的声明:
public class NlxBrowserJsEngine extends NlxJsEngine { /* Line 46 */
private JSObject windowObj;
static private Object evalLock = new Object();
JSObject 是 netscape.javascript.JSObject,由 Java 浏览器插件提供。
这里是基础的声明class:
public abstract class NlxJsEngine {
感谢您的任何见解!
我终于找到了原因。
如@immibis 的评论所示,异常发生在 class 初始化期间,而不是在实例构造期间。
class NlxBrowserJsEngine 的一个方法调用了另一个 class 的静态方法 - 这个 class 扩展自 netscape.javascript.JSObject.
在 oracle implementation 中,此 JSObject class 未声明为最终的,因此扩展它可以正常工作。
在 IcedTea 实现中,JSObject 是最终的。因此,在 IcedTea JVM 插件中加载此 class 时,会抛出异常。
我不确定这种不兼容性是代表 IcedTea 实施者的疏忽,还是故意造成的(也许是出于安全考虑?)。无论哪种方式都给我们带来了很大的问题。
通过大量重构本来可以克服这个问题,但在我们的案例中,这被认为是不值得的,因此我们决定要求我们的用户安装 Oracle JVM。
我们有一个 java 小程序,它在大多数客户端环境中工作正常,主要是 Windows 7,但最近我们被要求也支持 Ubuntu 客户端。
问题是当小程序在 Ubuntu 客户端(运行 Firefox 和本机安装的 "IcedTEA" Java VM 1.7.0_75) 我们得到这个异常:
java.lang.VerifyError: Cannot inherit from final class
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access0(URLClassLoader.java:71)
at java.net.URLClassLoader.run(URLClassLoader.java:361)
at java.net.URLClassLoader.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.access01(JNLPClassLoader.java:103)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.run(JNLPClassLoader.java:1636)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.run(JNLPClassLoader.java:1634)
at java.security.AccessController.doPrivileged(Native Method)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.findClass(JNLPClassLoader.java:1633)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.loadClassExt(JNLPClassLoader.java:1670)
at net.sourceforge.jnlp.runtime.JNLPClassLoader.loadClass(JNLPClassLoader.java:1471)
at com.renosci.Nlx.chartapplet.NlxBrowserJsEngine.<init>(NlxBrowserJsEngine.java:46)
at com.renosci.Nlx.chartapplet.UtilityApplet.init(UtilityApplet.java:87)
at sun.applet.AppletPanel.run(AppletPanel.java:436)
at java.lang.Thread.run(Thread.java:745)
我们在 Windows 下没有得到这个异常(诚然,不同的 JVM 构建和 Windows 我们正在使用 Oracle 提供的 VM 而不是此 IcedTea 版本)。
我理解异常的含义 - 并且快速 google 搜索发现多个 SO 问题,主要归结为构建路径与 class 不同的建议路径,这样在编译时基 class 不是最终的,但在运行时 classloader 发现它是最终的。
但是,我不明白怎么会这样:
引发错误的 class 是从作为我们代码库一部分的抽象基础 class 扩展而来的, 根据我们的版本控制系统,从来没有最终版本
我正在测试的目标机器上不可能有这个 class 的任何其他版本 - 事实上我为这个测试创建了一个新的 VM 并获得了问题马上
我假设 class 它在调用 loadClass 之前立即抱怨 NlxBrowserJsEngine,运行 - 对吗?这个 class 的基数 class 是它认为最终的那个吗?
为了完整性 - 这里是 class 的声明(这是异常抱怨的第 46 行)和它仅有的两个字段的声明:
public class NlxBrowserJsEngine extends NlxJsEngine { /* Line 46 */
private JSObject windowObj;
static private Object evalLock = new Object();
JSObject 是 netscape.javascript.JSObject,由 Java 浏览器插件提供。
这里是基础的声明class:
public abstract class NlxJsEngine {
感谢您的任何见解!
我终于找到了原因。
如@immibis 的评论所示,异常发生在 class 初始化期间,而不是在实例构造期间。
class NlxBrowserJsEngine 的一个方法调用了另一个 class 的静态方法 - 这个 class 扩展自 netscape.javascript.JSObject.
在 oracle implementation 中,此 JSObject class 未声明为最终的,因此扩展它可以正常工作。
在 IcedTea 实现中,JSObject 是最终的。因此,在 IcedTea JVM 插件中加载此 class 时,会抛出异常。
我不确定这种不兼容性是代表 IcedTea 实施者的疏忽,还是故意造成的(也许是出于安全考虑?)。无论哪种方式都给我们带来了很大的问题。
通过大量重构本来可以克服这个问题,但在我们的案例中,这被认为是不值得的,因此我们决定要求我们的用户安装 Oracle JVM。