java 调用 JavaCompiler 动态编译时,在 windows 上正常,linux 上 ClassNotFoundException 找不到依赖可能的原因
在写一个动态编译 java 项目代码并运行时,windows 系统上正常开发测试正常,部署云服务器后出现异常,通过查找日志出现 ClassNotFoundException 找不到依赖的问题。最终定位到是因为调用 JavaCompiler 时 windows/linux 传参依赖参数有不同分隔符限制导致的。冷门小问题
java.lang.ClassNotFoundException: com.example.SomeClass
at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:466)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at com.example.Main.main(Main.java:5)
windows
java -cp "lib\some-library.jar;lib\another-library.jar" com.example.Main
linux/macos
java -cp "lib/some-library.jar:lib/another-library.jar" com.example.Main
解决
看出来了吗?一个历史遗留问题,其实官方也提供了标准解决方式,只是平时不做此类需求不会注意到。使用 File.pathSeparator 代替,重新打包上线即可
File.pathSeparator
关于 java 分隔符的历史背景
Java中在不同操作系统使用不同的类路径分隔符(Windows 使用分号 ;,Linux/macOS 使用冒号 :)。这种差异的背后有一段历史背景,主要与早期操作系统的文件路径规则和Java的跨平台目标有关
-
不同操作系统的文件路径分隔符:
- Windows 使用反斜杠
\作为文件路径分隔符(例如:C:\libs\library.jar)。 - Unix 系统(包括 Linux 和 macOS)使用正斜杠
/作为文件路径分隔符(例如:/usr/libs/library.jar)。
为了保持类路径的通用性,Java 在不同操作系统之间就类路径的分隔符做了妥协,采用了不同的字符:
:在 Unix 系统中,;在 Windows 系统中。 - Windows 使用反斜杠
-
Java的跨平台设计理念: Java 的初衷之一就是“一次编写,到处运行”(Write Once, Run Anywhere),即应用程序能够在任何平台上运行。因此,Java 试图通过提供统一的编程接口来隐藏底层操作系统的差异。然而,在早期设计时,操作系统的文件路径分隔符差异未被统一考虑到,导致了类路径分隔符的差异。
-
与操作系统设计的兼容性: 在 Windows 上,
:字符通常用于区分磁盘驱动器(如C:),因此 Windows 在路径分隔符上选择了;,以避免与路径本身发生冲突。而在 Unix 系统中,:字符本身不用于路径表示,因此采用了:来分隔多个类路径。
为什么没有统一?
由于 Java 的设计哲学是跨平台的,它不能在不同操作系统之间做出过多的假设。不同平台使用不同的分隔符,这就意味着 Java 在设计时必须适应这些差异。即使随着时间的推移,这些差异看起来似乎有些冗余或不必要,但它们保持了与传统的兼容性。
此外,统一分隔符可能会影响现有的代码、工具和环境配置,因此保持现状被认为是一种相对安全且不破坏现有生态的做法。
现代工具的影响:
尽管 Java 保持了这种操作系统差异,但现代的构建工具(如 Maven、Gradle)已经在内部封装了 classpath 的处理,自动根据操作系统选择正确的路径分隔符。开发者通常不需要手动处理这些差异。举例来说,Maven 会自动为你设置好正确的类路径,并在构建时处理分隔符问题。
作者:https://blog.xn--rpv331d.com/望舒
链接:https://blog.xn--rpv331d.com/望舒/blog/137
转载请保留文章出处...
