JDK8新特性 笔记

JDK8新特性 笔记,第1张

目录

1.接口中默认方法修饰为普通方法

 2.函数式接口

3.lambda表达式(重要)

lambda无参方法调用

lambda有参方法调用

lambda 实例--forEach

lambda实例--Comparator

lambda实例--线程调用

4.方法构造和函数引用

什么是方法引入

方法引入的类型

静态方法引入

实例方法引入

 对象方法引入 (对比实例方法引入)

 构造方法引入

5.Stream流接口(重要)

Stream的创建:  

Stream的常见错误 

Stream -- List 转Set 

Stream -- List 转Map

Stream -- reduce计算求和

Stream -- 最大值最小值

Stream -- Match 匹配

 Stream -- Filiter 过滤

Stream -- Skip和Limit 分页

 Stream -- Sorted 排序

 6.Optional容器

isPresent()  判断参数为空

orElse() 设定默认值

filter(条件判断语句) 实现过滤

优化场景一:ifPresent (new Comsumer 消费型函数接口)

优化场景二:orElseGet  (new Supplier 供给型函数接口) 

 优化场景三:map (new Function 函数型函数接口)


1.接口中默认方法修饰为普通方法

JDK8之前,在Interface中

  • 变量必须是public、static、final的
  • 方法必须是public、abstarct的,而且是默认的,不能有方法体

JDK1.8之后,接口的方法支持使用staticdefault修饰方法体变为普通方法,普通方法不必重写

public interface JDK8Interface {
    /**
     * 方法定义默认为public、abstract
     * void add() = public abstract void add()
     */
    void add();

    /**
     * 使用 default 和 static 修饰的方法可以有方法体,不使用会报错
     */
    default void get(){
        System.out.println("jdk8_GET");
    }

    static void del(){
        System.out.println("jdk8_DEL");
    }
}


------------------------下面是实现类----------------------
public class JDK8InterfaceImpl implements JDK8Interface {
    
    //只需实现抽象方法即可
    @Override
    public void add() {
        System.out.println("jdk8_ADD");
    }
}


------------------------下面是测试类----------------------
public class Test01 {
    public static void main(String[] args) {
        JDK8InterfaceImpl jdk8Interface = new JDK8InterfaceImpl();
        jdk8Interface.get();
        jdk8Interface.add();
        
        //类名.方法名调用静态方法,不能用对象名.静态方法
        JDK8Interface.del();
    }
}

注:刚开始可能会报以下错误,选择第二个设置jdk1.8环境即可

 


 2.函数式接口

函数式接口的定义:

  • 在接口中只能有一个抽象方法
  • @FunctionalInterface 标记该接口为函数接口
  • 可以用defaut修饰普通方法,允许有方法体
  • 可以定义object类的抽象方法(不计入抽象方法的数目里,也不必重写
    //显式表明这是一个函数式接口,加入注解会帮助检查是否符合规范
    @FunctionalInterface
    public interface MyInterface{
        //定义一个抽象方法
        void get();
    
        //可以用default定义普通方法
        default void add(){
            System.out.println("add");
        }
        
        /**
         *Object父类中的方法可以在函数接口写,不纳入只有一个抽象方法的范围
         */
        String toString();
    
        //不能定义,会报错,除了Object父类的抽象方法外,只能写一个抽象方法
        //String toString01();
    }

3.lambda表达式(重要)

Lambda 表达式是一个匿名函数,简化我们调用匿名函数的过程,针对函数式接口

场景:有一个OrderService接口,里面有只有一个抽象方法,调用这个方法的方式有三种:

public interface OrderService {
    void get();
}

1.创建一个接口实现类重写方法,利用实现类对象调用(如果只调用一次get,这个方法太麻烦)

2.利用匿名内部类.方法名

new OrderService() {
            @Override
            public void get() {
                System.out.println("get");
            }
        }.get();

可以看出,如果只调用一次的话,这个代码还是比较繁琐的

注:凡是使用IDEA的,如符合使用lambda的,都有黄色的灯泡提示用lambda一键替换

3.lambda表达式(更简洁)

格式:   (参数列表) -> {方法体} 

精简写法:

  • 方法体只有一句话的时候,不需要写{}
  • 参数列表只有一个参数时,不用带括号
  • 如果方法体只有一条return的情况下不需要些{} 和return

lambda无参方法调用
//方式1
//最外面的括号表示这是一个匿名内部类对象
//(OrderSerive) 表示类型转换
((OrderService) () -> System.out.println("get")).get();

//方式2 : 接口声明指向内部类对象
OrderService orderService = () -> System.out.println("get02");       
orderService.get();

lambda有参方法调用
@FunctionalInterface
public interface MyInterface {
    String get(String i);
}

//接口引用 指向 匿名内部类重写
MyInterface myInterface = i -> i;  
System.out.println(myInterface.get("String"));

lambda 实例--forEach

定义一个String类型的ArrayList,添加元素,然后进行forEach遍历,分别用两种情况展示

ArrayList strings = new ArrayList<>();
strings.add("123");
strings.add("456");
strings.add("789");
        
//不用Lambda遍历      
//源码Comsumer是一个函数式接口,只有一个accept的抽象方法,故可以用lambda
strings.forEach(new Consumer() {
        @Override
        public void accept(String s) {
             System.out.println(s);
            }
        });
        
//使用lambda遍历
strings.forEach(s-> System.out.println(s));

lambda实例--Comparator

定义一个Interger类型的ArrayList,添加元素,然后进行升序排序,分别用两种情况展示

ArrayList ints = new ArrayList();
ints.add(9);
ints.add(6);
ints.add(10);

//方法一
//Comparator是一个 有两个参数的抽象方法 的函数式接口
ints.sort(new Comparator() {
     @Override
     public int compare(Integer o1, Integer o2) {
          return o1 - o2;
          }
      });

//方法二
ints.sort((o1,o2) ->  o1-o2);

lambda实例--线程调用
//传统的方法.匿名内部类
//其中Runnable是函数式接口
new Thread(new Runnable() {
     @Override
     public void run() {
          System.out.println(Thread.currentThread().getName());
      }
}).start();

//lambda表达式
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();

4.方法构造和函数引用 什么是方法引入

方法引入:需要结合lambda表达式能够让代码变得更加精简。

代码简单程度由上到下依次为:

  • 匿名内部类使用
  • Lambda调用匿名内部类
  • 方法引入
方法引入的类型
  • 静态方法引入   类名::(静态)方法名称
  • 对象方法引入   类名:: 实例方法名称
  • 实例方法引入   new对象 对象实例::方法引入
  • 构造函数引入   类名::new
静态方法引入

有一个函数式接口的抽象方法,返回类型为String,参数列表为int

有一个静态方法,它属于一个类,返回类型为String,参数列表为int

倒数第一行代码:接口引用 指向 静态方法,这就叫做静态方法引入,条件是:静态方法和抽象方法必须参数列表返回类型一致

这样做的结果就是,相当于静态方法 就是 抽象方法的重写

//函数式接口
@FunctionalInterface
public interface Int2String {
    String change(int num);
}

----------------新建一个测试类---------------------
public class Test04 {
    //参数为int,返回为String的静态方法
    public static String staticMethod(int num){
        return  String.valueOf(num);
    };

    public static void main(String[] args) {
        //lambda写法:重写了change()
        Int2String int2String_0 = (num) -> String.valueOf(num);

        //静态方法引入:接口引用 指向 静态方法: 静态方法相当于重写方法
        Int2String int2String = Test04::staticMethod;
    }
}

实例方法引入

有一个函数式接口的抽象方法,返回类型为String,参数列表为int

类里有一个普通方法。它属于对象的实例方法,返回类型为String,参数列表为int

原理与上述静态方法一致,对象的普通方法和抽象方法必须参数列表返回类型一致

--------------------函数式接口-------------------
@FunctionalInterface
public interface Int2String {
    String change(int num);
}

-------------------------新建一个测试类-----------------------
public class Test04 {
    //新建一个对象的实例方法
    public String method(int num){
        return  String.valueOf(num);
    };

    public static void main(String[] args) {
        //创建一个对象
        Test04 test04 = new Test04();
        
        //lambda写法
        Int2String int2String_0 = (num) -> test04.method(num);

        //对象方法引入
        Int2String int2String = test04::method;
    
}

 对象方法引入 (对比实例方法引入)

有一个函数式接口的抽象方法,返回类型为String,参数列表第一个为Test类,第二个为int

类里有一个普通方法。它属于对象的实例方法,返回类型为String,只有一个参数为int

对象方法引入可以不需要新建对象,用    类名::非静态方法(实例方法) 即可,为什么可以?

因为抽象方法的第一个参数为指定类,第二个参数开始的参数必须与实例方法一致(可有可无)

--------------------函数式接口-------------------
@FunctionalInterface
public interface Int2String {
    String change(Test tst,int num);
}

-------------------------新建一个测试类-----------------------
public class Test {
    //新建一个普通方法
    public String method(int num){
        return "普通方法";
    };

    public static void main(String[] args) {
        //lambda表达式
        Int2String int2String = (tst,num) -> "这是lambda表达式";
       
        //对象方法引入
        Int2String int2String = test::method;
    }
}

 构造方法引入

有一个函数式接口,抽象方法无参数,返回Student类型

有个Student类,其实有无参构造函数

原理与上述静态方法一致,类的构造方法和抽象方法必须参数列表返回类型一致

-------------------函数式接口------------------------
@FunctionalInterface
public interface StuInterface {
    Student method();
}

---------------------学生类--------------------------
public class Student{
    //无参构造
   public Student(){};
}


--------------------测试类---------------------------
public class Test04 {
    public static void main(String[] args) {
        //lambda写法
        StuInterface stuInterface0 = () ->  new Student();
        
        //构造引用的写法
        StuInterface stuInterface = Student::new;
    }
}


5.Stream流接口(重要)

Stream 是JDK1.8 中处理集合的关键抽象概念,它可以对集合进行的 *** 作,可以执行非常复杂的查找、过滤和映射数据等 *** 作

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

Stream的创建:  
  • Stream分为并行流和串行流,并行流效率较高
  • parallelStream为并行流采用多线程执行
  • Stream采用单线程执行 
//先定义一个学生集合,其中最后两个是重复的
ArrayList stus= new ArrayList<>();
stus.add(new Student("xiaoming", 20));
stus.add(new Student("xiaohong", 28));
stus.add(new Student("xiaosan", 35));
stus.add(new Stuent("xiaowen", 16));
stus.add(new Student("xiaowen", 16));

//创建集合的串行流
Stream stuStream = stus.stream();

//创建集合的并行流
Stream parStream = stus.parallelStream();

//也可以串行流转为并行流
parStream = stuStream.parallel();

Stream的常见错误 

stream has already been operated upon or closed;Exception in thread "main" java.lang.IllegalStateException:

错误的原因是因为:以下提到的SteamAPI对一个Stream对象只能使用一次,使用一次以上就会报错!

        ---除非流对象经过API处理后返回一个新的流对象,那就可以继续 *** 作

否则需要重新创建Stream对象,再使用API *** 作

Stream -- List 转Set 

要解决的问题:集合的去重

//先将集合转为流
Stream stuStream = stus.stream();

//运用流的API转为Set
Set collectSet = stuStream.collect(Collectors.toSet());

此时,若遍历打印collectSet,会发现Set里还有重复的

因为两个对象的内存地址不同,equals默认是对比两个对象的地址

所以导致虽然姓名和年龄一样的对象,系统也不会判定他俩是同一个人,因此需要在Student类里重写equals()与hashCode()方可解决

Stream -- List 转Map

要解决的问题:

集合stus只存放了Student对象,但是Map有Key与Value,我们先确定Map要怎么存,初步定为:Key = stuName,Value = Student对象

//先将集合转为流
Stream stuStream = stus.stream();


//key为String类型 value为Student类型 
Map collect = stuStream.collect(Collectors.toMap(new Function() {
            @Override
            public String apply(Student student) {
                return student.getName();
            }
}, new Function() {
            @Override
            public Student apply(Student student) {
                return student;
            }
}));

/**第一个new Function 的apply方法返回的是key
*第二个new Function 的apply方法返回的是value
*Function<参数1,参数2>,参数一表示集合里的类型,参数二表示返回类型
*以上可在IDEA点击黄色灯泡一键变为lambda表达式
*/

//lambda超精简写法
collect = stuStream.collect(Collectors.toMap(student -> student.getName(), student -> student));

Stream -- reduce计算求和

计算stus集合的所有学生的年龄总和,返回一个Optional对象

//先将集合转为流
Stream stuStream = stus.stream();

//默认要返回Student,所以最后是一个名字为NULL,age为总数的student
//原理是迭代运算
 Optional reduce = stuStream.reduce((student1, student2) -> {
            Student student = new Student();
            student.setAge(student1.getAge() + student2.getAge());
            student.setName("s");
            return student;
});

//返回结果:Optional[Student{name='null', age=99}]
System.out.println(reduce.get());

Stream -- 最大值最小值

注意API中max和min的使用,返回Optional对象

//先将集合转为流
Stream stuStream = stus.stream();

//求最大值
Optional max= stuStream.max((o1, o2) -> o1.getAge() - o2.getAge());

//求最小值,两种方法。
//方法一:把o1.getAge()和o2.getAge()交换,max方法不变
Optional min= stuStream.max((o1, o2) -> o2.getAge() - o1.getAge());

//方法二:不改变o1 - o2,直接把max方法改为min
Optional min= stuStream.min((o1, o2) -> o1.getAge() - o2.getAge());

Stream -- Match 匹配
  • anyMatch表示,判断的条件里,任意一个元素成功,返回true
  • allMatch表示,判断条件里的元素,所有的都是,返回true
  • noneMatch跟allMatch相反,判断条件里的元素,所有的都不是,返回true
  • //先将集合转为流
    Stream stuStream = stus.stream();
    
    //注意,以下API其实只能使用一次,不能三行连续写,否则报错,用一次就要重新创建对象
    //false
    boolean res1 = stuStream.noneMatch(student -> student.getAge() > 30);
    //true
    boolean res2 = stuStream.anyMatch(student -> student.getAge() > 30);
    //flase
    boolean res3 = stuStream.allMatch(student -> student.getAge() > 30);
     Stream -- Filiter 过滤
  • 要求:寻找学生年龄大于或等于20的学生。返回的是一个符合条件的新的流对象
  • //先将集合转为流
    Stream stuStream = stus.stream();
    
    //返回一个符合条件的新的流
    Stream studentStream = stuStream.filter(student -> student.getAge() >= 20);
    

    Stream -- Skip和Limit 分页
  • 要求:从第一条开始查询,查询两条数据。可以写for循环做分页查询。返回由查询结果组成的新的流对象
  •  //先将集合转为流
    Stream stuStream = stus.stream();
    
    //skip代表跳过几条,limit表示查几条
    Stream limit = stuStream.skip(1).limit(2);

     Stream -- Sorted 排序
  • 要求:按照学生年龄从小到大排序。返回一个重新排好序的流对象

  • //先将集合转为流
    Stream stuStream = stus.stream();
    
    //从小到大排序
    Stream sorted = stuStream.sorted((o1, o2) -> o1.getAge() - o2.getAge());

     6.Optional容器

Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象

Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测

Optional 类的引入很好的解决空指针异常

isPresent()  判断参数为空
  • ofNullable(可以传递一个空对象)
  • Of(不可以传递空对象):传空会报错
  • isPresent(): true 不为空    /     false 为空
Integer a1 = 1;
Optional a = Optional.ofNullable(a1);
System.out.println(a.isPresent());

orElse() 设定默认值

要求:如果传递的参数为空时,设置默认值;不为空则不变

Integer a1 = null;
//a = 10
Integer a = Optional.ofNullable(a1).orElse(10);

filter(条件判断语句) 实现过滤
Integer a1 = 16;
Optional a = Optional.ofNullable(a1);
//a3为null
Optional a3 = a.filter(a2 -> a2 > 17);
//a4 = 16
Optional a4 = a.filter(a2 -> a2 < 17);
//fasle
System.out.println(a3.isPresent());
//16
System.out.println(a4.get());

优化场景一:ifPresent (new Comsumer 消费型函数接口)

场景定义一个字符串,要求字符串为null就不做处理,字符串not null就做消费动作:打印

String name = "meite";

//优化前,要写三行代码
if (name != null) {
    System.out.println(name);
}

 //优化后
Optional name2 = Optional.ofNullable(name);
// 当value 不为空时,则不会调用
//lambda表达式,这是一个Comsumer的函数式接口
name2.ifPresent(s -> System.out.println(s));
//方法引入表达
name2.ifPresent(System.out::print);

优化场景二:orElseGet  (new Supplier 供给型函数接口) 

场景:现有一个Student类的stu对象,若它为空,则new对象。那么传统方法是写if语句;可以用orElseGet()做到简单

public class Test04 {
    private static Student stu = null;

    public static void main(String[] args) {
        Student stu = Test04.getstu();
        System.out.println(stu);

    }

    public static Student getstu() {
        // 优化前:四行代码
        if (stu == null) {
            return createstu();
        }
        return stu;

        // 优化后:一行代码
        return Optional.ofNullable(stu).orElseGet(Test04::createstu);
    }

    private static Student createstu() {
        return new Student("123456", 12);
    }
}
 优化场景三:map (new Function 函数型函数接口)

经过map处理后的返回值为Optional类型

flatMap中返回值保持不变,但必须是Optional类型,即Optional<返回值> -> Optional<返回值>

场景:有个Student类的stu对象,如果为空则new对象,不为空之后,把orderName字符串变为小写

---------------学生类--------------
public class Student {
    public String name;
    //构造方法
    public Student(String name) {
        this.name = name;
    }
    //初始化实例方法
    public Student getInit() {
        return new Student("xiaowen");
    }
}


-------------------------测试类--------------------------------
public class Test04 {
    public static void main(String[] args) {
        String stuName = Test04.getlowName();
        System.out.println(stuName);
    }

    public static String getlowName() {
        // 优化前写法:
        Student stu = new Student("xiaowen");
        if (stu != null) {
            stu= stu.getInit();    //若stu为空,则初始化
            if (stu.name != null) {
                return stu.name.toLowerCase(); //name 不为空,则转小写
            }
        }

        //优化后写法:
        return Optional.ofNullable(stu).map(Student -> Student.getInit()).map(stud -> stud.name.toLowerCase()).orElse(null);
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/726506.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存