JDK8新特性

Lambda表达式

1. 需求分析

创建一个新的线程,指定线程要执行的任务
在这里插入图片描述代码分析:

  1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心
  2. 为了指定run方法体,不得不需要Runnable的实现类
  3. 为了省去一个Runnable的实现类,不得不使用匿名内部类
  4. 必须覆盖重写抽象的run方法,所有的方法名称、方法参数、方法返回值、不得不都重写一遍,而且不能出错
  5. 而实际上,我们只在乎方法体中的代码

2.Lambda初体验

上述代码用Lambda表达式

  1. Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码;
  2. Lambda表达式的优点:简化了匿名内部类的使用,语法更加简单、
  3. 匿名内部类语法冗余,体验了Lambda表达式后,发现Lambda表达是简化匿名内部类的一种方式
    在这里插入图片描述

3.Lambda表达式的语法规则

  1. Lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成:
    (参数类型 参数名称 ) -> {
    代码体;
    }
  2. 练习1
// 定义一个接口
public interface UserService {
    void show();
}
// 然后在主方法使用
public class Demo02Lambda {
    public static void main(String[] args) {
        goShow(new UserService() {
            @Override
            public void show() {
                System.out.println("show  方法执行了...");
            }
        });
        System.out.println("--------------------------------");
        
        goShow(()->{
            System.out.println("lambda方式执行了");
        });
    }
    public static void goShow(UserService userService) {
        userService.show();
    }
}

输出

show  方法执行了...
--------------------------------
lambda方式执行了
  1. 练习2
    完成一个有参且有返回值的Lambda表达式案例
// 创建一个Person类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {
    private String name;
    private Integer age;
    private Integer height;
}

在这里插入图片描述

4.Lambda表达式省略写法

在Lambda表达式的标准写法基础上,可以使用省略写法规则

  1. 小括号内的参数类型可以省略
  2. 如果小括号内有且仅有一个参数,则小括号可以省略
  3. 如果大括号内仅有一行代码,可以同时省略大括号,return关键字及语句分号

5.Lambda表达式的使用前提

Lambda表达式的语法是非常简洁的,但是不是随便使用的,使用时有几个条件需要特别注意

  1. 方法的参数或局部变量类型必须为接口才能使用Lambda
  2. 接口中有且仅有一个抽象方法(@FunctionallInterface被这个注解修饰的接口只能有一个抽象方法)

6.Lambda和匿名内部类的对比

Lambda和匿名内部类的对比

  1. 所需类型不一样
    ->匿名内部类类型可以是类,抽象类,接口
    ->Lambda表达式需要的类型必须是接口
  2. 抽象方法的数量不一样
    ->匿名内部类所需的接口中的抽象方法的数量是随意的
    ->Lambda表达式所需的接口中只能有一个抽象方法
  3. 实现原理不一样
    ->匿名内部类是在编译后形成一个Class
    ->Lambda表达式是在程序运行的时候动态生成Class

接口中新增的方法

JDK8中接口的新增

在JDK8之前接口中只有静态常量和抽象方法
在JDK8之后接口做了增加,接口中可以有默认方法和静态方法
默认方法:
为什么要增加默认方法?

  1. 在JDK8以前接口中只能有抽象方法和静态变量,会存在以下问题
  2. 如果接口中新增抽象方法,那么实现类都必须要重写这个抽象方法,非常不利与接口扩展
  3. 默认方法实现类可以选择性的重写,解决了这个问题
 格式: 
 interface 接口名{
	public default String  test(){
		System.out.println("接口中的默认方法执行了")return "hello";
	}
}

静态方法

 格式: 
 interface 接口名{
	修饰符 static String  test2(){
		System.out.println("接口中的静态方法执行了")return "hello";
	}
}

静态方法的使用:
接口中的静态方法在实现类中是不能别重写的,调用的话只能通过接口类型来实现,接口名.静态方法名();
两者的区别

  1. 默认方法通过实例调用,静态方法通过接口名调用
    2.默认方法可以别继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法
    3.静态方法不能被继承,实现类不能重写接口的静态方法,只能用接口名调用

函数式接口

介绍:
在JDK中帮我们提供的有函数式接口,主要在java.util.function包中

Supplier(用来生产数据的)

无参有返回值的接口,对应的Lambda表达式需要提供一个返回数据的类型

@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

使用:

public class SupplierTest {
    public static void main(String[] args) {
        fun1(()->{
            int arr[] = {22,33,44,55,99,88};
            // 计算出数组中最大值
            Arrays.sort(arr);
            return arr[arr.length - 1];
        });
    }
    private static void fun1(Supplier<Integer> supplier) {
        // get() 是一个无参的有返回值的 抽象方法
        Integer max = supplier.get();
        System.out.println("max = "+max);
    }
}

Consumer(用来消费数据的)

有参无返回值的接口,使用的时候需要指定一个泛型来定义参数类型

@FunctionalInterface
public interface Consumer<T> {
    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);
    }

使用:
将输入的数据统一转换为小写输出

public class ConsumerTest {
    public static void main(String[] args) {
        test(str -> {
            System.out.println(str + "转换为小写:"+str.toLowerCase());
        });
    }
    public static void test(Consumer<String> consumer) {
        consumer.accept("Hello World");
    }
}

默认方法:andThen
如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法

    public static void main(String[] args) {
        test(str -> {
            System.out.println(str + "转换为小写:"+str.toLowerCase());
        },str ->{
            System.out.println(str + "转换为大写:"+str.toUpperCase());
        });
    }
    public static void test(Consumer<String> c1,Consumer<String> c2) {
        String str = "Hello World";
        c1.andThen(c2).accept(str); // 先转小写再转大写
    }

Function

有参有返回值,Function接口是根据一个类型的数据得到另一个类型的数据,前者称之为前置条件,后者称之为后置条件,有参数有返回值

@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);
}

使用:
传入一个字符串返回一个数字

public class FunctionTest {
    public static void main(String[] args) {
        test(msg -> {
            return Integer.parseInt(msg);
        });
    }
    public static void test(Function<String,Integer> function) {
        Integer apply = function.apply("666");
        System.out.println("apply = "+apply);
    }
}

Predicate

有参且返回值为布尔值,

@FunctionalInterface
public interface Predicate<T> {
    /**
     * Evaluates this predicate on the given argument.
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}

使用:

public class PredicateTest {
    public static void main(String[] args) {
        // 如果msg长度大于三返回true,否则返回false
        test(msg -> {
            return msg.length() > 3;
        },"HelloWorld");
    }
    private static void test(Predicate<String> predicate,String msg) {
        boolean b = predicate.test(msg);
        System.out.println("b:"+b);
    }
}

Stream API

1. 集合处理数据的弊端

当我们需要对集合中的元素进行操作的时候,除了必须的添加,删除,获取外,最典型的操作就是集合遍历

public class StreamTest1 {
    public static void main(String[] args) {
        // 定义一个list集合
        List<String> list = Arrays.asList("张三", "张三丰", "成龙", "周星驰");
        List<String> list1 = new ArrayList<>();
        // 获取所有姓张的信息
        for (String s: list) {
            if (s.startsWith("张")) {
                list1.add(s);
            }
        }
        // 获取名称长度为3的用户
        List<String> list2 = new ArrayList<>();
        for (String s : list1) {
            if (s.length() ==3) {
                list2.add(s);
            }
        }
        // 打印到控制台
        for (String s : list2) {
            System.out.println(s);
        }
    }
}

上面的代码针对不同的需求总是一次次遍历集合,这时我们希望有更加高效的处理方式,
这时我们就可以通过Stream API来解决这个问题了

Stream解决方案:

public class StreamTest2 {
    public static void main(String[] args) {
        // 定义一个list集合
        List<String> list = Arrays.asList("张三", "张三丰", "成龙", "周星驰");
        // 获取所有姓张的信息
        // 获取名称长度为3的用户
        // 打印到控制台
        list.stream()
                .filter(s -> s.startsWith("张"))
                .filter(s -> s.length() == 3)
        .forEach(System.out::println);
    }
}

上面的StreamAPI代码的含义:通过流,过滤张,过滤长度,逐一打印出来
Stream流式思想概述
注意:Stream和IO流没有任何关系,Stream流式思想类似于工厂车间的生产流水线,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理,Stream可以看做流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品
Stream流的获取方式
根据Collection获取

  1. 首先,java.util.Collection接口中加入了default方法Stream,也就是说Collection接口下的所有的实现都可以通过Stream方法来获取Stream流
public class StreamTest3 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.stream();
        Set<String> set = new HashSet<>();
        set.stream();
        Vector vector = new Vector();
        vector.stream();
    }
}
  1. 但是Map接口没有实现Collection接口,这时我们可以根据Map获取对应的key value的集合。
public class StreamTest4 {
    public static void main(String[] args) {
        Map<String,Object> map = new HashMap<>();
        Stream<String> stream = map.keySet().stream(); // key
        Stream<Object> stream1 = map.values().stream(); // value
        Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream(); // entry
    }
}

通过Stream的of方法
3. 我们在实际开发中不可避免还是会操作数组中的数据,由于数组对象不能添加默认方法,所有Stream接口中提供了静态方法of

Stream常用方法介绍

Stream流模型的操作很丰富,这里介绍一些常用的API,这些方法可以被分为两种
终结方法:返回值类型不再是Stream类型的方法,不在支持链式调用。
非终结方法:返回值类型仍然是Stream类型的方法,支持链式调用。除了终结方法外,其余都是非终结方法
Stream注意事项(重要)

  1. Stream只能操作一次
  2. Stream方法返回的是新的流
  3. Stream不调用终结方法,中间的操作不会执行

forEach
forEach用来遍历流中的数据的

    void forEachOrdered(Consumer<? super T> action);

该方法接受一个Consumer接口,会将每一个流元素交给函数处理

public class StreamTest6 {
    public static void main(String[] args) {
        Stream.of("a1", "a2", "a3")
        .forEach(System.out::println);
    }
}

count
Stream流中的count方法用来统计其中的元素个数的

    long count();

该方法返回一个long值,代表元素的个数

public class StreamTest6 {
    public static void main(String[] args) {
        long count = Stream.of("a1", "a2", "a3").count();
        System.out.println(count);
    }
}

filter
filter方法的作用是用来过滤数据的,返回符合条件的数据

    Stream<T> filter(Predicate<? super T> predicate);

该接口接收一个Predicate函数式接口参数作为筛选条件

public class StreamTest7 {
    public static void main(String[] args) {
        Stream.of("a1", "a2", "a3").filter((s)->s.contains("a"))
                .forEach(System.out::println);// 输出包含a满足条件的数据
    }
}

limit
Limit方法可以对流进行截取处理,只取前n个数据

    Stream<T> limit(long maxSize);

在这里插入图片描述参数是一个long类型的数值,如果集合当前长度大于参数,就进行截取

//  只取前两个元素
public class StreamTest8 {
    public static void main(String[] args) {
        Stream.of("a1", "a2", "a3").limit(2)
                .forEach(System.out::println);
    }
}

skip
如果希望跳过前面几个元素,可以使用skip方法获取一个截取之后的新流

    Stream<T> skip(long n);

应用:

public class StreamTest9 {
    public static void main(String[] args) {
        Stream.of("a1", "a2", "a3").skip(1)
                .forEach(System.out::println);
    }
}

map
如果我们需要将流中的元素映射到另一个流中,可以使用map方法,
该接口需要一个Function函数式接口参数,可以将当前流中的T类型数据转成另一种R类型数据

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

应用:

	public class StreamTest10 {
    public static void main(String[] args) {
            /* 把字符串类型的数据转换成整形 */
        Stream.of("1", "2", "3", "4", "5", "6", "7")
                // .map(msg -> Integer.parseInt(msg)) 
                .map(Integer::parseInt) //这里可以使用双冒号写法,更加简洁
                .forEach(System.out::println);
    }
}

sorted
如果需要将数据排序,可以使用sorted方法:

    Stream<T> sorted();

使用:

public class StreamTest11 {
    public static void main(String[] args) {
        Stream.of("1", "2", "7", "4","0","5", "6", "8","3")
                .map(Integer::parseInt) //先把字符串转换为整形
                .sorted() // 根据数据自然顺序排序
                .forEach(System.out::println);
    }
}
// Comparator
public class StreamTest11 {
    public static void main(String[] args) {
        Stream.of("1", "2", "7", "4", "0", "5", "6", "8", "3")
                .map(Integer::parseInt) //先把字符串转换为整形
                // .sorted() // 根据数据自然顺序排序
                .sorted((o1, o2) -> o2 - o1) // 降序
                .forEach(System.out::println);
    }
}

distinct
去除重复的数据
在这里插入图片描述

	    Stream<T> distinct();

使用:

	public class StreamTest12 {
    public static void main(String[] args) {
        Stream.of("1", "1", "7", "3", "5", "8", "3")
                .map(Integer::parseInt) //先把字符串转换为整形
                // .sorted() // 根据数据自然顺序排序
                .sorted((o1, o2) -> o2 - o1) // 降序
                .distinct() //去重
                .forEach(System.out::println);
    }
}

在这里插入图片描述
reduce
将所有数据归纳得到一个数据

	    T reduce(T identity, BinaryOperator<T> accumulator);

使用:

public class StreamTest13 {
    public static void main(String[] args) {
        Integer sum = Stream.of(4, 5, 3, 9)
                // identity 默认值
                //第一次的时候会将默认值赋值给x
                //之后每次会将上一次的操作结果赋值给x   y就是每次从数据中获取的元素
                .reduce(0, (x, y) -> {
                    System.out.println("x=" + x + ",y=" + y);
                    return x + y;
                });

        System.out.println(sum);
        // 取最大值
        Integer max = Stream.of(4, 5, 3, 9)
                .reduce(0, (x, y) -> {
                    return x > y ? x : y;
                });
        System.out.println("最大值:"+max);
    }
}

map和reduce的组合
在实际开发中我们经常会将map和reduce结合使用
使用:

public class StreamTest14 {
    public static void main(String[] args) {
        // 需求:求出所有年龄的总和
        Integer sumAge = Stream
                .of(
                        new Person("张三", 18),
                        new Person("李四", 19),
                        new Person("王五", 21),
                        new Person("赵六", 28)
                )
                .map(p -> p.getAge())
                .reduce(0, (x, y) -> x + y);
        System.out.println("年龄总和为:"+sumAge);

    }
}

mapToInt
concat

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JDK 8,也就是 Java Development Kit 8,有许多新的特性。以下是其中一些主要的新特性: 1. **Lambda 表达式和函数式接口**:这是 JDK 8 中最重要的新特性之一。Lambda 表达式允许开发者以更简洁的方式编写代码,通过使用匿名函数来实现。此外,JDK 8 还引入了函数式接口(如 `Supplier`, `Function`, `Consumer`, `BiFunction` 等),它们允许开发者创建更复杂的功能块。 2. **Stream API**:Java Stream API 是 JDK 8 中另一个重要的新特性。它提供了一种对数据进行操作和处理的方式,这种处理方式更接近于其他编程语言的数据处理库。 3. **新的集合类**:JDK 8 引入了一些新的集合类,如 `NavigableSet`, `ConcurrentHashMap` 等,这些类提供了更高效的数据结构和性能。 4. **G1垃圾收集器**:JDK 8 中的 G1垃圾收集器是一个可预测的、并行化的垃圾收集器,提供了更好的性能和响应时间。 5. **日期和时间 API**:JDK 8 引入了一个新的日期和时间 API,它提供了更简单、更一致的方式来处理日期和时间。 6. **模块系统**:Java 模块系统是 JDK 8 中的另一个新特性,它允许开发者创建独立的、可移植的软件包。 7. **流处理框架**:JDK 8 中的流处理框架 Stream API 支持用户自定义的流处理操作符,这使得开发者可以创建更复杂的流处理程序。 8. **新的异常处理机制**:JDK 8 中的新异常处理机制允许开发者使用 lambda 表达式来声明和处理异常。 9. **改进的 JDBC API**:JDK 8 中的 JDBC API 提供了一个更简单、更直观的方式来访问数据库。 以上就是 JDK 8 中一些主要的新特性,这些特性都为开发者提供了更高效、更简洁的开发体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值