JDK是java开发者的灵魂,每一个版本的迭代都代表着Java开发的朝向。我们按照时间轴顺序来总结JDK的更新版本和相关特性,以此来加深JDK版本功能特性记忆。由于篇幅有限,本文旨在介绍版本功能特性和差异性,不对每一个新功能做原理性详情介绍。
本篇引入的是JDK7后的版本,旨在理解JDKAPI和开发使用心得,并非官网API请读者留意。到笔者已经更新至14版。码字不易,欢迎批评指正。
JDK7(2011.07.18-2016.10.18)
功能特性
1、二进制字面量
2、在数字字面量使用下划线
3、switch可以使用string
4、实例创建的类型推断
5、使用Varargs *** 使用不可维护的形式参数时改进了编译器警告和错误
6、try-with-resources 资源的自动管理
7、捕捉多个异常类型和对重新抛出异常的高级类型检查
功能介绍和使用
二进制字面量
整数类型例如(byte,short,int,long)能够用二进制来表示了。通过在数字前面加入0b或者0B来标示一个二进制的字面量:
// An 8-bit 'byte' value: byte aByte = (byte)0b00100001; // A 16-bit 'short' value: short aShort = (short)0b1010000101000101; // Some 32-bit 'int' values: int anInt1 = 0b10100001010001011010000101000101; int anInt2 = 0b101; int anInt3 = 0B101; // The B can be upper or lower case. // A 64-bit 'long' value. Note the "L" suffix: Long aLong = 0b1010000101000101101000010100010110100001010001011010000101000101L;
在数字字面量使用下划线
你的代码包含一些数字有很多的位数,你能够用下划线字符把位数分为三组,类似于你用一个像逗号或者一个空格作为分隔符。
long creditCardNumber = 1234_5678_9012_3456L; long socialSecurityNumber = 999_99_9999L; float pi = 3.14_15F; long hexBytes = 0xFF_EC_DE_5E; long hexWords = 0xCAFE_BABE; long maxLong = 0x7fff_ffff_ffff_ffffL; byte nybbles = 0b0010_0101; long bytes = 0b11010010_01101001_10010100_10010010;
下划线只能出现在数字之间,下面的情形不能出现下划线:
A、数字的开头和结尾
B、在浮点数中与小数点相邻
C、F或者L后缀之前
D、在预期数字串的位置
switch可以使用string
在使用switch中可以使用String类型的表达式来分支处理。
泛型实例创建的类型推断
可以用空的类型参数集(<>)替换调用泛型类的构造函数所需的类型参数,只要编译器可以从上下文中推断类型参数即可。这对尖括号被非正式地称为菱形。
Map<String, List<String>> myMap = new HashMap<>();
try-with-resources语句
try-with-resources 声明是try 一个或多个资源的声明。一个资源作为一个对象在程序结束之后必须关闭它。try-with-resources声明保证每一个资源都会被关闭在声明结束的时候。任何实现了java.lang.AutoCloseable接口或者实现了java.io.Closeable,可以作为一个资源。
这意味着JDK以后的try块代码块将变的简洁,安全。
static String readFirstLineFromFile(String path) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(path))) { return br.readLine(); } }
捕捉多个异常类型和对重新抛出异常的高级类型检查
在Java SE 7和更高版本可以消除catch中异常的重复代码,可以指定了该块可以处理的异常类型,并且每种异常类型都用竖线(|)分隔。
try { System.out.println(10 / 0); System.out.println(arr[10]); // 数组长度为3 } catch (ArithmeticException | ArrayIndexOutOfBoundsException e) { e.printStackTrace(); }
总结
从功能特性可见JDK7更新的是比较基础的语法规则,在语法规则,异常机制做了优化。
JDK8(稳定版2014.03.18~2016.10.18)
功能特性
1、Lambda表达式
2、函数式接口
3、 *** 引用和构造器调用
4、Stream API
5、接口中的默认 *** 和静态 ***
6、新时间日期API
功能介绍和使用
Lambda表达式
lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码,它简洁直观,很容易上手使用。
//匿名内部类 Comparator<Integer> cpt = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1,o2); } }; TreeSet<Integer> set = new TreeSet<>(cpt); //改造成Lambda形式 Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y); TreeSet<Integer> set2 = new TreeSet<>(cpt2);
lambda表达式语法总结:
前置书写和语法要求
无参数无返回值
() -> System.out.println(“Hello WOrld”)
有一个参数无返回值
(x) -> System.out.println(x)
有且只有一个参数无返回值
x -> System.out.println(x)
有多个参数,有返回值,有多条lambda体语句
(x,y) -> {System.out.println(“xxx”);return xxxx;};
有多个参数,有返回值,只有一条lambda体语句
(x,y) -> xxxx
函数式接口
函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。什么是函数式接口?简单来说就是只定义了一个抽象 *** 的接口(Object类的public *** 除外),就是函数式接口,并且还提供了注解:@FunctionalInterface
A、消费型接口,有参无返回值
@Test public void test(){ changeStr("hello",(str) -> System.out.println(str)); } /** * Consumer<T> 消费型接口 * @param str * @param con */ public void changeStr(String str, Consumer<String> con){ con.accept(str); }
B、供给型接口,无参有返回值
@Test public void test2(){ String value = getValue(() -> "hello"); System.out.println(value); } /** * Supplier<T> 供给型接口 * @param sup * @return */ public String getValue(Supplier<String> sup){ return sup.get(); }
C、函数式接口,有参有返回值
@Test public void test3(){ Long result = changeNum(100L, (x) -> x + 200L); System.out.println(result); } /** * Function<T,R> 函数式接口 * @param num * @param fun * @return */ public Long changeNum(Long num, Function<Long, Long> fun){ return fun.apply(num); }
D、断言型接口,有参有返回值,返回值是boolean类
public void test4(){ boolean result = changeBoolean("hello", (str) -> str.length() > 5); System.out.println(result); } /** * Predicate<T> 断言型接口 * @param str * @param pre * @return */ public boolean changeBoolean(String str, Predicate<String> pre){ return pre.test(str); }
*** 引用
1、 *** 引用
2、构造器引用
3、数组引用
*** 引用
对象:实例 *** 名类:静态 *** 名类:实例 *** 名 (lambda参数列表中之一个参数是实例 *** 的调用 者,第二个参数是实例 *** 的参数时可用)
Stream API
Stream操作的三个步骤
A、创建stream
B、中间操作(过滤、map)
C、终止操作
并行流和串行流
在jdk1.8新的stream包中针对***的操作也提供了并行操作流和串行操作流。并行流就是把内容切割成多个数据块,并且使用多个线程分别处理每个数据块的内容。Stream api中声明可以通过parallel()与sequential() *** 在并行流和串行流之间进行切换。jdk1.8并行流使用的是fork/join框架进行并行操作。
ForkJoin框架
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。关键字:递归分合、分而治之。采用 “工作窃取”模式(work-stealing):当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能.。
/** * java8 并行流 parallel() */ @Test public void test2() { //开始时间 Instant start = Instant.now(); // 并行流计算 累加求和 LongStream.rangeClosed(0, 10000000000L).parallel() .reduce(0, Long :: sum); //结束时间 Instant end = Instant.now(); System.out.println(Duration.between(start, end).getSeconds()); }
Optional容器
使用Optional容器可以快速的定位NPE,并且在一定程度上可以减少对参数非空检验的代码量。
/** *Optional.of(T t); // 创建一个Optional实例 * Optional.empty(); // 创建一个空的Optional实例 * Optional.ofNullable(T t); // 若T不为null,创建一个Optional实例,否则创建一个空实例 * isPresent(); // 判断是够包含值 * orElse(T t); //如果调用对象包含值,返回该值,否则返回T * orElseGet(Supplier s); // 如果调用对象包含值,返回该值,否则返回s中获取的值 * map(Function f): // 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty(); * flatMap(Function mapper);// 与map类似。返回值是Optional * * 总结:Optional.of(null) 会直接报NPE */ Optional<Employee> op = Optional.of(new Employee("zhansan", 11, 12.32, Employee.Status.BUSY)); System.out.println(op.get()); // NPE Optional<Employee> op2 = Optional.of(null); System.out.println(op2); @Test public void test2(){ Optional<Object> op = Optional.empty(); System.out.println(op); // No value present System.out.println(op.get()); }
接口中可以定义默认实现 *** 和静态 ***
在接口中可以使用default和static关键字来修饰接口中定义的普通 ***
在JDK1.8中很多接口会新增 *** ,为了保证1.8向下兼容,1.7版本中的接口实现类不用每个都重新实现新添加的接口 *** ,引入了default默认实现,static的用法是直接用接口名去调 *** 即可。当一个类继承父类又实现接口时,若后两者 *** 名相同,则优先继承父类中的同名 *** ,即“类优先”,如果实现两个同名 *** 的接口,则要求实现类必须手动声明默认实现哪个接口中的 *** 。
新的日期API LocalDate | LocalTime | LocalDateTime
新的日期API都是不可变的,更使用于多线程的使用环境中。
之前使用的java.util.Date月份从0开始,我们一般会+1使用,很不方便,java.time.LocalDate月份和星期都改成了enum * java.util.Date和SimpleDateFormat都不是线程安全的,而LocalDate和LocalTime和最基本的String一样,是不变类型,不但线程安全,而且不能修改。 * java.util.Date是一个“万能接口”,它包含日期、时间,还有毫秒数,更加明确需求取舍 * 新接口更好用的原因是考虑到了日期时间的操作,经常发生往前推或往后推几天的情况。用java.util.Date配合Calendar要写好多代码,而且一般的开发人员还不一定能写对。
/** * 日期转换常用,之一天或者最后一天... */ public static void localDateTransferTest(){ //2018-05-04 LocalDate today = LocalDate.now(); // 取本月第1天: 2018-05-01 LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth()); // 取本月第2天:2018-05-02 LocalDate secondDayOfThisMonth = today.withDayOfMonth(2); // 取本月最后一天,再也不用计算是28,29,30还是31: 2018-05-31 LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth()); // 取下一天:2018-06-01 LocalDate firstDayOf2015 = lastDayOfThisMonth.plusDays(1); // 取2018年10月之一个周三 LocalDate thirdMondayOf2018 = LocalDate.parse("2018-10-01").with(TemporalAdjusters.firstInMonth(DayOfWeek.WEDNESDAY)); }
总结
1、Java 8允许我们给接口添加一个非抽象的 *** 实现,只需要使用 default关键字即可;
2、新增lambda表达式;
3、提供函数式接口;
4、Java 8 允许你使用关键字来传递 *** 或者构造函数引用;
5、我们可以直接在lambda表达式中访问外层的局部变量。JDK8作为稳定版长期被使用。
以下将介绍jdk9-11的相关特性,欢迎留下脚印~