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的方法:
-
count()、max()、min()方法
-
Filter 过滤方法
过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作。
list.stream() .filter(user -> user.getAge() % 2 == 0) .filter(user -> user.getAge() > 20)
-
distinct方法
去除重复
-
Sort 排序
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据是不会被修改的: -
limit:
对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;
-
skip:
返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;
-
Match 匹配
Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。
-
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中支持多重注解了