ConcurrentHashMap的java跨版本问题

文章来源:http://www.kissyu.org/2016/08/29/ConcurrentHashMap的java跨版本问题/

作者:@kissyu



  • 背景知识

  • ConcurrentHashMap

  • 解决方法

  • 总结



背景知识


javac


javac有两个指令:-source和-target,比如下述指令:

/usr/lib/jvm/java-8-oracle/bin/javac -source 1.7 -target 1.7 HelloWorld.java

-source:表示我的代码将采用哪个java版本来编译。该值影响的是编译器对语法规则的校验。比如HelloWorld.java中含有jdk8的语法,但是你的-source为1.7,那么编译器就会报错。

-target:表示生成的字节码将会在哪个版本(及以上)的jvm上运行。比如HelloWorld.java指定了-target为1.8,那么HelloWorld.class只能在1.8即以上的jvm中运行,如果在1.7的jvm上运行,就会报错。


rt.jar


jdk的rt.jar里面包含了jdk的核心类,比如String,集合等。JVM在加载类时,对于rt.jar包里面的所有的类持有最高的信任而不做任何校验。


ConcurrentHashMap


ConcurrentHashMap类有一个方法叫做keySet,用来返回当前map中的key集合。虽然返回的是key的集合,但是在1.7和1.8中用来表示该集合的类却完全不同。在1.7中,返回的是Set:

public Set<K> More ...keySet() {

Set<K> ks = keySet;

return (ks != null) ? ks : (keySet = new KeySet());

}

然而在1.8中返回的是KeySetView

public KeySetView<K,V> keySet() {

KeySetView<K,V> ks;

return (ks = keySet) != null ? ks : (keySet = new KeySetView<K,V>(this, null));

}

其中KeySetView其实是Set接口的一个实现类。我们再来看下述代码:

import java.util.Set;

import java.util.concurrent.ConcurrentHashMap;

public class HelloWorld {

public static void main(String[] args) {

ConcurrentHashMap<String, String> test = new ConcurrentHashMap<>();

Set<String> keySet = test.keySet();

}

}

然后我们用jdk8的javac来进行编译:

$ /usr/lib/java8/bin/javac -source 1.7 -target 1.7 HelloWorld.java

warning: [options] bootstrap class path not set in conjunction with -source 1.7

1 warning

或者中文版的报错信息如下:

警告: [options] 未与 -source 1.7 一起设置引导类路径

1 个警告

但是上述代码是可以通过编译的,因为KeySetViewSet的实现类,所以1.7的语法没有任何问题。但是编译生成的class文件无法在1.7版本的jvm上运行。我们看一下字节码的实际内容:

import java.util.concurrent.ConcurrentHashMap;

import java.util.concurrent.ConcurrentHashMap.KeySetView;

public class HelloWorld

{

public static void main(String[] paramArrayOfString)

{

ConcurrentHashMap localConcurrentHashMap = new ConcurrentHashMap();

ConcurrentHashMap.KeySetView localKeySetView = localConcurrentHashMap.keySet();

}

}

我们可以看到,在字节码中,实际上keySet返回的是1.8中指定的KeySetView类,但是这个类在jdk1.7中是不存在的,所以当用1.7的jvm运行时,会抛出NoSuchMethodError的异常。


解决方法


为了解决这个问题,还是要看编译时的警告信息(不能忽视任何一个警告)。从warning的信息中我们可以得知,当指定了-source时,我们还需要一起指定引导类即bootstrap类,否则可能会出现某些兼容性的问题,比如刚才我们遇到的ConcurrentHashMap的问题。所以我们在编译的时候需要再加上引导类:

$ /usr/lib/java8/bin/javac -source 1.7 -target 1.7 HelloWorld.java -bootclasspath /usr/lib/java7/jre/lib/rt.jar

我们先来反编译生成的class文件:

import java.util.Set;

import java.util.concurrent.ConcurrentHashMap;

public class HelloWorld

{

public static void main(String[] paramArrayOfString)

{

ConcurrentHashMap localConcurrentHashMap = new ConcurrentHashMap();

Set localSet = localConcurrentHashMap.keySet();

}

}

我们可以看到现在class文件中返回的类变为了Set,然后我们在用1.7的jvm来运行,发现一切正常,问题被解决了!


总结


以后在指定-source时,还需要同时指定-bootclasspath,否则就会默认使用当前javac所用到的jdk版本的核心jar包(比如rt.jar)。



>>>在阅读文章过程中如有疑问,请发布到极乐官网(点击阅读原文直达)>>>


阅读原文:http://mp.weixin.qq.com/s?__biz=MzA4MjYyOTQ0Mg==&mid=2649688441&idx=2&sn=773728afb1963ee4ae480985719a1f71&chksm=87996f54b0eee642f7748c32e070275ec7210f42c223b61b2b46d11dbc018c3699628a7b5dbb#rd
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,010评论 19 139
  • 前言 记录个人在2017年08月的学习和总结,不定期更新 2017-08-02 有序的Map HashMap是无序...
    Kevin_ZGJ阅读 421评论 0 0
  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,378评论 11 349
  • 这天乌云密布,万雷轰顶。龙飞看了看那不定时就轰上两声的天空。 我为什么只要去网吧就下大雨!我着你惹你了老天...
    王浠城阅读 216评论 0 1
  • 对于刚需而手头又不太宽裕的人来说,买房就像结婚,选什么样的都会后悔!贵的买不起,便宜的不想要,能接受的价位有时绿化...
    后知后觉的人阅读 175评论 0 2