Java8新特性

1.接口

1)增加default方法和static方法,这两种方法都可以有方法体

interface Interface1 {
    static void st1() {System.out.println("static Interface1.st1()");}
    default void df1() {System.out.println("default Interface1.df1()");}
}

2)default方法属于实例(对象访问),static方法属于类(接口,只可以通过接口名访问)

interface Interface1 {
    static void st1() {System.out.println("static Interface1.st1()");}
    default void df1() {System.out.println("default Interface1.df1()");}
}
public class Jdk8Test {
    public static void main(String[] args) {
        //static方法属于类,接口名访问
        Interface1.st1();
        
        //报错:default方法不可以通过接口名访问
        //Cannot make a static reference to the non-static method df1() from the type Interface1
        //Interface1.df1();
        
        Interface1 obj1 = new Interface1() {};
        //default方法属于实例,可以通过对象访问
        obj1.df1(); 
        
        //报错:static方法只可以通过接口名访问
        //This static method of interface Interface1 can only be accessed as Interface1.st1
        //obj1.st1();
    }
}

3)接口里面的static方法不会被继承,static变量可以被继承,default方法可以被继承

interface Interface1 {
    static void st1() {System.out.println("static Interface1.st1()");}
    static String st_str1 = "ststr1";
    default void df1() {System.out.println("default Interface1.df1()");}
}

interface Interface2 extends Interface1 {
    static void st2() {System.out.println("static Interface2.st2()");}
    default void df2() {System.out.println("default Interface2.df2()");}
}

public class Jdk8Test {
    public static void main(String[] args) {
        //报错,static方法不会被继承
        //The method st1() is undefined for the type Interface2
        //Interface2.st1();
        
        //static 变量则可以被继承,且可以通过接口名与变量名访问
        System.out.println(Interface2.st_str1);
        System.out.println(new Interface2() {}.st_str1);;    
        
        //default方法可以被继承
        new Interface2() {}.df1();
    }
}
4)一个类实现多个具有相同签名的default方法的接口,如果这些接口间没有继承关系,则报错,如果不想让它报错,可以重写这些同名方法
实例1:
interface Interface1 {
    default void df() {System.out.println("default Interface1.df1()");}
}

interface Interface2 {
    default void df() {System.out.println("default Interface2.df2()");}
}

//报错
//Duplicate default methods named df with the parameters () and () are inherited from the types Interface2 and Interface1
class Class1 implements Interface1,Interface2{}
实例2:
interface Interface1 {
    default void df() {System.out.println("default Interface1.df1()");}
}

interface Interface2 extends Interface1{
    default void df() {System.out.println("default Interface2.df2()");}
}

//上面加了继承,不再报错子接口的default方法覆盖父接口同名default方法
class Class1 implements Interface1,Interface2{}

实例3:
interface Interface1 {
    default void df() {System.out.println("default Interface1.df1()");}
}

interface Interface2{
    default void df() {System.out.println("default Interface2.df2()");}
}

//重写接口同名方法,将不会再报错,如果需要访问其中的一个default方法,可以在子类总使用<接口名>.super.方法名的方法指定访问哪一个
class Class1 implements Interface1,Interface2{
    public void df() {
        //指定访问Interface1的default方法
        Interface1.super.df();
    }
    public void df2() {
        //指定访问Interface2的default方法
        Interface2.super.df();
    }
}

实例4:接口的继承亦是如此
interface Interface1 {
    default void df() {System.out.println("default Interface1.df1()");}
}

interface Interface2{
    default void df() {System.out.println("default Interface2.df2()");}
}

//重写接口同名方法,将不会再报错,如果需要访问其中的一个default方法,可以在子类总使用<接口名>.super.方法名的方法指定访问哪一个
interface Interface3 extends Interface1,Interface2{
    default void df() {
        //指定访问Interface1的default方法
        Interface1.super.df();
    }
    default void df2() {
        //指定访问Interface2的default方法
        Interface2.super.df();
    }
}
//不重写将会报错
//Duplicate default methods named df with the parameters () and () are inherited from the types Interface2 and Interface1
interface Interface4 extends Interface1,Interface2{}
总之:上面的解决方法就是指定访问哪一个重名了的default方法,不过指定有一定的语法要求:
接口名.supper.重复的方法名;

 5)如果一个接口只有一个抽象方法(包括继承的),那么这个接口是一个函数式接口

  函数式接口可以使用Lambda表达式实现

  如果接口使用了@FunctionalInterface 注解,表明这个接口是一个函数式接口,接口内必须有且只可以有一个抽象方法。

 6)抽象方法可以在接口中实现(接口继承时在子接口的默认方法中实现,不可以在静态方法中实现)。

2.Lambda表达式

  Lambda表达式只可以对函数式接口使用。

2.1 Lambda表达式三部分:

  () :表示参数列表,不需要指定参数类型,会自动推断
  -> :表示连接符
  {} :表示方法体

  如下代码:

  

@FunctionalInterface
interface UserTest{
    void test();
}
public class TestLambda {
    public static void main(String[] args) {
        //java8前匿名内部类实现
        UserTest ut = new UserTest() {
            @Override
            public void test() {
                System.out.println("匿名内部类实现");
            }
        };
        ut.test();
        //java8后使用Lambda表达式实现
        //() :表示参数列表,不需要指定参数类型,会自动推断
        //-> :表示连接符
        //{} :表示方法体
        UserTest ut2 = () -> {
            System.out.println("使用Lambda表达式实现");
        };
        ut2.test();
    }
}

如果方法体只有一句话,{}可以简化掉,甚至有返回值的时候连return也可省略:

@FunctionalInterface
interface UserTest{
    void test();
}
@FunctionalInterface
interface UserTest2{
    String test();
}
public class TestLambda {
    public static void main(String[] args) {
        UserTest ut1 = () -> System.out.println("使用Lambda表达式实现UserTest");
        ut1.test();
        UserTest2 ut2 = () -> "使用Lambda表达式实现UserTest2";
        System.out.println(ut2.test());
    }
}

如果只有一个参数,()可以省去,两个及以上不可以省略():

@FunctionalInterface
interface UserTest3{
    int test(int x);
}
public class TestLambda {
    public static void main(String[] args) {
        UserTest3 ut3 = x -> ++x;
        System.out.println(ut3.test(1));
    }
}
输出结果:
2

Lambda表达式访问的外部变量是final的。

Lambda的带来的优点:

可以把java代码作为参数传入,有助于提高代码内聚,Lambda实际上遵循匿名内部类的规则,但不是匿名内部类(Lambda表达式编译后不会生成class文件)

2.2 方法的引用

1)引用实例方法:

  方法引用时会自动把调用方法的时候的参数,全部全给引用的方法

  <函数式接口>  <变量名> = <实例> ::<实例方法名>

  <变量名>.<函数式接口方法名>([实参]); 

2)引用类方法:

  方法引用时会自动把调用方法的时候的参数,全部全给引用的方法

  <函数式接口>  <变量名> = <类> ::<类方法名>

  <变量名>.<函数式接口方法名>([实参]); 

简单示例代码:

import java.util.Arrays;

interface MethodRef{
    void test(String s);
}
interface MethodRef2{
    void test(int[] arr);
}
public class TestFunctionRef {
    public static void main(String[] args) {
        //Lambda表达式
        MethodRef m1 = s -> System.out.println(s);
        m1.test("字符串的");
        
        //使用方法引用 :引用实例方法
        //这里System.out是一个实例
        MethodRef m2 = System.out::println;
        m2.test("方法的引用");
        
        //引用类的方法
        //这里sort方法是Arrays工具类的静态方法。
        MethodRef2 m2_1 = Arrays::sort;
        int[] arr = new int[] {3,2,4,7,1,6,5};
        m2_1.test(arr);
        System.out.println(Arrays.toString(arr));
    }
}

3)引用类的实例方法

  定义、调用接口方法的时候,需要多传入一个参数,并且参数的类型和引用示例方法的类型必须一致

  把第一个参数作为引用的实例,后面的每个参数全部传给引用的方法

  interface <函数式接口> {

    <返回值类型> <方法名> (<类1> <名称>[,其他参数 ...]);

  }

  <函数式接口> <变量名> = <类1>::<实例方法名>;  //注意两个类1要是一样的或者兼容的

  <变量名> . <方法名>(<类1的实例 [,其他参数]>);

  简单代码示例:

import java.io.PrintStream;

interface MethodRefX{
    void test(PrintStream ps,String s);
}
public class TestFunctionRef2 {
    public static void main(String[] args) {
        MethodRefX rx = PrintStream::println;
        rx.test(System.out, "第二参数");
    }
}

上面代码含义为:用类型为PrintStream的实例调用方法名为println的方法,参数为test方法的第二个【及以后】的参数,System.out为PrintStream的一个实例。

4)构造器引用

  引用构造器,根据函数式接口的方法名来推断引用哪一个构造器

interface FunctionRefY{
    String test(char[] chars);
}

public class TestFunctionRefy {
    public static void main(String[] args) {
        //引用String类型的public String(char value[])构造器
        FunctionRefY fy = String :: new;
        String ok = fy.test(new char[] {'o','k'});
        System.out.println(ok);
    }
}

 3.Stream

Stream API是Java 8中加入的一套新的API,主要用于处理集合操作。

Stream(流)是一个来自数据源的元素队列并支持聚合操作
元素:是特定类型的对象,形成一个队列,Java中的Stream并不会存储元素,而是按需计算。
数据源:流的来源,可以是集合,数组,I/O channel,产生器generator 等。
聚合操作,类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:
Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
内部迭代:以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式,通过访问者模式(Visitor)实现。

Stream API:
1)创建流
stream() 为集合创建流。
parallelStream() 为集合创建并行流。
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Stream<String> stream = list.parallelStream();

并行流:
对集合进行相同的操作,使用并行流一般会比普通遍历要快一些,快的程度与集合长度和每一次操作耗时均有关系。如下代码:
List ls = new ArrayList();
for(int i = 1;i <= 100;i ++)
ls.add(i);
Stream stream = ls.parallelStream();
long t1 = System.currentTimeMillis();
stream.forEach(a -> {
try{
Thread.sleep(100);
}catch(Exception e){}
});
long t2 = System.currentTimeMillis();
for(int i : ls){
try{
Thread.sleep(100);
}catch(Exception e){}
}
long t3 = System.currentTimeMillis();
System.out.println((t3-t2)/(double)(t2-t1));
最后输出结果约等于7.5

2)forEach
Stream 提供新的方法‘forEach’来迭代每一个数据
Stream<String> stream = list.stream();
stream.forEach(System.out::println);

3)map
map方法用于映射每一个元素到对应的结果,并返回一个新的流。如下代码:
List<Integer> ls = new ArrayList<>();
for(int i = 0;i < 5; i ++) ls.add(i);
Stream<Integer> stream = ls.stream();
Stream<Integer> stream2 = stream.map(n -> n*n);
List<Integer> ls2 = stream2.collect(Collectors.asList());
print(ls);
print(ls2);

最后输出:
ls:0,1,2,3,4
ls2:0,1,4,9,16

4)filter
filter 发发发用于通过过滤条件过滤元素,返回一个新的流。如下代码:
List<String> ls = Arrays.asList("aa","bb","","cc");
Stream stream = ls.stream();
Stream stream2 = stream.filter(str -> !str.isEmpty());
List<String> ls2 = stream2.collect(Collectors.asList());
print(ls);

最后输出结果:
ls:"aa","bb","cc"

5)limit
limit方法用于获取指定数量的流
List<Integer> ls = new ArrayList<>();
for(int i = 0;i < 50; i ++) ls.add(i);
Stream<Integer> stream = ls.stream();
Stream<Integer> stream2 = stream.limit(5);
print(stream2)
Stream<Integer> stream3 = stream.limit(5);
print(stream3)
最后输出结果:
stream2:0,1,2,3,4
Exception 。。。
第二次调用报错说明同一个流只可以调用limit方法一次

6)sorted
sorted对流进行排序,返回排序后的流
Random r = new Random();
r.ints().limit(10).sorted().forEach(System.out::println);

7)skip
跳过前面几个元素
List<Integer> ls = new ArrayList<>();
for(int i = 0;i < 30; i ++) ls.add(i);
Stream<Integer> stream = ls.stream();
Stream<Integer> skip= stream.skip(5);
skip.forEach(System.out::println);
syso("------------------");
stream .forEach(System.out::println);
输出:
10 ... 19
--------------
illegalStateException:stream has already closed
由报错信息可以确定,skip返回的还是原来的流,只不过读取位置跳过了10个元素

8)toArray 转换为数组

9)reduce
规约操作,将流中元素进行合并,形成一个新的值,常见规约有求和运算,如下求取总价:
List<Book> bks = new ArrayList<>();
for(int i = 0;i < 10;i ++)bks.add("bk-"+i,new Random().nextInt());
Optional<Inteter> opt = bks.stream().map(Book::getPrice).reduce((m,n) -> m+n);
syso(opt.get());
//opt.get()可以获取到规约后的总价

10)查询匹配
anyMatch:查询是否有符合指定匹配规则的,返回布尔值
allMatch:查询是否全部匹配指定匹配规则,返回布尔值
noneMatch:查询是否都不匹配指定规则,返回布尔值
boolean hasMatch = Stream.of("Java", "C#", "PHP", "C++", "Python").anyMatch(s -> s.equals("Java"));
findFirst(),findAny()返回的都是第一个元素,建议使用findAny()
Optional<String> element = Stream.of("Java", "C#", "PHP", "C++", "Python").findAny();
syso(element.get());//输出java

11)数据收集
数据收集是流式数据处理的终端处理,与中间处理不同,终端处理会消耗流,终端处理之后,流会关闭。
数据收集主要使用collect方法
该方法也属于归约操作,像reduce()方法那样可以接收各种做法作为参数,将流中的元素累积成一个汇总结果,具体的做法是通过定义新的Collector接口来定义的。

规约汇总:
取最值,计数等操作。
分组:
和关系型数据库类似,流也提供了类似数据库的group by的特性,由Collectors.groupingBy()方法提供
groupingBy()方法还支持多级分组,他有一个重载方法,除了接收一个Function类型的参数外,还接收一个Collector类型的参数

示例代码:

package Test_C02;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.alibaba.fastjson.JSON;

public class StreamGroup {
    static class Book{
        private String name;
        private Integer price;
        private Integer tag;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getPrice() {
            return price;
        }
        public void setPrice(Integer price) {
            this.price = price;
        }
        public Integer getTag() {
            return tag;
        }
        public void setTag(Integer tag) {
            this.tag = tag;
        }
        public Book(String name,Integer price,Integer tag) {
            this.name = name;
            this.price = price;
            this.tag = tag;
        }
    }
    public static void main(String[] args) {
        List<Book> bks = new ArrayList<StreamGroup.Book>();
        for(int i = 0;i < 10;i++) 
            bks.add(new Book("nm-"+i, i, i%3));
        //计数
        Long count = bks.stream().filter(bk -> bk.getPrice()>5).collect(Collectors.counting());
        System.out.println("价格大约5的数量:"+count);
        //最贵的书
        Book maxPriceBk = bks.stream().collect(Collectors.maxBy(Comparator.comparing(Book::getPrice))).get();
        System.out.println("最贵的书:"+JSON.toJSONString(maxPriceBk));
        //最便宜的书
        Book minPriceBk = bks.stream().collect(Collectors.minBy(Comparator.comparing(Book::getPrice))).get();
        System.out.println("最便宜的书:"+JSON.toJSONString(minPriceBk));
        //分组
        Map<Integer, List<Book>> group = bks.stream().collect(Collectors.groupingBy(Book::getTag));
        System.out.println("以tag进行分组:"+JSON.toJSONString(group));
        //多级分组
        Map<Integer, Map<String, List<Book>>> group2 = bks.stream().collect(Collectors.groupingBy(Book::getTag,Collectors.groupingBy(bk ->  bk.getPrice() > 5 ? "A" : "B")));
        System.out.println("多级分组:"+JSON.toJSONString(group2));
    }
}
原文地址:https://www.cnblogs.com/ShouWangYiXin/p/11300669.html