0%

3个JDK最好一致

3个JDK(源码所使用JDK,目标运行平台JDK,编译所使用JDK)最好一致,以避免一些奇怪的异常或者错误。

一、3个JDK不一致导致抛出NoClassDefFoundError异常的一种情形

实验代码如下:

import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {

    ConcurrentHashMap<String, AtomicInteger> domainCounts = new ConcurrentHashMap();

    public static void main(String[] args) {
        Main main = new Main();
        main.f();
    }

    public void f() {
        for (Iterator iterator = domainCounts.keySet().iterator(); ((Iterator)iterator).hasNext();) {
            String val = (String)iterator.next();

            System.out.println(val);
        }
    }
}

编译命令为~/jdk1.8/bin/javac -source 1.6 -target 1.6 Main.java,运行命令为~/jdk1.6/bin/java Main,抛出异常如下:

Exception in thread "main" java.lang.NoSuchMethodError: java.util.concurrent.ConcurrentHashMap.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
        at Main.f(Main.java:15)
        at Main.main(Main.java:11)

执行javap -v Main命令,发现f()方法对应的内容如下:

public void f();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=1
         0: aload_0
         1: getfield      #4                  // Field domainCounts:Ljava/util/concurrent/ConcurrentHashMap;
         4: invokevirtual #8                  // Method java/util/concurrent/ConcurrentHashMap.keySet:()Ljava/util/concurrent/ConcurrentHashMap$KeySetView;
         7: invokevirtual #9                  // Method java/util/concurrent/ConcurrentHashMap$KeySetView.iterator:()Ljava/util/Iterator;
        10: astore_1
        11: aload_1
        12: invokeinterface #10,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
        17: ifeq          40
        20: aload_1
        21: invokeinterface #11,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
        26: checkcast     #12                 // class java/lang/String
        29: astore_2
        30: getstatic     #13                 // Field java/lang/System.out:Ljava/io/PrintStream;
        33: aload_2
        34: invokevirtual #14                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        37: goto          11
        40: return
      LineNumberTable:
        line 15: 0
        line 16: 20
        line 18: 30
        line 19: 37
        line 20: 40
      StackMapTable: number_of_entries = 2
        frame_type = 252 /* append */
          offset_delta = 11
          locals = [ class java/util/Iterator ]
        frame_type = 250 /* chop */
          offset_delta = 28

查看上述内容,可发现绑定到了属于JDK1.8的java.util.concurrent.ConcurrentHashMap.keySet()Ljava/util/concurrent/ConcurrentHashMap$KeySetView,从而可知在使用JDK1.8编译的时候,一些特定于JDK1.8的依赖信息被写入到了CLASS文件中(笔者认为这是JDK1.8中javac命令实现的bug,因为我们指定了-target 1.6,自然期待的结果应该是特定于JDK1.6的依赖信息被写入到CLASS文件中),这最终导致了当运行于JDK1.6目标平台时会抛出java.lang.NoClassDefFoundError异常。

您的支持将鼓励我继续分享!