• 一文详解JDK8常用10个更新特性


    今日一语:当你发现编程的规律就是世界的规律时,你就是一名真正的程序员

    1 Lambda(蓝布达)表达式

    编译后会产生一个$XXXImpl1的编译文件,与匿名内部类相似,但不等同于匿名内部类。
    其原理是将方法作为参数进行传递,
    JVM会使用一个动态调用的指令调用函数式接口中的方法。
    同时使用lambda将会使代码更简洁易读。
    别名:【闭包】
    使用方式:①使用JDK自有的函数式接口【接口列表】②自定义函数式接口使用。
    如下:
    1)自定义函数式接口,并且只定义一个方法,返回值与参数没有限制

    /** 接口1 要点:参数再封装返回 */ 
    @FunctionalInterface 
    public interface EventExector { 
    	String event(String methodName); 
    } 
    /** 接口2 要点:计算两个数字之和返回 */
     @FunctionalInterface 
     public interface LambdaInterface1 { 
     	int mathOption(int a,int b); 
    }
     /** 接口3 要点:获取当前时间的时间戳 */ 
     @FunctionalInterface 
     public interface LambdaInterface2 { 
     	void catCurrentDate(Date date); 
     } 
     /** 接口4 要点:将Map中的value取出放入一个list中 */ 
     @FunctionalInterface 
     public interface LambdaInterface3 { 
     	void copyMap(Map<String,Integer> map, List<Integer> list); 
     } 
     /** 接口5 要点:无返回无参数 */ 
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2)将定义号的函数式接口通过参数传入

    /** 接口1注入方法1 */ 
    private static int operation(int a,int b,LambdaInterface1 interface1) { 
    	return interface1.mathOption(a,b); 
    } 
    /** 接口2注入方法2 */ 
    private static String realize(String target,EventExector exector) { 
    	return exector.event(target); 
    } 
    /** 接口3注入方法3 */ 
    private static void getDate(Date date,LambdaInterface2 interface2) { 
    	interface2.catCurrentDate(date); 
    } 
    /** 接口4注入方法4 */ 
    private static void replaceMap(LambdaInterface3 interface3, Map map,List list) { 
    	interface3.copyMap(map,list); 
    } 
    /** 接口5注入方法5 */ 
    private static void goToWay(LambdaInterface4 interface4) { 
    	interface4.onTheWay(); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    1. 调用方法,其中()中的参数为传入的参数, -> 代表lambda表达式的箭头指引符,
      箭头符号的后面,需注意,如果有返回值,有两种返回书写方式。
      在大括号中,使用return返回,没有大括号,直接将返回值写在箭头符后面。
      如果没有返回值,箭头符后面的指引内容将是纯粹的Java指令,不会有任何返回。
    /** 传入一个参数 有返回 */ 
    System.out.println(realize("xx", (x) -> "HELLO GUYS! " + x)); 
    /** lambda 传入两个参数 有返回 */ 
    System.out.println(operation(1, 2, (a, b) -> a + b)); 
    /** 传入一个参数,无返回 */ 
    getDate(new Date(), date -> { 
    	long time = date.getTime(); 
    	System.out.println("当前内容时间戳"+time); 
    }); 
    /** 传入两个参数,无返回 */ 
    Map<String,Integer> map = new Hashtable<>(); 
    map.put("one",1);
    map.put("two",2); 
    map.put("three",3); 
    map.put("four",4); 
    List list = new ArrayList(); 
    replaceMap((map1, list1) -> { 
    	for (String s : map.keySet()) {
    		 list.add(map.get(s)); 
     	} 
     },map,list); 
    /** 无参数无返回 */ 
    goToWay(() -> System.err.println("请纠正你的方向!")); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    4)使用lambda注意事项

    1. 外部局部变量[基本类型]放在lambda中使用时,必须使用final修饰.
      因为lambda中外部局部变量无法传递!变量值无法改变。
      final:
      ① 如果修饰给基本类型变量,则表示值不可改变;
      ② 如果修饰引用类型变量,那么变量的引用地址不可改变,但是属性值是可以改变的。

    2. 考虑性能,如果代码不繁多,可以不用lambda

    @Test 
    public void lambda2() { 
    	String name = "我醉了"; 
    	int age = 12; 
    	sayHello("高进",message -> { 
    		name = "dsa";  // 此处编译会报错 
    		age = 11; // 此处编译会报错 
    		System.out.println(message+name+age); 
    	}); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2 双冒号::语法

    这种 [方法引用] 或者说 [双冒号运算] 对应的参数类型是 Function T表示传入类型,R表示返回类型。
    比如表达式 person -> person.getAge(); 传入参数是 person,返回值是 person.getAge()
    那么方法引用 Person::getAge 就对应着 Function 类型。

    /** 所需函数式接口 */ 
    @FunctionalInterface 
    public interface Merchant<T> { 
    	T get(); 
    } 
    /** 接口使用类 */ 
    public class Computer { 
    	public static Computer create(final Merchant<Computer> merchant) { 
    		return merchant.get(); 
    	} 
    	public static void getComputer(final Computer computer) { 
    		System.out.println("地址:"+computer.toString()); 
    	} 
    	public void getPro(final Computer computer) { 
    		System.out.println(computer.hashCode()); 
    	} 
    	public void getDate() { 
    		System.out.println(LocalDate.now().get(ChronoField.YEAR)); 
    	} 
    	public void getTime() { 
    		System.out.println(LocalTime.now().get(IsoFields.DAY_OF_QUARTER)); 
    	} 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    主要有四种类型:
    1)构造器引用 [Class::new]
    Computer computer = Computer.create(Computer::new);
    2)静态方法引用 [Class::static_method]

    List<Computer> list = Arrays.asList(computer); 
    list.forEach(Computer::getComputer); 
    
    • 1
    • 2

    3)特定类的任意对象的方法引用 [Class::method]

    List<Computer> list = Arrays.asList(computer);
    list.forEach(Computer::getDate);
    list.forEach(Computer::getTime); 
    
    • 1
    • 2
    • 3

    4)特定对象的方法引用 [instance::new]

    List<Computer> list = Arrays.asList(computer); 
    final Computer huawei = Computer.create(Computer::new);`
    list.forEach(huawei::getPro); 
    
    • 1
    • 2
    • 3

    3 Optional

    JDK8处理空指针异常的工具类,减少空指针判断,可以存储空对象
    只有两个值,一个是原本值,一个是空值

    /** 静态方法 */ 
    Optional.empty() 返回一个空的Optional对象 
    Optional.of() 返回一个指定非null值的Optional 
    Optional.ofNullable() 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional	
    /** 实例方法 */ 
    optional.hashcode 返回存在值的哈希码,如果值不存在 返回 0 
    optional.toString 返回一个Optional的非空字符串,用来调试 
    optional.get 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException 
    optional.equals 判断其他对象是否等于 Optional 
    optional.isPresent 如果值存在则方法会返回true,否则返回 false 
    optional.ifPresent 如果值存在则使用该值调用 consumer , 否则不做任何事情 
    optional.filter 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional
    optional.flatMap 如果值存在,返回基于Optional包含的映射方法的值,否则返回一个空的Optional 
    optional.orElse 如果存在该值,返回值, 否则返回 other 
    optional.map 如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,
    		     则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional 
    optional.orElseGet 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果 
    optional.orElseThrow 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4 Stream

    /** 生成流 */ 
    list.stream() list.parallelStream() 
    /** 所有操作 */ 
    forEach() -> 循环操作 filter() -> 条件满足 map() -> 值处理 sorted()
    		  -> 排序 limit() -> 控制数量 summaryStatistics() -> 数据统计
    在之前需要使用mapTo系列方法,也就是前提需要有数据流(int,float,double),主要有最大值,最小值,总和,总数,平均数
    collect(Collectors.toList()) -> 转换成集合 collect(Collectors.joining(分隔符)) -> 将字符串内容合并到一起 
    /** 代码示例 */ 
    /** Stream 得到集合中元素值大于等于7的元素数量 */ 
    List<Integer> numbers = Arrays.asList(1, 2, 2752, 2200, 7857, 6, 7, 8, 45, 54, 78, 857, 3, 357857, 86, 200780); 
    numbers.stream()
    	.filter(obj -> obj % 2 == 0)
    	.limit(5).map(obj -> obj << 2)
    	.sorted((o1, o2) -> o2 - o1)
    	.forEach(integer -> {
    		System.out.print(integer+"\t"); 
    	}); 
    /** 使用Join */ 
    List<String> stringList = Arrays.asList("张三","李四","王五","赵六","钱七","胡八"," "); 
    String collect = stringList.stream()
    						   .collect(Collectors.joining("先生,"))
    						   .trim(); 
    // 取出字符串尾部的 ',' 
    System.out.println(collect.substring(0,collect.length()-1)); 
    ========控制台出入如下========= 
    11008	8800	32	24	8	 
    张三先生,李四先生,王五先生,赵六先生,钱七先生,胡八先生 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    5 时间API

    /** 月份支持 获取单独月份的支持 */
    Month 
    /*月份天数支持 可以获取月日格式的时间 */
    MonthDay 
    /**年份支持 可以获取年份*/
    Year 
    /*年份月支持 获取年月格式的时间*/
    YearMonth 
    /*时间戳操作 sql包中的获取时间戳中的方法*/
    TimeStamp
    /*瞬时时间 获取时间戳格式的时间*/ 
    Instant 
    /*星期支持 星期天数的格式时间获取*/
    DayOfWeek 
    /*日期支持 只获取基本的日期格式的时间。如【年-月-日】*/
    LocalDate 
    /*日期时间支持 获取日期时间支持,不包含时区与星期。,如【年-月-日T时:分:秒.Nano秒】,其中T分隔符*/
    LocalDateTime
    /*时间支持 只支持时间,不封装时区及任何时间信息*/ 
    LocalTime 
    /*日期时间转换器 提供日期时间格式的转换*/
    DateTimeFormatter 
    /*单线程日期时间转换器 只有一个返回字符串的静态方法*/
    DateTimeFormatterBuilder
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    6 重复注解与类型注解

    /** 重复注解,可以重复使用,原理是用了注解容器 **/ 
    @Retention(RetentionPolicy.RUNTIME) 
    @Repeatable(Annos.class) 
    public @interface Anno { 
    	String value(); 
    } 
    @Retention(RetentionPolicy.RUNTIME)  
    @interface Annos { 
    	Anno[] value(); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用重复注解的原理使用了注解容器,实际上被声明的类上的注解是容器注解

    /** 类型注解主要就是泛型与声明类型的时候可以使用,保证了程序的健壮性 */ 
    /** 定义一个可以使用在类型上的注解 */ 
    @Retention(RetentionPolicy.RUNTIME) 
    // TYPE_USE 用于类型 
    TYPE_PARAMETER 用于表示泛型 
    @Target(ElementType.TYPE_USE) 
    public @interface MyTest { 
    	String value() default ""; 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    7 concurrent并发包

    // JUC包是jdk1.8为并发编程提供的工具包 提供了一些并发工具实现,后续补充

    8 默认方法

    在JDK8之前,一旦修改了接口中的方法,所有实现类都必须改动。
    为了不影响之前的版本,提供在接口中定义默认方法的实现。
    在方法前修饰default,并在代码域中编写默认内容

    public interface Vehicle { 
    	/** 默认方法 */ 
    	default void print() { 
    		System.out.println("我是一辆车!"); 
    	} 	
    	/** 静态方法 */
    	static void hornBlock() { 
    		System.out.print("我要超车了!");
    	} 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    9 Base64编码

    // 编码 
    private static String printEncoding(String str) { 
    	Base64.Encoder encoder = Base64.getEncoder(); 
    	byte[] encode = encoder.encode(str.getBytes()); 
    	return new String(encode); 
    } 
    // 解码 
    private static String printDecoding(String str) { 
    	Base64.Decoder decoder = Base64.getDecoder(); 
    	byte[] decode = decoder.decode(str.getBytes()); 
    	return new String(decode); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    10 内置函数式接口的应用

    /** 菜鸟教程实例 */ 
    https://www.runoob.com/java/java8-functional-interfaces.html 
    /** 方法调用 */ 
    public static void main(String[] args) { 
    /** BiConsumer 接收两个参数,不返回任何结果 */ 
    getSomeSubject((s, integer) -> System.out.println(integer),"张三",12); 
    /** BiFunction 接收两个参数,返回一个结果 */ 
    String someSubject = getSomeSubject((num1, num2) -> {
    	return num1*num2+"";
    },12,10); 
    System.out.println("返回的结果为:"+someSubject); } 
    // 方法定义 
    private static String getSomeSubject(BiFunction<Integer,Integer,String> function,Integer num1,Integer num2) { 
    	return function.apply(num1,num2); 
    } 
    private static void getSomeSubject(BiConsumer<String,Integer> consumer,String name,Integer age) {
    	consumer.accept(name,age); 
    } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    感谢你的关注!有更多建议或疑问欢迎下方讨论…

  • 相关阅读:
    美国零售电商平台Target,值得入驻吗?如何入驻?
    CFdiv2-Beautiful Mirrors-(期望)
    PCB Layout爬电距离、电气间隙如何确定-安规
    软件工程的概念及其重要性
    三、T100应付管理之采购应付-入库应付管理篇
    【ES6知识】Promise 对象
    手机界面设计中12种常用布局 优漫动游
    MySQL8.0版安装教程 + Workbench可视化配置教程(史上最细、一步一图解)
    (c++)类和对象 下篇
    自定义qtquick 插件模块,支持qmldesigner
  • 原文地址:https://blog.csdn.net/qq_45903258/article/details/132582185