JDK8新特性


JAVA8新特性简介

1.Lambda 表达式:Lambda 表达式是函数式编程的重要组成部分。它们提供了一种简洁的方式来表示匿名函数,可以作为参数传递给方法或使用函数式接口进行操作。

2.Stream API:Stream API 提供了一种流式处理集合数据的方式。它支持函数式编程风格的操作,可以进行过滤、映射、排序、归约等操作,简化了对集合数据的处理。

3.方法引用:方法引用提供了一种更简洁的语法来调用已经存在的方法。它可以通过名称引用静态方法、实例方法或构造函数,并且可以与Lambda表达式配合使用。

4.默认方法(Default Methods):默认方法是接口中的一种新特性,它允许在接口中定义具有默认实现的方法。这样,在接口中添加新方法时,现有的实现类不需要修改代码即可获得默认方法的实现。

5.函数式接口:函数式接口是只有一个抽象方法的接口。Java 8引入了@FunctionalInterface注解,用于标识函数式接口。函数式接口可以被Lambda表达式直接使用。

6.Optional 类:Optional 类是一个容器对象,用于包装可能为null的值。它提供了一种优雅的方式来处理可能为空的对象,避免了空指针异常。

7.新的日期/时间 API(java.time包):Java 8引入了全新的日期和时间API,提供了更强大、更可靠的日期和时间处理功能。这些API具有不可变性、线程安全性和更好的设计。

除了上述特性之外,还有一些其他改进,例如重复注解、类型注解、CompletableFuture 异步编程、新的并发工具等。
请添加图片描述

Lambda表达式

语法

Lambda 表达式: 在Java 8 语言中引入的一种新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:

  • 匿名内部类实现的接口一定是函数接口(只有一个抽象方法的接口)
  • 左侧: 指定了 Lambda 表达式需要的参数列表
  • 右侧: 指定了 Lambda 体, 是抽象方法的实现逻辑,也即Lambda 表达式要执行的功能。

类型推断

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

/**
 * 一个参数的语法
 */
public class LamdbaDemo5 {

    public static void show(B b) {
        b.f1("hello lambda 表达式");
    }

    /**
     * 匿名内部类
     */
    public void test1() {
        //匿名内部类
        B b = new B() {
            @Override
            public void f1(String s) {
                System.out.println(s);
            }
        };
        show(b);
    }

    /**
     * lambda表达式
     */
    @Test
    public void test2() {
        //匿名内部类
//        B b = (String str) -> {
//            System.out.println(str);
//        };
//        show(b);

        show((String str) -> {
            System.out.println(str);
        });
    }

    /**
     * lambda表达式左边参数列表,可以根据上下文判断参数类型
     */
    @Test
    public void test3() {
        show((str) -> {
            str += " hello abc";
            System.out.println(str);
        });
    }

    /**
     * 只有一个参数
     */
    @Test
    public void test4() {
        show(str -> {
            str += " hello abc";
            System.out.println(str);
        });
    }

    @FunctionalInterface
    interface B {
        void f1(String s);
    }


}

两个参数

/**
 * 多个参数的语法
 */
public class LamdbaDemo6 {

    public static void show(C c) {
        c.f1(10, 20);
    }

    /**
     * 打印两个数的大的值
     */
    @Test
    public void test1() {
        C c = new C() {
            @Override
            public void f1(int x, int y) {
                System.out.println(x > y ? x : y);
            }
        };
        show(c);
    }

    @Test
    public void test2() {
        show((x, y) -> {
//            System.out.println(x > y ? x :y);
            System.out.println(x + y);
        });
    }


    @FunctionalInterface
    interface C {
        void f1(int x, int y);
    }

    @Test
    public void test3() {
        List list = new ArrayList();

        list.add("555");
        list.add("zzz");
        list.add("xxx");
        list.add("yyy");
        list.add("mmm");
        list.add("nnn");
        list.add("aaa");
        list.add("aaa");
        list.add("111");
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");

        Comparator com = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                String s1 = (String) o1;
                String s2 = (String) o2;
                return -s1.compareTo(s2);
            }

        };
        Collections.sort(list, com); //定制排序

        System.out.println(list);
    }


    @Test
    public void test4() {
        List list = new ArrayList();

        list.add("555");
        list.add("zzz");
        list.add("xxx");
        list.add("yyy");
        list.add("mmm");
        list.add("nnn");
        list.add("aaa");
        list.add("aaa");
        list.add("111");
        list.add("111");
        list.add("222");
        list.add("333");
        list.add("444");

//        Comparator com = new Comparator() {
//            @Override
//            public int compare(Object o1, Object o2) {
//                String s1 = (String) o1;
//                String s2 = (String) o2;
//                return -s1.compareTo(s2);
//            }
//
//        };

        //定制排序器
        Comparator<String> com = (s1, s2) -> {
            return -s1.compareTo(s2);
        };
        Collections.sort(list, com); //定制排序

        System.out.println(list);
    }



}

函数式接口

函数式接口是指只包含一个抽象方法的接口,用于支持函数式编程和Lambda表达式。在Java中,函数式接口使用@FunctionalInterface注解进行标识。

函数式接口可以作为方法的参数或返回值,用于简化代码并提供更灵活的编程方式。Java 8及以后版本提供了一些常见的函数式接口,如下所示:

1.Consumer<T>:消费型接口,代表接受一个输入参数并且没有返回值的操作。方法是 void accept(T t)

2.Supplier<T>:供给型接口,代表一个无参数的函数,用于提供一个返回值。方法是 T get()

3.Function<T, R>:函数型接口,代表接受一个输入参数并产生一个结果的函数。方法是 R apply(T t)

4.Predicate<T>:断言型接口,代表一个输入参数的判断条件,返回一个布尔值。方法是 boolean test(T t)

5.BiConsumer<T, U>:代表接受两个输入参数并且没有返回值的操作。方法是 void accept(T t, U u)

6.BiFunction<T, U, R>:代表接受两个输入参数并产生一个结果的函数。方法是 R apply(T t, U u)

除了上述常见的函数式接口,Java还提供了一些其他的函数式接口,如UnaryOperator<T>BinaryOperator<T>等,用于特定的场景和操作。

函数式接口的使用可以大大简化代码,并提高代码的可读性和灵活性。使用Lambda表达式可以方便地实现函数式接口的抽象方法,从而实现函数式编程的思想。

自定义函数式接口

@FunctionalInterface
 public interface MyInterface {
 
     void method1();
 
 //    void method2();
 }

Consumer函数式接口

Consumer:接受一个输入参数,并且不返回任何结果。它对输入参数执行某些操作,可以用来消费(处理)一个对象。

Consumer<String> printer = str -> System.out.println(str);
printer.accept("Hello, world!");

Supplier函数式接口

不接受任何参数,但是返回一个结果。它用于生成(提供)一个值。

Supplier<Integer> randomNumberGenerator = () -> new Random().nextInt(100);
int randomNumber = randomNumberGenerator.get();

Function<T, R>函数式接口

接受一个输入参数,并且返回一个结果。它将输入参数转换为特定类型的输出。

Function<Integer, String> convertToString = num -> String.valueOf(num);
String strNumber = convertToString.apply(42);

Predicate函数式接口

接受一个输入参数,并且返回一个布尔值结果。它用于判断输入参数是否满足某个条件。

Predicate<Integer> isEven = num -> num % 2 == 0;
boolean result = isEven.test(7);

函数式接口在具体案例中的用法

public class ConsumerTest1 {

    @Test
    public void test1() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 18));
        list.add(new User(2, "john", 18));
        list.add(new User(3, "doc", 18));
        list.add(new User(4, "cat", 18));
        list.add(new User(5, "tom", 18));
        list.add(new User(6, "tomcat", 18));
        list.add(new User(7, "mysql", 18));
        list.add(new User(8, "java", 18));

        /**
         * stream中使用Consumer
         */
        Consumer<User> c = o -> System.out.println(o);
        list.forEach(c);
    }

    @Test
    public void test2() {
        Consumer<String> c = s -> System.out.println(s);

        Consumer<String> c2 = s -> {
            System.out.println(s.substring(6));
        };
        c.accept("hello world");
        c2.accept("hello world");
    }

    /**
     * 处理金额
     * @param c
     * @param money
     * 这个处理金额方法是因为,在创建完Consumer函数式接口之后是不会自动调用所写的方法体的
     * 用这个方法来调用所实现的方法体
     */
    public void f1(Consumer<Double> c, Double money) {
        c.accept(money);
    }

    @Test
    public void test4() {
        f1(m -> {
            DecimalFormat df = new DecimalFormat("#,###.##");
            System.out.println(df.format(m));
        }, 12345623434.789);
        System.out.println("----------------------");
        f1(m -> {
            DecimalFormat df = new DecimalFormat("0,000.000");
            System.out.println(df.format(m));
        }, 12345623434.789755555);
    }

    /**
     * DecimalFormat保留2位小数,每三位一个多厚(金额)
     */
    @Test
    public void test3() {
        DecimalFormat df = new DecimalFormat("#,###.##");
        System.out.println(df.format(12345623434.789));
    }


}

案例2

public class PredicatTest1 {

    /**
     * Predicate在Stream中的应用
     */
    @Test
    public void test1() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 22));
        list.add(new User(2, "john", 33));
        list.add(new User(3, "doc", 19));
        list.add(new User(4, "cat", 20));
        list.add(new User(5, "tom", 20));
        list.add(new User(6, "tomcat", 70));
        list.add(new User(7, "mysql", 73));
        list.add(new User(8, "java", 84));

        /**
         * 删除年龄是20的元素
         */
        list.removeIf(u ->  u.getAge() == 20);
        System.out.println(list);

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

        /**
         * 查找>=5的用户,循环
         */
        list.stream().filter(u -> {
            return u.getId() >= 5;
        }).forEach((u) -> {
            System.out.println(u);
        });

    }

    public static List<String> filterList(List<String> list, Predicate<String> p) {
        List<String> result = new ArrayList<>();
        for (String s : list) {
            if (p.test(s)) { //对s进行判断
                result.add(s);
            }
        }
        return result;
    }

    @Test
    public void test2() {
        List<String> list = new ArrayList<>();
        list.add("hello abc");
        list.add("welcome abc");
        list.add("hello world");
        list.add("你好 abc");
        list.add("大家好 abc");
        list.add("hello mickey");

        filterList(list, s -> {
            return s.contains("hello");
        }).forEach(s -> System.out.println(s));

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

        filterList(list, s -> {
            return s.contains("abc");
        }).forEach(s -> System.out.println(s));
    }


}

案例3

public class FunctionTest1 {

    /**
     * function在stream的使用
     */
    @Test
    public void test1() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 18));
        list.add(new User(2, "john", 18));
        list.add(new User(3, "doc", 18));
        list.add(new User(4, "cat", 18));
        list.add(new User(5, "tom", 18));
        list.add(new User(6, "tomcat", 18));
        list.add(new User(7, "mysql", 18));
        list.add(new User(8, "java", 18));

//        //得到集合中的id
//        List<Integer> ids = new ArrayList<>();
//        for (User user : list) {
//            ids.add(user.getId());
//        }

        //得到集合中的所有的id
        List<String> ids = list.stream().map(u -> u.getId() + u.getName()).collect(Collectors.toList());
        System.out.println(ids);
    }

    public Object performUser(Function<User, Object> f, User u) {
        return f.apply(u);
    }

    @Test
    public void test2() {
        User user = new User(1, "mickey", 18);

        //得到学员名字
        System.out.println(performUser(u -> {
            return u.getName();
        }, user));

        System.out.println("-------");
        System.out.println(performUser(u -> {
            return u.getName() + u.getAge();
        }, user));

    }

}

案例4

public class SupplierTest1 {

    public static void printBean(Supplier s) {
        System.out.println(s.get());
    }

    @Test
    public void test1() {
        printBean(() -> new User(1, "mickey", 18));
        System.out.println("------");
        printBean(() -> "hello stream");
    }

}

案例5

public class BinaryOperatorTest1 {

    @Test
    public void test1() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 18));
        list.add(new User(2, "john", 18));
        list.add(new User(3, "doc", 18));
        list.add(new User(4, "cat", 18));
        list.add(new User(5, "tom", 18));
        list.add(new User(6, "tomcat", 18));
        list.add(new User(7, "mysql", 18));
        list.add(new User(8, "java", 18));

        //得到所有的id,然后相加得到结果 -> List<Integer> ids =
        Optional<Integer> optionalInteger = list.stream().map(u -> u.getId()).reduce((d1, d2) ->  d1 + d2);
        System.out.println(optionalInteger.get());
    }

}

方法引用

方法引用是Lambda表达式的一个语法糖,也就是lambda的简化
要求:
实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致!

  • 如下三种主要使用情况:
  • 对象::实例方法名
    /**
     * lambda表达式的语法糖
     */
    @Test
    public void test2() {
        printBean(User :: new);
    }

  • 类::静态方法名
    @Test
    public void test1() {
        List<User> list = new ArrayList<>();
        list.add(new User(1, "mickey", 18));
        list.add(new User(2, "john", 18));
        list.add(new User(3, "doc", 18));
        list.add(new User(4, "cat", 18));
        list.add(new User(5, "tom", 18));
        list.add(new User(6, "tomcat", 18));
        list.add(new User(7, "mysql", 18));
        list.add(new User(8, "java", 18));

        //匿名内部类
        list.forEach(new Consumer<User>() {
            @Override
            public void accept(User user) {
                System.out.println(user);
            }
        });

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

        /**
         * stream中使用Consumer
         */
        list.forEach(o -> System.out.println(o));
        System.out.println("---------------");

        //方法引用
        list.forEach(System.out :: println);
    }
  • 类::实例方法名
    @Test
    public void test5() {
        //匿名内部类
        int i1 = f1(new BiFunction<Integer, Integer, Integer>() {
            @Override
            public Integer apply(Integer d1, Integer d2) {
                return Integer.max(d1, d2);
            }
        }, 12, 15);

        System.out.println("--------");
        //lambda表达式
        int i2 = f1((d1, d2) -> Integer.max(d1, d2), 12, 15);

        //方法引用-lambda表达式的语法糖
        int i3 = f1(Integer::max, 12, 15);

        System.out.println(i1 + " " + i2 + " " + i3);

    }

Stream API

Stream API是Java 8引入的一个强大的功能,它提供了一种用于处理集合数据的函数式编程方式。Stream(流)可以被视为一种高级迭代器,它允许对集合进行一系列的操作,如过滤、映射、排序、聚合等。

Stream API的主要特点和优势包括:

1.函数式编程:Stream API是基于函数式编程思想实现的,可以使用Lambda表达式来进行操作,使代码更简洁、可读性更强。

2.延迟执行:Stream API支持延迟执行,只有在终止操作被调用时才会真正进行计算,这样可以提高效率并节省资源。

3.内部迭代:Stream API封装了对集合的遍历和操作过程,可以自动进行迭代,简化了代码编写。

4.并行处理:Stream API支持并行处理,可以在多核处理器上利用并行性来提高性能。

Stream API的使用通常包括以下几个步骤:

1.创建流:通过集合、数组、IO通道等方式创建一个流。

2.中间操作:对流进行一系列的中间操作,如过滤、映射、排序、去重等。这些操作可以串联起来形成一个操作流水线。

3.终止操作:对流进行最终的操作,如收集结果、聚合数据、遍历元素等。终止操作会触发流的计算,并返回最终结果。

Stream API提供了丰富的操作方法,如filter()map()sorted()distinct()等,可以根据需要进行选择和组合。使用Stream API可以简化集合数据的处理,提高代码的可读性和可维护性,并且可以充分利用多核处理器的优势来提高性能。

中间操作

Stream API提供了丰富的中间操作方法,可以在流的元素上进行过滤、映射、排序、去重等操作。下面是一些常用的中间操作方法:

1.filter(Predicate<T> predicate): 过滤符合条件的元素,接受一个谓词(Predicate)作为参数,返回一个包含符合条件元素的新流。

2.map(Function<T, R> mapper): 对流的每个元素应用一个函数,将元素映射为另一种类型,接受一个函数(Function)作为参数,返回一个包含映射结果的新流。

3.flatMap(Function<T, Stream<R>> mapper): 将流的每个元素映射为一个流,然后将这些流合并为一个流,接受一个函数(Function)作为参数,返回一个新流。

4.sorted(): 对流的元素进行排序,默认按照自然顺序进行排序。

5.sorted(Comparator<T> comparator): 对流的元素进行排序,根据指定的比较器(Comparator)进行排序。

6.distinct(): 去除流中重复的元素,根据元素的equals()方法和hashCode()方法判断是否相同。

7.limit(long maxSize): 截断流,获取流的前n个元素。

8.skip(long n): 跳过流的前n个元素,返回一个新流。

9.peek(Consumer<T> action): 对流的每个元素执行一个操作,不会改变流的内容。

这些中间操作方法可以按需组合使用,形成一个操作流水线。这些操作都是惰性执行的,只有当终止操作被调用时才会触发计算。通过合理地使用中间操作方法,可以对流的元素进行灵活、高效的处理。

终止操作

Stream API提供了各种终止操作方法,用于触发流的计算并返回最终结果。下面是一些常用的终止操作方法:

1.forEach(Consumer<T> action): 对流的每个元素执行指定操作,无返回值。

2.count(): 返回流中元素的总个数。

3.collect(Collector<T, A, R> collector): 将流中的元素收集到一个结果容器中,使用指定的收集器(Collector)进行处理,并返回最终结果。

4.min(Comparator<T> comparator): 返回流中的最小元素,根据指定的比较器(Comparator)进行比较。

5.max(Comparator<T> comparator): 返回流中的最大元素,根据指定的比较器(Comparator)进行比较。

6.findFirst(): 返回流中的第一个元素(按照流的遍历顺序)。

7.findAny(): 返回流中的任意一个元素。

8.allMatch(Predicate<T> predicate): 检查流中的所有元素是否都符合指定条件,返回一个布尔值。

9.anyMatch(Predicate<T> predicate): 检查流中是否存在至少一个元素符合指定条件,返回一个布尔值。

10.noneMatch(Predicate<T> predicate): 检查流中是否没有任何元素符合指定条件,返回一个布尔值。

11.reduce(T identity, BinaryOperator<T> accumulator): 将流中的元素通过累加器(BinaryOperator)进行累积操作,返回一个包含累积结果的值。

12.reduce(BinaryOperator<T> accumulator): 将流中的元素通过累加器(BinaryOperator)进行累积操作,返回一个Optional对象,表示累积结果。

这些终止操作可以根据需求选择使用,它们会触发流的计算,并返回最终的结果。通过合理地组合中间操作和终止操作,可以灵活、高效地处理流的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值