在JDK6,升级为JDK7或者JDK8的过程中会遇到一些奇怪的问题,简单的介绍一下经典的ClassNotFound:sun.nio.*、com.sun.image.codec.jpeg.*等。
背景简介
很多项目使用在JDK6升级到JDK7或者JDK8的过程中,会遇到一些问题。
本文主要介绍一下,在升级过程中,JDK的部分类在Android Studio、Eclipse(以下概称IDE,不包括神器NetBeans)的编译过程中没有提示。
但是使用Maven、Ant、Gradle(这三个工具在下文中概称为打包工具)打包的过程中,会出现错误‘ClassNotFound’、‘程序包sun.net.sdp不存在’。
例如:
- com.sun.image.codec.jpeg.JPEGCodec
- com.sun.image.codec.jpeg.JPEGEncodeParam
- jdk.internal.org.objectweb.asm.commons.AdviceAdapter
- sun.net.sdp.SdpSupport
示例
JDK6可以编译通过、JDK7和JDK8在IDE中可以编译通过,但是使用打包工具,则编译不通过。
1 2 3 4
| import com.sun.image.codec.jpeg.JPEGCodec; public class TestCtSymJdk6Pass { private JPEGCodec jpegCodec; }
|
编译尝试
jdk1.6/bin/javac TestCtSymJdk6Pass.java.java
通过,但是会有两个警告。
1 2 3 4 5 6 7
| TestCtSymJdk6Pass.java:1: 警告:com.sun.image.codec.jpeg.JPEGCodec 是 Sun 的专用 API,可能会在未来版本中删除 import com.sun.image.codec.jpeg.JPEGCodec; ^ TestCtSymJdk6Pass.java:3: 警告:com.sun.image.codec.jpeg.JPEGCodec 是 Sun 的专用 API,可能会在未来版本中删除 private JPEGCodec jpegCodec; ^ 2 警告
|
jdk1.7/bin/javac TestCtSymJdk6Pass.java
jdk1.8/bin/javac TestCtSymJdk6Pass.java
不通过,两个错误
1 2 3 4 5 6 7 8 9
| TestCtSymJdk6Pass.java:1: 错误: 程序包com.sun.image.codec.jpeg不存在 import com.sun.image.codec.jpeg.JPEGCodec; ^ TestCtSymJdk6Pass.java:3: 错误: 找不到符号 private JPEGCodec jpegCodec; ^ 符号: 类 JPEGCodec 位置: 类 TestCtSymJdk6 2 个错误
|
JDK6-8都编译不通过
1 2 3 4 5 6
| import com.sun.image.codec.jpeg.JPEGCodec; import sun.net.sdp.SdpSupport; public class TestCtSymJdk6NotPass { private JPEGCodec jpegCodec; private SdpSupport sdpSupport; }
|
JDK6报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| TestCtSymJdk6NotPass.java:1: 警告:com.sun.image.codec.jpeg.JPEGCodec 是 Sun 的专用 API,可能会在未来版本中删除 import com.sun.image.codec.jpeg.JPEGCodec; ^ TestCtSymJdk6NotPass.java:2: 软件包 sun.net.sdp 不存在 import sun.net.sdp.SdpSupport; ^ TestCtSymJdk6NotPass.java:4: 警告:com.sun.image.codec.jpeg.JPEGCodec 是 Sun 的专用 API,可能会在未来版本中删除 private JPEGCodec jpegCodec; ^ TestCtSymJdk6NotPass.java:5: 找不到符号 符号: 类 SdpSupport 位置: 类 TestCtSymJdk6NotPass private SdpSupport sdpSupport; ^ 2 错误 2 警告
|
添加编译参数:忽略链接文件
jdk1.6/bin/javac -XDignore.symbol.file TestCtSymJdk6NotPass.java
jdk1.7/bin/javac -XDignore.symbol.file TestCtSymJdk6Pass.java
jdk1.8/bin/javac -XDignore.symbol.file TestCtSymJdk6Pass.java
以上三条命令都可以正常执行。
原因
在JDK6以及以后的版本,JDK在目录下新增了一个链接文件${JDK_HOME}/lib/ct.sym文件。
在使用javac命令进行编译代码时,默认使用该文件进行编译时class类的检查和链接,而不是使用rt.jar。
该文件保存了JDK建议使用的类描述信息。com.sun.*包和sun.*包,以及新的jdk.*都不是Open的API,是JDK内部的私有类,这些类的接口可能在之后的版本变动,也不保证平台移植性。
事实上,JDK提供的Public API,仅有三个包:java.*、javax.*、org.*。它们是官方支持的公共接口(Official、Supported、Public )。
ct.sym文件是一个zip压缩包,它里面包含了部分rt.jar中的类。
ct.sym中的类文件都是简单的空函数,不包含函数体,所以非常小。
ct.sym中如果没有该类,则会出现ClassNotFound的错误。
比如JDK6中的sun.net.sdp.SdpSupport类。在ct.sym中就没有sun.net.sdp包。
比如JDK7中的com.sun.image.codec.jpeg.JPEGCodec类。
解决方案
方案-1 【建议】
使用JDK开放的接口实现这部分功能。
方案-2 【临时方案】
在编译的时候加入参数-XDignore.symbol.file.
Maven
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.6.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> <compilerArgs> <arg>-XDignore.symbol.file</arg> </compilerArgs> <fork>true</fork> </configuration> </plugin> </plugins> </build>
|
Ant
1 2 3 4 5 6 7 8 9 10 11
| <target name="compile"> <javac srcdir="src" destdir="bin" encoding="UTF-8" source="1.8" target="1.8"> <compilerarg value="-XDignore.symbol.file"/> <classpath refid="classpath" /> </javac> </target>
|
Gradle
1
| compileJava.options.compilerArgs = [ '-XDignore.symbol.file=true' ]
|
方案-3 【不建议】
非常粗暴!直接删除ct.sym文件。
PS
PS
神器-NetBeans在IDE中就会提示该错误。