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 list1 = Arrays.asList("aaa", "ddd", "ccc",  "bbb");
            list1.sort(new Comparator() {
                  @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 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 con = (s)->System.out.println(s); // lambda
        con.accept("hello,world"); // hello,world
        Consumer con2 = System.out::println; // 方法引用
        con2.accept("哈哈哈"); // 哈哈哈
        
        // String也可以是自定义类
        String s = new String("hello,world");
        //Supplier sup = ()->s.length(); // lambda
        Supplier sup = s::length; // 方法引用
        System.out.println(sup.get()); // 11
        
        // 2.类名::静态方法(不常用)
        //Comparator com = (x,y)->Integer.compare(x,y); // lambda
        Comparator com = Integer::compare; // 方法引用
        System.out.println( com.compare(1, 2) ); // -1: 1 < 2
        
        TreeSet ts = new TreeSet(com);
        System.out.println(ts); // ts就会遵循com指向的 Integer中的Compare方法进行排序
        
        // 3.类名::实例方法名
        //Function fun = s->s.hashCode(); // lambda
        Function fun = String::hashCode; // 方法引用
        Integer hash = fun.apply(s);
        System.out.println(hash); // 2137655864
        
        // 4.类::new 即 类名::构造方法
        //Supplier supp = ()->new String(); // lambda
        Supplier 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 and(Predicate other)
返回一个组合的断言,表示该断言与另一个断言的短路逻辑AND。
static  Predicate isEqual(Object targetRef)
返回根据 Objects.equals(Object, Object)测试两个参数是否相等的 断言 。
default Predicate negate()
返回表示此断言的逻辑否定的断言。
default Predicate or(Predicate other)
返回一个组合的断言,表示该断言与另一个断言的短路逻辑或。

基本使用:

Predicate p1 = str -> str.length() == 9; // 字符串长度是否等于9
Predicate p2 = str -> str.startsWith("j"); // 是否以j开头
Predicate p3 = p1.and(p2); // 字符串是否长度为9并且以j开头
Predicate p4 = p1.or(p2); // 字符串是否长度为9或者以j开头
Predicate p5 = p1.negate(); // 字符串长度是否不等于9
Predicate 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 list = new ArrayList();
            list.add("zhangsan");
            list.add("lisi");
            list.add("wangwu");
            list.add("zhaoliu");
            list.add("zhangqi");
            
            // Predicate断言:过滤出来已zhang开头的元素
            List ls = filter(list, (s)->s.startsWith("zhang"));
            for (String string : ls) {
                  System.out.print(string + " "); // zhangsan zhangqi
            }
      }
      
      public static List filter(List list,  Predicate p) {
            List l = new ArrayList();
            for (String s : list) {
                  if (p.test(s)) {
                        l.add(s);
                  }
            }
            return l;
      }
}

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

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

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

基本使用:

// 消费:给其内容,不关心其作何使用,没有返回值
Consumer 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 consumer)  {
            consumer.accept(money);
      }
      public static void m2(int num, Consumer consumer) {
            consumer.accept(num);
      }
}

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

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

T get()
获得结果。

基本使用:

Supplier 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 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 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  Function andThen(Function after)
返回一个组合函数,首先将该函数应用于其输入,然后将 after函数应用于结果。
default  Function compose(Function before)
返回一个组合函数,首先将 before函数应用于其输入,然后将此函数应用于结果。
static  Function identity()
返回一个总是返回其输入参数的函数。

基本使用:

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

5. Stream (流)接口 API

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

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

5.1 stream 基本操作

public class TestStream {
      public static void main(String[] args) {
            // Stream --> 数组、集合
            List list = Arrays.asList("zhangsan", "lisi",  "wangwu", "zhaoliu", "lucy");
            
            // Stream 中存储的是数据的操作,自身不保存数据。
            // 1.创建 Stream
            Stream stream = list.stream();
            // 2.中间操作
            Stream 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 filter(Predicate predicate)
返回由与此给定谓词匹配的此流的元素组成的流。

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

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

 Stream map(Function mapper)
返回由给定函数应用于此流的元素的结果组成的流。

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

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

中间操作示例:

public class TestStreamAPI {
      public static void main(String[] args) {
            List 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 ls = new ArrayList();
            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 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 action)
对此流的每个元素执行遍历操作。

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

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

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

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

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

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

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

终止操作示例:

public class TestStreamEnd {
      public static void main(String[] args) {
            List 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 list = new ArrayList();
            
            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毫秒
      }
}

你可能感兴趣的:(jdk1.8新特性及用法总结)