《HotSpot实战》—— 1.1 JDK概述

    xiaoxiao2023-12-20  151

    本节书摘来异步社区《HotSpot实战》一书中的第1章,第1.1节,作者:陈涛,更多章节内容可以访问云栖社区“异步社区”公众号查看。

    1.1 JDK概述

    Java是一门不断发展和壮大的语言,随着理论和应用的飞速发展,它不断地吸收有益营养,许多优良特性也在JDK的各个版本中逐渐添加进来。

    JDK在一开始并不是开源的。随着开源运动的蓬勃发展,Sun公司在2006年的JavaOne大会上声称将对Java开放源代码,开源的Java平台开发主要集中在OpenJDK项目上。Sun公司于2009年4月15日正式发布OpenJDK。Java 7则是Java开源后发布的第一个正式版本。

    任何组织和个人都可以为Java的发展做出贡献,如果你愿意为OpenJDK项目添砖加瓦,可以从AdoptOpenJDK起步,AdoptOpenJDK是一个旨在帮助开发人员更好地加入OpenJDK项目而提供指导的社区1。

    在Java和OpenJDK的发展中,JCP起到了重要的推动作用。

    1.1.1 JCP与JSR

    JCP(Java Community Process)是一套制定Java技术规范的机制,通过制定和审查JSR(Java Specification Requests)推动Java技术规范的发展。

    一个已提交的JSR要想成为最终状态,需要经过正式的公开审查,并由JCP 执行委员会投票决定。最终的JSR会提供一个参考实现,它是免费而且公开源代码的;除此之外,还需要提供一个用来验证是否符合API规范的技术兼容性工具包(Technology Compatibility Kit)。

    JSR并非只由Oracle管理,任何个人都可以注册并参与审查JSR。只要你有足够的兴趣和热情,都可以注册成为JCP成员,并参加JSR的专家组,甚至提交自己的JSR建议。

    在JCP官网中可以查看所有的JSR。对Java语言发展动态感兴趣的读者来说,跟踪JSR的发展动态是一条不错的学习途径。目前提交的JSR大概有300多个,包括我们熟知的一些特性都是通过JSR提交的,如Java模块化、动态语言支持等,如表1-1所示。

    1.1.2 JDK的发展历程

    接下来,就让我们翻开历史的画卷,看一看在JDK的发展历程中各个优良的特性是如何添加到Java中的。

    1.混沌初开

    1996年1月23日,第一个稳定的正式版本JDK发布。该版本编号为JDK 1.0.2,Sun公司官方称为Java 1。

    2.JDK 1.1

    1997年2月19日,Sun公司发布JDK 1.1。在此版本中为Java增加了诸如AWT、内部类、Java Beans、JDBC、RMI和反射等特性。

    3.J2SE的开始

    JDK 1.2也称为J2SE 1.2。1998年12月8日,Sun公司发布JDK 1.2,工程代号Playground(运动场)。

    自该版本开始一直到后来的5.0版本,统称为Java 2平台。Java 2平台根据应用领域的不同,提供了3个版本:

    J2SE(Java 2 Standard Edition,Java 2标准版);J2EE(Java 2 Enterprise Edition,Java 2企业版);J2ME(Java 2 Micro Edition,Java 2嵌入式版)。

    也正是从该版本开始,官方开始使用新的术语“J2SE”来描述Java 2标准版的变化。JDK 1.2为Java带来了许多的变化,包括strictfp关键字、Swing、Java Plug-in、Java IDL、Collections集合框架等技术,并首次在虚拟机中引入了JIT(即时)编译器。

    4.J2SE 1.3

    JDK 1.3也称为J2SE 1.3。2000年5月8日,Sun正式发布JDK 1.3,其工程代号为Kestrel(红隼)。该版本引入的技术包括:

    HotSpot虚拟机; RMI兼容CORBA; JDDI; JPDA; JavaSound等。``` ####5.J2SE 1.4 JDK 1.4也称为J2SE 1.4。2002年2月6日,Sun正式发布JDK 1.4,其工程代号为Merlin(灰背隼)。该版本是经JCP的努力发布的第一个版本,变化很大,JSR 59定义了改进的相关技术,包括: - assert关键字; - 正则表达式; - 异常链; - NIO(JSR 51); - IPv6支持; - 日志API(JSR 47); - 图形图像API; - 内置XML和XSTL解析器(即JAXP,Java API for XML Processing,见JSR 5和JSR 63); - 内置安全和加密类库JCE/JSSE/JAAS、Java Web Satrt(JSR 56)等。 - JDK 1.4有两个修订版本:2002年9月16日发布的工程代号为Grasshopper(草蜢)的JDK 1.4.1,以及在2003年6月26日发布的工程代号为Mantis(螳螂)的JDK 1.4.2。 ####6.Java 5 JDK 1.5也称为JDK 5、J2SE 5或Java 5。从该版本起,官方开始在公开版本(Product Version)中使用5.0、6.0或7.0的版本号命名方式,仅在开发版本中沿用JDK 1.5、JDK 1.6或JDK 1.7的命名,在Java开发人员中,也广泛使用后者的命名方式。2004年9月30日,Sun官方正式发布JDK 1.5,工程代号为Tiger(虎),对Java语法做了很多改进(JSR 176),包括: - 范型(JSR 14); - 注解(JSR 175); - 装箱(JSR 201); - 枚举(JSR 201); - 可变长参数; - foreach循环(JSR 201); - 改进了JMM(Java Memory Model,即Java内存模型)等。 ####7.Java 6 JDK 1.6也称为JDK 6、J2SE 6或Java 6。2006年12月11日,Sun官方发布JDK 6。该版本的变化来自JSR 270的定义,工程代号为Mustang(野马)。主要变化为: - 脚本语言支持(JSR 223); - 改进了Web Service支持,JAX-WS(JSR 224); - JDBC 4.0(JSR 221); - Java编译器API(JSR 199),允许Java程序调用Java编译器; - JAXB 2.0; 在虚拟机方面,提供了同步机制优化、编译器性能优化、垃圾收集增加新的算法、应用程序启动性能优化等功能。 ####8.Java 7 JDK 1.7也常称为JDK 7、J2SE 7或Java 7。工程代号为Dolphin(海豚)。Oracle收购Sun后,为了保证让进度一再延后的JDK 7正式版能够在2011年7月28日如期发布,不得不削减了原先的发布计划,将许多招致延误的项目迁移到了JDK 8的开发分支中。这样,在正式版最终发布后,JDK 7的主要变化在于以下几个方面。 - 在虚拟机方面,提供一款性能优秀的G1收集器; - 增强对动态语言的支持(JSR 292); - 在64位系统中,提供可压缩的对象指针(-XX:+UseCompressedOops); - Coin项目贡献的Java“语法糖”; - 并发工具API(JSR 166); - NIO.2; - 网络协议库对新的协议支持,如SCTP协议和SDP协议等。 JDK 7中曾计划但未按时完成的一些特性2,如Lambda表达式和部分来自Jigsaw项目和Coin项目的特性,将加入JDK 8的开发计划,预计随JDK 8在2014年春季发布。 ####9.Java 8 Oracle计划在2014年3月正式发布JDK 8 3。该版本将包含原本在JDK 7的计划中但最终未能及时发布的特性,包括: - Lambda表达式,由Lambda项目4开发; - 部分Coin项目提供的特性; - JavaScript引擎(JSR 223),允许Java程序嵌入JavaScript代码。由Nash- orn项目5开发; - Java类型注解(JSR 308); - 日期和时间API(JSR 310)。 ####10.Java 9 JDK 9也称为Java SE 9。在2011年的JavaOne大会上,Oracle表示希望在2016年发布JDK 9。JDK 9可以很好地管理数G的Java堆,能够更好地与本地代码集成,并做到虚拟机自我调节。这些开发计划还包括: - 模块化(JSR 294); - 货币API(JSR 354); - 对JavaFX更好地集成。 ####1.1.3 Java 7的语法变化 现在,我们将介绍一些Java 7带来的变化。说到Java 7的改进,Coin项目为其做出了重要贡献。Coin项目6是OpenJDK的子项目,它成立的主要目的是为Java语言贡献语法增强特性。本节将介绍Coin项目为Java 7贡献的几个语法新特性。这些新特性通过JSR 334提交到JCP。 这些新特性在并未对Java底层做很大改动的基础上,丰富了Java语法的表现形式,为Java程序员提供了更便捷的方式表达业务逻辑。这些特性主要包括: - 允许switch语句中使用String表达式; - 允许数值以下划线分隔; - 允许数值以二进制表示; - 异常处理增强; - TWR; - 简化范型定义、简化变长参数的方法调用等。 下面我们选取一些有代表性的特性举例说明。本书在后续章节也安排了对一些特性的实现机制的探讨,感兴趣的读者敬请留意。 ####1.switch语句中使用String 在Java 7之前,使用switch语句的条件表达式类型只能是枚举类型,或者byte、char、short和int类型以及它们的包装类Byte、Character、Short和Integer。若要根据String类型进行条件选择,则需要做额外的转换。换句话说,尚需跨过Java语法层面的“障碍”才能进行业务逻辑处理,从某种程度上来说,这为程序员带来了额外的负担。 在Java 7中,对条件表达式的类型有所放宽,允许在条件表达式中出现实际应用中最常用的String类型。 举例来说,在金融应用中,常常需要根据银行名获得对应的银行机构代码。在清单1-1中,通过改进的switch语句,我们可以根据bankName轻松获取bankId,中间无需转换。 清单1-1 来源:com.hotspotinaction.demo.chap1.Switch 描述:switch语句中使用String

    1 public String getBankIdByName(String bankName) {2 String bankId = "";3 switch (bankName) {4 case "ICBC" :5 bankId = "B00001";6 break;7 case "ABC" :8 bankId = "B00002";9 break;10 case "CCB" :11 bankId = "B00003";12 break;13 case "BOC" :14 bankId = "B00004";15 break;16 default :17 bankId = "UNVALID";18 }

    19 return bankId;20 }`可以看到,这一处改进,让Java语法更加灵活,这样更利于程序员对业务逻辑进行处理。

    练习1

    想一想,在Java 6及以前的版本中,如何实现getBankIdByName()?2.数值字面量增强(1)二进制。

    在Java 7之前,Java支持的整数字面量包括3种进制:十进制(默认)、八进制(前缀“0”)和十六进制(前缀“0X”或“0x”),但是不支持二进制的直接表示,为此,我们需要进行内部的进制间转换。

    例如,为了创建二进制数“0100”,我们可能需要使用十六进制“0x04”来间接表示。或者调用包装类Integer的parseInt()方法得到它,如Integer.parseInt(“0100”, 2)。但这些方式,都并不直观。

    在Java 7中,可以直接使用二进制表示:“0b0100”或“0B0010”,如清单1-2所示,读者要体会新特性带来的微妙变化。

    清单1-2来源:com.hotspotinaction.demo.chap1.Literals描述:数值字面量增强

    1 public int getBinaryInt(String number) { /* befor Java 7 */ 2 int a = -1; 3 try { 4 a = Integer.parseInt(number, 2); 5 } catch (NumberFormatException e) { 6 // TODO 异常处理 7 } 8 return a; 9 } 10 public int getBinaryIntLiterals() { /* Java 7: 二进制字面量 */ 11 int a = 0b0100; 12 return a; 13 }``` 在Java 7以前,为了获得以二进制表示的数据,我们可能会使用类似getBinaryInt()函数这样的实现方式。如第1~9行代码所示,getBinaryInt()函数根据传入的字符串参数,经过一番数据转换,方能得到期望的数据。而在这些转换过程中,为了程序的健壮性,还不得不编写更多的异常处理代码。而这一切,在Java 7中却可以用一种直观优雅的方式表现出来。如第11行代码所示,我们可以直接声明一个二进制整数,而无需做任何转换。 (2)利用下划线增强可读性。 假设我们要处理一个长串数据,如人民币“10000000”,想象一下在电子商务系统中,如果多敲入或者少敲入一个零将会造成多大的麻烦。 在Java 7中,允许对这样的数据使用下划线进行字符分隔表示,如清单1-3所示。 清单1-3 来源:com.hotspotinaction.demo.chap1.Literals 描述:用下划线表示数值字面量

    1 long a = 10_000_000L;2 int b = 0b1100_1000_0011_0000;`这样,数据便能一目了然地置于代码中,程序的可读性也大大增强了。

    3.异常处理增强在Java 6及之前的版本,我们要捕获多个异常,可以通过多个catch语句实现,如清单1-4所示。

    清单1-4来源:com.hotspotinaction.demo.chap1.MutilCatch描述:Java 7之前捕获多个异常的方式

    1 try { 2 a = a / i; 3 a = Integer.parseInt(number, 2); 4 } catch (NumberFormatException e) { 5 // TODO 异常处理 6 } catch (ArithmeticException e) { 7 // TODO 异常处理 8 }``` 在Java 7中,可以在一个catch表达式中对多种类型异常进行合并捕获,如清单1-5所示。 清单1-5 来源:com.hotspotinaction.demo.chap1.MutilCatch 描述:Java 7在一个catch中捕获多个异常

    1 try {2 a = a / i;3 a = Integer.parseInt(number, 2);4 } catch (NumberFormatException | ArithmeticException e) {5 // TODO 异常处理6 }4.TWR(try-with-resources)`Java 7中新增了Try-With-Resources 7(缩写为TWR)语句,允许声明一个或多个资源。资源(resource)对象在使用它的程序结束时必须进行关闭。使用TWR语句,能够确保在语句运行结束前关闭资源。

    任何实现了java.lang.AutoCloseable接口的对象,都可以成为TWR的资源。Java 7中对JDK中大部分资源对象都重新定义了一番,让它们都实现了java.lang.AutoCloseable接口。如果需要在应用程序中自定义新的资源,请确保它已实现了java.lang.AutoCloseable接口,这样就可以充分应用TWR特性了。

    下面我们实现一个打开Http接口并读取返回数据的工具方法。在Java 6以前的版本中,可以这样实现,如清单1-6所示。

    清单1-6来源:com.hotspotinaction.demo.chap1.HttpUtil描述:TWR

    1 public static String openUrl(String strUrl, String postParams) { 2 InputStream is = null; 3 OutputStream os = null; 4 String message = ""; 5 try { 6 URL webURL = new URL(strUrl); 7 HttpURLConnection conn = (HttpURLConnection) webURL.openConnection(); 8 conn.setDoOutput(true);// 打开写入属性 9 conn.setDoInput(true);// 打开读取属性 10 conn.setRequestMethod("POST");// 设置提交方法 11 conn.connect(); 12 os = conn.getOutputStream(); 13 os.write(postParams.toString().getBytes()); 14 os.flush(); 15 if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { 16 System.err.println("response error=" + conn.getResponseCode()); 17 } 18 is = conn.getInputStream(); 19 message = getReturnValueFromInputStream(is); 20 } catch (Exception e) { 21 // TODO 异常处理 22 } finally { 23 if (is != null) { 24 try { 25 is.close(); 26 } catch (IOException e) { 27 // TODO 异常处理 28 } 29 } 30 if (os != null) { 31 try { 32 os.close(); 33 } catch (IOException e) { 34 // TODO 异常处理 35 } 36 } 37 } 38 return message; 39 }``` 显然,在finally代码块(第22~37行)中关闭InputStream和OutputStream需要写上不少代码,并且这种关闭的语法很不友好,一旦遗漏finally语句块那就很有可能造成应用程序的资源泄露,遗漏某个非空判断则也会导致程序在运行时出现异常。 Coin项目提供了一个很贴心的改进—TWR,它让我们能以一种更为便捷的方式实现资源关闭,如清单1-7所示。 清单1-7 来源:com.hotspotinaction.demo.chap1.HttpUtil 描述:TWR

    1 public static String openUrlTWR(String strUrl, String postParams) {2 URL webURL = null;3 HttpURLConnection con = null;4 String message = "";5 try {6 webURL = new URL(strUrl);7 con = (HttpURLConnection) webURL.openConnection();8 con.setDoOutput(true); 9 con.setDoInput(true); 10 con.setRequestMethod("POST");11 con.connect();12 try (OutputStream os = con.getOutputStream(); InputStream is=con.getInputStream()) {13 os.write(postParams.toString().getBytes());14 os.flush();15 if (con.getResponseCode() != HttpURLConnection.HTTP_OK) {16 System.err.println("response error=" + con.getResponseCode());17 }18 message = getReturnValueFromInputStream(is);19 }20 } catch (Exception e) {21 // TODO 异常处理22 }23 return message;24 }`只需要在try语句中声明程序块需使用的资源即可,省去了繁琐的资源泄露处理。将资源关闭任务交给系统去做,程序员只需要关注业务逻辑的实现就行了。

    5.简化泛型定义泛型是一种强大而又安全的设计工具,它能够让程序员在编译期就能避免把错误类型的对象添加到容器中。

    清单1-8来源:com.hotspotinaction.demo.chap1.SimplifiedGeneric描述:简化泛型定义

    1 HashMap<String, HashMap<Long, String>> map1 = new HashMap<String, HashMap<Long, String>>(); 2 HashMap<String, HashMap<Long, String>> map2 = new HashMap<>();``` 如清单1-8所示,第1行代码首先向我们展示了在Java 6之前是如何创建一个类型明确的容器的。我们看到,在等号左边声明了一个类型为容器HashMap的对象map1,这个容器以String类型作为key,但它的value类型同样是一个HashMap容器,这个内嵌的容器以Long类型作为key并以String类型作为value。这种容器内嵌入容器的设计方式,在日常工作中是很常见的。麻烦的是在等号右边,还要敲入与等号左边相同的类型声明代码。显然,这是多余的,毕竟,等号左边已经明确了map1的具体类型。 这种繁琐的语法在Java 7中得到了简化。第2行代码演示了应用Java 7改进语法后的效果。程序员在等式右边只需要输入少量代码,剩下的类型推断工作交给编译器去做就行了。本来就应该这样,不是吗? 相关资源:敏捷开发V1.0.pptx
    最新回复(0)