跳至主要內容

JDK8

观风大约 7 分钟JDK8

JDK8

Oracle 于 2014 发布了 Java8(jdk1.8)

Interface的默认方法

因为interface修改后,实现类也必须要修改。

为了优化这个问题, interface 的方法可以用default 或 static修饰,这样就可以有方法体,实现类也不必重写此方法。

  • default修饰的方法,是普通实例方法,可以用this调用,可以被子类继承、重写。
  • static修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用。
public interface InterfaceNew {
    static void stc() {
        System.out.println("interface提供的方式实现");
    }
    default void def() {
        System.out.println("interface default方法");
    }
    //须要实现类重写
    void f();
}

functional interface 函数式接口

也称 SAM 接口,即 Single Abstract Method interfaces,有且只有一个抽象方法,但可以有多个非抽象方法的接口。

函数式接口有@FunctionalInterface的注解,提供函数式编程。在Lambda表达式中经常使用。

Lambda 表达式

Lambda 表达式是一个匿名函数,java 8 允许把函数作为参数传递进方法中。

语法格式

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

常用案例

1.Runnable 接口

new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("guan feng!");
    }
}).start();

//用lambda
new Thread(() -> System.out.println("guan feng!")).start();

2.Comparator 接口

List<Integer> strings = Arrays.asList(1, 2, 3);

Collections.sort(strings, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
    return o1 - o2;}
});

//Lambda
Collections.sort(strings, (Integer o1, Integer o2) -> o1 - o2);
//分解开
Comparator<Integer> comparator = (Integer o1, Integer o2) -> o1 - o2;
Collections.sort(strings, comparator);

3.Listener 接口

Button button = new Button();
button.addItemListener(new ItemListener() {
    @Override
    public void itemStateChanged(ItemEvent e) {
       e.getItem();
    }
});
//lambda
button.addItemListener(e -> e.getItem());

4.集合迭代

void lamndaFor() {
   List<String> strings = Arrays.asList("1", "2", "3");

   //传统foreach
   for (String s : strings) {
       System.out.println(s);
   }

   //Lambda foreach
   strings.forEach((s) -> System.out.println(s));
   //or
   strings.forEach(System.out::println);

   //map
   Map<Integer, String> map = new HashMap<>();
   map.forEach((k,v)->System.out.println(v));
} 

5.方法调用

Java 8 允许使用 :: 关键字来传递方法或者构造函数引用,无论如何,表达式返回的类型必须是 functional-interface。

public class LambdaClassSuper {
    LambdaInterface sf(){
        return null;
    }
}

public class LambdaClass extends LambdaClassSuper {
    public static LambdaInterface staticF() {
        return null;
    }

    public LambdaInterface f() {
        return null;
    }

    void show() {
        //1.调用静态函数,返回类型必须是functional-interface
        LambdaInterface t = LambdaClass::staticF;

        //2.实例方法调用
        LambdaClass lambdaClass = new LambdaClass();
        LambdaInterface lambdaInterface = lambdaClass::f;

        //3.超类上的方法调用
        LambdaInterface superf = super::sf;

        //4. 构造方法调用
        LambdaInterface tt = LambdaClassSuper::new;
    }
}

Stream

Stream流可以检索和逻辑处理集合数据、包括筛选、排序、统计、计数等。可以想象成是 Sql 语句。它的源数据可以是 Collection、Array 等。由于它的方法参数都是函数式接口类型,所以一般和 Lambda 配合使用。

流类型

  • stream 串行流
  • parallelStream 并行流,可多线程执行

filter

过滤方法filter用于对Stream中的元素进行筛选,只保留符合指定条件的元素。其函数式接口为Predicate<T>,其方法为boolean test(T t),接受一个T类型的对象,并返回一个boolean类型值。当该方法返回true时,说明该元素符合条件,将被保留在Stream 中

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<String> stream = list.stream().filter(s -> s.length() > 5);

其中的lamdba表达式s -> s.length() > 5用于筛选长度大于5的字符串。

map

映射方法map用于将Stream中的元素根据指定规则进行转换。其函数式接口为Function<T, R>,其方法为R apply(T t),接受一个T类型的对象,并返回一个R类型的对象。实质上,map方法就是对Stream中各元素做一个同类型的映射。

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<Integer> stream = list.stream().map(String::length);

sorted

排序方法sorted用于对Stream中的元素进行排序。其函数式接口为Comparator<T>,其方法为int compare(T o1, T o2),接受两个T类型的对象,并返回一个int类型值。当返回值为负数时,说明o1应排在o2前面;当返回值为正数时,说明o1应排在o2后面;当返回值为0时,说明o1与o2的顺序不确定。

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
Stream<String> stream = list.stream().sorted();

distinct

去重方法distinct用于将Stream中的重复元素去除,只保留一个。其使用equals方法进行比较,因此需要保证数据源中的元素正确实现了equals方法。

List<String> list = Arrays.asList("apple", "banana", "orange", "banana");
Stream<String> stream = list.stream().distinct();

count

计数方法count用于返回Stream中元素的数量,返回值为long类型。

List<String> list = Arrays.asList("apple", "banana", "orange", "pear");
long count = list.stream().count();

reduce

归约方法reduce用于将Stream中的元素归约成一个值。其函数式接口为BinaryOperator<T>,其方法为apply(T t1, T t2),用于对两个T类型值进行归约,返回一个T类型值。reduce方法接受两个参数:第一个参数表示归约操作的初始值,可以为任意类型的对象;第二个参数为一个BinaryOperator类型的对象,用于对Stream中所有元素递归地进行归约操作。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
int sum = list.stream().reduce(0, Integer::sum);

forEach

forEach方法用于对Stream中的每个元素执行指定的操作,其函数式接口为Consumer<T>,其方法为void accept(T t)forEach是一个终端操作,对于同一个Stream只能进行一次,一旦执行了终端操作,该Stream就不能再重复使用了。

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.stream().forEach(System.out::println);

collect

collect方法用于将流转为Collection中List等对象

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List res = list.stream().filter(s->s<3).collect(Colletors.toList());

并发流

@Test
public void parallelStreamTest(){
   List<Integer> numbers = Arrays.asList(1, 2, 5, 4);
   numbers.parallelStream() .forEach(num->System.out.println(Thread.currentThread().getName()+">>"+num));
}
//执行结果
main>>5
ForkJoinPool.commonPool-worker-2>>4
ForkJoinPool.commonPool-worker-11>>1
ForkJoinPool.commonPool-worker-9>>2

Optional

Optional是为了防止出现NullPointerException的。

出现NullPointerException的情况:

  • 1) 返回类型为基本数据类型,return 包装数据类型的对象时,自动拆箱有可能产生 NPE。反例:public int f() { return Integer 对象}, 如果为 null,自动解箱抛 NPE。
  • 2) 数据库的查询结果可能为 null。
  • 3) 集合里的元素即使 isNotEmpty,取出的数据元素也可能为 null。
  • 4) 远程调用返回对象时,一律要求进行空指针判断,防止 NPE。
  • 5) 对于 Session 中获取的数据,建议进行 NPE 检查,避免空指针。
  • 6) 级联调用 obj.getA().getB().getC();一连串调用,易产生 NPE。

传统解决NPE的方式:

Zoo zoo = getZoo();
if(zoo != null){
   Dog dog = zoo.getDog();
   if(dog != null){
      int age = dog.getAge();
      System.out.println(age);
   }
}

Optional 是这样的实现的:

Optional.ofNullable(zoo).map(o -> o.getDog()).map(d -> d.getAge()).ifPresent(age ->
    System.out.println(age)
);

Date-Time API

解决了 Date 类的大部分痛点:

  • 非线程安全
  • 时区处理麻烦
  • 各种格式化、和时间计算繁琐
  • 设计有缺陷,Date 类同时包含日期和时间;还有一个 java.sql.Date,容易混淆。

java.time 主要类java.util.Date 既包含日期又包含时间,而 java.time 把它们进行了分离

LocalDateTime.class //日期+时间 format: yyyy-MM-ddTHH:mm:ss.SSS
LocalDate.class //日期 format: yyyy-MM-dd
LocalTime.class //时间 format: HH:mm:ss

格式化

public void newFormat(){
    //format yyyy-MM-dd
    LocalDate date = LocalDate.now();
    System.out.println(String.format("date format : %s", date));

    //format HH:mm:ss
    LocalTime time = LocalTime.now().withNano(0);
    System.out.println(String.format("time format : %s", time));

    //format yyyy-MM-dd HH:mm:ss
    LocalDateTime dateTime = LocalDateTime.now();
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    String dateTimeStr = dateTime.format(dateTimeFormatter);
    System.out.println(String.format("dateTime format : %s", dateTimeStr));
}

字符串转日期格式

LocalDate date = LocalDate.of(2021, 1, 26);
LocalDate.parse("2021-01-26");

LocalDateTime dateTime = LocalDateTime.of(2021, 1, 26, 12, 12, 22);
LocalDateTime.parse("2021-01-26 12:12:22");

LocalTime time = LocalTime.of(12, 12, 22);
LocalTime.parse("12:12:22");

日期计算

public void pushWeek(){
     //一周后的日期
     LocalDate localDate = LocalDate.now();
     //方法1
     LocalDate after = localDate.plus(1, ChronoUnit.WEEKS);
     //方法2
     LocalDate after2 = localDate.plusWeeks(1);
     System.out.println("一周后日期:" + after);

     //算两个日期间隔多少天,计算间隔多少年,多少月
     LocalDate date1 = LocalDate.parse("2021-02-26");
     LocalDate date2 = LocalDate.parse("2021-12-23");
     Period period = Period.between(date1, date2);
     System.out.println("date1 到 date2 相隔:"
                + period.getYears() + "年"
                + period.getMonths() + "月"
                + period.getDays() + "天");
		 //打印结果是 “date1 到 date2 相隔:0年9月27天”
     //这里period.getDays()得到的天是抛去年月以外的天数,并不是总天数
     //如果要获取纯粹的总天数应该用下面的方法
     long day = date2.toEpochDay() - date1.toEpochDay();
     System.out.println(date1 + "和" + date2 + "相差" + day + "天");
     //打印结果:2021-02-26和2021-12-23相差300天
}

获取指定日期

public void getDayNew() {
    LocalDate today = LocalDate.now();
    //获取当前月第一天:
    LocalDate firstDayOfThisMonth = today.with(TemporalAdjusters.firstDayOfMonth());
    // 取本月最后一天
    LocalDate lastDayOfThisMonth = today.with(TemporalAdjusters.lastDayOfMonth());
    //取下一天:
    LocalDate nextDay = lastDayOfThisMonth.plusDays(1);
    //当年最后一天
    LocalDate lastday = today.with(TemporalAdjusters.lastDayOfYear());
    //2021年最后一个周日,如果用Calendar是不得烦死。
    LocalDate lastMondayOf2021 = LocalDate.parse("2021-12-31").with(TemporalAdjusters.lastInMonth(DayOfWeek.SUNDAY));
}