当前位置 博文首页 > 良月柒:【原创】万字长文带你了解 JDK8 - JDK13 的新特性
程序员的成长之路
互联网/程序员/技术/资料共享?
阅读本文大概需要 22.8 分钟。
某次面试中,面试官问到我这个问题,只勉强说出了 JDK 8 的部分特性,今天就来盘一盘!
画外音:JDK 8 就是 JDK 1.8 版本的意思,后续同理。
Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。
1. 接口增强,default,static 关键字
在 JDK 8 之前接口中只能使用抽象方法,而不能有任何方法的实现。
JDK 8 里面则可以声明 default 和 static 修饰的方法。
/** * jdk8中接口可以使用声明default和static修饰的方法 * static修饰的方法和普通的方法一样,可以被直接调用 * default修饰的方法有方法体,就和普通的方法一样,可以被重写,有点像抽象类的方法一样,但是java是单继承多实现的 */public interface Today {
void dream();
void striver();
default void victory(){??????? System.out.println("公众号:程序员的成长之路"); }
static void test(){ System.out.println("接口里的静态方法"); }
// jdk9 中还新增了private方法 private void test3() { System.out.println("私有方法"); };}
2.Base64 API
Base64 是网络上最常见的用于传输 8Bit 字节码的编码方式之一,Base64 就是一种基于 64 个可打印字符来表示二进制数据的方法,基于 64 个字符 A-Z,a-z,0-9,+,/ 的编码方式,是一种能将任意二进制数据用 64 种字元组合成字符串的方法。
JDK?8?以前的写法
编码和解码的效率比较差,公开信息说以后的版本会取消这个方法。
BASE64Encoder?encoder?=?new?BASE64Encoder();BASE64Decoder decoder = new BASE64Decoder();byte[] textByte = "公众号:程序员的成长之路".getBytes("UTF-8");//编码String encodedText = encoder.encode(textByte);//5Zyj6a2U5a+85biI(类似这样的)System.out.println(encodedText);//解码?//公众号:程序员的成长之路System.out.println(new String(decoder.decodeBuffer(encodedText),"UTF-8"));
JDK 8 的写法
编解码效率远大于 sun.misc 和 Apache commons Codec,可以自己动收压测一下速度。
Base64.Decoder?decoder2?=?Base64.getDecoder();Base64.Encoder encoder2 = Base64.getEncoder();byte[] textByte2 = "公众号:程序员的成长之路".getBytes("UTF-8");//编码String encodedText2 = encoder2.encodeToString(textByte2);//5Zyj6a2U5a+85biI(类似这样的)System.out.println(encodedText);//解码 //公众号:程序员的成长之路System.out.println(new String(decoder2.decode(encodedText2), "UTF-8"));
3.日期处理类
JDK 8 之前,SimpleDateFormate,Calendar 等类 API涉及比较差,涉及的比较差,日期时间的计算,比较都相对的麻烦,java.util.Date 还是非线程安全的 API。
JDK 8 新增日期处理类:LocalDate、LocalTime、Instant、Duration 以及 Period,这些类都包含在 java.time 包中。
LocalTime localTime = LocalTime.now();// 现在时间:18:00:49.476System.out.println("现在时间:"+localTime);
4.Optionnal类
Optional类的作用:
主要解决的问题是:空指针异常 NullPointerException
本质上是一个包含有可选值的包装类,Opertional类既可以为含有对象也可以为空。
import java.util.Optional;
public class Java8Tester { public static void main(String args[]){
Java8Tester java8Tester = new Java8Tester(); Integer value1 = null; Integer value2 = new Integer(10);?????? // Optional.ofNullable - 允许传递为 null 参数 Optional<Integer> a = Optional.ofNullable(value1);
// Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException Optional<Integer> b = Optional.of(value2); System.out.println(java8Tester.sum(a,b)); }
public Integer sum(Optional<Integer> a, Optional<Integer> b){
??????//?Optional.isPresent?-?判断值是否存在 System.out.println("第一个参数值存在: " + a.isPresent()); System.out.println("第二个参数值存在: " + b.isPresent());
// Optional.orElse - 如果值存在,返回它,否则返回默认值 Integer value1 = a.orElse(new Integer(0));
//Optional.get - 获取值,值需要存在 Integer value2 = b.get(); return value1 + value2; }}
输出结果为:
第一个参数值存在: false第二个参数值存在: true10
5.方法引用
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号?::?。
构造器引用,它的语法是Class::new,或者更一般的Class< T >::new实例如下:
Demo demo = Demo::new;
静态方法引用,它的语法是Class::static_method,实例如下:
Demo::静态方法();
特定类的任意对象的方法引用:它的语法是Class::method实例如下:
Demo::方法名();
特定对象的方法引用:它的语法是instance::method实例如下:
demo::方法名();
6.Lambda 与 函数式编程
函数式编程
面向对象编程是对数据的抽象(各种各样的POJO类);
函数式编程则是对行为的抽象(将行为作为一个参数进行传递);
所谓的函数编程,即可理解是将一个函数(也称为“行为”)作为?个参数进行传递。
================== 自定义函数是编程(1) ====================
/** * 像Runnable接口一样用FunctionalInterface注解修饰 * 加了这个注解后,接口里面必须有且只能有一个方法 */@FunctionalInterfacepublic interface Test1<R,T>{
// R表示返回值,T表示参数类型,t1 t2是具体参数 R operator(T t1, T t2);}
public class Compute { /** * 定义一个函数方法 * 需要传入a和b两个参数, * 后面的Test1<Integer,Integer> of就是传入的一个函数(行为),of是随便起的一个别名 */ public static Integer operator(Integer a,Integer b,Test1<Integer,Integer> of){ return of.operator(a,b); }}
public class Main { public static void main(String[] args) throws Exception { System.out.println(Compute.operator(2,3,(a,b)-> a+b)); System.out.println(Compute.operator(2,3,(a,b)-> a-b)); System.out.println(Compute.operator(2,3,(a,b)-> a*b)); System.out.println(Compute.operator(2,3,(a,b)-> a/b)); }}
================== 自定义函数是编程(2) ====================
@FunctionalInterfacepublic interface Test2{
void test();}
public class Main { public static void main(String[] args) throws Exception { Main.casesc(()-> System.out.println("函数式编程案例二")); }
public static void casesc(Test2 t){ t.test(); }}
Lambda
Lambda表达式,使?场景:接口中只能有一个方法;
比如Runnable接口里的run方法;Comparator接口里的compareTo方法;
Lambda 表达式的实现方式在本质是以匿名内部类的方式进行实现的。
public?static?void?main(String[]?args)?throws?Exception?{ new Thread(new Runnable() { @Override public void run() { System.out.println("jdk8以前创建线程"); } }); //()对应run()没有一个参数,->后面是方法体内容 //如果{}中的代码只有?行,?论有返回值,可以省略{}、return、分号,其他则需要加上 new Thread(()-> System.out.println("lambda表达式创建线程"));
List<String> list = Arrays.asList("a","c","d","b","e"); // jdk8以前排序 Collections.sort(list, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o2.compareTo(o1); } }); // lambda表达式排序 //,前面的对应接口前面的参数,a b 对应compare里面的参数 Collections.sort(list,(a,b)->b.compareTo(a));}
7.?四大核心函数式接口
Lambda表达式必须先定义接口,创建相关方法之后才能调用,这样做十分不便,其实java8已经内置了许多接口, 例如下面四个功能型接口.所有标注@FunctionalInterface注解的接口都是函数式接口。
Consumer 消费型接口:有入参,无返回值。适用场景:因为没有出参,常?用于打印、发送短信等消费。
public class Main { public static void main(String[] args) throws Exception { Consumer<String> c1 = obj->System.out.println(obj+": 调?用短信接?口发送短信,或者打印?日志"); c1.accept("订单id—001");
Consumer<List> c2 = obj->{ if(obj==null || obj.size()<1)return; obj.forEach(o-> System.out.println(o)); }; List<Integer> list = Arrays.asList(2,4,0,8,9,7); c2.accept(list); }}
Supplier 供给型接口:无入参,有返回值。适用场景:泛型一定和方法的返回值类型是同一种类型,并且不需要传入参数,例如:无参的工厂方法。
public class Main { public static void main(String[] args) throws Exception { Student student = newStudent(); System.out.println(student); }
public static Student newStudent(){ Supplier<Student> supplier = ()-> { Student student = new Student(); student.setName("默认名称"); return student; }; return supplier.get(); }}
class Student{ private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; }}
Function与BiFunctionObj 函数型接口:有入参,有返回值。适用场景:传入参数经过函数的计算返回另一个值。
Function:只能接受一个参数
用法一(用一个类实现接口里面的逻辑,然后直接调用)
public class FunctionObj implements Function {
@Override public Object apply(Object o) { return "对参数:"+o+"经过处理后返回结果"; }}
public class Main { public static void main(String[] args) throws Exception { System.out.println(test("hello",new FunctionObj())); }
public static String test(String a ,FunctionObj functionObj){ return functionObj.apply(a).toString(); }}
用法二(左边规定好参数和返回值类型,右边写方法体具体逻辑)
public class Main { public static void main(String[] args) throws Exception { Function<Integer,Integer> func = p->{ return p*100; }; System.out.println(func.apply(12));
Function<Integer,Boolean> function = a->a<100; System.out.println(function.apply(97)); }}
=================================================================
BiFunctionObj:能接受两个参数
// 用法一
public class BiFunctionObj implements BiFunction { @Override public Object apply(Object o, Object o2) { return (Integer.valueOf(o.toString())+Integer.valueOf(o2.toString())); }}
public class Main { public static void main(String[] args) throws Exception { System.out.println(test(2,5,new BiFunctionObj())); }
public static Integer test(Integer a,Integer b, BiFunctionObj func){ return Integer.valueOf(func.apply(a,b).toString()); }}// 用法二
public class Main { public static void main(String[] args) throws Exception { BiFunction<Integer, Integer,Boolean> func1 = (a,b)->{ return a>b; }; System.out.println(func1.apply(1,5));
BiFunction<String, String,String> func2 = (a,b)->a+b; System.out.println(func2.apply("hellow","world")); }}
Predicate 断言型接口:有入参,有返回值,返回值类型确定是boolean。适用场景:接收一个参数,用于判断是否满一定的条件,过滤数据。
public class Main { public static void main(String[] args) throws Exception { Predicate<Integer> predicate = a->a>10;
List<Integer> list = Arrays.asList(1,54,9,34,3); for(Integer l : list){ if(predicate.test(l)) System.out.println(l); } }}
8.流操作
Stream:通过将集合转换为这么?种叫做 “流”的元素队列,能够对集合中的每个元素进行任意操作。
总共分为4个步骤:
数据元素便是原始集合:如List、Set、Map等
生成流:可以是串行流stream() 或者并行流 parallelStream()
中间操作:可以是 排序,聚合,过滤,转换等
终端操作:统一收集返回一个流
一般都采用stream,因为集合操作一般里面就几百条数据,多线程的并行流效率不一定就高,还会出现线程安全问题。
public class Main { public static void main(String[] args) { List<String> list = Arrays.asList("张麻子","李蛋","王二狗","Angell"); List<Student> users = Arrays.asList(new Student("张三", 23), new Student("赵四", 24), new Student("二狗", 23), new Student("田七", 22), new Student("皮特", 20), new Student("Tony", 20), new Student("二柱子", 25)); /** * map:对集合的每个对象做处理 */ List<String> collect = list.stream().map(obj->"哈哈"+obj).collect(Collectors.toList()); list.forEach(obj->System.out.println(obj)); System.out.println("----------------"); collect.forEach(obj->System.out.println(obj)); /** * filter:boolean判断,用于条件过滤 */ System.out.println("----------------"); Set<String> set = list.stream().filter(obj->obj.length()>2).collect(Collectors.toSet()); set.forEach(obj->System.out.println(obj)); /** * sorted:对流进行自然排序 */ System.out.println("----------------"); Set<String> sorteds = list.stream().sorted().collect(Collectors.toSet()); sorteds.forEach(obj->System.out.println(obj)); // 自定义排序规则 // 根据长度排序(正序) System.out.println("----------------"); List<String> resultList = list.stream().sorted(Comparator.comparing(obj -> obj.length())).collect(Collectors.toList()); resultList.forEach(obj->System.out.println(obj)); System.out.println("----------------"); // 根据长度排序(倒序) List<String> resultList2 = list.stream().sorted(Comparator.comparing(obj -> obj.length(),Comparator.reverseOrder())).collect(Collectors.toList()); resultList2.forEach(obj->System.out.println(obj)); System.out.println("----------------"); // 手动指定排序规则(根据年龄大小排序) List<Student> collect2 = users.stream().sorted( Comparator.comparing(Student::getAge,(x,y)->{ if(x>y) { return 1; }else { return -1; } }) ).collect(Collectors.toList()); collect2.forEach(obj->System.out.println(obj.getAge()+" : "+obj.getProvince())); /** * limit:截取包含指定数量的元素 */ System.out.println("----------------"); List<String> collect3 = list.stream().limit(2).collect(Collectors.toList()); collect3.forEach(obj->System.out.println(obj)); /** * allMatch:匹配所有元素,只有全部符合才返回true */ System.out.println("----------------"); boolean flag = list.stream().allMatch(obj->obj.length()>2); System.out.println(flag); System.out.println("----------------"); /** * anyMatch:匹配所有元素,至少一个元素满足就为true */ boolean flag2 = list.stream().anyMatch(obj->obj.length()>2); System.out.println(flag2); System.out.println("----------------"); /** * max和min:最大值和最小值 */ Optional<Student> max = users.stream().max(Comparator.comparingInt(Student::getAge)); System.out.println(max.get().getAge()+" : "+max.get().getProvince()); System.out.println("----------------"); Optional<Student> min = users.stream().min((s1, s2)->Integer.compare(s1.getAge(),s2.getAge())); System.out.println(min.get().getAge()+" : "+min.get().getProvince());
/** * reduce:对Stream中的元素进行计算后返回一个唯一的值 */ // 计算所有值的累加 int value = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1 + item2).get(); // 100作为初始值,然后累加所有值 int value2 =Stream.of(1, 2, 3, 4, 5).reduce(100, (sum, item) -> sum + item); // 找出最大值 int value3 =Stream.of(1, 4, 5, 2, 3).reduce((x,y)->x>y?x:y).get(); System.out.println(value); System.out.println(value2); System.out.println(value3); }}
class Student { private String province; private int age; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Student(String province, int age) { this.age = age; this.province = province; }}
9.终端操作收集器:Collector
/** * 数据结构收集:Collectors */public class Main { public static void main(String[] args) throws Exception { List<String> data = Arrays.asList("张三","王五","李四"); List<String> list = data.stream().collect(Collectors.toList()); Set<String> set = data.stream().collect(Collectors.toSet()); LinkedList<String> linkedList = data.stream().collect(Collectors.toCollection(LinkedList::new)); System.out.println(list); System.out.println(set); System.out.println(linkedList); /* Collectors.toMap() Collectors.toSet() Collectors.toCollection() :?用?自定义的实现Collection的数据结构收集 Collectors.toCollection(LinkedList::new) Collectors.toCollection(CopyOnWriteArrayList::new) Collectors.toCollection(TreeSet::new) */ }}
//============================================================
/** * 拼接函数:joining */public class Main { public static void main(String[] args) throws Exception { List<String> list = Arrays.asList("springBoot","springCloud","netty"); String result1 = list.stream().collect(Collectors.joining()); String result2 = list.stream().collect(Collectors.joining("——")); String result3 = list.stream().collect(Collectors.joining("—", "【",""));
String result4 = Stream.of("hello", "world").collect(Collectors.joining("—", "【", "】"));
System.out.println(result1); System.out.println(result2); System.out.println(result3); System.out.println(result4); }}
//============================================================
/** * 分组:partitioningBy */public class Main { public static void main(String[] args) throws Exception { List<String> list = Arrays.asList("sdfsdf","xxxx","bbb","bbb"); Map<Boolean, List<String>> collect = list.stream().collect(Collectors.partitioningBy(obj -> obj.length() > 3)); System.out.println(collect); }}
//============================================================
/** * 分组:group by * 统计:counting */public class Main { public static void main(String[] args) throws Exception { List<Student> students = Arrays.asList( new Student("?东", 23), new Student("?东", 24), new Student("?东", 23), new Student("北京", 22), new Student("北京", 20), new Student("北京", 20), new Student("海南", 25)); // 通过名称分组 Map<String, List<Student>> listMap = students.stream().collect(Collectors.groupingBy(obj -> obj.getProvince())); listMap.forEach((key, value) -> { System.out.println("========"); System.out.println(key); value.forEach(obj -> { System.out.println(obj.getAge()); }); }); System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); // 根据名称分组,并统计每个分组的个数 Map<String, Long> map = students.stream().collect(Collectors.groupingBy(Student::getProvince, Collectors.counting())); map.forEach((key,value)->{ System.out.println(key+"省人数有"+value); }); }}
class Student { private String province; private int age; public String getProvince() { return province; } public void setProvince(String province) { this.province = province; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Student(String province, int age) { this.age = age; this.province = province; }}
//============================================================
/** * 统计函数:summarizing */public class Main { public static void main(String[] args) throws Exception { List<Student> students = Arrays.asList( new Student("?东", 23), new Student("?东", 24), new Student("?东", 23), new Student("北京", 22), new Student("北京", 20), new Student("北京", 20), new Student("海南", 25)); // summarizingInt;summarizingLong;summarizingDouble IntSummaryStatistics summaryStatistics = students.stream().collect(Collectors.summarizingInt(Student::getAge)); System.out.println("平均值:" + summaryStatistics.getAverage()); System.out.println("人数:" + summaryStatistics.getCount()); System.out.println("最大值:" + summaryStatistics.getMax()); System.out.println("最小值:" + summaryStatistics.getMin()); System.out.println("总和:" + summaryStatistics.getSum()); }}
/** * reduce:对Stream中的元素进行计算后返回一个唯一的值 */ // 计算所有值的累加 int value = Stream.of(1, 2, 3, 4, 5).reduce((item1, item2) -> item1 + item2).get(); // 100作为初始值,然后累加所有值 int value2 =Stream.of(1, 2, 3, 4, 5).reduce(100, (sum, item) -> sum + item); // 找出最大值 int value3 =Stream.of(1, 4, 5, 2, 3).reduce((x,y)->x>y?x:y).get(); System.out.println(value); System.out.println(value2); System.out.println(value3);
JDK 9 发布于 2017 年 9 月 22 日,带来了很多新特性,其中最主要的变化是已经实现的模块化系统。
1. try-with-resource
import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;
public class Main { public static void main(String[] args) throws Exception { String path = "/Users/jack/Desktop/t.txt"; /** * jdk 1.7以前关闭资源一般是在finally里面操作的 */ OutputStream out = new FileOutputStream(path); try {????????????out.write(("公众号:程序员的成长之路").getBytes()); } catch (Exception e) { e.printStackTrace(); }finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } /** * jdk1.7的时候,可以在try()里声明资源,会在try-catch代码块结束后自动关闭掉。 * try结束后自动调用的close方法,这个动作会早于finally里调用的方法 * bu管是否出现异常,try()里的实例都会被调用close方法 * try里面可以声明多个自动关闭的对象,越早声明的对象,会越晚被close掉 */ try (OutputStream out2 = new FileOutputStream(path);){ out2.write(("公众号:程序员的成长之路").getBytes()); } catch (Exception e) { e.printStackTrace(); } /** * jdk1.9之后,对try()做了改进,在try外进行初始化,在括号内引用 */ OutputStream out3 = new FileOutputStream(path); try (out3) {????????????out3.write(("公众号:程序员的成长之路").getBytes()); } catch (Exception e) { e.printStackTrace(); } }}
2. stream
public class Main { /** * jdk9中的Stream流新增了两个api */ public static void main(String[] args) throws Exception { /** * takeWhile:遍历每个对象,直到遇到第?个false时,返回前面所有元素,如果没有false,将返回?一个空的 Stream */ List<String> list1 = List.of("springboot","java","html","","git").stream() .takeWhile(obj->!obj.isEmpty()).collect(Collectors.toList()); System.out.println(list1); /** * dropWhile:与takeWhile相反 */ List<String> list2 = List.of("springboot","java","html","","git").stream() .dropWhile(obj->!obj.isEmpty()).collect(Collectors.toList()); System.out.println(list2); }}
3.?of 创建只读集合
public class Main { public static void main(String[] args) throws Exception { /** * JDK9之前创建只读集合 */ List<String> list = new ArrayList<String>(); list.add("张三"); list.add("李四"); list.add("王五"); list.remove(0); System.out.println(list); //设置为只读List集合 // unmodifiableMap(map); unmodifiableMap(set); list = Collections.unmodifiableList(list); // 报错:java.lang.UnsupportedOperationException //list.remove(0); System.out.println(list); /** * jdk9创建只读集合 */ List<String> list2 = List.of("mysql", "linux", "redis"); list2.remove(0); System.out.println(list2); }}