• 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));
        }
    }
  • 相关阅读:
    [转]boost.bind 如何 bind continue.1
    Maven configure settins.xml
    JUNIT测试通过,EMMA通不过,java.lang.NoClassDefFoundError: oracle/security/pki/OracleWallet
    java中OOA,OOT, OOP, OOD, OOSM,OOM英文表示的含义是什么?
    关于navicat连接oracle 报 ORA12737 set CHS16GBK错误的解决方案
    configure integration Hibernate3,Spring3,Struts2,JPA
    Proguard returned with error code 1. See console
    Spring内置提供的NativeJdbcExtractor转换器
    oracle opreation instruction(表空间,用户etc)
    Struts2,oracle实现GOOGLE的分页样式
  • 原文地址:https://www.cnblogs.com/ShouWangYiXin/p/11300669.html
Copyright © 2020-2023  润新知