java8新特性

本文介绍了Java8的主要新特性,包括Lambda表达式的概念、语法和使用示例,函数式接口的定义以及Java内置的函数式接口,方法引用的三种形式,以及StreamAPI的特点和操作流程,展示了如何通过Stream进行数据处理。
摘要由CSDN通过智能技术生成

Java8(JDK1.8)新特性

1、Lamdba表达式

2、函数式接口

3、方法引用和构造引用

4、Stream API

5、接口中的默认方法和静态方法

6、新时间日期API

7、OPtional

8、其他特性

新特性简介

1、速度快;

2、代码少、简介(新增特性:lamdba表达式);

3、强大的Stream API;

4、使用并行流和串行流;

5、最大化较少空指针异常Optional;

其中最为核心的是Lambda表达式和Stream API

新特性详细介绍

Lambda表达式

Lambda表达式是什么?

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

从匿名类到Lambda转换

public class Demo01 {
    private static Logger log = LoggerFactory.getLogger(Demo01.class);
​
    public static void main(String[] args) {
        Runnable t1 =new Runnable(){
            @Override
            public void run(){
                log.info("我是没有使用Lambda表达式:不简洁");
            }
        };
        
        Runnable t2 = () -> log.info("我是使用Lambda表达式:简洁、灵活");
        
        t1.run();
        t2.run();
        
    }
}

打印的信息结果

11:41:41.239 [main] INFO org.muyu.Demo01 - 我是没有使用Lambda表达式:不简洁
11:41:41.257 [main] INFO org.muyu.Demo01 - 我是使用Lambda表达式:简洁、灵活

Lambda表达式语法

Lambda表达式在java中引入了一种新的语法元素和操作。这种操作符号为"->",Lambda操作符或箭头操作符,它将Lambda表达式分割为两部分。 左边:指Lambda表达式的所有参数 右边:指Lambda体,即表示Lambda表达式需要执行的功能。

语法格式一:无参数、无返回值,只需要一个Lambda体

new Thread(() -> log.info("我是一个线程")).start();

语法格式二:lambda有一个参数、无返回值

public class Demo03 {
    private static Logger log = LoggerFactory.getLogger(Demo03.class);
    public static void main(String[] args) {
        Consumer<String> consumer = new Consumer<String>() {
            @Override
            public void accept(String s) {
                log.info(s);
            }
        };
        consumer.accept("爱与被爱的区别");
​
        Consumer<String> consumer1 = (s) -> log.info(s);
        consumer1.accept("接受爱不一定爱对方,爱一定付出真心爱");
    }
}

语法格式三:Lambda只有一个参数时,可以省略()

Consumer<String> consumer1 = s -> log.info(s);
consumer1.accept("接受爱不一定爱对方,爱一定付出真心爱");

语法格式四:Lambda有两个参数时,并且有返回值

Comparator<Integer> comparatorMin =  new Comparator<Integer>() {
    @Override
    public int compare (Integer o1, Integer o2) {
        return o1 > o2 ? o1 : o2;
    }
};
int compare = comparatorMin.compare(19, 12);
log.info("获取最大值:{}", compare);


Comparator<Integer> comparator =  (Integer o1, Integer o2) -> {
  return o1 > o2 ? o1 : o2;
}
log.info("获取最大值:{}", comparator.compare(12,14));

语法格式五:当Lambda体只有一条语句的时候,return和{}可以省略掉

Comparator<Integer> comparatorMin =  new Comparator<Integer>() {
    @Override
    public int compare (Integer o1, Integer o2) {
        return o1 > o2 ? o1 : o2;
    }
};
int compare = comparatorMin.compare(19, 12);
log.info("获取最大值:{}", compare);


Comparator<Integer> comparator =  (Integer o1, Integer o2) -> o1 > o2 ? o1 : o2;
log.info("获取最大值:{}", comparator.compare(12,14));

语法格式六:类型推断:数据类型可以省略,因为编译器可以推断得出,成为“类型推断”

Comparator<Integer> comparatorMin =  new Comparator<Integer>() {
    @Override
    public int compare (Integer o1, Integer o2) {
        return o1 > o2 ? o1 : o2;
    }
};
int compare = comparatorMin.compare(19, 12);
log.info("获取最大值:{}", compare);


Comparator<Integer> comparator =  (o1, o2) -> o1 > o2 ? o1 : o2;
log.info("获取最大值:{}", comparator.compare(12,14));

函数式接口

什么是函数式接口?

函数式接口:只包含一个抽象方法的接口,称为函数式接口,并且可以使用lambda表达式来创建该接口的对象,可以在任意函数式接口上使用@FunctionalInterface注解,来检测它是否是符合函数式接口。同时javac也会包含一条声明,说明这个接口是否符合函数式接口。

自定义函数式接口

@FunctionalInterface
public interface BaseMsg {


    /**
     * 发送信息
     * @param msg
     * @return
     */
    public boolean send(String msg);
}

泛型函数式接口

@FunctionalInterface
public interface BaseMsg<T> {


    /**
     * 发送信息
     * @param msg
     * @return
     */
    public boolean send(T msg);
}

java内置函数式接口

(Function、Consumer、Supplier、Predicate) java.util.function

Function(内置式接口的类型)

有入参有返回值

 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@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);

Consumer(消费型接口)

消费型接口:有入参,没有会有返回值

* @param <T> the type of the input to the operation
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Consumer<T> {
​
    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

Supplier(供给型接口)

供给型接口:没有输入参数,有返回值

 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {
​
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

Predicate(断定型接口)

断言型接口:既有输入参数也有返回值,返回类型是boolean类型

* @param <T> the type of the input to the predicate
 *
 * @since 1.8
 */
@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);

方法引用和构造器引用

方法引用

当要传递给Lambda体的操作已经有实现方法,可以直接使用方法引用(实现抽象方法的列表,必须要和方法引用的方法参数列表一致)

方法引用:使用操作符“::”将方法名和(类或者对象)分割开来。

有下列三种情况

  • 对象::实例方法
  • 类::实例方法
  • 类::静态方法
public static void main(String[] args) {
    BaseMsg<String> strName = s -> System.out.println(s);
    strName.send("Lambda表达式没有使用方法引用");


    //方法引用
    BaseMsg<String> strName2 = System.out::println;
    strName2.send("使用方法引用");
}

方法(构造器)引用

本质上:构造器引用和方法引用相识,只是使用了一个new方法

使用说明:函数式接口参数列表和构造器参数列表要一致,该接口返回值类型也是构造器返回值类型

方法:ClassName::方法名

格式:ClassName :: new

public class Test {
    /**
     * 把string类型转换为int
     * @param str
     * @return
     */
    public static Integer strToInt(String str){
        return new Integer(str);
    }
}


public static void main(String[] args) {
    Function<String, Integer> function1 = new Function<String, Integer>() {
        // apply 方法入参 -> String
        // apply 方法返回值 -> Integer
        @Override
        public Integer apply (String s) {
            // integer -> 构造函数 -> 入参 -> String / 返回值 -> Integer
            return new Integer(s);
        }
    };
    Function<String, Integer> function2 = s -> new Integer(s);
    Function<String, Integer> function3 = Integer::new;
    // 我们进行大胆的猜测,构造器 的入参和 apply方法的入参 保持一直
    // 构造器的返回值和apply方法的出参保持一直
    // 我们就可以使用 类::new 的形式
    // 不管是 构造,还是有参、无参 都是属于 方法调用
    // 通过这个知识推理出来 :: 是用来快捷调用 方法的
    // 所以 我们进行大胆的测试 写了一个 入参和出参与apply相匹配的对象进行测试
    // 测试结果 -> 符合我的猜想
    Function<String, Integer> function4 = Test::strToInt;
    //构造器引用
    Function<String, Integer> fun1 = (num) -> new Integer(num);
    Function<String, Integer> fun2 = Integer::new;
    //数组引用
    Function<Integer,Integer[]> fun3 = (num) ->new Integer[num];
    Function<Integer,Integer[]> fun4 = Integer[]::new;
}

强大的Stream API

什么是Stream?

Java8中两个最为重要特性:第一个的是Lambda表达式,另一个是Stream API。

StreamAPI它位于java.util.stream包中,StreamAPI帮助我们更好地对数据进行集合操作,它本质就是对数据的操作进行流水线式处理,也可以理解为一个更加高级的迭代器,主要作用是遍历其中每一个元素。简而言之,StreamAP提供了一种高效且易于使用的处理数据方式。

Stream特点:

1、Stream自己不会存储数据。

2、Stream不会改变源对象。相反,它们会返回一个持有结果的新Stream对象

3、Stream操作时延迟执行的。这就意味着它们等到有结果时候才会执行。

和list不同,Stream代表的是任意Java对象的序列,且stream输出的元素可能并没有预先存储在内存中,而是实时计算出来的。它可以“存储”有限个或无限个元素。

例如:我们想表示一个全体自然数的集合,使用list是不可能写出来的,因为自然数是无线的,不管内存多大也没法放到list中,但是使用Sream就可以

Stream操作的三个步骤

  • 创建Stream:一个数据源(例如:set 、list),获取一个流
  • 中间操作:一个中间操作连接,对数据源的数据进行处理
  • 终止操作:一个终止操作,执行中间操作连,产生结果。

创建流

集合创建

public static void createStream(){
    // list
    List<Clazz> clazzList = new ArrayList<>();
    clazzList.add(new Clazz(1L, "2001A"));
    clazzList.add(new Clazz(2L, "2002A"));
    clazzList.add(new Clazz(3L, "2003A"));
    clazzList.add(new Clazz(4L, "2004A"));
    clazzList.add(new Clazz(5L, "2005A"));
    Stream<Clazz> streamList = clazzList.stream();
    // set
    Set<Clazz> clazzSet = new HashSet<>();
    clazzSet.add(new Clazz(1L, "2001A"));
    clazzSet.add(new Clazz(2L, "2002A"));
    clazzSet.add(new Clazz(3L, "2003A"));
    clazzSet.add(new Clazz(4L, "2004A"));
    clazzSet.add(new Clazz(5L, "2005A"));
    Stream<Clazz> streamSet = clazzSet.stream();
    // map
    Map<Long, Clazz> clazzMap = new HashMap<>();
    clazzMap.put(1L, new Clazz(1L, "2001A"));
    clazzMap.put(2L, new Clazz(2L, "2002A"));
    clazzMap.put(3L, new Clazz(3L, "2003A"));
    clazzMap.put(4L, new Clazz(4L, "2004A"));
    clazzMap.put(5L, new Clazz(5L, "2005A"));
    Stream<Map.Entry<Long, Clazz>> streamMap = clazzMap.entrySet().stream();
    
    // 数组
    Clazz[] clazzArr = new Clazz[] {
            new Clazz(1L, "2001A"),
            new Clazz(2L, "2002A"),
            new Clazz(3L, "2003A"),
            new Clazz(4L, "2004A"),
            new Clazz(5L, "2005A")
    };
    Stream<Clazz> streamArr = Arrays.stream(clazzArr);
    
    // 自主创建
    Stream<Clazz> clazzStream = Stream.of(
            new Clazz(1L, "2001A"),
            new Clazz(2L, "2002A"),
            new Clazz(3L, "2003A"),
            new Clazz(4L, "2004A"),
            new Clazz(5L, "2005A")
    );
    //迭代
    //遍历10个奇数
    Stream.iterate(1,t->t+2).limit(10).forEach(System.out::println);
​
    //生成
    //生成10个随机数
    Stream.generate(Math::random).limit(10).forEach(System.out::println);
​
}

测试数据

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Long id;
    private String name;
    private Integer age;
    private Double price;
    private List<Clazz> clazzList;
}


@Data
@NoArgsConstructor
@AllArgsConstructor
public class Clazz {
    private Long id;
    private String name;
}


public static List<Student> getStudentList(){
    Clazz clazz1 = new Clazz(1L, "2001A");
    Clazz clazz2 = new Clazz(2L, "2002A");
    Clazz clazz3 = new Clazz(3L, "2003A");
    Clazz clazz4 = new Clazz(4L, "2004A");
    Clazz clazz5 = new Clazz(5L, "2005A");
    List<Clazz> ClazzList1 = new ArrayList<Clazz>(){{
        add(clazz1);
        add(clazz2);
        add(clazz5);
    }};
    List<Clazz> ClazzList2 = new ArrayList<Clazz>(){{
        add(clazz1);
        add(clazz2);
        add(clazz3);
    }};
    List<Clazz> ClazzList3 = new ArrayList<Clazz>(){{
        add(clazz3);
        add(clazz4);
        add(clazz5);
    }};
    Student student1 = new Student( 1L, "张三", 18, 5623.46, ClazzList1);
    Student student2 = new Student( 2L, "李四", 75, 1523.16, ClazzList2);
    Student student3 = new Student( 3L, "王五", 13, 46513.63, ClazzList3);
    Student student4 = new Student( 4L, "赵六", 62, 95263.95, ClazzList1);
    Student student5 = new Student( 5L, "张三", 52, 5134.46, ClazzList3);
    Student student6 = new Student( 6L, "李四", 32, 98161.48, ClazzList2);
    List<Student> studentList = new ArrayList<>();
    studentList.add(student1);
    studentList.add(student2);
    studentList.add(student3);
    studentList.add(student4);
    studentList.add(student5);
    studentList.add(student6);
    return studentList;
}

中间操作

一个流可以后面跟随着0个或者多个中间操作,其目的是打开流,做出某种程度的数据过滤、去重、排序、映射、跳过等。然后返回一个新的流,交给下一个使用,仅仅是调用这个方法,没有真正开始遍历。

map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered

过滤(filter)

public static void filter(){
    List<Student> studentList = getStudentList();
    // 筛选 年龄 大于 18岁的
    studentList.stream()
            .filter(student -> student.getAge() > 18)
            .forEach(System.out::println);
}
结果

结果: 

 

流转换(map)

public static void map(){
    List<Student> studentList = getStudentList();
    // 输出所有人的 姓名
    studentList.stream()
            .map(Student::getName)
            .forEach(System.out::println);
    System.out.println("-------------年龄大于20----------------");
    // 输出 年龄 大于 20 岁的 姓名
    studentList.stream()
            .filter(student -> student.getAge() > 20)
            .map(Student::getName)
            .forEach(System.out::println);
    }

结果

 

流转换(flatmap)、去重(distinct)

public static void flatMap(){
    List<Student> studentList = getStudentList();
    studentList.stream()
            .flatMap(student -> student.getClazzList().stream())
            .map(Clazz::getName)
            .distinct()
            .forEach(System.out::println);
}

结果: 

 

排序(sorted)

public static void sorted(){
    List<Student> studentList = getStudentList();
    studentList.stream()
            .sorted((student, student1) -> student1.getAge() - student.getAge())
            .forEach(System.out::println);
}

结果: 

 

终止操作

一个终止操作,执行中间操作连,产生结果。

forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值