当前位置: 首页 > news >正文

Java16都快上线了,你该不会连Java8的特性都不会用吧?

听说微信搜索《Java鱼仔》会变更强哦!

本文收录于JavaStarter ,里面有我完整的Java系列文章,学习或面试都可以看看哦

(一)前言

2020年,Java16有了新的消息,预计将在2021年的3月16日正式发布。但是这一消息对于百分之九十的国内程序员来说都只是一个新闻而已,因为国内的绝大部分公司依然使用着Java8。这款发布于2014年的JDK版本深受各大公司的喜爱,最大的原因取决于它的稳定性。

即使如此,依然有一半以上的程序员对于Java8的特性不了解,于是我用一个周末的时间把JDK8的”新“特性肝了一遍,希望对大家有所帮助。

(二)Lambda表达式

Lambda是一个匿名函数,它允许你通过表达式来代替功能接口,使用lambda可以让代码变得更加简洁。简单来讲,lambda使用更简洁的方式给接口添加实现方法。

Lambda只能在函数式接口中使用,函数式接口就是那些只有一个抽象方法的接口,可以用注解@FunctionalInterface修饰,如果一个接口中有多个方法,就不能再使用Lambda了。

我们常用的Runnable、Comparator都是函数式接口:

Comparator

Runnable

lambda的语法格式如下:

(parameters) -> expression
或
(parameters) ->{ statements; }

2.1 无参数、无返回值

lambda实现的是对功能接口代码的简洁化,比如下面这一段:

Runnable runnable=new Runnable() {
    @Override
    public void run() {
        System.out.println("run");
    }
};

我们在创建一个Runnable对象的时候需要通过匿名内部类实现接口中定义的方法,而使用lambda表达式,上面的代码一行就可以搞定:

Runnable runnable2= () -> System.out.println("run");

2.2 有参数,无返回值

我们先写一个这样的接口:

public interface Student {
    public void getAge(int age);
}

在以前的代码中,我们需要首先写一个类去继承这个接口,或者是写一个匿名内部类,如:

Student student=new Student() {
    @Override
    public void getAge(int age) {
        System.out.println(age);
    }
};

现在就变得简单了:

Student student2=(age) -> { System.out.println(age); };

如果只有一个参数,小括号可以不写:

Student student3=age -> { System.out.println(age); };

2.3 有参数,有返回值

写一个接口:

public interface Student {
    public int getAge(int age);
}

直接写lambda表达式了:

Student student5=age -> { return age; };
System.out.println(student5.getAge(1));

(三)内置函数式接口

在前面已经介绍了什么是函数式接口,函数式接口就是那些只有一个抽象方法的接口,Java8中内置了四种函数式接口:

Consumer<T> : void accept(T t);
Supplier<T> : T get();
Function<T,R> : R apply(T t);
Predicate<T> : boolean test(T t)

这四种接口和我们平常写的没有什么区别,可以在Java8中直接调用:

3.1 Consumer

消费型接口,提供了一个参数、无返回值的接口方法,就和消费一样,花出去就没有了。

public static void main(String[] args) {
    consume(100,money -> {
        System.out.println("消费了"+money+"元");
    });
}
public static void consume(double money, Consumer<Double> consumer){
    consumer.accept(money);
}

3.2 Supplier

供给型接口,提供了无参数,有返回值的接口方法,主要起到供给数据的作用,比如实现一个生成随机数的功能:

public static void main(String[] args) {
    System.out.println(getRandomNum(()->(int)(Math.random()*100)));
}
public static int getRandomNum(Supplier<Integer> supplier){
    return supplier.get();
}

3.3 Function

函数型接口,提供带参数和返回值的接口方法,可用来对数据进行处理后再返回,比如实现一个替换字符串给功能:

public static void main(String[] args) {
   //去掉前后空格
   System.out.println(handlerString("\t\tJava鱼仔\t\t",(str)->str.trim()));
}
public static String handlerString(String string, Function<String,String> function){
    return function.apply(string);
}

3.4 Predicate

断言型接口,提供带参数和返回值的接口方法,只不过返回值是boolean类型,可用于进行判断,比如实现一个判断某个字符串是否是整数的需求

public static void main(String[] args) {
     Pattern pattern = Pattern.compile("^[-\\+]?[\\d]*$");
     if (isDigit("111",s -> {
         return pattern.matcher(s).matches();
     })){
         System.out.println("is Digit");
     }
}
public static String handlerString(String string, Function<String,String> function){
    return function.apply(string);
}

(四)Stream API

Java8中两个最重大的改变,一个是Lambda表达式,另外一个就是Stream API

Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

Stream的执行过程分为三步

1、创建stream

2、操作stream(查找、筛选、过滤等等)

3、执行stream

stream的操作属于惰性求值,即所有的操作是在执行stream时一次性执行。

4.1 创建stream

创建流的方式有四种,这里直接把创建方式的注释写进代码里

public static void main(String[] args) {
    //创建Stream
    //1、通过Collection系列集合提供的stream()或parallelStream()
    List<String> list=new ArrayList<>();
    Stream<String> stream = list.stream();

    //2、通过Arrays中的静态方法stream()获取
    Integer[] integers=new Integer[10];
    Stream<Integer> stream1 = Arrays.stream(integers);

    //3、通过Stream类中的静态方法of()
    Stream<Integer> stream2 = Stream.of(integers);

    //4、迭代创建
    Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2);
}

4.2 操作stream

stream有多种操作集合的方法,下面一一进行讲解:

为了更方便的演示,新建一个实体类User:

public class User {
    private String name;
    private int age;
    private String sex;
    //这里省略构造方法、get、set、toString方法
}

filter过滤–通过某种条件过滤元素:

filter通过Predicate接口过滤元素,下面这段代码将年龄超过30岁的过滤出来

public static void main(String[] args) {
    List<User> list= Arrays.asList(
            new User("javayz",23,"male"),
            new User("张三",25,"male"),
            new User("李四",30,"female"),
            new User("王五",33,"female")
            new User("王五",33,"female")
    );
    //过滤出年龄大于30岁的人
    Stream<User> userStream = list.stream().filter((e) -> e.getAge() >= 30);
   userStream.forEach((e)-> System.out.println(e));
}

limit截断–使元素不超过给定的数量

//对取出来的数据只展示一条
Stream<User> userStream = list.stream()
        .filter((e) -> e.getAge() >= 30)
        .limit(1);
userStream.forEach((e)-> System.out.println(e));

skip–跳过集合中的前n个数

可以和limit联合使用取出指定的范围的数

Stream<User> userStream = list.stream().skip(2);

distinct–去重,通过元素的hashcode()和equals()去除重复元素

去重是根据hashcode()和equals()方法判断重复元素的,因此实体类了需要重写hashcode()和equals()方法

Stream<User> userStream = list.stream().distinct();

map–通过Function接口对stream中的每个数据处理后返回新的数据

比如我想把所有的英文改为大写,就可以这样

List<String> list1=Arrays.asList("aa","bb","cc");
Stream<String> stringStream = list1.stream().map((str) -> str.toUpperCase());
stringStream.forEach((e)-> System.out.println(e));

flatmap–通过Function接口把stream的每一个值都换成另外个stream,最后再把所有stream连接成一个stream

这个方法的使用可能会比较难懂,通过一个例子,把包含三个字符串的集合中的每个字符串都分成一个个字符提取出来。比如把一个集合{“aa”,“bb”,“cc”}变成{‘a’,‘a’,‘b’,‘b’,‘c’,‘c’},使用map就需要这样:

List<String> list1=Arrays.asList("aa","bb","cc");
Stream<Stream<Character>> streamStream = list1.stream().map((str) -> {
    List<Character> characters = new ArrayList<>();
    for (Character ch : str.toCharArray()) {
        characters.add(ch);
    }
    return characters.stream();
});
streamStream.forEach((stream)->{
    stream.forEach((character -> System.out.println(character)));
});

它第一个stream的返回值是stream中套一个字符流,输出的时候也需要先遍历最外层的stream再遍历内层的stream,比较麻烦,于是就可以使用flatmap,他会把多个stream合并为一个。

Stream<Character> characterStream = list1.stream().flatMap((str) -> {
    List<Character> characters = new ArrayList<>();
    for (Character ch : str.toCharArray()) {
        characters.add(ch);
    }
    return characters.stream();
});
characterStream.forEach((e)-> System.out.println(e));

sorted–自然排序,按照Comparable方式排序

Stream<String> stringStream = list.stream().sorted();

sorted(Comparator comparator)–定制排序,自己写一个Comparator 实现排序

List<User> list= Arrays.asList(
        new User("javayz",23,"male"),
        new User("张三",25,"male"),
        new User("李四",30,"female"),
        new User("王五",33,"female")
);

Stream<User> sorted = list.stream().sorted((e1, e2) -> {
    return e1.getAge() == e2.getAge() ? 0 : e1.getAge() > e2.getAge() ? 1 : -1;
});
sorted.forEach((e)-> System.out.println(e));

4.3 执行stream

如果只是操作stream,流的数据是不会变化的,接下来介绍执行stream的一系列方法,首先把接下来会用的数据创建进来:

List<User> list= Arrays.asList(
        new User("javayz",23,"male"),
        new User("张三",25,"male"),
        new User("李四",30,"female"),
        new User("王五",33,"female")
);

allMatch–检查是否匹配所有元素

//判断所有的元素的sex是否都是male,这里返回false
boolean male = list.stream().allMatch((e) -> e.getSex().equals("male"));
System.out.println(male);

anyMatch–检查是否至少匹配一个元素

boolean male = list.stream().anyMatch((e) -> e.getSex().equals("male"));
System.out.println(male);//这里返回true

noneMatch–检查是不是没有元素能够匹配指定的规则

boolean male = list.stream().noneMatch((e) -> e.getSex().equals("male"));
System.out.println(male);//这里返回false

findFirst–返回第一个元素

Optional<User> first = list.stream().findFirst();
System.out.println(first.get());

findAny–返回当前流中的任意一个元素

Optional<User> first = list.stream().findAny();
System.out.println(first.get());

count–返回当前流中的元素总个数

long count = list.stream().count();
System.out.println(count);

max–返回当前流中的最大值

返回最大值和最小值依旧需要实现comparator接口方法

Optional<User> max = list.stream().max((e1, e2) -> {
    return Integer.compare(e1.getAge(), e2.getAge());
});
System.out.println(max.get());

min–返回当前流中的最小值

Optional<User> max = list.stream().min((e1, e2) -> {
    return Integer.compare(e1.getAge(), e2.getAge());
});
System.out.println(max.get());

reduce–规约,将流中的元素反复结合起来,得到一个值

List<Integer> list=Arrays.asList(1,2,3,4,5,6,7,8,9);
//从第0个元素开始,对list求和
Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(reduce);

collection–收集,将流中的数据接收成其他的形式

比如我们把上面User数据的名字收集到List数组中:

List<String> collect = list.stream().map((e) -> e.getName()).collect(Collectors.toList());
System.out.println(collect);

Collectors提供了大量的转化为其他方式的实现,这里不做过多介绍。

(五)接口中的默认方法和静态方法

在1.8之前,接口中的方法只能声明无法实现,在Java8中,接口中可以添加默认方法和静态方法了。

首先介绍默认方法,通过default修饰的方法可以在接口中增加实现。

public interface MyInterface {
    default void defaultMethod(){
        System.out.println("hello");
    }
}

另外是接口中的静态方法:

public interface MyInterface {
    public static void staticMethod(){
        System.out.println("hello");
    }
}

(六)新时间日期API

JAVA8中增加了新的时间日期API,通过代码来混个眼熟:

6.1 LocalDate、LocalTime、LocalDateTime

@Test
public void test1(){
    //获取当前时间
    LocalDateTime localDateTime=LocalDateTime.now();
    System.out.println(localDateTime);
    //增加两年
    System.out.println(localDateTime.plusYears(2));
    //减少两年
    System.out.println(localDateTime.minusYears(2));
    //获取年、月、日、小时、分钟、秒
    System.out.println(localDateTime.getYear());
    System.out.println(localDateTime.getMonthValue());
    System.out.println(localDateTime.getDayOfMonth());
    System.out.println(localDateTime.getHour());
    System.out.println(localDateTime.getMinute());
    System.out.println(localDateTime.getSecond());
}

6.2 Instant :时间戳

@Test
public void test2(){
    Instant instant=Instant.now();
    //获取当前时间戳
    System.out.println(instant.toEpochMilli());
}

6.3 Duration :计算两个时间间隔

@Test
public void test3(){
    Instant instant1=Instant.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Instant instant2=Instant.now();
    Duration between = Duration.between(instant1, instant2);
    //计算出两个日期相差的毫秒、分钟、小时等数据
    System.out.println(between.toMillis());
    System.out.println(between.toMinutes());
    System.out.println(between.toHours());
}

6.4 Period:计算两个日期间隔

@Test
public void test4(){
    LocalDate localDate1=LocalDate.of(2020,11,1);
    LocalDate localDate2=LocalDate.now();
    Period between = Period.between(localDate1, localDate2);
    //计算相差几年几个月零几天
    System.out.println(between.getYears());
    System.out.println(between.getMonths());
    System.out.println(between.getDays());
}

6.5 TemporalAdjuster:时间矫正器

TemporalAdjuster用于对时间进行操作,我们可以通过TemporalAdjusters这个类调用大量对时间操作的方法,比如下个周日等等。

@Test
public void test5(){
    LocalDateTime localDateTime=LocalDateTime.now();
    LocalDateTime with = localDateTime.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
    System.out.println(with);
}

6.6 格式化时间

时间格式化类在JAVA8之前用的最多的就是SimpleDateFormat,现在多了一个叫做DateTimeFormatter的格式化类:

@Test
public void test6(){
    DateTimeFormatter dtf= DateTimeFormatter.ofPattern("yyyy-MM-dd");
    LocalDateTime localDateTime=LocalDateTime.now();
    System.out.println(localDateTime.format(dtf));
}

(七)总结

总的来讲,Java8的新东西还是有很多的,虽然现在很多程序员都不习惯使用新的语法,但对这些新语法也不要抗拒,毕竟现在最新的Java版本已经到16了呢!

相关文章:

  • ubuntu 更改密码
  • 从考研失败到最具成长力员工,这个2020就像过山车一样
  • 解析MySQL权限表
  • 图解Mysql索引的数据结构!看不懂你来找我
  • 如何用Java写一个规范的http接口?
  • Getting started with Java EE 8 MVC(1)
  • 产品经理问我:手动创建线程不香吗,为什么非要用线程池呢?
  • 将桌面上的硬盘移除
  • 白话Mysql的锁和事务隔离级别!死锁、间隙锁你都知道吗?
  • Jquery datatables 使用方法
  • 基于SpringBoot实现文件的上传下载
  • 作为一个后端开发,你需要了解多少Nginx的知识?
  • CAShapeLayer(持续更新)
  • 一个成熟的Java项目如何优雅地处理异常
  • UITableView分页
  • -------------------- 第二讲-------- 第一节------在此给出链表的基本操作
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • 【刷算法】求1+2+3+...+n
  • 77. Combinations
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • Angular数据绑定机制
  • Babel配置的不完全指南
  • Django 博客开发教程 16 - 统计文章阅读量
  • ECS应用管理最佳实践
  • egg(89)--egg之redis的发布和订阅
  • ES6核心特性
  • HashMap ConcurrentHashMap
  • Java多态
  • Lucene解析 - 基本概念
  • mockjs让前端开发独立于后端
  • React-redux的原理以及使用
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 前端每日实战 2018 年 7 月份项目汇总(共 29 个项目)
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 微服务框架lagom
  • 优化 Vue 项目编译文件大小
  • 这几个编码小技巧将令你 PHP 代码更加简洁
  • 《天龙八部3D》Unity技术方案揭秘
  • Nginx实现动静分离
  • ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  • $.ajax()
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (附源码)ssm教材管理系统 毕业设计 011229
  • (接口自动化)Python3操作MySQL数据库
  • (离散数学)逻辑连接词
  • (四)linux文件内容查看
  • (原創) X61用戶,小心你的上蓋!! (NB) (ThinkPad) (X61)
  • (转)socket Aio demo
  • 、写入Shellcode到注册表上线
  • .htaccess配置常用技巧
  • .NET Framework 服务实现监控可观测性最佳实践
  • .NET的微型Web框架 Nancy
  • .net利用SQLBulkCopy进行数据库之间的大批量数据传递
  • .NET设计模式(11):组合模式(Composite Pattern)