# java-jdk-new-feature **Repository Path**: langkeyin/java-jdk-new-feature ## Basic Information - **Project Name**: java-jdk-new-feature - **Description**: java 常用版本jdk的新特性 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-12-03 - **Last Updated**: 2020-12-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Java jdk 各个版本的新特性及代码案例学习笔记 ## 一、jdk8新特性 附录: 学习来源和感谢:[菜鸟教程-java8新特性](https://www.runoob.com/java/java8-new-features.html) ### 1. Lambda表达式 Lambda表达式,也称作闭包或者匿名函数,是Java8**最重要**的特性,lambda允许把函数作为一个方法的参数(函数作为参数传递到方法中) 语法表达式 ``` (parameters) -> expression 或者 (parameters) -> {statements;} 感觉有点像JS ES的箭头函数,也是闭包的概念 # 相关代码见/java8/LambdaDemo.java ``` 注意: Lambda访问的局部变量虽然可以不用声明为final,但是为隐式final,及不可修改其中的变量。其次不可以提前定义表达式()中的变量 ```java // 定义外层局部变量 // 测试0: 不能定义闭包里面的同名变量 // int param = 7; (放开会报错) int num = 1; // param, 是闭包函数,箭头函数的参数,不能和外面的重名 Converter s = (param) -> { System.out.println(param + num); }; // 这里的5传入后就是param? // 输出是? s.convert(5); // 测试1:输出是? 接口里面只能有一个方法,不然报错, // #s.convert2(7); 函数式接口,只能有一个抽象方法 // 测试2:同样在这里修改num,闭包位置也会报错,显示num必须是final // num = 5; // 放开会报错 # 相关代码见/java8/LambdaDemo.java ``` ### 2. 方法引用和函数式接口 **java 8 函数式接口 @FunctionalInterface** Lambda表式的时候, 注意到比如上一demo中 Converter s中,只能有一个convert方法。 所谓的函数式接口,首先是一个接口, 然后在接口里面只能有一个抽象方法。这种类型的接口称作**SAM**(Single Abstract Method interfaces)接口 **函数式接口的用途** 主要用在Lamdba表达式和方法引用中 ```java // lambda中 @FunctionnalInterface interface GreetingServcie { void sayMessage(String message) } // 使用lambda表达式来表示该接口的一个实现. (Java8之前一般使用匿名类来实现) GreetingService greetingService = (message) -> System.out.println("hello "+ message) ``` **@FunctionalInterface注解的作用** Java8为函数式引用了改新注解, 主要用于SAM编译错误检查。 比如在demo01Lambda中。 我定义了两个convert() 和 convert01(), 但是会存运行时才提示报错,加了注解那么书写时就会提示了。 **函数式接口里允许定义默认的方法** 函数式接口定义里面,只能有一个**抽象方法**sayMessage(), 但是静态方法不是抽象方法,是一个已经实现了的方法,所以满足SAM。 by the way, **抽象类**: 被abstract关键字修饰的类, 可以包含一个或者多个抽象方法,也可以包含普通实例方法和静态方法,还可以包含成员变量和构造方法。抽象类不能被实例化,只能作为父类。定义抽象类,将一类对应的共同点抽象出来。抽象类不一定有抽象方法,有抽象方法的类一定是抽象类或者接口。**抽象方法**: 抽象方法没有具体的方法体,使用abstract关键字修饰,有点类似接口,所以大多人写抽象类抽象方法,也会写成接口实现。 ```java // lambda中 @FunctionnalInterface interface GreetingServcie { void sayMessage(String message); default void doSomeMoreWork(){ // methods body } default void doSomeOtherWork() { // methods body } } ``` **绕远了,返回重点方法引用** 方法引用通过方法的名字来指向一个方法,使用一对::。方法引用和lambda表达式有着相同的特性(都存在一个目标类型,并需要准华为函数式接口实例),不过不需要为方法引用提供方法体,可以直接使用方法名调用以后的方法。一个小问题:**方法引用**和**方法调用**的区别?和**Lambda表达式**的区别是? ```java // 引用Car里面的方法 // 1. 构造器引用, 语法Class::new final Car car = Car.create(Car::new); final List cars = Arrays.asList(car); System.out.println("cars size = " + cars.size()); // 2. 静态方法引用, Class::method cars.forEach(Car::collide); // 3. 特定类任意对象方法引用, 语法是 Class::method cars.forEach(Car::repair); // 4. 特定对象的方法引用, 语法是instance::method final Car police = Car.create(Car::new); // 重点, car实例对象police调用类中的方法follow cars.forEach(police::follow); // 上面一行的函数一行, 等于下面一行的Lambda引用, 输出是一样的 cars.forEach(param -> param.follow(param)); ``` **方法引用的种类** 1. 静态方法引用: ClassName::methodName 2. 实例上的实例方法引用: instanceReference::methodName 3. 超类上的实例方法引用:super::methodName 4. 类型上的实例方法引用:ClassName::methodName 5. 构造方法引用:Class::new 6. 数组构造方法引用 TypeName[]::new ### 3. 默认方法 默认方法就是指,接口可以有实现的方法,不需要实现类去实现其方法(主要是为了解决业务变更,接口修改和对应实现不兼容的问题) 静态默认方法,是在默认方法基础上添加static关键字 ```java public class Demo03DefaultMethod implements Vehicle, FourWheeler { public static void main(String[] args) { Demo03DefaultMethod defaultMethod = new Demo03DefaultMethod(); defaultMethod.showMessage(); } // 实现了两个接口,都存在showMessage默认方法,怎么确定调用的是谁的showMessage? @Override public void showMessage() { // 使用supper关键字来指定接口默认的方法 Vehicle.super.showMessage(); } } // 多个默认方法的问题 interface Vehicle { /** * 接口的默认方法 */ default void showMessage() { System.out.println("Vehicle.showMessage:我是默认方法1"); } } interface FourWheeler { /** * 与另外一个接口同名的默认方法,如果同时implements两个接口,那怎么区分调用的是谁呢? */ default void showMessage() { System.out.println("FourWheeler.showMessage: 我是自轮车的默认方法"); } /** * 静态默认方法, 同传统的静态方法一样,不是要实例对象,直接接口即可调用 */ static void sayHi() { System.out.println("FourWheeler.sayHi: 我是静态默认方法"); } } ``` ### 4. Stream流式处理数据 以一种声明的方式来处理数据 Stream(流),是一个来自数据源的元素队列并支持聚合操作, 不同于Collection操作, Stream还有两个特征,**Piplining**: 多个操作可串联成一个管道;**内部迭代**:不同于对集合通过Iterator和forEach实现外部迭代, Stream通过访问者模式(Visitor)实现内部迭代。 ```java Collection integers = Arrays.asList(1, 3, 2, 7, 5, 4); /** * 集合接口创建流 * 筛选出大于2的数, 存在内部迭代 * .collect(), 将集合变成List, */ List filtered = integers.stream().filter(num -> num >= 2).collect(Collectors.toList()); /** * 流可以通过forEach进行迭代 */ filtered.stream().forEach(System.out::println); /** * 流排序后输出 */ filtered.stream().sorted().forEach(System.out::println); ``` ### 5. Optional类 Optional类是一个可以为Null的容器对象, 值存在则用isPresent()返回true, 使用get()可以取出对象 主要是解决对象为空时候引起的空指针异常,比如数据库根据id查询,没有查询到返回空对象汇报空指针错误 ```java public static void main(String[] args) { Demo05Optional demo05Optional = new Demo05Optional(); Integer value1 = null; Integer value2 = new Integer(10); // Optional.ofNullable - 允许传递一个为null的参数 Optional a = Optional.ofNullable(value1); // Optional.of - 如果传递参数为null,则会抛出空指针异常,当然Optional不止这些类方法,可以查看对应api Optional b = Optional.of(value2); System.out.println("Demo05Optional.main: value1+value2="+demo05Optional.sum(a, b)); } private Integer sum(Optional a, Optional b) { // 处理null值,并将null更正为0 System.out.println("第一个参数存在? "+ a.isPresent()); System.out.println("第二个参数存在? "+ b.isPresent()); System.out.println("a = " + a + ", b = " + b); // 如a存在则返回,否则返回0, jdk11中对orElse又做了更深的扩展允许多分支条件判断,而不是两个分支if else Integer value1 = a.orElse(new Integer(0)); Integer value2 = b.orElse(new Integer(0)); return value1 + value2; } ``` ### 6. Nashorn JavaScript引擎 !注意:改引擎在java11中别标记为删除,java15中直接删除,不建议使用 在Java环境下使用Java调用或者执行JS脚本, 或者在Java环境下,执行包含有java代码的js代码,感觉很扯,略过吧 ### 7. 新的日期时间API Java8发布了新的API来对进一步加强对日期和时间的处理,解决之前日期时间存在的的诸多问题。比如 1. **非线程安全**:-java.util.Date是非线程安全的,所有日期类都是可变的,这是java日期最大的问题 2. **设计很差**:java.util和java.sql中都有日期类, 互相间有包含。有点不合理 3. **时区处理麻烦**: 也提供了新的api, java.time 1. **Local**(本地):不需要处理时区问题 2. **Zoned**(时区):制定时区 ### 8. Base64 Java8内置了base64编码的编码器和解码器,有三种方式解码编码 1. 基本 2. URL 3. MIME