Java 8发布已经快一个月了,我敢肯定,大家都在探索JDK 8的新功能。但是,在您完全研究Java 8之前,是时候重新审视Java 7上引入的一些很棒的功能了。记住,Java 6并没有什么功能,它只与JVM的更改和性能有关,但是JDK 7确实引入了一些很酷的功能,这些功能改善了开发人员的日常任务。 为什么我现在要写这篇文章? 为什么我在谈论Java 1. 7,而每个人都在谈论Java 8? 好吧,我认为,并非所有Java开发人员都熟悉JDK 7中引入的更改,并且与欢迎新版本相比,什么时候可以重新访问早期版本。 我什至没有看到开发人员在日常生活中使用自动资源管理 ,即使IDE已经为此提供了内容帮助。 尽管我看到程序员在Switch和Diamond运算符中使用String进行类型推断,但是对于fork join框架 ,在一个catch块中捕获多个异常或在数字文字上使用下划线的了解还是很少。 因此,我借此机会撰写了一些总结性的文章,以修改这些方便的更改并将其应用到日常编程生活中。 在NIO和新的File API上有一些不错的更改,在API级别上还有很多其他更改,这也值得一看。 我敢肯定,结合Java 8 lambda表达式 ,这些功能将导致更好更好的代码。
- 类型推断
在JDK 1.7之前引入了新的运算符<<,即菱形运算符,以使类型推断也可用于构造函数。 在Java 7之前,类型推断仅适用于方法,并且Joshua Bloch在有效Java 2nd Edition中正确地进行了预测,现在它也适用于构造函数 。 在JDK 7之前的版本中,您可以在对象创建表达式的左侧和右侧输入更多类型,以指定类型,但是现在只需在左侧创建类型,如下面的示例所示。
先前的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<>();
因此,在使用Collections(我们大量使用Generics)时 ,您不必在Java 7中键入更多内容。 有关Java中Diamond运算符的更多详细信息,请参见此处。
- 开关中的弦
在JDK 7之前,只能将整数类型用作switch-case语句的选择器。 在JDK 7中,可以将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"); }
比较时使用java.lang.String中的equals()和hashcode()方法,该方法区分大小写。 在switch中使用String的好处是,与使用嵌套的if-then-else语句相比,Java编译器可以生成更有效的代码。 有关如何在Switch case语句中使用String的更多详细信息,请参见此处。
- 自动资源管理
在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(); 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"); } 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和Closeable接口,例如Streams,Files,Socket句柄,数据库连接等。JDK7引入了try-with-resources语句,该语句可确保通过调用AutoClosable的close()方法在语句末尾关闭try(resources)中的每个资源。 现在,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。
- 叉连接框架
fork / join框架是ExecutorService接口的实现,该接口使您可以利用现代服务器中可用的多个处理器。 它是为可以递归分解为较小部分的工作而设计的。 目标是使用所有可用的处理能力来增强应用程序的性能。 与任何ExecutorService实现一样,fork / join框架将任务分配给线程池中的工作线程。 分支联接框架与众不同,因为它使用了一种工作窃取算法,该算法与生产者消费者算法有很大不同。 工作用尽的工作线程可以从其他仍很忙的线程中窃取任务。 fork / join框架的中心是ForkJoinPool类,它是AbstractExecutorService类的扩展。 ForkJoinPool实现了核心的工作窃取算法,并且可以执行ForkJoinTask进程。 您可以将代码包装在ForkJoinTask子类中,例如RecursiveTask(可以返回结果)或RecursiveAction。 有关Java中的fork联接框架的更多信息,请参见此处 。
- 在数字文字下划线
在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
有关更多信息和用例,请参阅我的文章有关如何在数字文字上使用下划线 。
- 在单个捕获块中捕获多个异常类型
-
例如,在JDK 7之前,您需要两个catch块来捕获两种异常类型,尽管它们都执行相同的任务:
try { ...... } catch(ClassNotFoundException ex) { ex.printStackTrace(); } catch(SQLException ex) { ex.printStackTrace(); }
在JDK 7中,您可以使用一个单独的catch块,其异常类型之间用'|'分隔。
try { ...... } catch(ClassNotFoundException|SQLException ex) { ex.printStackTrace(); }
顺便说一句,请记住, 多重捕获语句中的Alternatives不能通过子类关联。 例如,如下所示的多捕获语句将抛出编译时错误:
try { ...... } catch (FileNotFoundException | IOException ex) { ex.printStackTrace(); }
Multi-catch语句中的替代项不能通过子类关联,它会在编译时引发错误:java.io.FileNotFoundException是Test.main(Test.java:18)的替代java.io.IOException的子类。
请参阅此处以了解有关Java SE 7中改进的异常处理的更多信息。
- 前缀为“ 0b”的二进制文字
在JDK 7中,类似于C / C ++语言,您可以使用整数类型(字节,短型,整型和长型)的前缀“ 0b”(或“ 0B”)以二进制形式表示文字值。 在JDK 7之前,您只能使用八进制值(带有前缀“ 0”)或十六进制值(带有前缀“ 0x”或“ 0X”)。
int mask = 0b01010000101;
甚至更好
int binary = 0B0101_0000_1010_0010_1101_0000_1010_0010;
- Java NIO 2.0
Java SE 7引入了java.nio.file软件包及其相关软件包java.nio.file.attribute,为文件I / O和访问默认文件系统提供了全面的支持。 它还引入了Path类,该类允许您表示操作系统中的任何路径。 新的文件系统API是对较早版本的补充,并提供了几种有用的方法检查,删除,复制和移动文件。 例如,现在您可以检查Java中是否隐藏了文件 。 您还可以从Java代码创建符号链接和硬链接。 JDK 7新文件API还能够使用通配符搜索文件。 您还将获得支持以查看目录中的更改。 我建议检查新文件包的Java文档,以了解有关此有趣的有用功能的更多信息。
- G1垃圾收集器
JDK 7引入了一个称为G1 Garbage Collection的新垃圾收集器,它是垃圾的缩写形式。 G1垃圾收集器在垃圾最多的地方执行清理。 为了实现此目的,它将Java堆内存分为多个区域,而不是Java 7版本之前的3个区域(新,旧和永久性空间)。 据说G1完全可以预测,并且可以为内存密集型应用程序提供更高的吞吐量。
- 更精确地抛出异常
与早期版本的Java SE相比,Java SE 7编译器对重新抛出的异常执行更精确的分析。 这使您可以在方法声明的throws子句中指定更特定的异常类型。 在JDK 7之前,重新抛出异常被视为抛出catch参数的类型。 例如,如果您的try块可以引发ParseException和IOException。 为了捕获所有异常并重新抛出它们,您将必须捕获Exception并将您的方法声明为抛出Exception。 这是一种模糊的非精确抛出,因为您正在抛出常规的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子句中声明Exception的类型时更加精确。 从以下事实确定抛出哪个异常的精度是:如果从catch块重新抛出异常,则实际上是在抛出以下异常类型:
- 您的try块可能会抛出,
- 之前没有任何捕获块处理过,并且
- 是声明为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,因为您可以重新抛出一个异常,该异常是throws中声明的任何类型的超类型,我们将抛出java.lang.Exception,它是所有选中的Exception的超类。 同样在某些地方,您将看到带有catch参数的final关键字,但这不再是必需的。
这就是您可以在JDK 7中进行修改的全部内容。Java 7的所有这些新功能对于实现干净的代码和提高开发人员的生产率非常有用。 随着Java 8中引入的lambda表达式,这一实现Java中更干净代码的目标已达到另一个里程碑。 让我知道,如果您认为我遗漏了Java 1.7的任何有用功能,那么您认为应该在这里。
PS:如果您喜欢书籍,那么您可能也喜欢Packet Publishing的Java 7 New features Cookbook。
翻译自: https://www.javacodegeeks.com/2014/04/10-jdk-7-features-to-revisit-before-you-welcome-java-8.html