文章目录
- 一、函数式接口
- 二、Java内置函数式接口
- 三、接口内允许添加默认实现的方法
- 四、Lambda表达式
- 五、方法引用
- 六、Stream API
- 七、Stream API示例
- 1. Filter过滤
- 2. Sorted排序
- 3. Map转换
- 4. Match匹配
- 5. Count计数
- 6. Reduce
- 7. Parallel-Streams并行流
- 8. Map集合
- 八、新时间日期接口API
- 九、Annotations注解
- 回顾注解
- Java8注解
- 类型注解
- 重复注解
参考文章:放大招了,肝了一篇8万字的Java8新特性总结,赶快收藏
一、函数式接口
函数式接口:只包含一个抽象方法的接口,称为函数式接口,并且可以使用lambda表达式来创建该接口的对象,可以在任意函数式接口上使用@FunctionalInterface注解,来检测它是否是符合函数式接口。同时javac也会包含一条声明,说明这个接口是否符合函数式接口。
1. 自定义函数式接口
//自定义函数式接口
@FunctionalInterface
public interface MyNuber{
public double getValue();
}
2. 泛型函数式接口
//函数式接口中使用泛型
@FunctionalInterface
public interface MyFunc<T>{
public T getValue(T t);
}
二、Java内置函数式接口
三、接口内允许添加默认实现的方法
在下面这个接口中,我们除了定义了一个抽象方法 study,还定义了一个带有默认实现的方法 play。 我们在实现这个接口时,可以只需要实现 study方法,默认方法 play可以直接调用即可,也就是说我们可以不必强制实现 sqrt 方法。
// 定义一个公式接口
interface school{
// 学习
void study();
// 做游戏
default string play(String name) {
return "我和"+ name + "一起做游戏";
}
}
注:通过 default 关键字这个新特性,可以非常方便地对之前的接口做拓展,而此接口的实现类不必做任何改动,减少了耦合性。
School school = new School() {
@Override
public void study() {
system.out.println("学习中");
}
};
四、Lambda表达式
函数式接口(Functional Interface)就是只包含一个抽象方法的声明。针对该接口类型的所有 Lambda 表达式都会与这个抽象方法匹配。
(参数列表) -> { 方法体 }
- 参数列表的数据类型可以省略
- 参数列表只有一个参数,可以把()去掉
- 如果方法体只有一条语句,可以把 { } 和 return 去掉
- 无参、无返回值
public void test1(){
Runnable r1 = new Runnable(){
@Override
public void run(){
System.out.println("我爱北京天安门");
}
};
Runnable r2 = ()->{
System.out.println("我爱北京天安门");
};
}
- 需要一个参数,但是没有返回值;
@Test
public void test2(){
Consumer<String> con1 = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s + 1);
}
};
Consumer<String> con2 = (String s)->{
System.out.println(s + 2);
}
//数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
Consumer<String> con3 = (s)->{
System.out.println(s + 3);
}
}
- Lambda 若只需要一个参数时,参数的小括号可以省略
@Test
public void test3(){
Consumer<String> con1 = (s) -> {
System.out.println(s);
};
Consumer<String> con2 = s -> {
System.out.println(s);
};
}
- Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
@Test
public void test4(){
Comparator<Integer> com1 = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1 + o2);
System.out.println(o1);
}
};
Comparator<Integer> com2 = (o1,o2) -> {
System.out.println(o1 + o2);
return o1.compareTo(o2);
};
}
- 当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
@Test
public void test5(){
Comparator<Integer> com1 = (o1,o2) -> {
return o1.compareTo(o2);
};
Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
}
五、方法引用
有下列三种情况
- 对象 :: 实例方法
- 类 :: 实例方法
- 类 :: 静态方法
- 方法引用
package com.chen.test.JAVA8Features;
public class MethodRefDemo {
public static void main(String[] args) {
FunctionGeneric<String> strName = s -> System.out.println(s);
strName.fun("Lambda表达式没有使用方法引用");
//方法引用
FunctionGeneric<String> strName2 = System.out::println;
strName2.fun("使用方法引用");
}
}
- 构造器引用
package com.chen.test.JAVA8Features;
import java.util.function.Function;
public class MethodRefDemo {
public static void main(String[] args) {
//构造器引用
Function<String, Integer> fun1 = (num) -> new Integer(num);
Function<String, Integer> fun2 = Integer::new;
//数组引用
Function<Integer,Integer[]> fun3 = (num) ->new Integer[num];
Function<Integer,Integer[]> fun4 = Integer[]::new;
}
}
六、Stream API
StreamAPI帮助我们更好地对数据进行集合操作,它本质就是对数据的操作进行流水线式处理,也可以理解为一个更加高级的迭代器,主要作用是遍历其中每一个元素。简而言之,StreamAP提供了一种高效且易于使用的处理数据方式。
Stream操作的三个步骤
- 创建流
//第一种:通过集合:对于Collection接口(List 、Set、Queue等)直接调用Stream()方法可以获取Stream
List<String> list = new ArrayList<>();
Stream<String> stringStream = list.stream(); //返回一个顺序流
Stream<String> parallelStream = list.parallelStream(); //返回一个并行流(可多线程)
//第二种:通过数组
Stream<String> stream1 = Arrays.stream(new String[]{"CBB", "YJJ", "CB", "CJJ"});
//第三种:Stream.of()静态方法直接手动生成一个Stream
Stream<String> stream = Stream.of("A", "B", "C", "D");
- 中间操作:一个流可以后面跟随着0个或者多个中间操作,其目的是打开流,做出某种程度的数据过滤、去重、排序、映射、跳过等。然后返回一个新的流,交给下一个使用,仅仅是调用这个方法,没有真正开始遍历。
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
3、终止操作:一个终止操作,执行中间操作连,产生结果。
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
七、Stream API示例
首先,我们创建一个List集合
//首先我们创建一个List集合
List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");
1. Filter过滤
stringCollection
.stream()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa2", "aaa1"
2. Sorted排序
stringCollection
.stream()
.sorted()
.filter((s) -> s.startsWith("a"))
.forEach(System.out::println);
// "aaa1", "aaa2"
3. Map转换
stringCollection
.stream()
.map(String::toUpperCase)
.sorted((a, b) -> b.compareTo(a))
.forEach(System.out::println);
// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
4. Match匹配
// 验证 list 中 string 是否有以 a 开头的, 匹配到第一个,即返回 true
boolean anyStartsWithA =
stringCollection
.stream()
.anyMatch((s) -> s.startsWith("a"));
System.out.println(anyStartsWithA); // true
// 验证 list 中 string 是否都是以 a 开头的
boolean allStartsWithA =
stringCollection
.stream()
.allMatch((s) -> s.startsWith("a"));
System.out.println(allStartsWithA); // false
// 验证 list 中 string 是否都不是以 z 开头的,
boolean noneStartsWithZ =
stringCollection
.stream()
.noneMatch((s) -> s.startsWith("z"));
System.out.println(noneStartsWithZ); // true
5. Count计数
// 先对 list 中字符串开头为 b 进行过滤,让后统计数量
long startsWithB =
stringCollection
.stream()
.filter((s) -> s.startsWith("b"))
.count();
System.out.println(startsWithB); //
6. Reduce
Optional<String> reduced =
stringCollection
.stream()
.sorted()
.reduce((s1, s2) -> s1 + "#" + s2);
reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"
7. Parallel-Streams并行流
stream 流是支持顺序和并行的。顺序流操作是单线程操作,而并行流是通过多线程来处理的,能够充分利用物理机 多核 CPU 的优势,同时处理速度更快。
首先,我们创建一个包含 1000000 UUID list 集合
int max = 1000000;
List<String> values = new ArrayList<>(max);
for (int i = 0; i < max; i++) {
UUID uuid = UUID.randomUUID();
values.add(uuid.toString());
}
分别通过顺序流和并行流,对这个 list 进行排序,测算耗时:
顺序流:
// 纳秒:获取当前时间1
long t0 = System.nanoTime();
//获取串行流输出
long count = values.stream().sorted().count();
System.out.println(count);
//纳秒:获取当前时间2
long t1 = System.nanoTime();
// 纳秒转微秒
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("顺序流排序耗时: %d ms", millis));
// 顺序流排序耗时: 899 ms
并行流
// 纳秒:获取当前时间1
long t0 = System.nanoTime();
//并行流输出
long count = values.parallelStream().sorted().count();
System.out.println(count);
//纳秒:获取当前时间2
long t1 = System.nanoTime();
// 纳秒转微秒
long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
System.out.println(String.format("并行流排序耗时: %d ms", millis));
// 并行流排序耗时: 472 ms
同样的逻辑处理,通过并行流,我们的性能提升了近 50%。完成这一切,我们需要做的仅仅是将 stream 改成了 parallelStream。
8. Map集合
前面已经提到过 Map 是不支持 Stream 流的,因为 Map 接口并没有像 Collection 接口那样,定义了 stream() 方法。
注:我们可以对其 key, values, entry 使用 流操作,如 map.keySet().stream(), map.values().stream() 和 map.entrySet().stream().
八、新时间日期接口API
参考文章:Java自定义注解
九、Annotations注解
回顾注解
- 四个常用的元注解:负责注解其他注解
- @Target:用于描述注解的使用范围:接口、类、枚举等等
- @Retention:表示在”source、class、runtime“哪个阶段,注解依然存在
- @Documented:说明该注解将被包含在javadoc中
- @Inherited:说明子类可以继承父类中的该注解
//使用自定义注解
//通过四个元注解自定义注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface MyAnnotation{
}
@MyAnnotation
public class Test01{
public void test(){
System.out.println("hello");
}
}
Java8注解
对于注解(也被称做元数据),Java 8 主要有两点改进:类型注解和重复注解。
类型注解
类型注解被用来支持在Java的程序中做强类型检查。配合第三方插件工具Checker Framework,可以在编译的时候检测出runtime error,以提高代码质量。这就是类型注解的作用。
import checkers.nullness.quals.*;
public class TestDemo{
void sample() {
@NonNull Object my = new Object(); //通过,不报错
}
}
重复注解
允许在同一声明类型(类,属性,或方法)上多次使用同一个注解。
Java8以前的版本使用注解有一个限制是相同的注解在同一位置只能使用一次,不能使用多次。
Java 8 引入了重复注解机制,这样相同的注解可以在同一地方使用多次。重复注解机制本身必须用 @Repeatable 注解。
实际上,重复注解不是一个语言上的改变,只是编译器层面的改动,技术层面仍然是一样的。
1) 自定义一个包装类Hints注解用来放置一组具体的Hint注解
@interface MyHints {
Hint[] value();
}
@Repeatable(MyHints.class)
@interface Hint {
String value();
}
使用包装类当容器来存多个注解(旧版本方法)
@MyHints({@Hint("hint1"), @Hint("hint2")})
class Person {}
使用多重注解(新方法)
@Hint("hint1")
@Hint("hint2")
class Person {}
- 完整类测试
public class RepeatingAnnotations {
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Filters {
Filter[] value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Filters.class)
public @interface Filter {
String value();
}
@Filter("filter1")
@Filter("filter2")
public interface Filterable {
}
public static void main(String[] args) {
for (Filter filter : Filterable.class.getAnnotationsByType(Filter.class)) {
System.out.println(filter.value());
}
}
}
输出结果:
filter1
filter2