由于无法加载主类错误,无法运行JAR文件。

5

我使用Gradle构建框架构建了我的项目并生成了一个JAR文件。但是,在这种情况下,输出的jar文件无法加载主类(miner.Tracker)中的主要方法。

正如我之前提到的,使用-jar选项运行失败。

$ java -jar Backtracker.jar
Error: Could not find or load main class miner.Tracker

我也尝试使用-cp选项直接运行类,但是失败了。

$ java -cp Backtracker.jar miner.Tracker
Error: Could not find or load main class miner.Tracker

最后,我解压了 jar 文件并从内部调用了类。这一次,成功找到并运行了带有 main 方法的类。
$ mkdir classes
$ cd classes
$ classes $ tar xvf ../Backtracker.jar
x META-INF/
x META-INF/MANIFEST.MF
x CHANGELOG.md
x com/
1. ...

classes $ java miner.Tracker
2021-04-12 22:47:48.008 | Logging started
...

以下是META-INF/MANIFEST.MF文件的内容。
Manifest-Version: 1.0
Implementation-Title: BackTracker
Implementation-Version: 1.9.xx
Specification-Title: release
Specification-Version: 1.9.xx
Main-Class: miner.Tracker

我使用的是 Oracle Java 1.8 版本来运行它。

$ java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)

感谢您的帮助。

更新:

$ tar tvf Backtracker.jar |grep miner\/Tracker

-rw-r--r-- 0 0 0  2175 Apr 14 20:38 miner/TrackerUtil.class
-rw-r--r-- 0 0 0 40963 Apr 14 20:38 miner/Tracker.class

1
你的包名不完整,能否发布你的应用程序结构? - code_mechanic
1
如果您分享JAR文件,那将会更容易。 - Olivier
1
认真的吗?你分享了所有这些信息,却省略了“...”后面真正重要的部分,也就是你的目录结构中主类在哪里?恕我直言:这是某种谜题或测验吗?为什么要让每个人都更难帮助你呢? - kriegaex
关于检查与目录结构不符的包名的建议,我在这里重现了这种情况,Java 9+ 应该会报告类似于“原因:java.lang.NoClassDefFoundError: minerxxx/Tracker (wrong name: miner/Tracker)”的错误信息,这是在“找不到或加载主类”后出现的。但是 Java 8 不会报告任何错误原因,所以如果您在 Java 8 上运行,除非您检查源文件中的包名,否则您可能永远都不会注意到这一点。实际上,如果这是问题,即使解压缩也不应该正常工作。进行更多的推测的唯一方法是检查您真正的 JAR 文件。 - kriegaex
1
“Jar文件包含数万个类文件”,为什么不制作一个简单的测试jar,其中只有一个名为miner.Tracker的类,它只打印“Hello World”?你不觉得这样更容易进行故障排除吗? - Olivier
显示剩余9条评论
1个回答

5

经过一些实验,我同意用户11thdimension的看法,你的清单文件可能有问题。虽然我不认为这与非数字版本有关,但是我试过了,它能运行。

我发现,如果你添加了一行包含多于普通空格(字符32 dec,20 hex)的内容,你将会看到你所描述的错误,因为清单无法解析。显然,清单解析器期望每个非空行都像MyKey: MyValue那样。即使在中间的空行也可能导致相同的问题,如果它们在所谓的“段落”中。因此,如果你有以下任何一种清单,你将会看到一个ClassNotFoundException,无论你使用-cp还是-jar

Manifest-Version: 1.0
Main-Class: miner.Tracker
x

Manifest-Version: 1.0

Main-Class: miner.Tracker

即使在最后一行之后,您有一行仅包含制表符的字符,也会发生错误:
Manifest-Version: 1.0
Main-Class: miner.Tracker
    

相同的情况也适用于您使用其他不可见字符(例如非标准的Unicode空格)的行。

正如我在先前的评论中所说:在清单中的任何位置检查此类字符。如果没有结果,请还要检查您的Java源代码文件,特别是包和类名。


更新:这是一个简单的Java示例,展示了我之前所解释的内容:

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.jar.Manifest;

public class ManifestValidator {
  public static void main(String[] args) {
    // Valid: nothing unusual
    parseManifest("Manifest-Version: 1.0\nMain-Class: miner.Tracker\n");
    // Valid: trailing empty line
    parseManifest("Manifest-Version: 1.0\nMain-Class: miner.Tracker\n\n");
    // Valid: line continuation by leading space
    parseManifest("Manifest-Version: 1.0\nMain-Class: miner.Track\n er\n");
    // Valid: last line is ignored if it does not end with a line feed
    parseManifest("Manifest-Version: 1.0\nMain-Class: miner.Tracker\nSome garbage");

    // Invalid: line beginning with tab
    parseManifest("Manifest-Version: 1.0\nMain-Class: miner.Tracker\n\t\n");
    // Invalid: not a key-value pair "key: value"
    parseManifest("Manifest-Version: 1.0\nMain-Class: miner.Tracker\nFoo=bar\n");
    // Invalid: empty line in section
    parseManifest("Manifest-Version: 1.0\n\nMain-Class: miner.Tracker\n");
    // Invalid: non-default unicode space instead of normal one
    parseManifest("Manifest-Version: 1.0\nMain-Class:\u00A0miner.Tracker\n");
  }

  public static void parseManifest(String content) {
    try {
      Manifest manifest = new Manifest(new ByteArrayInputStream(content.getBytes()));
      System.out.println(manifest.getMainAttributes().entrySet());
    }
    catch (IOException e) {
      System.out.println(e);
    }
  }
}

[Manifest-Version=1.0, Main-Class=miner.Tracker]
[Manifest-Version=1.0, Main-Class=miner.Tracker]
[Manifest-Version=1.0, Main-Class=miner.Tracker]
[Manifest-Version=1.0, Main-Class=miner.Tracker]
java.io.IOException: invalid header field
java.io.IOException: invalid header field
java.io.IOException: invalid manifest format
java.io.IOException: invalid header field

1
更新:我添加了一些示例代码,使用相应的JDK类解析有效和无效的清单。 - kriegaex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接