• Java8 新特性


    日期处理

    在Java8之前,操作日期的话不是很方便,有些地方需要自己编写实现。在Java8中的java.time包下新增很多关于日期处理的类,通过这些类可以使开发者更便捷的操作日期,这些类都是final修饰的,并且是线程安全的。

    java8新增的关于日期处理的包:

      • java.time
        主要的日期处理相关的类都在这个包下
      • java.time.chrono
        这个包中的类主要是对应不同国家的日历记录的方式,国际上通用的日历是2018-01-01这种,但是有些国家有自己独特的记录日历的方式,比如我们国家的农历,不过可惜的是在这个包下没有农历相关的类。
      • java.time.format
        日期格式化相关的类在这个包下
      • java.time.temporal
        该包下存放着不同国家记录时间的方式相关的类
      • java.time.zone
        这个包下存放着设置时区相关的类,目前我们国家是在东八区。

    LocalDate:只能处理日期相关的数据,不包含时间

     1     public static void main(String[] args) {
     2         //获取当前日期
     3         LocalDate date1 = LocalDate.now();
     4         System.out.println(date1);
     5         
     6         int year = date1.getYear();
     7         int month = date1.getMonthValue();
     8         int day = date1.getDayOfMonth();
     9         
    10         //日期格式化
    11         String date2 = date1.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
    12         System.out.println(date2);
    13         
    14         //是否是闰年
    15         boolean leap = date1.isLeapYear();
    16         
    17         //判断当前月份的天数
    18         int len = date1.lengthOfMonth();
    19         
    20         //自定义日期方式一
    21         LocalDate date3 = LocalDate.parse("2017-07-11");
    22         //自定义日期方式二
    23         LocalDate date4 = LocalDate.of(2017, 7, 11);
    24         
    25         //判断日期是否相等
    26         if(date3.equals(date4)){
    27             System.out.println("日期相等");
    28         }
    29         
    30         //获取一周后的日期
    31         LocalDate date5 = date1.plus(1, ChronoUnit.WEEKS);
    32         System.out.println("一周后的日期:" + date5);
    33     }

    * LocalTime:只能处理时间,不包括日期

     1     public static void main(String[] args) {
     2         //获取当前时间
     3         LocalTime time1 = LocalTime.now();
     4         //获取当前时间,不包含毫秒
     5         LocalTime time2 = LocalTime.now().withNano(0);
     6         System.out.println(time1);
     7         System.out.println(time2);
     8         
     9         //一小时后
    10         LocalTime time3 = time1.plusHours(1);
    11         System.out.println(time3);
    12         
    13         //自定义时间
    14         LocalTime time4 = LocalTime.parse("06:12:30");
    15         LocalTime time5 = LocalTime.of(6, 30, 12);
    16         
    17     }

    * LocalDateTime:可以处理日期和时间

    1     public static void main(String[] args) {
    2         LocalDateTime date1 = LocalDateTime.now();
    3         System.out.println(date1);
    4         
    5         LocalDateTime date2 = LocalDateTime.of(2017, 7, 20, 13, 20, 15);
    6     }

    * Duration和period

     1     public static void main(String[] args) {
     2         LocalDate date1 = LocalDate.of(2017, 7, 11);
     3         LocalDate date2 = LocalDate.of(2017, 7, 13);
     4         //Period处理LocalDate
     5         Period period = Period.between(date1, date2);
     6         System.out.println(period.getDays());
     7         
     8         
     9         LocalTime time1 = LocalTime.of(18, 20, 10);
    10         LocalTime time2 = LocalTime.of(18, 30, 10);
    11         
    12         Duration duration = Duration.between(time1, time2);
    13         System.out.println(duration.getSeconds());
    14     }

    设定时区

    通过ZoneId这个类中的of方法可以设定时区:

    1     //获取本地的时区时间
    2     ZonedDateTime now = ZonedDateTime.now();
    3     System.out.println(now);
    4 
    5     //设定时区为美国洛杉矶
    6     ZoneId zone = ZoneId.of("America/Los_Angeles");
    7     //获取指定的时区时间
    8     ZonedDateTime usa = ZonedDateTime.now(zone);
    9     System.out.println(usa);

    函数式接口

    jdk8中新增了函数式接口,在一个接口中只有一个抽象方法的接口被称为函数式接口,例如java.lang.Runnable,java.util.concurrent.Callable。jdk8中新增了@FunctionalInterface注解来标注一个函数式接口。

    default方法

    jdk8中新增了default方法,jdk8之前接口中的方法必须都是抽象的,在jdk8中允许接口中定义非抽象方法,在接口中的非抽象方法上使用default修饰即可,比如在jdk8中新增了一个函数式接口:java.util.function。java8中打破了接口中的方法必须都是抽象的这一规范,有一个好处就是可以提高程序的兼容性,在java.lang.Iterable接口中新增了forEach方法,该方法就是使用default修饰的。
    在接口中定义default方法可以变相的让java支持"多继承"

    自定义函数接口:

    1 @FunctionalInterface
    2 public interface MyInterface {
    3     void m1(int a, int b);
    4 
    5     default String m2(String s){return null;};
    6 
    7     default void m3(){};
    8 }

    lambda表达式格式

    可以将lambda看做是一个匿名方法,lambda只能用于函数式接口

    (类型 参数, 类型 参数, …, 类型 参数) -> {
    代码块;

    return 结果;
    }

    主要由这几个符合构成:
    () -> {}

    lambda优点:使用lambda表达式可以编写出比匿名内部类更简洁的代码

     1 public class LambdaTest01 {
     2 
     3     public static void main(String[] args) {
     4 
     5         // 不使用lambda
     6         MyInterface oldType = new MyInterface() {
     7 
     8             @Override
     9             public void m1(int a, int b) {
    10                 System.out.println(a + b);
    11             }
    12 
    13         };
    14 
    15         oldType.m1(10, 10);
    16 
    17 
    18 
    19 
    20         // 使用lambda
    21         MyInterface newType = (int a, int b) -> {
    22         System.out.println(a+b);
    23         };
    24 
    25 
    26         // 编译器都可以从上下文环境中推断出lambda表达式的参数类型,因此可以省略参数类型
    27 /*        LambdaInterface newType = (a, b) -> {
    28             System.out.println(a + b);
    29         };*/
    30 
    31         newType.m1(20, 20);
    32     }
    33 
    34 }

    使用lambda在多线程中的写法:

     1     public static void main(String[] args) {
     2 
     3         //不使用lambda的写法
     4         new Thread(new Runnable() {
     5             @Override
     6             public void run() {
     7                 System.out.println("no lambda");
     8             }
     9         }).start();
    10 
    11 
    12         //lambda写法
    13         //因为Thread类中接收Runnable类型的对象,所以编译器会识别出lambda表达式是Runnable对象
    14         new Thread(() -> {System.out.println("lambda");}).start();

    lambda表达式为什么只能用于函数式接口中

    lambda表达式实际上就是重写了接口中的抽象方法,在函数式接口中只有一个抽象方法,此时编译器会认定重写的就是该唯一的抽象方法。
    倘若一个接口中有多个抽象方法,而lambda表达式是没有匿名的,编译器就无法判断其重写的是哪个抽象方法了。

    forEach方法

    在jdk8中的java.lang.Iterable接口中新增了非抽象的forEach方法。可以使用该方法配合lambda来遍历集合。

     1     public static void main(String[] args) {
     2 
     3         List<Integer> list = new ArrayList<>();
     4         list.add(1);
     5         list.add(2);
     6         list.add(3);
     7         list.add(4);
     8         list.add(5);
     9         list.add(6);
    10 
    11         //java8之前
    12         for(Integer i : list){
    13             System.out.println(i);
    14         }
    15 
    16 
    17         // Java 8之后,在Iterator接口中新增了非抽象的forEach方法:
    18         list.forEach((Integer n) -> {System.out.println(n);});
    19 
    20         //如果在lambda表达式中只有一个参数一行语句的时候,可以简写为下面格式
    21         list.forEach(n -> System.out.println(n));
    22 
    23         // Java 8新增方法引用,方法引用由::双冒号操作符标示
    24         list.forEach(System.out::println);
    25 
    26     }

    方法引用

    方法引用主要是用来简写lambda表达式,有些lambda表达式里面仅仅是执行一个方法调用,这时使用方法引用可以简写lambda表达式。

     1     public static void main(String[] args) {
     2         Integer[] num = {5,8,13,6};
     3 
     4         //不使用lambda
     5         Arrays.sort(num, new Comparator<Integer>(){
     6 
     7             @Override
     8             public int compare(Integer i1, Integer i2) {
     9 
    10                 return Integer.compare(i1, i2);
    11             }
    12 
    13         });
    14 
    15         //使用lambda
    16         Arrays.sort(num, (x,y) -> Integer.compare(x, y));
    17 
    18         //方法引用
    19         Arrays.sort(num, Integer :: compare);
    20 
    21     }

    一共有四种类型的方法引用
    静态方法引用,类名::方法名
    某个对象的引用,对象变量名::方法名
    特定类的任意对象的方法引用,类名::方法名
    构造方法,类名::new

    下面代码演示构造方法的引用,先创建一个Car类型,添加一个buy方法,为了能够使用lambda表达式,这里使用java.util.function.supplier接口,这个接口是java8新增的一个函数式接口,里面只有一个抽象的get方法。

    1 public class Car {
    2 
    3     public static Car buy(Supplier<Car> s){
    4 
    5         //通过get方法获取传入的Car类型的对象
    6         return s.get();
    7     }
    8 }

    创建一个测试类:

     1 /**
     2  * 构造方法引用
     3  *
     4  */
     5 public class LambdaTest05 {
     6 
     7     public static void main(String[] args) {
     8         Car car = Car.buy(Car :: new);
     9         System.out.println(car);
    10     }
    11 
    12 }

    Java8新特性之Stream

    stream简介

    jdk8中新增stream API,需要注意的是该stream跟之前学习的IO流没有关系,这个stream主要是用来处理集合数据的,可以将其看做是一个高级迭代器。在Collection接口中新增了非抽象的stream方法来获取集合的流。使用stream后可以写出更简洁的代码来处理集合中的数据。

     1     public static void main(String[] args) {
     2         List<Student> stuList = new ArrayList<>(10);
     3 
     4         stuList.add(new Student("刘一", 85));
     5         stuList.add(new Student("陈二", 90));
     6         stuList.add(new Student("张三", 98));
     7         stuList.add(new Student("李四", 88));
     8         stuList.add(new Student("王五", 83));
     9         stuList.add(new Student("赵六", 95));
    10         stuList.add(new Student("孙七", 87));
    11         stuList.add(new Student("周八", 84));
    12         stuList.add(new Student("吴九", 100));
    13         stuList.add(new Student("郑十", 95));
    14 
    15         //需求:列出90分以上的学生姓名,并按照分数降序排序
    16 
    17         //以前的写法,代码较多,每个操作都需要遍历集合
    18         List<Student> result1 = new ArrayList<>(10);
    19 
    20         //遍历集合获取分数大于90以上的学生并存放到新的List中
    21         for(Student s : stuList){
    22             if(s.getScore() >= 90){
    23                 result1.add(s);
    24             }
    25         }
    26 
    27         //对List进行降序排序
    28         result1.sort(new Comparator<Student>(){
    29 
    30             @Override
    31             public int compare(Student s1, Student s2) {
    32                 //降序排序
    33                 return Integer.compare(s2.getScore(), s1.getScore());
    34             }
    35 
    36         });
    37 
    38         System.out.println(result1);
    39 
    40         //使用Stream的写法
    41         /*
    42          * 1.获取集合的stream对象
    43          * 2.使用filter方法完成过滤
    44          * 3.使用sort方法完成排序
    45          * 4.使用collect方法将处理好的stream对象转换为集合对象
    46          */
    47         result1 = stuList.stream()
    48                 .filter(s -> s.getScore()>=90)
    49                 //.sorted((s1,s2) -> Integer.compare(s2.getScore(), s1.getScore()))
    50                 //使用Comparator中的comparing方法
    51                 .sorted(Comparator.comparing(Student :: getScore).reversed())
    52                 .collect(Collectors.toList());
    53         System.out.println(result1);
    54     }

    map和reduce

    • map用来归类,结果一般是一组数据,比如可以将list中的学生分数映射到一个新的stream中
    • reduce用来计算值,结果是一个值,比如计算最高分
     1 /**
     2  * map和reduce
     3  *
     4  */
     5 public class StreamTest03 {
     6 
     7     public static void main(String[] args) {
     8         //初始化List数据同上
     9         List<Student> list = InitData.getStudent();
    10 
    11         //使用map方法获取list数据中的name
    12         List<String> names = list.stream()
    13                                     .map(Student::getName)
    14                                     .collect(Collectors.toList());
    15         System.out.println(names);
    16 
    17         //使用map方法获取list数据中的name的长度
    18         List<Integer> length = list.stream()
    19                                     .map(Student::getName)
    20                                     .map(String::length)
    21                                     .collect(Collectors.toList());
    22         System.out.println(length);
    23 
    24         //将每人的分数-10
    25         List<Integer> score = list.stream()
    26                 .map(Student::getScore)
    27                 .map(i -> i - 10)
    28                 .collect(Collectors.toList());
    29         System.out.println(score);
    30 
    31 
    32         //计算学生总分
    33         Integer totalScore1 = list.stream()
    34                 .map(Student::getScore)
    35                 .reduce(0,(a,b) -> a + b);
    36         System.out.println(totalScore1);
    37 
    38         //计算学生总分,返回Optional类型的数据,改类型是java8中新增的,主要用来避免空指针异常
    39         Optional<Integer> totalScore2 = list.stream()
    40                 .map(Student::getScore)
    41                 .reduce((a,b) -> a + b);
    42         System.out.println(totalScore2.get());
    43 
    44         //计算最高分和最低分
    45         Optional<Integer> max = list.stream()
    46                                     .map(Student::getScore)
    47                                     .reduce(Integer::max);
    48         Optional<Integer> min = list.stream()
    49                                     .map(Student::getScore)
    50                                     .reduce(Integer::min);
    51 
    52         System.out.println(max.get());
    53         System.out.println(min.get());
    54 
    55     }
    56 
    57 }

    在java8中新增了三个原始类型流来解决这个问题:
    IntStream、DoubleStream、LongStream

     1 /**
     2  * 数值流
     3  *
     4  */
     5 public class StreamTest04 {
     6 
     7     public static void main(String[] args) {
     8         List<Student> list = InitData.getStudent();
     9 
    10         //将stream转换为IntStream
    11         int totalScore = list.stream()
    12                                 .mapToInt(Student::getScore)
    13                                 .sum();
    14         System.out.println(totalScore);
    15 
    16         //计算平均分
    17         OptionalDouble avgScore = list.stream()
    18                             .mapToInt(Student::getScore)
    19                             .average();
    20         System.out.println(avgScore.getAsDouble());
    21 
    22         //生成1~100之间的数字
    23         IntStream num = IntStream.rangeClosed(1, 100);
    24 
    25         //计算1~100之间的数字中偶数的个数
    26         long count = IntStream.rangeClosed(1, 100)
    27                     .filter(n -> n%2 == 0)
    28                     .count();
    29         System.out.println(count);
    30     }
    31 
    32 }

    创建流

    除了上面的流之外,我们还可以自己创建流。
    下面代码中展示了三种创建流的方式:

     1    //使用Stream.of创建流
     2     Stream<String> str =  Stream.of("i","love","this","game");
     3     str.map(String::toUpperCase).forEach(System.out::println);
     4 
     5     //使用数组创建流
     6     int[] num = {2,5,9,8,6};
     7     IntStream intStream = Arrays.stream(num);
     8     int sum = intStream.sum();//求和
     9     System.out.println(sum);
    10 
    11     //由函数生成流,创建无限流
    12     Stream.iterate(0, n -> n+2)
    13             .limit(10)
    14             .forEach(System.out::println);

    Optional简介

    空指针异常是在学习和开发中最常见的问题之一,为了解决这个问题,在java8中新增了Optional类。这个类在java.util包下,使用这个类可以更好的支持函数式编程,并且可以简化以前对null的判断。

     1 /**
     2  * Optional简介
     3  *
     4  */
     5 public class OptionalTest01 {
     6 
     7     public static void main(String[] args)   {
     8         List<Student> stuList = InitData.getStudent();
     9         Optional<Integer> count = stuList.stream()
    10                 .filter(s -> s.getScore()<60)
    11                 .map(Student::getScore)
    12                 .reduce((a,b) -> a+b);
    13         System.out.println(count.orElse(0));
    14 
    15 
    16         Map<Integer,String> map = new HashMap<>();
    17         map.put(1001, "篮球");
    18         map.put(1002, "足球");
    19         map.put(1003, "羽毛球");
    20         map.put(1004, "乒乓球");
    21         String sport = Optional.ofNullable(map.get(1005))
    22                                         .orElse("无");
    23         System.out.println(sport);
    24     }
    25 
    26 
    27 }

    遍历集合的四种方式

    下面以ArrayList为例展示一下遍历集合的四种方式,首先初始化一个ArrayList并填充一些测试数据

    1     List<Integer> list = new ArrayList<>();
    2 
    3     for (int i = 0; i < 10; i++) {
    4         list.add(i);
    5     }

    遍历集合方式一:使用普通for循环:

    1     for (int i = 0; i < list.size(); i++) {
    2         System.out.println(list.get(i));
    3     }

    遍历集合方式二:增强for循环,底层就是迭代器:

    1     for (Integer i:list){
    2         System.out.println(i);
    3     }

    遍历集合方式三:迭代器

    1     Iterator<Integer> iterator = list.iterator();
    2     while(iterator.hasNext()){
    3         Integer i = iterator.next();
    4         System.out.println(i);
    5     }

    遍历集合方式四:使用Iterable接口中jdk1.8新增的default方法forEach+lambda表达式

    1     list.forEach((i) -> {System.out.println(i); });//普通lambda
    2     list.forEach(i -> System.out.println(i));//只有一行语句时可以简写的形式
    3     list.forEach(System.out :: println);//方法引用

    四种迭代方式对比

    • 普通for循环
      此种方式在遍历ArrayList时效率会高一些,因为ArrayList底层使用的是数组实现的,所以可以认为ArrayList中的元素都是有下标的,而此种普通for循环中的变量i可以快速的定位到ArrayList中的元素。
    • 增强for循环和迭代器
      可以认为增强for循环是迭代器的一种简便的写法,而迭代器比较适合遍历LinkedList,因为它底层使用的是链表的数据结构。
    • 使用forEach方法+lambda表达式
      如果你使用的是jdk8以上的版本,那么建议使用此种方式,该方式内部默认的使用增强for循环去遍历集合,不过在ArrayList类中重写了forEach方法,里面使用了普通的for循环去遍历。不管你使用哪一种,这种方式底层会选择最优的遍历方式

    Iterable接口中的forEach方法源码:

    1 default void forEach(Consumer<? super T> action) {
    2     Objects.requireNonNull(action);
    3     for (T t : this) {
    4         action.accept(t);
    5     }
    6 }

    ArrayList中重写的forEach方法源码:

     1 @Override
     2 public void forEach(Consumer<? super E> action) {
     3     Objects.requireNonNull(action);
     4     final int expectedModCount = modCount;
     5     @SuppressWarnings("unchecked")
     6     final E[] elementData = (E[]) this.elementData;
     7     final int size = this.size;
     8     for (int i=0; modCount == expectedModCount && i < size; i++) {
     9         action.accept(elementData[i]);
    10     }
    11     if (modCount != expectedModCount) {
    12         throw new ConcurrentModificationException();
    13     }
    14 }

    因此建议使用jdk8中Iterable接口中新增的forEach方法遍历集合。

  • 相关阅读:
    .netcore3.1添加swagger及JWT Authorize 验证
    Quartz.Net实现简单定时任务调度
    基于.Net Framework进行配置Swagger
    升级anaconda的python版本命令
    使用jmeter自带的html查看测试报告
    软件质量模型的6大特性和27个子特性
    PyQt5基础教程及官方文档
    使用jmeter进行下载并发测试
    selenium官方入门文档
    web driver 驱动配置位置
  • 原文地址:https://www.cnblogs.com/samuraihuang/p/10749739.html
Copyright © 2020-2023  润新知