栏目分类:
子分类:
返回
文库吧用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
文库吧 > IT > 软件开发 > 后端开发 > Java

java8新特性

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java8新特性

文章目录
  • Lambda表达式
    • 实例
    • 优化方式一:策略设计模式
    • 优化方式二:匿名内部类
    • 优化方式三:Lambda表达式
    • 优化方式四:Stream API
    • Lambda表达式语法
  • 函数式接口
    • 自定义函数式接口
    • 作为参数传递Lambda表达式
    • Java 内置四大核心函数式接口
  • 方法引用与构造器引用
  • Stream API
    • 创建Stream
    • Stream 的中间操作
    • Stream的终止操作
  • 接口中的默认方法与静态方法
  • 新时间日期API
  • 其他新特性

Lambda表达式

Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

    //原来的匿名内部类
    @Test
    public void test1(){
        Comparator comparator = new Comparator() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        TreeSet ts = new TreeSet<>(comparator);
    }
    //Lambda表达式
    @Test
    public void test2(){
        Comparator comparator = (x,y) -> Integer.compare(x,y);
        TreeSet ts = new TreeSet<>(comparator);
    }
实例

员工实体类

package com.xxxx.lln;


public class Employee {

    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

    public Employee(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    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 getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "name='" + name + ''' +
                ", age=" + age +
                ", salary=" + salary +
                '}';
    }
}

定义员工集合

    //员工集合
    List employees = Arrays.asList(
            new Employee("张三",18,9999.99),
            new Employee("李四",38,6666.66),
            new Employee("王五",35,5555.55),
            new Employee("赵六",13,3333.33),
            new Employee("田七",26,8888.88)
    );

需求1:获取年龄大于35的员工信息

	//过滤
    public List filterEmployees(List list){
        List emps =new ArrayList<>();

        for (Employee emp : list) {
            if (emp.getAge()>=35){
                emps.add(emp);
            }
        }
        return emps;
    }

    //调用
    @Test
    public void test3(){
        List list = filterEmployees(employees);
        for(Employee emp : list){
            System.out.println(emp.toString());
        }
    }

需求2:获取工资大于5000的员工信息
    //过滤
    public List filterEmployees2(List list){
        List emps =new ArrayList<>();
        for (Employee emp : list) {
            if (emp.getSalary()>=5000){//只改变了方法名和这一句
                emps.add(emp);
            }
        }
        return emps;
    }
    //调用
    ......

每次都要写过滤函数,太麻烦了

优化方式一:策略设计模式

定义一个接口类

package com.xxxx.lln;


public interface MyPredicate{

    public boolean test(T t);

}

定义过滤的方法函数

    //过滤员工,需要传入员工集合和过滤条件的接口类
    public List filterEmployee(List list , MyPredicate mp){
        List emps = new ArrayList<>();
        for (Employee employee:list) {
            if (mp.test(employee)){
                emps.add(employee);
            }
        }
        return emps;
    }

需要的时候去实现接口类

package com.xxxx.lln;


public class FilterEmployeeByAge implements MyPredicate{
    @Override
    public boolean test(Employee employee) {
        return employee.getAge()>=35;
    }

}
public class FilterEmployeeBySalary implements MyPredicate{
    @Override
    public boolean test(Employee employee) {
        return employee.getSalary()>=5000;
    }
}

调用:

    @Test
    public void test4(){
        List list = filterEmployee(employees,new FilterEmployeeByAge());
        for (Employee employee : list) {
            System.out.println(employee);
        }

        System.out.println("------------------------------------");

        List list2 = filterEmployee(employees,new FilterEmployeeBySalary());
        for (Employee employee : list2) {
            System.out.println(employee);
        }

    }

优化方式二:匿名内部类
    @Test
    public void test5(){
        List list = filterEmployee(employees, new MyPredicate() {
            @Override
            public boolean test(Employee employee) {
                return employee.getSalary()>=5000;
            }
        });
        for (Employee employee : list){
            System.out.println(employee);
        }
    }

优化方式三:Lambda表达式
    //语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)
    @Test
    public void test6(){
    	//仍需要调用过滤的那个接口
        List list = filterEmployee(employees,(e) -> e.getSalary() >= 5000);
        list.forEach(System.out::println);
    }

优化方式四:Stream API
    @Test
    public void test7(){
    	//不需要用上面的接口
        employees.stream()
                .filter((e) -> e.getSalary() >= 5000)
                .limit(2)//取前两个
                .forEach(System.out::println);

        System.out.println("---------------------------");

        //把所有名字拿出来
        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);
    }

Lambda表达式语法

Lambda 表达式在Java 语言中引入了一个新的语法元 素和操作符。这个操作符为 -> , 该操作符被称 为 Lambda 操作符或剪头操作符。

它将 Lambda 分为 两个部分:
左侧:指定了 Lambda 表达式需要的所有参数
右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。


这里给出六个接口,后文的全部操作都利用这六个接口来进行阐述。

@FunctionalInterface
public interface NoReturnMultiParam {
    void method(int a, int b);
}


@FunctionalInterface
public interface NoReturnNoParam {
    void method();
}


@FunctionalInterface
public interface NoReturnOneParam {
    void method(int a);
}


@FunctionalInterface
public interface ReturnMultiParam {
    int method(int a, int b);
}


@FunctionalInterface
public interface ReturnNoParam {
    int method();
}


@FunctionalInterface
public interface ReturnOneParam {
    int method(int a);
}

语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)。

import lambda.interfaces.*;

public class Test1 {
    public static void main(String[] args) {
        //无参无返回
        NoReturnNoParam noReturnNoParam = () -> {
            System.out.println("NoReturnNoParam");
        };
        noReturnNoParam.method();

        //一个参数无返回
        NoReturnOneParam noReturnOneParam = (int a) -> {
            System.out.println("NoReturnOneParam param:" + a);
        };
        noReturnOneParam.method(6);

        //多个参数无返回
        NoReturnMultiParam noReturnMultiParam = (int a, int b) -> {
            System.out.println("NoReturnMultiParam param:" + "{" + a +"," + + b +"}");
        };
        noReturnMultiParam.method(6, 8);

        //无参有返回值
        ReturnNoParam returnNoParam = () -> {
            System.out.print("ReturnNoParam");
            return 1;
        };

        int res = returnNoParam.method();
        System.out.println("return:" + res);

        //一个参数有返回值
        ReturnOneParam returnOneParam = (int a) -> {
            System.out.println("ReturnOneParam param:" + a);
            return 1;
        };

        int res2 = returnOneParam.method(6);
        System.out.println("return:" + res2);

        //多个参数有返回值
        ReturnMultiParam returnMultiParam = (int a, int b) -> {
            System.out.println("ReturnMultiParam param:" + "{" + a + "," + b +"}");
            return 1;
        };

        int res3 = returnMultiParam.method(6, 8);
        System.out.println("return:" + res3);
    }
}

Lambda 语法简化

我们可以通过观察以下代码来完成代码的进一步简化,写出更加优雅的代码。

import lambda.interfaces.*;

public class Test2 {
    public static void main(String[] args) {

        //1.简化参数类型,可以不写参数类型,但是必须所有参数都不写
        NoReturnMultiParam lamdba1 = (a, b) -> {
            System.out.println("简化参数类型");
        };
        lamdba1.method(1, 2);

        //2.简化参数小括号,如果只有一个参数则可以省略参数小括号
        NoReturnOneParam lambda2 = a -> {
            System.out.println("简化参数小括号");
        };
        lambda2.method(1);

        //3.简化方法体大括号,如果方法条只有一条语句,则可以省略方法体大括号
        NoReturnNoParam lambda3 = () -> System.out.println("简化方法体大括号");
        lambda3.method();

        //4.如果方法体只有一条语句,并且是 return 语句,则可以省略方法体大括号
        ReturnOneParam lambda4 = a -> a+3;
        System.out.println(lambda4.method(5));

        ReturnMultiParam lambda5 = (a, b) -> a+b;
        System.out.println(lambda5.method(1, 1));
    }
}

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

 

函数式接口

什么是函数式接口

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

函数式接口中使用泛型:

    @FunctionalInterface
    public interface MyFunction{
        public T getValue();
    }
作为参数传递Lambda表达式
    public String toUpperString(MyFunction m,String s){
        return m.getValue(s);
    }
    //作为参数传递
    String newStr = toUpperString((str)->str.toUpperCase(),"abcdef");
    System.out.println(newStr);

需求:对一个数进行运算


    @FunctionalInterface
    public interface MyFunction {
        public Integer getValue(Integer num);
    }

    
    public Integer operation(Integer num , MyFunction m){
        return m.getValue(num);
    }
    
    
    @Test
    public void test(){
        Integer num = operation(100,x -> x * x);
        System.out.println(num);

		System.out.println(operation(200,x -> x+300));
    }

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

 

方法引用与构造器引用

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

如下三种主要使用情况:

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


注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName

        //对象::实例方法名
        //Consumer con = x -> System.out.println(x);
        Consumer con = System.out::println;
        con.accept("asdfghjkl");

        System.out.println("------------------------------------");

        Employee emp = new Employee("lln",18,999);
        //Supplier supplier = () -> emp.getName();
        Supplier supplier = emp::getName;
        System.out.println(supplier.get());

        System.out.println("------------------------------------");

        //类::静态方法名
        Comparator com = (x,y) -> Integer.compare(x,y);
        //Comparator com = Integer::compare;
        System.out.println(com.compare(1,2));

        System.out.println("------------------------------------");

        //类::实例方法名
        //BiPredicate bp = (x,y)->x.equals(y);
        //规则:第一个参数是方法的调用者,第二个参数是要调用方法的参数时,可以使用类::实例方法名
        BiPredicate bp = String::equals;

构造器引用

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

ClassName::new

interface ItemCreatorBlankConstruct {
    Item getItem();
}
interface ItemCreatorParamContruct {
    Item getItem(int id, String name, double price);
}

public class Exe2 {
    public static void main(String[] args) {
        ItemCreatorBlankConstruct creator = () -> new Item();
        Item item = creator.getItem();

        ItemCreatorBlankConstruct creator2 = Item::new;
        Item item2 = creator2.getItem();

        ItemCreatorParamContruct creator3 = Item::new;
        Item item3 = creator3.getItem(112, "鼠标", 135.99);
    }
}

数组引用

        //数组引用
        //Function fun = (x) -> new String[x];
        Function fun = String[]::new;
        String[] strs = fun.apply(10);
        System.out.println(strs.length);

 

Stream API

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一 个则是 Stream API(java.util.stream.*)。

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对 集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。

也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

流(Stream) 到底是什么呢?

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

“集合讲的是数据,流讲的是计算!”

注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

 

创建Stream

1.可以通过Collection系列集合提供的stream()或parallelStream()

Java8 中的 Collection 接口被扩展,提供了 两个获取流的方法:

  • default Stream stream() : 返回一个顺序流
  • default Stream parallelStream() : 返回一个并行流
    List list = new ArrayList<>();
    Stream stream1 = list.stream();

2.由数组创建

Java8 中的 Arrays 的静态方法 stream() 可 以获取数组流:

static Stream stream(T[] array): 返回一个流

    Employee[] emps = new Employee[10];
    Stream stream2 = Arrays.stream(emps);

3.由值创建流

通过Stream类中的静态方法of()

可以使用静态方法 Stream.of(), 通过显示值 创建一个流。它可以接收任意数量的参数。

Stream stream3 = Stream.of("aa","bb","cc");

4.由函数创建流:创建无限流

可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。

  • 迭代 public static Stream iterate(final T seed, final UnaryOperator f)

  • 生成 public static Stream generate(Supplier s) :

        //迭代
        Stream stream4 = Stream.iterate(0,(x) -> x + 2);
        stream4.limit(10)
                .forEach(System.out::println);

        //生成
        Stream.generate(()->Math.random())
                .limit(5)
                .forEach(System.out::println);
Stream 的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”。

    //员工集合
    List employees = Arrays.asList(
            new Employee("张三",18,9999.99),
            new Employee("李四",38,6666.66),
            new Employee("王五",35,5555.55),
            new Employee("赵六",13,3333.33),
            new Employee("田七",26,8888.88)
    );

    //内部迭代:迭代操作由Stream API完成
    @Test
    public void test1(){
        //中间操作:不会执行任何操作
        Stream stream = employees.stream()
                .filter((e)->e.getAge()>35);
        //终止操作:一次性执行全部内容,即“惰性求值”
        stream.forEach(System.out::println);

    }


    //外部迭代
    @Test
    public void test2(){

        Iterator it = employees.iterator();

        while(it.hasNext()){
            System.out.println(it.next());
        }
        
    }

筛选与切片

    @Test
    public void test3(){
        employees.stream()
                .filter((e)->e.getSalary()>5000)
                .limit(2)
                .forEach(System.out::println);
    }
//只要迭代到满足条件的两个数据后,后续操作就不进行了
    @Test
    public void test4(){
        employees.stream()
                .filter((e)->e.getSalary()>5000)
                //跳过前两个满足条件的数据
                .skip(2)
                .forEach(System.out::println);
    }

去重

    @Test
    public void test5(){
        employees.stream()
                .filter((e)->e.getSalary()>5000)
                .distinct()
                .forEach(System.out::println);
    }

映射

    @Test
    public void test6(){
        List list = Arrays.asList("aaa","bbb","ccc");
        list.stream()
                .map((str)->str.toUpperCase())
                .forEach(System.out::println);

        System.out.println("--------------------");

        //提取名字
        employees.stream()
                .map(Employee::getName)
                .distinct()
                .forEach(System.out::println);

    }

flatMap

    @Test
    public void test7(){
        List list = Arrays.asList("aaa","bbb","ccc");
//        {{a,a,a},{b,b,b},{c,c,c}}
//        Stream> stream = list.stream()
//                .map(TestStreamAPI2::filterCharacter);
//
//        stream.forEach((sm)->{
//            sm.forEach(System.out::println);
//        });

        System.out.println("-----------------------");

        //{a,a,a,b,b,b,c,c,c}
        Stream sm = list.stream()
                .flatMap(TestStreamAPI2::filterCharacter);
        sm.forEach(System.out::println);
        
    }


    public static Stream filterCharacter(String str){
        List list = new ArrayList<>();

        for (Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }

排序

Stream的终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的 值,例如:List、Integer,甚至是 void 。

查找和匹配

allMatch——检查是否匹配所有元素 传入断定型接口

anyMatch——检查是否至少匹配一个元素 传入断定型接口

noneMatch——检查是否没有匹配的元素 传入断定型接口

findFirst——返回第一个元素

findAny——返回当前流中的任意一个元素

count——返回流中元素的总个数

max——返回流中最大值 传入Comparator接口

min——返回流中最小值 传入Comparator接口

 

接口中的默认方法与静态方法 新时间日期API 其他新特性
转载请注明:文章转载自 www.wk8.com.cn
本文地址:https://www.wk8.com.cn/it/1040220.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 wk8.com.cn

ICP备案号:晋ICP备2021003244-6号