【Java】jdk1.8新特性及用法总结

1. 接口中的实现方法

① 使用 default 关键字就可以给接口增加一个非抽象的方法实现
② 接口还可以存在 static 静态方法实现,使用 接口名.静态方法名 的形式直接调用;

包括声明@FunctionalInterface限制接口只有1个抽象方法时,也可以增加①或②。

代码示例:

public class TestInterface {
      public static void main(String[] args) {
            TestInter ti = new TestInter() {
                  @Override
                  public void m1() {
                        System.out.println("m1():匿名内部类实现该接口,只需要覆盖m1()");
                  }
            };
            
            ti.m1();
            ti.m2();
            TestInter.m3();
      }
}
/**
* 接口
* 测试Java8新特性,default和static在接口中定义的场景
* @FunctionalInterface 声明为函数式接口,只能有 1 个公开抽象方法
*/
@FunctionalInterface
interface TestInter {
      void m1(); // 公开抽象方法
      default void m2() {
            System.out.println("m2():default修饰的方法实现,在接口中...");
      }
      public static void m3() {
            System.out.println("m3():static修饰的方法实现,在接口中...");
      }
}

2. Lambda 表达式

概念:允许把函数作为一个方法的参数,代码简洁紧凑(函数作为参数传递到方法中)
语法:

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

新的操作符:-> (箭头操作符)

  • 箭头操作符左侧 (参数1,参数2,…)-> 表示参数列表
  • 箭头操作符右侧 ->{…} 表示方法体

Lambda 表达式特点:

  • 形参列表的数据类型会自动推断
  • 如果形参列表为空,只需保留()
  • 如果形参列表只有1个参数,可以省略(),只要参数名字即可
  • 如果执行语句只有1句,且无返回值,{}可以省略
  • 若有返回值,仍想省略{},return也省略,必须保证执行语句为1句
  • Lambda表达式不会生成单独的内部类.class文件
  • Lambda表达式访问局部变量时,变量要修饰为 final,如果没加会隐式自动添加

示例代码:

public class TestBasicLambda {
      public static void main(String[] args) {
            // 普通方式
            List<String> list1 = Arrays.asList("aaa", "ddd", "ccc",  "bbb");
            list1.sort(new Comparator<String>() {
                  @Override // 按首字符排序规则
                  public int compare(String o1, String o2) {
                        return o1.charAt(0) > o2.charAt(0) ? 1 : -1;
                  }
            });
            System.out.println(list1); // aaa bbb ccc ddd
            
            // Lambda表达式方式:实现接口中唯一1个方法的匿名内部类
            List<String> list2 = Arrays.asList("aaa", "ddd", "ccc",  "bbb");
            list2.sort( (s1, s2)->{ return s1.charAt(0)>s2.charAt(0) ?  1 : -1; });
            System.out.println(list2); // aaa bbb ccc ddd
      }
}

3. 方法引用 ::

Lambda表达式的一种简写形式。
如果Lambda表达式方法体中只是调用一个特定的已存在的方法,则可以使用方法引用。
使用 :: 操作符将对象或类和方法的名字分割开,有 4 种形式:
① 对象::实例方法
② 类::静态方法
③ 类::实例方法
④ 类::new

注意:调用的方法的参数列表与返回值类型,都与函数式接口中的方法参数列表与返回值类型一致

代码示例(使用到了函数式编程的 4 个核心接口):

public class TestMethodRef {
    public static void main(String[] args) {
        // Lambda表达式简化了匿名内部类,方法引用简化了Lambda表达式
        // 1.对象::方法名
        Consumer<String> con = (s)->System.out.println(s); // lambda
        con.accept("hello,world"); // hello,world
        Consumer<String> con2 = System.out::println; // 方法引用
        con2.accept("哈哈哈"); // 哈哈哈
        
        // String也可以是自定义类
        String s = new String("hello,world");
        //Supplier<Integer> sup = ()->s.length(); // lambda
        Supplier<Integer> sup = s::length; // 方法引用
        System.out.println(sup.get()); // 11
        
        // 2.类名::静态方法(不常用)
        //Comparator<Integer> com = (x,y)->Integer.compare(x,y); // lambda
        Comparator<Integer> com = Integer::compare; // 方法引用
        System.out.println( com.compare(1, 2) ); // -1: 1 < 2
        
        TreeSet<Integer> ts = new TreeSet<Integer>(com);
        System.out.println(ts); // ts就会遵循com指向的 Integer中的Compare方法进行排序
        
        // 3.类名::实例方法名
        //Function<String, Integer> fun = s->s.hashCode(); // lambda
        Function<String, Integer> fun = String::hashCode; // 方法引用
        Integer hash = fun.apply(s);
        System.out.println(hash); // 2137655864
        
        // 4.类::new 即 类名::构造方法
        //Supplier<String> supp = ()->new String(); // lambda
        Supplier<String> supp = String::new; // 方法引用
        System.out.println(supp.get().getClass()); // class java.lang.String
    }
}

4. 函数式接口 × 4

函数式编程:函数的参数也是函数,函数返回的也是函数。

概念:如果一个接口只有 1 个公开抽象方法,则该接口为函数式接口。

  • 为了确保接口达到只有1个方法的要求,接口名上添加注解 @FunctionalInterface
  • Java8内置 4 个核心函数式接口interface。
    位置:java.util.function
public class TestMethodInterface {
      public static void main(String[] args) {
            // 接口引用 指向 Lambda表达式的匿名内部类对象
            Interface t = ()->System.out.println("函数式编程...");
            t.m(); // 函数式编程...
      }
}

@FunctionalInterface
interface Interface {
      void m();
}

① Predicate 接口(断言、返回真假)

根据赋值的Lambda表达式逻辑,用作一个参数的断言(布尔值函数)
成员方法:

boolean test(T t)
在给定的参数上执行这个断言逻辑。
default Predicate<T> and(Predicate<? super T> other)
返回一个组合的断言,表示该断言与另一个断言的短路逻辑AND。
static <T> Predicate<T> isEqual(Object targetRef)
返回根据 Objects.equals(Object, Object)测试两个参数是否相等的 断言 。
default Predicate<T> negate()
返回表示此断言的逻辑否定的断言。
default Predicate<T> or(Predicate<? super T> other)
返回一个组合的断言,表示该断言与另一个断言的短路逻辑或。

基本使用:

Predicate<String> p1 = str -> str.length() == 9; // 字符串长度是否等于9
Predicate<String> p2 = str -> str.startsWith("j"); // 是否以j开头
Predicate<String> p3 = p1.and(p2); // 字符串是否长度为9并且以j开头
Predicate<String> p4 = p1.or(p2); // 字符串是否长度为9或者以j开头
Predicate<String> p5 = p1.negate(); // 字符串长度是否不等于9
Predicate<String> p6 = Predicate.isEqual("Java"); // 字符串是否等于Java
System.out.println(p1.test("aaa")); // false
System.out.println(p2.test("java")); // true
System.out.println(p3.test("jjjaaabbb"));// true
System.out.println(p4.test("ja"));// true
System.out.println(p5.test("123456789"));// false
System.out.println(p6.test("java"));// false 

函数传参:

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class TestPredicate {
      public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("zhangsan");
            list.add("lisi");
            list.add("wangwu");
            list.add("zhaoliu");
            list.add("zhangqi");
            
            // Predicate断言:过滤出来已zhang开头的元素
            List<String> ls = filter(list, (s)->s.startsWith("zhang"));
            for (String string : ls) {
                  System.out.print(string + " "); // zhangsan zhangqi
            }
      }
      
      public static List<String> filter(List<String> list,  Predicate<String> p) {
            List<String> l = new ArrayList<String>();
            for (String s : list) {
                  if (p.test(s)) {
                        l.add(s);
                  }
            }
            return l;
      }
}

② Consumer 接口(消费、有去无回)

根据赋值的Lambda表达式逻辑,接受单个输入参数并且不返回结果的操作。
成员方法:

void accept(T t)
对给定的参数执行此操作。
default Consumer<T> andThen(Consumer<? super T> after)
返回一个组合的 Consumer ,按顺序执行该操作,然后执行 after操作。

基本使用:

// 消费:给其内容,不关心其作何使用,没有返回值
Consumer<String> c = s->System.out.println(s);;
c.accept("hello world!"); // hello world!
// andThen后执行
c.andThen(s->System.out.println("hello," + s)).accept("world"); // world hello,world

函数传参:

import java.util.function.Consumer;
public class TestConsumer {
      public static void main(String[] args) {
            m1(100, (a)->System.out.println("今天挣钱了:" + a)); // 今天挣了100
            
            m2(10, (a)->{
                  for (int i = 0; i < 10; i++) {
                        System.out.println(i);
                  }
            }); // 0 1 2 3 4 5 6 7 8 9
      }
      public static void m1(double money, Consumer<Double> consumer)  {
            consumer.accept(money);
      }
      public static void m2(int num, Consumer<Integer> consumer) {
            consumer.accept(num);
      }
}

③ Supplier 接口(创造、无中生有)

根据赋值的Lambda表达式逻辑,根据只有1个抽象方法T get(),没有参数,返回一个T类型的结果。
成员方法:

T get()
获得结果。

基本使用:

Supplier<Double> sup = ()->new Double(Math.random());
System.out.println( sup.get() ); // 输出1个随机<1的小数

函数传参:

import java.util.Random;
import java.util.function.Supplier;
public class TestSupplier {
      public static void main(String[] args) {
            int result = getSum(10, ()->new Random().nextInt(100));
            System.out.println(result); // 10个100以内随机整数的和
            
            Supplier<Integer> sup = ()->{
                  int sum = 0;
                  for (int i = 0; i <= 100; i++) {
                        sum += i;
                  }
                  return sum;
            };
            System.out.println("1-100的和:" + sup.get()); // 5050
      }
      
      public static int getSum(int num, Supplier<Integer> supplier) {
            int sum = 0;
            for (int i = 0; i <= num; i++) {
                  sum += supplier.get();
            }
            return sum;
      }
}

④ Function 接口(传递、返回数据)

根据赋值的Lambda表达式逻辑,计算T类型的值,返回R类型的值。
成员方法:

R apply(T t)
将此函数应用于给定的参数。
default <V> Function<T,V> andThen(Function<? super R,? extends V> after)
返回一个组合函数,首先将该函数应用于其输入,然后将 after函数应用于结果。
default <V> Function<V,R> compose(Function<? super V,? extends T> before)
返回一个组合函数,首先将 before函数应用于其输入,然后将此函数应用于结果。
static <T> Function<T,T> identity()
返回一个总是返回其输入参数的函数。

基本使用:

// String 为传入 Lambda 表达式参数的类型T,Integer 为返回值的类型R
Function<String, Integer> up1 = (s)->s.length();
System.out.println( up1.apply("hello") ); // 5
// 将 up1 的 lambda 运行后,作为结果再执行 up2,组合在一起
Function<String, String> up2 = (s)->"aaa" + s;
System.out.println( up1.compose(up2).apply("12345") ); // 3+5==8
// 将 up2 的 lambda 运行后,作为结果然后再执行 apply
Function<String, String> up3 = (s)->"bbb" + s;
System.out.println( up3.andThen(up2).apply("11111") ); // aaabbb11111
// identity() 静态方法,总是返回其输入的参数
Function<String, String> up4 = Function.identity();
System.out.println( up4.apply("Jerry") );

函数传参:

import java.util.function.Function;
public class TestFunction {
      public static void main(String[] args) {
            String up = stringUpper("hello,world",  (s)->s.toUpperCase());
            System.out.println(up); // HELLO,WORLD
      }
      
      public static String stringUpper(String s, Function<String,  String> fun) {
            return fun.apply(s);
      }
}

5. Stream (流)接口 API

Stream 接口: 支持对一系列元素进行顺序和并行的聚合操作功能接口,是Java8中处理数组、集合的抽象概念。

  • 可以执行非常复杂的查找、过滤、映射等操作。
public interface Stream<T>
    extends BaseStream<T,Stream<T>>

5.1 stream 基本操作

public class TestStream {
      public static void main(String[] args) {
            // Stream --> 数组、集合
            List<String> list = Arrays.asList("zhangsan", "lisi",  "wangwu", "zhaoliu", "lucy");
            
            // Stream 中存储的是数据的操作,自身不保存数据。
            // 1.创建 Stream
            Stream<String> stream = list.stream();
            // 2.中间操作
            Stream<String> center = stream.filter((s)->s.length()>=5);
            // 3.最终操作
            System.out.println("名字长度>=5的是:");
            center.forEach(System.out::println);
            
            // 一行套娃版:
            System.out.println();
            list.stream().filter(s->s.length()>=5).forEach(System.out::println);
      }
}
// zhangsan wangwu zhaoliu

5.2 stream 中间操作

常用API: filter limit distinct map sorted

Stream<T> filter(Predicate<? super T> predicate)
返回由与此给定谓词匹配的此流的元素组成的流。

Stream<T> limit(long maxSize)
返回由此流的元素组成的流,截短长度不能超过 maxSize 。

Stream<T> distinct()
返回由该流的不同元素(根据 Object.equals(Object) )组成的流。

<R> Stream<R> map(Function<? super T,? extends R> mapper)
返回由给定函数应用于此流的元素的结果组成的流。

Stream<T> sorted()
返回由此流的元素组成的流,根据自然顺序排序。

Stream<T> sorted(Comparator<? super T> comparator)
返回由该流的元素组成的流,根据提供的 Comparator进行排序。

中间操作示例:

public class TestStreamAPI {
      public static void main(String[] args) {
            List<String> list = Arrays.asList("zhangsan", "lisi",  "wangwu", "zhaoliu", "zuee", "zhangsan");
            
            // filter:过滤,指定规则
            System.out.println("--------------filter---------------");
            list.stream().filter(s->s.startsWith("z")).filter(s->s.contains("an")).forEach(System.out::println); // zhangsan ×2
            
            // limit:截断,返回不超过指定数量
            System.out.println("--------------limit---------------");
            list.stream().limit(2).forEach(System.out::println); //  zhangsan lisi
            // distinct:筛选,利用 hashCode 和 equals,不会影响原数据
            System.out.println("--------------distinct---------------");
            List<String> ls = new ArrayList<String>();
            list.stream().distinct().forEach(s->ls.add(s));
            ls.stream().forEach(System.out::println); // 过滤掉了重复的1个 zhangsan
            
            // map:给到T返回R,自动推断类型。不是集合!不是集合!不是集合!
            System.out.println("--------------map---------------");
            list.stream().map(s->s.toUpperCase()).forEach(System.out::println); //  全转大写
            list.stream().map(String::toUpperCase).forEach(System.out::println); //  全转大写(方法引用)
            
            // sorted:自然排序,默认升序(基本类型直接排序,引用类型需要实现 Comparable 接口中的 compareTo)
            System.out.println("--------------sorted---------------");
            list.stream().sorted().forEach(System.out::println);
            System.out.println("--------------sorted()---------------");
            // sorted(Comparator<? super T> comparator):定制排序
            list.stream().sorted((x,y)->x.charAt(0) -  y.charAt(0)).forEach(System.out::println); // 实现 Comparable 接口的匿名内部类
            
      }
}

5.3 stream 终止操作

常用API: count forEach anyMatch allMatch noneMatch findFirst findAny min max

long count()
返回流中的元素个数。

void forEach(Consumer<? super T> action)
对此流的每个元素执行遍历操作。

boolean anyMatch(Predicate<? super T> predicate)
或,流中是否有包含指定规则的元素

boolean allMatch(Predicate<? super T> predicate)
且,流中是否全部包含指定规则的元素

boolean noneMatch(Predicate<? super T> predicate)
非,流中是否都不包含指定规则的元素

Optional<T> findFirst()
返回流的第一个元素的Optional,如果流为空,则返回一个空的Optional 。

Optional<T> findAny()
返回流的任意一个随机元素的Optional,如果流为空,则返回一个空的Optional 。

Optional<T> max(Comparator<? super T> comparator)
根据提供的 Comparator 返回此流的最大元素。

Optional<T> min(Comparator<? super T> comparator)
根据提供的 Comparator 返回此流的最小元素。

终止操作示例:

public class TestStreamEnd {
      public static void main(String[] args) {
            List<String> list = Arrays.asList("changsan", "lisi",  "wangwu", "zhaoliu", "zuee");
            
            // 1.forEach: 遍历
            list.stream().forEach(System.out::println); // changsan  lisi wangwu zhaoliu zuee
            
            // 2.count: 流中元素的个数
            long count = list.stream().filter(s->s.length()>4).count();
            System.out.println("流中长度>4元素个数:" + count); // 3
            
            // 3.anymatch: 或,是否有包含的元素
            boolean bool1 =  list.stream().filter(s->s.length()>4).anyMatch(s->s.startsWith("w"));
            System.out.println("流中是否是有包含 w 开头的元素:" +  bool1); // true
            
            // 4.allmatch: 且,是否全部都包含的元素
            boolean bool2 =  list.stream().filter(s->s.length()>4).allMatch(s->s.startsWith("w"));
            System.out.println("流中是否全是包含 w 开头的元素:" +  bool2); // false
            // 5.noneMatch: 非,是否没有匹配的元素
            boolean bool3 =  list.stream().filter(s->s.length()>4).noneMatch(s->s.startsWith("a"));
            System.out.println("流中是否没有包含 a 开头的元素:" +  bool3); // true
            
            // 6.findFirst: 返回流中第一个元素
            String s1 =  list.stream().filter(s->s.length()>4).findFirst().get();
            System.out.println("流中的第一个元素是:" + s1); //  changsan
            
            // 7.findAny: 返回流中任意一个元素
            String s2 = list.stream().findAny().get();
            String s3 = list.parallelStream().findAny().get(); // 并行流
            System.out.println("流中的随机1个元素是:" + s2 + " " +  s3); // changsan wangwu
            
            // 8.max/min: 返回流中的一个最大/最小的元素,需要在max/min中指定 Comparable 接口的比较规则
            String max = list.stream().max((e1,  e2)->e1.charAt(0)-e2.charAt(0)).get();
            System.out.println("首字母最大的是:" + max); // zhaoliu
      }
}

串行与并行的 Stream 效率对比:

public class TestStreamOther {
      public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            
            for (int i = 0; i < 1000000; i++) {
                  list.add(UUID.randomUUID().toString());
            }
            
            // stream串行:一条执行路径,单线程
            System.out.println("串行运行时间:");
            long start = System.currentTimeMillis();
            long count = list.stream().sorted().count();
            System.out.println("排序的元素数量:" + count);
            System.out.println("用时:" + (System.currentTimeMillis() -  start)); // ≈1050毫秒
            // parallelStream 并行:多条执行路径,多线程
            System.out.println("并行运行时间:");
            start = System.currentTimeMillis();
            count = list.parallelStream().sorted().count();
            System.out.println("排序的元素数量:" + count);
            System.out.println("用时:" + (System.currentTimeMillis() -  start)); // ≈520毫秒
      }
}
发布了320 篇原创文章 · 获赞 311 · 访问量 66万+

猜你喜欢

转载自blog.csdn.net/sinat_36184075/article/details/105000639