java8完全解读

java8完全解读
前言
java8的一些新特性
1.为什么要用java8?
1.1首先想到的逻辑应该是如下
1.2使用策略模式来解这个问题
1.3使用策略模式和内部类来解决问题
1.4使用策略模式和lambda方式来解决这个问题
1.5使用stream流来解决这个问题
2.lambda基础语法
2.1 无参数无返回值
2.2有一个参数,并且无返回值
2.3 若只有一个参数,小括号可以省略不写
2.4 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句函数体需要用{}
2.5 若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
2.6 Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
3.函数式接口
3.1自定义函数式接口
3.2内置四大核心函数式接口
Consumer : 消费型接口 (只传递参数,不返回参数)
Supplier : 供给型接口(无参,但返回结果)
Function : 函数型接口(传入一个类型参数,返回一个类型的结果)
Predicate : 断言型接口 (传入一个类型参数,返回一个boolean类型的结果)
其他重要函数
4.方法引用和构造器引用
4.1方法引用
4.2构造器引用
4.3数组引用

前言

java8已经发布很久了,本人在学完java8后感觉java8确实好用,本文是根据尚硅谷java8视频整理而来,文章徐徐渐进容易理解。

java8的一些新特性

  • Lambda 表达式
  • 函数式接口
  • 方法引用与构造器引用
  • Stream API
  • 接口中的默认方法与静态方法
  • 新时间日期API
  • 其他新特性

其中最重要的就是Lambda和Stream的使用

1.为什么要用java8?

这里通过一个小案例来讲解用java8的方便之处

  • 假如有这么一道面试题,求公司员工中年龄>35岁的的信息和薪水>5000的员工信息?

员工类(Employee.clsss)

package java8.whytostudylamdba;

public class Employee {
    private String name;
    private int age;
    private double sal;
    public Employee(String name, int age, double sal) {
        this.name = name;
        this.age = age;
        this.sal = sal;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public double getSal() {
        return sal;
    }
    public void setSal(double sal) {
        this.sal = sal;
    }
    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sal=" + sal +
                '}';
    }
}

给出的员工表

List<Employee> employees= Arrays.asList(
            new Employee("张三",18,3000.00),
            new Employee("李思思",25,3500.00),
            new Employee("王五",30,6000.00),
            new Employee("赵六",36,4500.00),
            new Employee("天琪",50,6000.00),
            new Employee("王八",18,38000.00)
            );

好了下面开始写逻辑

1.1首先想到的逻辑应该是如下

public List<Employee> filterEmpByAge(List<Employee> employeeList) {
        List<Employee> emps = new ArrayList<>();
        for (Employee e:employeeList) {
            if (e.getAge() > 35) {
                emps.add(e);
            }
        }
        return emps;
    }
    public List<Employee> filterEmpBySal(List<Employee> employeeList){
        List<Employee> emps = new ArrayList<>();
        for (Employee e:employeeList) {
            if (e.getSal()>5000) {
                emps.add(e);
            }
        }
        return emps;
    }
    @Test
    public void test2(){
        List<Employee> employees = filterEmpByAge(this.employees);
        for (Employee e : employees) {
            System.out.println(e);
        }
        System.out.println("---------");
        List<Employee> employees1 = filterEmpBySal(this.employees);
        for (Employee e : employees1) {
            System.out.println(e);
        }
    }

这个解决方法肯定是可以的,但是这个方法是人都可以想到,你能用这个解决方法上交吗?肯定不行,下面进行优化.

1.2使用策略模式来解这个问题

我们主要是对员工的年龄和工资进行判断,所以需要的是一个传入员工,返回boolean类型的方法,策略接口MyFilter

public interface MyFilter<T> {
    boolean filter(T t);
}

过滤年龄类(FilterByAge.class)

public class FilterByAge implements MyFilter<Employee> {
    @Override
    public boolean filter(Employee employee) {
        return employee.getAge()>35;
    }
}

过滤薪水类(FilterBySal.class)

public class FilterBySal implements MyFilter<Employee> {
    @Override
    public boolean filter(Employee employee) {
        return employee.getSal()>5000;
    }
}

使用策略模式解决这个问题

public List<Employee> filterEmp(List<Employee> employeeList, MyFilter<Employee> filter) {
        List<Employee> emps = new ArrayList<>();
        for (Employee e:employeeList) {
            if (filter.filter(e)) {
                emps.add(e);
            }
        }
        return emps;
    }
    @Test
    public void test3(){
        List<Employee> emps = filterEmp(employees, new FilterByAge());
        for (Employee e : emps) {
            System.out.println(e);
        }
        System.out.println("-------------");
        List<Employee> emps1 = filterEmp(employees, new FilterBySal());
        for (Employee e : emps1) {
            System.out.println(e);
        }
    }

这个方法和上个方法比使用了策略模式,肯定要比上一个方法好,但是每实现一个条件就新建一个类继承MyFilter好像有点不太好,这时想到了内部类的实现方式来解决这个缺点.

1.3使用策略模式和内部类来解决问题

上面的MyFilter接口代码不用动,两个实现类的可以删掉了,使用上面的public List<Employee> filterEmp(List<Employee> employeeList, MyFilter<Employee> filter)方法,以内部类形式实现。

@Test
    public void test4(){
        List<Employee> emp1 = filterEmp(this.employees, new MyFilter<Employee>() {
            @Override
            public boolean filter(Employee employee) {
                return employee.getAge() > 35;
            }
        });
        for (Employee e : emp1) {
            System.out.println(e);
        }
        System.out.println("------------");
        List<Employee> emp2 = filterEmp(this.employees, new MyFilter<Employee>() {
            @Override
            public boolean filter(Employee employee) {
                return employee.getSal() > 5000;
            }
        });
        for (Employee e : emp2) {
            System.out.println(e);
        }
    }

这个比上面的方式更进一步,如果没有lambda这个方式已经是很好的了,下面就开始我们今天的重头戏:使用lambda方式来对上面代码进行再次优化

1.4使用策略模式和lambda方式来解决这个问题

和1.3中的方法一样,不用变什么,就是在调用filterEmp方法时使用labmda表达式

@Test
    public void test5(){
        List<Employee> em1 = filterEmp(this.employees, (e) -> e.getAge() > 35);
        em1.forEach(System.out::println);
        System.out.println("----------");
        List<Employee> em2 = filterEmp(this.employees, (e) -> e.getSal()>5000);
        em2.forEach(System.out::println);//1.8方法
    }

看到没有,对于内部类,使用lambda表达式是一件很轻松的事,现在看不懂?没事后面会说。对应优化做到这种地步,已经无法再优化了,可以使用这种方式提交答案。下面再介绍一种更加新的方式来解决这个问题.

1.5使用stream流来解决这个问题

stream流是1.8新增的,在Collection类中有个stream的静态方法,所以所有集合都可以使用stream流.下面这个方法使用,前面什么方法都不要,假如你刚看到这个题,只有Employee类和相应的List集合数据.

@Test
    public void test6(){
        employees.stream()
                .filter((e)->e.getAge()>35)
                .forEach(System.out::println);
        System.out.println("----------");
        employees.stream()
                .filter((e) -> e.getSal() > 5000)
                .forEach(System.out::println);
    }

可以看到非常容易就解决了上面的问题,是不是很强大,为什么学java8,这就是其中之一的理由.

2.lambda基础语法

Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或 Lambda 操作符,箭头操作符将 Lambda 表达式拆分成两部分:

  • 左侧:Lambda 表达式的参数列表
  • 右侧:Lambda 表达式中所需执行的功能,即Lambda体

2.1 无参数无返回值

() -> System.out.println("Hello Lambda!");

2.2有一个参数,并且无返回值

(x) -> System.out.println(x)

2.3 若只有一个参数,小括号可以省略不写

x -> System.out.println(x)

2.4 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句函数体需要用{}

Comparator<Integer> com = (x, y) -> {
         System.out.println("函数式接口");
         return Integer.compare(x, y);
        };

2.5 若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写

Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

2.6 Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”

(Integer x, Integer y) -> Integer.compare(x, y);

Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,程序依然可以编译,这是因为javac根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的"类型推断"

  • 上联:左右遇一括号省
  • 下联:左侧推断类型省
  • 横批:能省则省

3.函数式接口

  • 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。
  • 你可以通过Lambda 表达式来创建该接口的对象。(若Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
  • 我们可以在任意函数式接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口,同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。

3.1自定义函数式接口

如果我们想将将一串字母转化成大写,这个时候我们通过自定义函数式接口,分析:我们需用传入一个T(String)类型的元素,经过一系列操作(如将字母变为大写),返回T(String)类型的元素

  • 函数为(MyFuntion)
@FunctionalInterface
public interface MyFunction<T>{
    T apply(T t);
}

可以看到将接口上使用@FunctionalInterface注解,注意:接口中方法前面不要用public修饰

  • 将字母转化成大写的方法
public String toUpperString3(String str,MyFunction<String> func) {
        return func.apply(str);
    }

这里看到将str传入了MyFunction的apply方法中了

  • 使用
public void test() {
        String upStr = toUpperString3("abc", (e) -> e.toUpperCase());
        System.out.println(upStr);
    }

一般自定义函数接口就是这样使用的,这里面最重要的就是自定义的函数接口,函数接口要符合我们要操作的类型。为例避免我们每一次使用都有新建函数接口,java8中定义好了一系列的函数接口。

3.2内置四大核心函数式接口

下面四个函数式接口是我们经常使用到的,一些其他的接口都是通过继承这四大种类函数来进行扩展,这四种接口一定要记住

Consumer
  • 方法:void accept(T t);
  • 作用:对类型为T的对象应用操作

使用

//1.消费型
    public void happy(String name, Consumer<String> con) {
        con.accept(name);
    }
    @Test
    public void test1() {
        happy("李四",(x)-> System.out.println(x+"大保健"));
    }
Supplier
  • 方法:T get();
  • 作用:返回类型为T的对象

    使用
//2.供给型
    public List<Integer> getNum(Integer size, Supplier<Integer> supplier) {
        List<Integer> nums = new ArrayList<>();
        for (int i=0;i<size;i++) {
            Integer integer = supplier.get();
            nums.add(integer);
        }
        return nums;
    }
    @Test
    public void test2() {
        List<Integer> nums = getNum(6, () -> (int) (Math.random() * 100));
        for (Integer i : nums) {
            System.out.println(i+"");
        }
    }
Function
  • 方法:R apply(T t);
  • 作用:对类型为T的对象应用操作,并返回结果。结果是R类型的对象。

使用

public String subStr(String str, Function<String, String> function) {
        return function.apply(str);
    }
    @Test
    public void test3() {
        String str = subStr("hellokotlin", (x) -> x.substring(5));
        System.out.println(str);
    }
Predicate
  • 方法:boolean test(T t);
  • 作用:确定类型为T的对象是否满足某约束,并返回boolean 值。

使用

public boolean isContinKotlin(String str, Predicate<String> pre) {
        return pre.test(str);
    }
    @Test
public void test5() {
        System.out.println(isContinKotlin("stKotlin",(str)->str.contains("Kotlin")));
    }
其他重要函数
函数接口 参数类型 返回类型 用途
BiFunction < T,U,R> T,U R 对类型为T,U参数应用操作,返回R类型的结果。包含方法为Rapply(Tt,Uu);
UnaryOperator< T>(Function子接口) T T 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为Tapply(Tt);
BinaryOperator< T>(BiFunction子接口) T,T T 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为Tapply(Tt1,Tt2);
BiConsumer< T,U> T,U T 对类型为T,U参数应用操作。包含方法为voidaccept(Tt,Uu)

4.方法引用和构造器引用

4.1方法引用

当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表,必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符“::” 将方法名和对象或类的名字分隔开来。

如下三种主要使用情况:

  • 对象 :: 实例方法
  • :: 静态方法
  • :: 实例方法

1.对象 ::** 实例方法**

Consumer<String> con = (str) -> ps.println(str);
//等同于:
Consumer<String> con3 = System.out::println;

System.out是一个对象

2.类 ::** 静态方法 **

BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);
//等同于
BiFunction<Double, Double, Double> fun2 = Math::max;

3.类 ::** 实例方法**

BiPredicate<String, String> bp = (x, y) -> x.equals(y);
//等同于
BiPredicate<String, String> bp2 = String::equals;

注意:

①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!

②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时(第三种),格式: ClassName::MethodName

4.2构造器引用

与函数式接口相结合,自动与函数式接口中方法兼容。可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

//无参构造函数
Supplier<Employee> sup = () -> new Employee();
//等同于,调用的是无参构造函数
Function<String, Employee> fun = Employee::new;

//有参构造函数
Function<String,Employee> fun1=(e)->new Employee(e);
//等同于,调用的是有参构造函数
Function<String, Employee> fun = Employee::new;

4.3数组引用

//传入一个Integer返回一个Integer长度的数组
Function<Integer, String[]> fun = (args) -> new String[args];
//等同于
Function<Integer,String[]> fun3=String[]::new;

java8完全解读一的更多相关文章

  1. java8完全解读二

    继续着上次的java完全解读一 继续着上次的java完全解读一1.强大的Stream API1.1什么是Stream1.2 Stream操作的三大步骤1.2.1 创建Stream1.2.2 Strea ...

  2. Java8 JVM参数解读

    附录:https://www.liangzl.com/get-article-detail-134315.html 摘要: 我们知道java虚拟机启动时会带有很多的启动参数,Java命令本身就是一个多 ...

  3. java8特性深入解读文章合集

    Java 8新特性列表 官方OpenJDK java8核心类库新特性列表 Lambda表达式 java8 lambda表达式被誉为java语言10年来最大的突破,给用户提供了scala和clojure ...

  4. 解读Java8的Thread源码

    1.创建的一个无参的Thread对象,默认会有一个线程名,以Thread-开头,从0开始计数,采用了一个static修饰的int变量,当对象初始化一次时一直存放在jvm方法区中 2.构造Thread的 ...

  5. 深度解析Java8 – AbstractQueuedSynchronizer的实现分析(上)

    本文首发在infoQ :www.infoq.com/cn/articles/jdk1.8-abstractqueuedsynchronizer 前言: Java中的FutureTask作为可异步执行任 ...

  6. Java8初体验(二)Stream语法详解

    感谢同事[天锦]的投稿.投稿请联系 tengfei@ifeve.com 上篇文章Java8初体验(一)lambda表达式语法比 较详细的介绍了lambda表达式的方方面面,细心的读者会发现那篇文章的例 ...

  7. 从java8 说起函数式编程

    写在前面 为什么要用函数式编程.看例子: final List<BigDecimal> prices = Arrays.asList( new BigDecimal("10&qu ...

  8. JAVA8之数据流Stream

    在JAVA8之前的传统编程方式,如果我们需要操作一个集合数据,需要使用集合提供的API,通过一个循环去获取集合的元素,这种访问数据的方式会使代码显得臃肿,JAVA8新引入的Stream类,用于重新封装 ...

  9. Java8的Stream语法详解(转载)

    1. Stream初体验 我们先来看看Java里面是怎么定义Stream的: A sequence of elements supporting sequential and parallel agg ...

随机推荐

  1. Visual Studio 2010多线程编程

    随着处理数据量的逐渐增大,串行单核的程序,犹如残灯缺月,无法满足运用需求.大规模集群的出现,解决了这一技术难题.本文旨在探讨如何使用多CPU并行编程,关于CUDA的并行前面文章已有讲述.本文结构分为三 ...

  2. iOS&nbsp;动画总结—UIView动画

    1.概述 UIKit直接将动画集成到UIView类中,实现简单动画的创建过程.UIView类定义了几个内在支持动画的属性声明,当这些属性发生改变时,视图为其变化过程提供内建的动画支持. 执行动画所需要 ...

  3. MyEclipse 报错:Errors running builder 'DeploymentBuilder' on project '工程名'

    并没有更换MyEclipse版本,只是重新卸载了下,然后就报错误,参考了网上的文章 解决版本 .就是删除工程下部署文件

  4. 《java入门第一季》之面向对象(重头戏多态)

    接下来介绍java第三大特性--多态性 /* 多态:同一个对象(事物),在不同时刻体现出来的不同状态. 举例: 猫是猫,猫是动物. 水(液体,固体,气态). 多态的前提: A:要有继承关系. B:要有 ...

  5. ViewPager适配器学习记录( pageAdapter和FragmentPagerAdapter/FragmentStatePagerAdapter))

    1.概述 ViewPager,顾名思义实现控件的滚动功能,是Support-v4的包中类,使用前要先导包.使用的时候跟listView有点相似,需要设置对应的适配器,通常有俩大类 [pageAdapt ...

  6. 如何反编译APK?

    1.概述 一些商业的app都包含很多精美的图片还有一些比较好的配置文件,以前某师兄就说过apk把后缀改为zip,然后解压一下就可以获得很多图片资源,但是这时候你打开一下解压出来的xml资源全是乱码.通 ...

  7. Android遍历获取指定目录的文件

    我们经常遇到一个问题,需要获取指定目录的某些扩展名的文件,并将其存在Vector中,怎么来实现呢? // 获取当前目录下所有的mp4文件 public static Vector<String& ...

  8. Win8 HTML5与JS编程学习笔记(二)

    近期一直受到win8应用的Grid布局困扰,经过了半下午加半个晚上的奋斗,终于是弄明白了Grid布局方法的规则.之前我是阅读的微软官方的开发教程,书中没有详细说明CSS3的布局规则,自己鼓捣了半天也是 ...

  9. cocos2d-x 游戏开发之有限状态机(FSM) (四)

    cocos2d-x 游戏开发之有限状态机(FSM) (四) 虽然我们了解了FSM,并且可以写自己的FSM,但是有更好的工具帮我们完成这个繁琐的工作.SMC(http://smc.sourceforge ...

  10. VueJs(5)---V-bind指令

    V-bind指令 一.概述 v-bind  主要用于属性绑定,比方你的class属性,style属性,value属性,href属性等等,只要是属性,就可以用v-bind指令进行绑定. 示例: < ...