Javac命令使用ct.sym文件约束可使用的类

2018-08-07 17:00:08 浏览数 (1)

在JDK6,升级为JDK7或者JDK8的过程中会遇到一些奇怪的问题,简单的介绍一下经典的ClassNotFoundsun.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中可以编译通过,但是使用打包工具,则编译不通过。

代码语言:javascript复制
import com.sun.image.codec.jpeg.JPEGCodec;
public class TestCtSymJdk6Pass {
    private JPEGCodec jpegCodec;
}
编译尝试

jdk1.6/bin/javac TestCtSymJdk6Pass.java.java

通过,但是会有两个警告。

代码语言:javascript复制
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

不通过,两个错误

代码语言:javascript复制
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都编译不通过

代码语言:javascript复制
import com.sun.image.codec.jpeg.JPEGCodec;
import sun.net.sdp.SdpSupport;
public class TestCtSymJdk6NotPass {
    private JPEGCodec jpegCodec;
    private SdpSupport sdpSupport;
}

JDK6报错

代码语言:javascript复制
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

代码语言:javascript复制
<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

代码语言:javascript复制
<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

代码语言:javascript复制
compileJava.options.compilerArgs = [ '-XDignore.symbol.file=true' ]
方案-3 【不建议】

非常粗暴!直接删除ct.sym文件。

PS

PS

神器-NetBeans在IDE中就会提示该错误。

0 人点赞