BaiFan
文章目录
  1. 1. 背景简介
  2. 2. 示例
    1. 2.1. 编译尝试
  3. 3. 原因
  4. 4. 解决方案
    1. 4.1. 方案-1 【建议】
    2. 4.2. 方案-2 【临时方案】
    3. 4.3. 方案-3 【不建议】
  • PS
  • 在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中可以编译通过,但是使用打包工具,则编译不通过。

    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中的类文件都是简单的空函数,不包含函数体,所以非常小。
    JPEGCodec反编译

    ct.sym中如果没有该类,则会出现ClassNotFound的错误。
    比如JDK6中的sun.net.sdp.SdpSupport类。在ct.sym中就没有sun.net.sdp包。
    sun.net.sdp
    比如JDK7中的com.sun.image.codec.jpeg.JPEGCodec类。
    com.sun.image.codec.jpeg

    解决方案

    方案-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中就会提示该错误。

    文章目录
    1. 1. 背景简介
    2. 2. 示例
      1. 2.1. 编译尝试
    3. 3. 原因
    4. 4. 解决方案
      1. 4.1. 方案-1 【建议】
      2. 4.2. 方案-2 【临时方案】
      3. 4.3. 方案-3 【不建议】
  • PS