java - 在非 MACOS JRE 上捕获 java.lang.NoClassDefFoundError

标签 java swing java-8 macos-sierra

我正在使用 java swing 开发一个简单的应用程序,我想在 Windows、MacOS 和 Linux 中使用它。 当然,我正在尽力将其与操作系统集成。

对于 MacOS,我有这段代码,允许我在全局菜单中设置应用程序名称以及“关于”按钮的操作。

我使用以下代码:

  if(System.getProperty("os.name").toUpperCase().startsWith("MAC")){
        System.setProperty("com.apple.mrj.application.apple.menu.about.name", "My app name");
        System.setProperty("apple.awt.application.name", "My app name");
        //Need for macos global menubar
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        try{
        com.apple.eawt.Application app = com.apple.eawt.Application.getApplication();
        app.setDockIconImage(Toolkit.getDefaultToolkit().getImage(MainGUI.class.getResource("images/icon.png")));
        app.setAboutHandler(new com.apple.eawt.AboutHandler() {
            @Override
            public void handleAbout(com.apple.eawt.AppEvent.AboutEvent aboutEvent) {
                AboutDialog a = new AboutDialog();
                a.setTitle("About");
                a.pack();
                a.setResizable(false);
                centerDialogInScreen(a);
                a.setVisible(true);
            }
        });
        } catch (Throwable e){
            //This means that the application is not being run on MAC OS.
            //Just do nothing and go on...
        }
    }

当我在非 macos JAVA 上运行我的应用程序时,因为它的 JRE 没有 com.apple.eawt.* 类,JVM 应该抛出一个 NoDefClassFoundError,我正在捕获它并继续,对吗?

它似乎没有这样做,当我启动我的应用程序“.jar”时,我得到以下内容(在 Windows 上):

Error: A JNI error has occurred, please check your installation and  try again
Exception in thread "main" java.lang.NoClassDefFoundError:  com/apple/eawt/AboutHandler
       at java.lang.Class.getDeclaredMethods0(Native Method)
       at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
       at java.lang.Class.privateGetMethodRecursive(Unknown Source)
       at java.lang.Class.getMethod0(Unknown Source)
       at java.lang.Class.getMethod(Unknown Source)
       at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
       at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.apple.eawt.AboutHandler
       at java.net.URLClassLoader.findClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
       at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
       at java.lang.ClassLoader.loadClass(Unknown Source)
       ... 7 more

我在这里缺少什么?

最佳答案

抛出 NoClassDefFoundError 的确切时间并不固定,但可能取决于 JVM 实现细节。就您而言,这是对类(class)的验证。在 HotSpot 中, validator 不会加载所有引用的类,但显然它尝试在您的情况下加载 AboutHandler ,这可能是由于您的类有一个实现 AboutHandler 的内部类造成的。 code> 和 Verifier 想要检查类型层次结构的一致性,即 AboutHandler 是否真的是一个接口(interface)。确切的细节并不那么重要,因为即使您设法解决它,结果也会很脆弱,并且可能会在其他版本或替代 JVM 实现中崩溃。

如果您想安全起见,则不得直接引用可能不存在的类。因此,您可以使用 Class.forName、Method.invoke 等以反射方式完成整个操作,但这会使任何重要的代码变得麻烦。更简单的解决方案是将整个 MacOS 特定代码放入其自己的类中,例如 MacSetup。然后,您只需通过 Class.forName 加载此类(仅在检查您正在 MacOS 上运行之后)即可将其分离。

public class MacSetup implements Runnable {
    @Override
    public void run() {
        System.setProperty("com.apple.mrj.application.apple.menu.about.name", "My app name");
        System.setProperty("apple.awt.application.name", "My app name");
        //Need for macos global menubar
        System.setProperty("apple.laf.useScreenMenuBar", "true");
        com.apple.eawt.Application app = com.apple.eawt.Application.getApplication();
        app.setDockIconImage(Toolkit.getDefaultToolkit().getImage(MainGUI.class.getResource("images/icon.png")));
        app.setAboutHandler(new com.apple.eawt.AboutHandler() {
            @Override
            public void handleAbout(com.apple.eawt.AppEvent.AboutEvent aboutEvent) {
                AboutDialog a = new AboutDialog();
                a.setTitle("About");
                a.pack();
                a.setResizable(false);
                centerDialogInScreen(a);
                a.setVisible(true);
            }
        });
    }
}

主类:

if(System.getProperty("os.name").toUpperCase().startsWith("MAC")) {
    try {
        Class.forName("MacSetup").asSubclass(Runnable.class).newInstance().run();
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
        // since this code is only executed when we *are* on MacOS, we should report it
        Logger.getLogger("MacSetup").log(Level.SEVERE, null, ex);
    }
}

因此,主类与 MacSetup 类及其所有引用分离,因为它以反射方式加载该类,并通过始终存在的 Runnable 调用其实现方法接口(interface),将反射操作减少到必要的最低限度。

关于java - 在非 MACOS JRE 上捕获 java.lang.NoClassDefFoundError,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42818440/

相关文章:

java - 某些 Java 8 功能可以在 Android SDK 23 中使用,而其他功能则不能?

java - 来自 .NET Webservice 的压缩数组字节 Java

java - 使用单选按钮组在 Android Studio 中打开新 Activity

java - 具有多个参数的 MapStruct QualifiedByName

Java 持久键值存储

java - Apache 的 HttpClient 在 Swing 应用程序中睡着了

java - 贪吃蛇游戏,开始

java - TextField 不符合条件

java - 在 JRE 8 中使用 JavaFX, “Access restriction” 错误

lambda - 如何获取由 lambda 产生的方法的名称