Loading

Java8新特性

Java8新特性

一、接口的默认方法

在接口中新增了default方法和static方法,这两种方法可以有方法体

1.1、static方法

接口中的static方法不能被继承,也不能被实现类调用,只能被自身调用

示例代码:

static void staticMethod(){
    System.out.println("staticMethod方法");
}

1.2、default方法

default方法可以被子接口继承也可被实现类调用

default void defaultMethod(){
    System.out.println("defaultMethod方法");
}
//实现类调用
public static void main(String[] args) {
    Test test = new Test();
    test.defaultMethod();	//调用default方法
}

default方法被继承时,可以被子接口覆写

//子接口覆写
public interface TestNewInterfaceExtend extends TestNewInterface{
    @Override
    default void defaultMethod() {
        
    }
}

1.3、一个类实现了多个接口

如果一个类实现了多个接口

这些接口中无继承关系

这些接口中若有相同的(同名,同参数)的default方法,则接口实现类会报错

接口实现类必须通过特殊语法指定该实现类要实现那个接口的default方法
特殊语法:<接口>.super.<方法名>([参数])

TestNewInterface.super.defaultMethod();

二、Lambda 表达式

Lambda表达式可以看成是匿名内部类,使用Lambda表达式时,接口必须是函数式接口

2.1、语法

<函数式接口>  <变量名> = (参数1,参数2...) -> {
	//方法体
}

说明:

(参数1,参数2…)表示参数列表;->表示连接符;{}内部是方法体
1、=右边的类型会根据左边的函数式接口类型自动推断;
2、如果形参列表为空,只需保留();
3、如果形参只有1个()可以省略,只需要参数的名称即可;
4、如果执行语句只有1句,且无返回值{}可以省略,若有返回值,则若想省去{},则必须同时省略return,且执行语句也保证只有1句
5、形参列表的数据类型自动推断
6、lambda不会生成一个单独的内部类文件
7、lambda表达式若访问了局部变量,则局部变量必须是final的,若是局部变量没有加final关键字,系统会自动添加,此后在修改该局部变量,会报错;

2.2、Lambda表达式其他特性

2.2.1、引用实例方法

语法:

<函数式接口>  <变量名> = <实例>::<实例方法名>
//调用
<变量名>.接口方法([实际参数...])
LambdaTest lt1 = s-> System.out.println(s);
lt1.print("原方式");
        
//改写为:
LambdaTest lt2 = System.out::println;
lt2.print("实例引用方式");

将lt2调用时的实际参数传递给了PrintStream类中的println方法,并调用该方法

2.2.2、引用类方法

语法:

<函数式接口>  <变量名> = <类>::<类方法名称>
//调用
<变量名>.接口方法([实际参数...])

现有数组list,要给其排序:

LambdaTest lt = Collections::sort;
lt.sort(list, (a,b) -> {
    return a-b;
});

后面的(a,b)是Comparator接口的lambda表达式写法

2.2.3、引用类的实例方法

定义、调用接口时,需要多传递一个参数,并且参数的类型与引用实例的类型一致
语法:

//定义接口
interface <函数式接口>{
    <返回值> <方法名>(<类><类名称>,[其他参数...]); 
}
<函数式接口>  <变量名> = <类>::<类实例方法名>
//调用
<变量名>.接口方法(类的实例,[实际参数...])

将调用方法时的传递的实际参数,从第二个参数开始(第一个参数指定的类的实例),全部传递给引用的方法,执行引用的方法;

2.2.4、引用构造器方法

把方法的所有参数全部传递给引用的构造器,根据参数类型自动推断调用的构造器方法;

语法:

<函数式接口>  <变量名> = <类>::<new>
//调用
<变量名>.接口方法([实际参数...])

根据传入的参数类型,自动匹配构造函数

2.3、函数式接口

如果一个接口只有一个抽象方法,则该接口称之为函数式接口,默认方法不算抽象方法
函数式接口可以使用Lambda表达式,lambda表达式会被匹配到这个抽象方法上

@FunctionalInterface 注解,这个接口多于一个抽象方法的时候会报错的

示例代码:

@FunctionalInterface
interface MyFuncInterface<F, T> {
    T myMethod(F f);
}

2.4、Lambda 作用域

在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量

2.5、四大函数式接口

2.5.1、Function函数式接口

public static void main(String[] args) {
    //Function<String, String> function = new Function<String, String>() {
    //    @Override
    //    public String apply(String s) {
    //        return s;
    //    }
    //};

    Function<String, String> function = (s)->{
        return s;
    };

    System.out.println(function.apply("AAA"));
}

2.5.2、断定型接口

public static void main(String[] args) {
    //    Predicate<String> predicate = new Predicate<String>() {
    //        @Override
    //        public boolean test(String s) {
    //            return s.equals("AAA");
    //        }
    //    };
    Predicate<String> predicate = (s)->{
        return s.equals("AAA");
    };

    System.out.println(predicate.test("AAA"));
}

2.5.3、消费型接口

public static void main(String[] args) {
//    Consumer<String> consumer = new Consumer<String>() {
//        @Override
//        public void accept(String s) {
//            System.out.println(s);
//        }
//    };
    Consumer<String> consumer = (s) ->{
        System.out.println(s);
    };

    consumer.accept("AAA");
}

2.5.4、供给型接口

public static void main(String[] args) {
//    Supplier<String> supplier = new Supplier<String>() {
//        @Override
//        public String get() {
//            return "AAA";
//        }
//    };

    Supplier<String> supplier = ()->{
        return "AAA";
    };

    System.out.println(supplier.get());
}

三、Stream

3.1、什么是Stream

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的Iterator。

3.2、创建stream方法

3.2.1、基本创建方法

// 1、of创建
Stream stream = Stream.of("a", "b", "c");

// 2、Arrays
String[] strArray = new String[]{"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);

// 3、Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();
stream.forEach(System.out::println);

3.2.2、数值流的构造

对于基本数值型,目前有三种对应的包装类型 Stream:IntStream、LongStream、DoubleStream。

3.2.3、流转换为其他数据结构

// 3、流转换为其他数据结构
Stream<String> stream1 = Stream.of(new String[]{"1", "2", "3"});
//        List<String> list1 = stream1.collect(Collectors.toList());
//        List<String> list2 = stream1.collect(Collectors.toCollection(ArrayList::new));
// 一个 Stream 只可以使用一次
String str = stream1.collect(Collectors.joining());
System.out.println(str);

3.3、Stream的操作类型

  • 中间操作(Intermediate Operation):一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。

    • 无状态操作(Stateless Operation):操作是无状态的,不需要知道集合中其他元素的状态,每个元素之间是相互独立的,比如map()、filter()等操作。

    • 有状态操作(Stateful Operation):有状态操作,操作是需要知道集合中其他元素的状态才能进行的,比如sort()、distinct()。

  • 终止操作(Terminal Operation):一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果。

    • 短路操作(short-circuiting):短路操作是指不需要处理完所有元素即可结束整个过程
    • 非短路操作(non-short-circuiting):非短路操作是需要处理完所有元素之后才能结束整个过程

3.4、stream的方法:

  1. count()、max()、min()方法

  2. Filter 过滤方法

    过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作。

    list.stream()
        .filter(user -> user.getAge() % 2 == 0)
        .filter(user -> user.getAge() > 20)
    
  3. distinct方法

    去除重复

  4. Sort 排序

    排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
    需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据是不会被修改的:

  5. limit:

    对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;

  6. skip:

    返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;

  7. Match 匹配

    Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。

  8. Count 计数

    计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。

四、Date API

Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。对日期与时间的操作一直是Java程序员最痛苦的地方之一。标准的 java.util.Date以及后来的java.util.Calendar一点没有改善这种情况(可以这么说,它们一定程度上更加复杂)。
这种情况直接导致了Joda-Time——一个可替换标准日期/时间处理且功能非常强大的Java API的诞生。Java 8新的Date-Time API (JSR 310)在很大程度上受到Joda-Time的影响,并且吸取了其精髓。

4.1、LocalDate类

LocalDate date = LocalDate.now(); // 当前日期

date = date.plusDays(1); // 增加一天
date = date.plusMonths(1); // 增加一个月
System.out.println(date);
date = date.minusDays(1); // 减少一天
date = date.minusMonths(1); // 减少一个月
System.out.println(date);

/*
结果如下:
================
2020-08-23
2020-07-22
================
*/

4.2、LocalTime类

LocalTime time = LocalTime.now(); // 当前时间
time = time.plusHours(1);// 增加一小时
time = time.plusMinutes(1); // 增加一分钟
time = time.plusSeconds(1); // 增加一秒
System.out.println(time);
time = time.minusHours(1);  //减小一小时
time = time.minusMinutes(1); // 减少一分钟
time = time.minusSeconds(1); // 减少一秒
System.out.println(time);

/*
结果如下:
================
22:27:55.349
21:26:54.349
================
*/

4.3、LocalDateTime类

LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime); // UTC格式
System.out.println(
    localDateTime.format(
        DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
    )
); // 自定义格式
/*
结果如下:
================
2020-07-22T21:29:03.781
2020-07-22 21:29:03
================
*/

4.4、ZoneDateTime类

ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);

ZonedDateTime zonedDatetimeFromZone = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
System.out.println(zonedDatetimeFromZone);

ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);
/*
结果如下:
================
2020-07-22T21:37:26.233+08:00[Asia/Shanghai]
2020-07-22T06:37:26.235-07:00[America/Los_Angeles]
Asia/Shanghai
================
*/

4.5、Clock类

//它通过指定一个时区,然后就可以获取到当前的时刻,日期与时间。
// Clock可以替换System.currentTimeMillis()与TimeZone.getDefault()

Clock utc = Clock.systemUTC(); // 世界标准时间
System.out.println(LocalDateTime.now(utc));

Clock shanghai = Clock.system(ZoneId.of("Asia/Shanghai")); // 上海时间
System.out.println(LocalDateTime.now(shanghai));
/*
结果如下:
================
2020-07-22T13:32:17.832
2020-07-22T21:32:17.895
================
*/

4.6、Duration类

计算两个日期之间差的天数

LocalDateTime from = LocalDateTime.parse("2020-07-21 20:35:50", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime to = LocalDateTime.parse("2020-07-22 21:35:50", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Duration duration = Duration.between(from, to);
System.out.println("Duration in days: " + duration.toDays());
System.out.println("Duration in hours: " + duration.toHours());

五、Annotation 注解

在Java 8中支持多重注解了

posted @ 2020-07-22 21:42  shimeath  阅读(197)  评论(0编辑  收藏  举报