Java 8?还记得那年大明湖畔的Java 7吗?

Published:21 Apr 2014Category:Java

译注:但见新人笑,哪闻旧人哭。在大家都在兴致勃勃的讨论Java 8的时候,那个早被遗忘的Java 7,或许你从来都没有记得它的好。

Java 8的发布也有一个月了,我相信现在大家都在探索JDK 8中的新特性。但是,在你彻底开始钻研Java 8之前,最好先来回顾下Java 7有哪些新特性。如果你还记得的话,Java 6是没有增加任何特性的,只是JVM的一些改动以及性能的提升,不过JDK 7倒是增加了不少有助于提升开发效率的很给力的特性。我现在写这篇文章的目的是什么呢?为什么别人都在讨论Java 8的时候,我却还在聊Java1.7的事?因为我认为并不是所有的Java开发人员都很清楚JDK 7中的改动,还有什么时候比新版本发布的时候更适合介绍上一版本的特性的呢?我还很少看见有开发人员在代码中使用自动资源管理(ARM),尽管IDE的辅助工具都已经支持这个特性了。不过确实看到有人在用string的switch功能以及<>在做类型推导,另外,也很少有人知道fork-join框架,或者在一个catch块里捕获多个异常,或者在数值型字面量中使用下划线。因此我借这个机会来写一篇简短的摘要,回顾一下这些能方便我们日常开发工作的改动。NIO以及新的文件接口,还有很多API层面的改动也同样值得关注。我相信和Java 8的lambda表达式结合起来后,写出来的代码肯定会更加简洁。

  1. 类型推导

JDK 1.7引入一个新的操作符<>,也被称作钻石操作符,它使得构造方法也可以进行类型推导 。在Java 7之前,类型推导只对方法可用,正如Joshua Bloch在Effiective Java第二版中所预言 的那样,现在终于在构造方法中实现了。在这之前,你得在对象创建表达式的左右两边同时指定类型,现在你只需要在左边指定就可以了,就像下面这样。

JDK 7之前
Map<String, List<String>> employeeRecords =  new HashMap<String, List<String>>();
List<Integer> primes = new ArrayList<Integer>();
JDK 7
Map<String, List<String>> employeeRecords =  new HashMap<>();
List<Integer> primes = new ArrayList<>();

在Java 7中可以少敲些代码了,尤其是在使用集合的时候,因为那里大量用到了泛型。点击这里了解更多关于Java钻石操作符的信息。(译注:原文没提供链接啊)

  1. 在switch中支持String

在JDK 7之前 ,只有整型才能用作switch-case语句的选择因子。在JDK7中,你可以将String用作选择因子了。比如:

String state = "NEW";switch (day) {case "NEW": System.out.println("Order is in NEW state"); break;case "CANCELED": System.out.println("Order is Cancelled"); break;case "REPLACE": System.out.println("Order is replaced successfully"); break;case "FILLED": System.out.println("Order is filled"); break;default: System.out.println("Invalid");}

比较的时候会用到String的equals和hashCode()方法,因此这个比较是大小写敏感的。在switch中使用String的好处是,和直接用if-else相比 ,编译器可以生成更高效的代码。更详细的说明请点击这里。

  1. 自动资源管理(Automatic Resource Management)

在JDK 7之前,我们需要使用一个finally块,来确保资源确实被释放掉,不管try块是完成了还是中断了。比如说读取文件或者输入流的时候,我们需要在finally块中关闭它们,这样会导致很多的样板代码,就像下面这样:

public static void main(String args[]) {FileInputStream fin = null;BufferedReader br = null;try {fin = new FileInputStream("info.xml");br = new BufferedReader(new InputStreamReader(fin));if (br.ready()) {String line1 = br.readLine();
09System.out.println(line1);}} catch (FileNotFoundException ex) {System.out.println("Info.xml is not found");} catch (IOException ex) {System.out.println("Can't read the file");} finally {try {if (fin != null) fin.close();if (br != null) br.close();} catch (IOException ie) {System.out.println("Failed to close files");}}}

看下这段代码 ,是不是很多样板代码?

而在Java 7里面,你可以使用try-with-resource的特性来自动关闭资源,只要是实现了AutoClosable和Cloaeable接口的都可以,Stream, File, Socket,数据库连接等都已经实现了。JDK 7引入了try-with-resource语句,来确保每个资源在语句结束后都会调用AutoCLosable接口的close()方法进行关闭。下面是Java 7中的一段示例代码,它看起来可是简洁多了:

public static void main(String args[]) {try (FileInputStream fin = new FileInputStream("info.xml");BufferedReader br = new BufferedReader(new InputStreamReader(fin));) {if (br.ready()) {String line1 = br.readLine();System.out.println(line1);}} catch (FileNotFoundException ex) {System.out.println("Info.xml is not found");} catch (IOException ex) {System.out.println("Can't read the file");}
}

由于Java负责关闭那些打开的资源比如文件和流这种,因此文件描述符泄露的事情应该不会再发生了,应该也不会再看到文件描述符错误的提示了。甚至JDBC 4.1都已经开始支持了AutoClosable了。

  1. Fork Join框架

Fork/join框架是ExecutorService接口的实现,它使得你可以充分利用现代服务器多处理器带来的好处。这个框架是为了那些能递归地拆分成更小任务的工作而设计的。它的目标是去压榨处理器的能力以提升程序的性能。就像别的ExecutorService的实现一样,fork/join框架也是把任务分发给线程池中的多个线程。它的不同之处在于它使用的是一种工作窃取算法(work-stealing algorithm),这和生产者消费者的算法有很大的不同。已经处理完任务的工作线程可以从别的繁忙的线程那里窃取一些任务来执行。fork/join框架的核心是ForkJoinPool类,它继承自AbstractExecutorService。ForkJoinPool类实现了核心的工作窃取算法,可以执行ForkJoinTask进程。你可以把代码封装在一个ForkJoinTask的子类里,比如RecursiveTask或者RecursiveAction。更多信息就参考这里。

  1. 数值字面量中使用下划线

JDK 7中,你可以在数值字面量中使用'_'来提升可读性。这对在源代码中使用了大数字的人来说尤其有用,例如在金融或者计算领域中。比方说这么写,

int billion = 1_000_000_000;  // 10^9
long creditCardNumber =  1234_4567_8901_2345L; //16 digit number
long ssn = 777_99_8888L;
double pi = 3.1415_9265;
float  pif = 3.14_15_92_65f;

你可以在合适的位置插入下划线使得它可读性更强,比如说一个很大的数字可以每隔三位放一个下划线,对于信用卡卡号而言,通常是16位长度,你可以每隔4个数字就放一个下划线,就如它们在卡片上所显示的那样。顺便说一句,要记住,你不能在小数后面,或者数字的开始和结束的地方放下划线。比如说,下面的数值字面量就是不正确的,因为它们错误地使用了下划线:

double pi = 3._1415_9265; // underscore just after decimal point
long creditcardNum = 1234_4567_8901_2345_L; //underscore at the end of number
long ssn = _777_99_8888L; //undersocre at the beginning

你可以读下我的这篇文章了解更多的一些关于下划线使用的例子。

  1. 在一个catch块中捕获多个异常

JDK 7中,单个catch块可以处理多个异常类型。

比如说在JDK 7之前,如果你想捕获两种类型的异常你得需要两个catch块,尽管两个的处理逻辑都是一样的:

try {......} catch(ClassNotFoundException ex) {ex.printStackTrace();
} catch(SQLException ex) {ex.printStackTrace();
}

而在JDK 7中,你只须使用一个catch块就搞定了,异常类型用‘|’进行分隔:

try {......} catch(ClassNotFoundException|SQLException ex) {ex.printStackTrace();}

顺便说一句,这种用法是不包括异常的子类型的。比如说,下面这个多个异常的捕获语句就会抛出编译错误:

try { ......} catch (FileNotFoundException | IOException ex) {ex.printStackTrace();
}

这是因为FileNotFoundException是IOException 的子类,在编译的时候会抛出下面的错误: java.io.FileNotFoundException is a subclass of alternative java.io.IOException at Test.main(Test.java:18)。

了解更多请点击这里。

  1. 使用"ob"前缀的二进制字面量

JDK7中,对于整型类型(byte, short, int 和long)来说,你可以用'0b'前缀来表明这是一个二进制的字面量,就像C/C++中那样。在这之前,你只能使用8进制(前缀'0')或者16进制(前缀是'0x'或者‘0X')的字面量。

int mask = 0b01010000101;

这样写好处更明显:

int binary = 0B0101_0000_1010_0010_1101_0000_1010_0010;

8.Java NIO 2

Java SE 7中引入了java.nio.file包,以及相关的java.nio.file.attibute包,全面支持了文件IO以及对默认文件系统的访问。它同时还引入了Path 类,你可以用它来代表操作系统中的任意一个路径。新的文件系统API兼容老的版本,并且提供了几个 非常实用的方法,可以用来检查,删除,拷贝和移动文件。比如,你可以在Java中判断一个文件是否是隐藏文件。你还可以在Java中创建软链接和硬链接。JDK 7的新的文件API还能够使用通配符来进行文件的搜索。你还可以用它来监测某个目录 是否有变动。我推荐你看下它的官方文档来了解更多的一些有意思的特性。

  1. G1垃圾回收器

JDK7中引入了一个新的垃圾回收器,G1,它是Garbage First的缩写。G1回收器优先回收垃圾最多的区域。为了实现这个策略它把堆分成了多个区域,就好比Java 7之前分成三个区域那样(新生代,老生代和持久代)。G1回收器是一个可预测的回收器,同时对那些内存密集型的程序它还能保证较高的吞吐量。

  1. 重抛异常的改进

Java SE 7的编译器和之前的版本相比,在重新抛出异常这块进行了更精确的分析。这使得你在方法声明的throws子句中可以指定更精确的异常类型。在JDK 7之前,重抛的异常的类型被认为是catch参数中的指定的异常类型。比如说,如果你的try块中抛出了一个ParseException以及一个IOException,为了捕获所有的异常,然后重新抛出来,你会去捕获Exception类型的异常,并且声明你的方法抛出的异常类型是Exception。这种方式有点不太精确,因为你实际抛出的是一个通用的Exception类型,而调用你的方法的语句需要去捕获这个通用的异常。看一下Java 1.7前的这段异常处理的代码可能你会更明白些:

public void obscure() throws Exception{try {new FileInputStream("abc.txt").read();new SimpleDateFormat("ddMMyyyy").parse("12-03-2014");       } catch (Exception ex) {System.out.println("Caught exception: " + ex.getMessage());throw ex;}
}

JDK 7以后你就可以在方法的throws子句中明确的指定异常类型了。精确的异常重抛指的是,如果你在catch块中重新抛出异常,实际真正抛出的异常类型会是:

  1. 你的try块抛出的异常
  2. 还没有被前面的catch块处理过,并且
  3. catch的参数类型是Exception的某个子类。

这使得异常重抛变得更精确。你可以更准确的知道方法抛出的是何种异常,因此你可以更好的处理它们,就像下面这段代码这样:

public void precise() throws ParseException, IOException {try {new FileInputStream("abc.txt").read();new SimpleDateFormat("ddMMyyyy").parse("12-03-2014");       } catch (Exception ex) {System.out.println("Caught exception: " + ex.getMessage());throw ex;}
}

Java SE 7的编译器允许你在preciese() 方法声明的throws子句中指定ParseException和IOException类型,这是因为你抛出的异常是声明的这些异常类型的父类,比如这里我们抛出的是java.lang.Exception,它是所有受检查异常的父类。有的地方你会看到catch参数中带final关键字,不过这个不再是强制的了。

这些就是JDK 7中所有你应该回顾的内容了。这些新特性对写出整洁的代码以及提升开发效率非常有用。有了Java 8中的lambda表达式,Java中的代码整洁之道则又上了一个新的里程碑。如果你认为我这篇文章中漏掉了Java 1.7任何有用的特性,请记得提醒我。

P.S. 如果你喜欢读书的话,那你也一定会喜欢Packet Publication的这本Java 7 New features Cookbook。

原创文章转载请注明出处:Java 8?还记得那年大明湖畔的Java 7吗?

英文原文链接

还记得那年大明湖畔的Java 7吗相关推荐

  1. 写了这么久Java项目,是否还记得你的第一行Java代码

    前言 个人情况 首先介绍一下本人的情况,我来自于一个双非渣渣二本学院,目前处于大四阶段,由于在小学的时候就开始接触了电脑,一直以来也对IT的各方面有着浓厚的兴趣,所以在高考结束填写志愿书的时候,就毅然 ...

  2. 你还记得当年上课天天玩 JAVA游戏吗

    回头再玩玩以前的游戏,发现意外的好玩唉,这三个我玩了好多次,我个人觉得当年的JAVA游戏中,有些RPG游戏比某些国产大IP的RPG游戏还要好玩,当年那种手机出很多Java 大作, 包括红警也有, 七夜 ...

  3. java最早的手机网游_还记得你们玩过的最早的手机网游是什么吗?

    不知你们是否记得一个叫做<冒泡社区>的手游平台,在那个传统的功能机时代,冒泡社区可以算是国内第一的手游社区平台了,还记得初一的时候,我爸买了个杂牌子手机,当前用里面的"JAVA& ...

  4. 少 年 不 惧 岁 月 长——你是否还会记得那年少时的梦呢?

    那些在深夜里睡不着的日子 那些面对深渊想要逃跑的瞬间 在日后看来都是非常珍贵的时刻 他让我们更加诚实的面对自己 只有认识他,接受他 我们才可能真正的超越他. 每一个人都必定出发于他的少年时 只是很多年 ...

  5. 那些年的java游戏_那些年我们曾经玩过的游戏,你还记得几个

    标题:那些年我们曾经玩过的游戏,你还记得几个 随着时间长河的推进,我们已经长大了.你还记得我们那些年一起玩过的游戏么? 弹弓 一般用树枝做弓架,也可以用旱伞的伞骨做弹弓架.要买弹力很大的像皮筋,就和那 ...

  6. 还记得八皇后的解法吗

    "还记得八皇后的解法吗?" "上个世纪的事情,不记得了." "-- 现在回忆一下?" "开会,回头说." " ...

  7. 激活层是每一层都有吗_每一个日出日落,都是岁月痕迹。这些在东软的第一次,你还记得吗...

    一学期又要结束了 今年的九月,学校又会迎来一批新生 不禁感慨,时间真的太快了 回首在大学的这些日子 你还记得那些"第一次"吗? 1.你还记得第一次出远门吗? 怀着忐忑的心情,第一次 ...

  8. 安卓 10 周岁了:这些消失的经典 APP 你还记得吗?

    来自:太平洋电脑网  作者:Aimo 链接:http://pcedu.pconline.com.cn/1005/10051864_all.html 不知不觉,安卓系统已经迈入到第十个年头了.在很多老网 ...

  9. MySQL中的几个“L”,你还记得否?

    大家好,我是松哥,今天给大家分享:MySQL中的几个"L",还记得否? 在我们刚刚开始学数据库知识的时候,首先会接触到各种L,这里的L也就是语言language的首字母. 大致有如 ...

  10. 什么?你还在花一两万学Java,快来看看小白学习java全路线吧

    ---------------------------龙珠悟空------------------------- 龙珠悟空一个写故事的程序员,小白学java专栏我会以故事的方式,通过老师讲解同学讨论个 ...

最新文章

  1. java Serializable和Externalizable序列化反序列化详解--转
  2. SecureCRT设置背景颜色和目录(文件夹)颜色
  3. DOM4J介绍与代码示例 (强大的xml处理工具)
  4. 面向中后台复杂场景的低代码实践思路
  5. 信息学奥赛一本通C++语言——1046:判断一个数能否同时被3和5整除
  6. redis.conf 配置详解
  7. OPENGL中的glViewport
  8. boost 静态库命名规则
  9. 数据分析------数据处理(2)及 AutoML 学习
  10. 10分钟带你光速入门运维工具之-Puppet
  11. 可实现ffmpeg转码的cuda显卡
  12. 巧用QQ文件中转站在办公室与住所间作大文件传递
  13. 【精品收藏】世界上最有智慧的人是怎样理性思考的?查理·芒格的100个思维模型...
  14. 分享咖啡基础知识——从咖啡小白到咖啡发烧友需要了解的那些事儿!
  15. EF System.NotSupportedException
  16. python开方 运算符_[转载] Python中的算数运算符
  17. 第四章USB数据流模型
  18. 青柠开车Spring Cloud(三) —— Spring cloud Eureka
  19. Android无限滑动控件实现
  20. 【Python】P1008 [NOIP1998 普及组] 三连击

热门文章

  1. i2c我们用得很多,i3c又是什么?
  2. 中图杯获奖作品计算机组,我校代表队参加首届“中图杯”全国大学生先进制图技术与技能大赛取得优异成绩...
  3. 不积跬步无以至千里010
  4. java 纯真地址库_JAVA解析纯真IP地址库
  5. cms10——友情链接
  6. [游戏程序] 经典游戏服务器端架构概述
  7. L13过拟合欠拟合及其解决方案
  8. 内存保护单元(Memery Protection Unit)
  9. 前端开发日报:20190818
  10. 网络 DNS 解析与CDN加速