Java8新特性

Java8新特性

Java发展历史

目录

Java8新特性

Java发展历史

java发展历史

OpenJDK和OracleJDK

Lambda表达式

产生背景

Lambda定义

Lambda的语法规则

Lambda表达式的省略写法

@FunctionalInterface注解

@Lambda表达式的原理

接口中新增方法

前后对比

默认方法

静态方法

函数式接口

产生背景

定义

函数式接口类型

方法引用

产生背景

方法引用语法格式

Stream API

产生背景

Stream定义

Stream的获取方式

Stream使用注意

Stream常用方法

Stream常用方法实例

Stream结果收集

并行的Stream流

Optional类

产生背景

定义

获得Optional对象

Optional常用方法

新时间日期API

产生背景

JDK8新日期时间API

日期时间常见操作

格式化和解析操作

Instant类

时间校正器

小结

其他新特性

重复注解

类型注解


java发展历史

sun 91年绿色计划,开发一种可以在机顶盒、冰箱等电子消费产品上运行的程序框架 Java语言前身-Oak(橡树)

14年3月到17年9月Java 8

OpenJDK和OracleJDK

java由Sun公司开发,OpenJDK是06年把java开源形成的项目,由Sun和Java社区提供支持

OracleJDK完全由Oracle公司开发,是基于OpenJDK开发的商业版本

Lambda表达式

产生背景

比如说我们在创建线程的时候我们用new Thread的方式,该方式的参数是Runnable接口

一个方法的参数是一个接口,在使用时,我们需要new该接口的实现类,并且实现接口中的方法。方法名、返回值、参数列表等等都需要重写一遍且不可有错误,但我们关注的仅仅是方法的方法体代码。

Lambda定义

简化了匿名内部类的使用,可以理解为是一段可传递的代码,使用的时候该接口必须是函数式接口

Lambda表达式是一种语法糖,简化了匿名内部类的冗余代码。并且每一个表达式必须有一个函数式接口与之对应。(语法糖就是指编程语言中可以更容易的表达一个操作的语法,他可以使这个操作变得更清晰更简便。)

Lambda的语法规则

(参数类型 参数名称)->{

代码题;

}

方法的参数或局部变量类型必须为接口

接口中有且仅有一个抽象方法

Lambda表达式的省略写法

1、方法体只有一行代码,可省略{}同时省略;

2、参数类型可以省略

3、只有一个参数,()也可以省略

@FunctionalInterface注解

信息性注解,用来指示接口是函数式接口。

函数式接口,就是只有一个抽象方法,其他方法有默认实现。

@Lambda表达式的原理

匿名内部类的本质是在编译时生成一个Class文件。类名$.class文件我们可以通过反编译工具来看看生成的代码(SJad工具)但写有Lambda表达式生成的文件反编译报错,需要使用JDK自带的javap工具(可以对字节码进行反汇编操作) javap -c -p 文件.calss c对代码进行反汇编 p显示所有类和成员

总结:

匿名内部类编译时候就是生成一个class文件

Lambda表达式程序运行时动态生成class文件会新增一个私有静态方法+一个内部类

其中内部类实现了函数式接口,并且在实现方法中会调用生成的静态方法

私有静态方法的方法体就是我们写的Lambda表达式的方法体。

从而达到实现了函数接口,又执行了我们写的方法体。

接口中新增方法

前后对比

接口中的方法类型:

JDK8之前:静态常量、抽象方法

JDK8之后:静态常量、抽象方法、默认方法(default)、静态方法(static)

默认方法

产生背景

jdk8以前接口中只能有抽象方法和静态常量,但是如果在接口中新增抽象方法,所有实现类中就都必须实现该方法,非常不利于接口扩展

默认方法语法规则

default String test(){
    方法体
}

静态方法

产生背景

也是为了利于接口的扩展

静态方法语法规则

接口中的静态方法在实现类中不可以被重写,调用只能通过接口名.静态方法名();

函数式接口

产生背景

Lambda表达式使用前提是得有函数式接口,JDK中提供了大量常用函数式接口

定义

只有一个抽象方法的接口,只需要一个函数必须要实现

函数式接口类型

JDK提供的函数式接口主要在java.util.function包中

Supplier

无参有返回值 T get()

Consumer

有参无返回值 void receive(T t)

要实现 消费一个数据,首先做一个操作,然后再做一个操作,实现组合。可以使用Consumer接口中的 默认方法——andThen()

Function

有参有返回 R apply(T t)

Predicate

有参返回Boolean boolean test(T t)

方法引用

产生背景

解决Lambda表达式冗余,如果Lambda表达式方法体中的操作已经有现成的方法实现,则可以直接引用该方法

方法引用语法格式

符号表示::

如果Lambda表达式所要实现的方案,已经有其他方法存在相同代码,就可以引用

对象名::方法名 类名::静态方法 类名::普通方法 new类名::new 构造器

Stream API

产生背景

对集合元素进行操作,除了必须的增删改查,最多的操作就是遍历集合,针对不同需求可能需要多次遍历,为了更高效处理引入流

Stream定义

这里的Stream和IO流没半毛钱关系。

流式思想类似于工厂车间流水线,每个相同的商品相当于元素中的集合,流水线可以对集合中的元素进行多道工序的加工操作。

Stream的功能有筛选、切片、映射、查找、去重、统计、匹配、规约

Stream的获取方式

根据Collection获取

Collection接口加入了一个新的默认方法stream,Collection所有的实现都可以通过调用改默认方法来获取Stream流

但是Map接口没有实现Collection,我们可以获取对应的key、value集合

map=new HashMap<>();
key=map.keySet().stream();
value=map.values().stream();
//entry
map.entrySet.stream();

通过Stream的of()

数组对象不可能添加默认方法,所有Stream接口中提供了静态方法of()

Stream<String> a=Stream.of("aaa","bbb");

Stream使用注意

Stream只能操作一次

Stream方法返回的是新流

Stream不调用终结方法,中间的操作都不会执行

Stream常用方法

count、forEach、filter、limit、skip、map(映射)、concat(组合)、match(匹配)、find(查找)

方法分类为:终结方法count、forEach、match(返回值类型不再是Stream,不支持链式调用)、非终结方法

Stream.of("a","b","c")
.filter((s)->s.contains("a"))
.forEach(System.out :: println)

Stream常用方法实例

map

map是将一个流中的数据映射到另一个流,T类型转化成R类型

Stream.of("a","b","c")
    .map(Integer :: parseInt)
    .forEach(System.out::println)

sorted

sorted是将数据排序

Stream.of("1","2","3")
    .map(Integer::parseInt)
    .sorted()//升序
    //降序.sorted((o1,o2)->o2-o1)
    .forEach(Systeam.out::println)

match 匹配

anyMatch任一满足条件、allMatch元素都满足、noneMatch元素都不满足

Stream.of("1","2","3")
    .map(Integer::parseInt)
    .allmatch(s -> s>0)//返回false

find查找

findFirst查找第一个元素

findAny查找任何元素

Optional<String> first = Stream.of("1", "3", "4", "5", "6", "1", "7").findFirst();
        System.out.println(first.get());
​
        Optional<String> any = Stream.of("1", "3", "4", "5", "6", "1", "7").findAny();
        System.out.println(any.get());

max、min

Optional<Integer> max = Stream.of("1", "3", "4", "5", "6", "1", "7")
                .map(Integer :: parseInt)
                .max((o1, o2) -> o1 - o2);
        System.out.println(max.get());
​
        System.out.println("-----------------------");
        Optional<Integer> min = Stream.of("1", "3", "4", "5", "6", "1", "7")
                .map(Integer :: parseInt)
                .min((o1, o2) -> o1 - o2);
        System.out.println(min.get());

reduce

所有数据归纳为一个数据

Integer sum = Stream.of(4, 5, 6, 9, 2, 3)
                //identity默认值
                //第一次时,会将默认值赋值给x
                //之后每次会将上一次操作结果赋值给x,y就是每次从数据中获取到元素
                .reduce(0, (x ,y) -> {
                    System.out.println("x=" + x + ",y=" + y);
                    return x+y;
                });
        System.out.println(sum);
        System.out.println("------------");

map、reduce组合

Integer count = Stream.of("张", "李", "刘", "王", "刘"
        ).map(ch -> "刘".equals(ch) ? 1 : 0)
                .reduce(0, Integer :: sum);
        System.out.println("count=" + count);

Stream结果收集

结果收集到集合中

收集到List

.collect(Collectors.toList());

.collect(Collectors.toCollection(ArrayList::new));

收集到Set

.collect(Collectors.toSet());

.collect(Collectors.toCollection(HashSet::new));

结果收集到数组中

.toArray() 返回类型Object[]

.toArray(String[]::new) 返回类型 String[]

对流中的数据做聚合计算

我们使用Stream流处理数据后,可以像数据库打的聚合函数一样对某个字段进行操作,比如获得最大、最小、求和、平均值、数据统计等等

.collect(Collectors.minBy((p1,p2)->p1.getAge()-p2.getAge()));

对流中数据做分组操作

Stream流处理数据后,可以根据某个属性将数据分组

.collect(Collectors.groupingBy(Student::getName));

.collect(Collectors.groupingBy(Student::getAge()>=18 ? "成年":"未成年"));

对流中数据做分区操作

根据值是否为true,把集合中的数据分割为两个列表,一个true列表,一个false列表

.collect(Collectors.partitioningBy(s->s.getAge()>18));

对流中数据做拼接

根据指定连接符,将所有元素连接成一个字符串

.collect(Collectors.joining("_","开始","结束"));

开始a_b_c结束

并行的Stream流

前面使用的Stream都是串行,也就是在一个线程上面执行

并行流定义

parallelStream其实就是一个并行执行的流,它通过默认的ForkJoinPool,可以提高多线程任务的速度

获取并行流

List接口中的parallelStream方法

lsit.parallelStream();

串行流转为并行流

Stream.of(1,2,3).parallel();

并行流VS串行流

对大规模数据做流操作,并行流效率最高

线程安全问题

list中有一千个元素

list.parallelStream().forEach(listNew::add); 可能会抛出异常

解决方案

加同步锁 synchronized

使用线程安全的容器 Vector、List<Integer> synchronizedList = Collections.synchronizedList(listNew);

通过Stream中的toArray/collect操作

Optional类

产生背景

这个Optional类是解决空指针的问题

之前对null值得处理if(xxx!=null)

定义

optional是一个没有子类的工具类,optional是一个可以为null的容器对象

获得Optional对象

Optional.of("wang");//方法不支持null

Optional.ofNullable("wang");//方法支持null

Optional.empty();//直接创建一个空的Optional对象

Optional常用方法

public String getNameForOptional(Optional<Student> op){
    if(op.isPresent()){
        String msg = op.map(Student::getName)
            .map(String::toUpperCase)
            .orElse("空值");
        return msg;
    }
    return null;

新时间日期API

产生背景

在java.util和java.sql的包中都有日期类,java.util.Date同时包含日期和时间,java.sql.Date仅仅包含日期。用于格式化和解析的类在java.text包下

java.util.Date线程不安全,所有日期类可变

时区处理麻烦

JDK8新日期时间API

线程安全,日期时间API位于java.time包

关键类:

1、LocalDate:表示日期2022-12-24

2、LocalTime:表示时间16:55:55.158549300

3、LocalDateTime 表示日期时间 2022-12-24T6:55:55.158549300

4、DateTimeFormatter日期时间格式化类

5、Instant 时间戳,表示一个特定的时间瞬间

6、Duration 计算2个时间的距离(LocalTime)

7、Period 计算2个日期的距离(LocalDate)

8、ZonedDateTime 包含时区的时间

日期时间常见操作

//LocalDate
LocalDate.of(2022,12,24);
LocalDate.now();
//LocalTime
LocalTime.of(9,12,33,12345);
LocalTime.now();
//LocalDateTime
LocalDateTime.of(2022,12,24,9,12,33,12346);
LocalDateTime.now();

格式化和解析操作

通过java.time.format.DateTimeFormatter类进行日期解析和格式化操作

LocalDateTime now = LocalDateTime.now();
        //指定格式:使用系统默认的格式
        DateTimeFormatter isoLocalDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        //将日期时间转换为字符串
        String format1 = now.format(isoLocalDateTime);
        System.out.println("format1=" + format1);
​
        //通过 ofPattern 方法来指定特定的格式
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String format2 = now.format(dateTimeFormatter);
        System.out.println("format2=" + format2);
​
        //将字符串解析为一个 日期时间类型
        LocalDateTime parse = LocalDateTime.parse("1991-05-13 22:22:22", dateTimeFormatter);
        System.out.println("parse=" + parse);

Instant类

JDK8新增了一个Instant类(时间戳/时间线),内部保存了从1970年1月1日00:00:00以来的秒和纳秒

Instant now=Instant.now();

时间校正器

将日期调整到【下个月的第一天】等操作

public void test01() {
        LocalDateTime now = LocalDateTime.now();
        //将当前的日期调整到下个月的一号
        TemporalAdjuster adjuster = (temporal) -> {
            LocalDateTime dateTime = (LocalDateTime) temporal;
            LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1);
            System.out.println("nextMonth=" + nextMonth);
            return nextMonth;
        };
        LocalDateTime nextMonth1 = now.with(adjuster);
        System.out.println("nextMonth1=" + nextMonth1);
​
        //我们可以通过 TemporalAdjusters 来实现
        LocalDateTime nextMonth2 = now.with(TemporalAdjusters.firstDayOfNextMonth());
        System.out.println("nextMonth2=" + nextMonth2);
    }

小结

新版日期时间API中,日期和时间对象不可变,操作日期不会影响原来的值,而是生成一个新的实例

TemporalAdjuster可以更精确的操作日期,还可以自定义日期调整期

线程安全

其他新特性

重复注解

产生背景

注解广泛应用,但是在同一个地方不能多次使用同一个注解

JDK8引入了重复注解的概念使用@Repeatable可以定义重复注解

类型注解

学习资料:波波老师的史上最全Java8新特性详解_小学生波波的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值