Java8新特性 十二大总结 (面试篇)

Posted Bug 终结者

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java8新特性 十二大总结 (面试篇)相关的知识,希望对你有一定的参考价值。

📢📢📢📣📣📣

哈喽!大家好,我是【Bug 终结者,【CSDN新星创作者】🏆,阿里云技术博主🏆,51CTO人气博主🏆,INfoQ写作专家🏆

一位上进心十足,拥有极强学习力的【Java领域博主】😜😜😜

🏅【Bug 终结者】博客的领域是【面向后端技术】的学习,未来会持续更新更多的【后端技术】以及【学习心得】。 偶尔会分享些前端基础知识,会更新实战项目,面向企业级开发应用
🏅 如果有对【后端技术】、【前端领域】感兴趣的【小可爱】,欢迎关注【Bug 终结者】💞💞💞


❤️❤️❤️ 感谢各位大可爱小可爱! ❤️❤️❤️

文章目录

一、Lambda表达式

什么是Lambda表达式?

Lambda 表达式是一个匿名函数(指的是没有函数名的函数),直接对应于其中的 Lambda 抽象,Lambda 表达式可以表示闭包

我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码作为实参),也可以理解为函数式编程,将一个函数作为参数进行传递。

Lambda 表达式允许把函数作为一个方法的参数,Lambda 表达式的基本语法如下:

(parameters) -> expression 或 (parameters) -> statements;

Lambda 的使用如下例所示

Arrays.asList(1, 2, 6).forEach(i -> System.out.println(i))

以上的写法,是编辑器自动推测出来的参数类型,也可以指定参数类型

Arrays.asList(1, 2, 6).forEach((Integer i) -> System.out.println(i))

在Java8之前,Java语言通过匿名函数的方法来替代Lambda表达式。

对于列表的排序,如果列表中里存放的是自定义的类,那么通常需要指定自定义的排序方法,传统方式如下

Person对象

package com.wanshi.common.bean;

public class Person 

    private String name;

    private int age;


    public Person(String name, int age) 
        this.name = name;
        this.age = age;
    


    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;
    

    @Override
    public String toString() 
        return "Person" +
                "name='" + name + '\\'' +
                ", age=" + age +
                '';
    


Test测试类

package com.wanshi.common.bean;

import javax.imageio.plugins.jpeg.JPEGImageReadParam;
import java.util.Arrays;
import java.util.Comparator;

public class Test2 

    public static void main(String[] args) 
        Person[] people = new Person("James", 25), new Person("Jack", 21);
        //自定义类排序方法,通过年龄进行排序
        Arrays.sort(people, new Comparator<Person>() 
            @Override
            public int compare(Person o1, Person o2) 
                return o1.getAge() - o2.getAge();
            
        );
        for (Person person : people) 
            System.out.println(person);
        
    


采用Lambda表达式优化后,如下

//两种方式
Arrays.sort(people, (Person a, Person b) -> a.getAge() - b.getAge());

运行结果如下:

可见,我们采用Lambda表达式优化后,代码会更加简洁

Lambda表达式是用过函数式接口(只有一个方法的普通接口)来实现的,函数式接口可以被隐式转换为Lambda表达式,为了与普通的接口区分开,JDK1.8新增加了一种特殊的注解@FunctionalInterface 如下

@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);

二、方法的默认实现和静态方法

JDK1.8通过使用关键字default可以给接口中的方法添加默认实现,此外,接口中还可以定义静态方法,如下

package com.wanshi.common.bean;


/**
 * Lambda表达式是通过函数式接口(只有一个方法得接口)来实现的。函数式接口可以被隐式地转换为Lambda表达式,
 * 为了与普通的接口区分开(普通接口中可能会有多个方法),jdk1.8新增加了一种特殊的注解@FunctionalInterface
 */
@FunctionalInterface
public interface Fun 

    void f();


    default void g() 
        System.out.println("this is default method in interface");
    

    static void h() 
        System.out.println("this is static method in interface");
    


那么为什么要引入接口中方法的默认实现呢?

其实,这样做的最重要的一个目的就是为了实现接口升级。在原有的设计中,如果想要升级接口,例如给接口中添加一个新的方法,那么会导致所有实现这个接口i的类都要被修改,这给Java语言已有的一些框架进行升级带来了很大的麻烦,如果接口能够支持默认方法的实现,那么可以给这些类库的升级带来许多便利,例如,为了支持Lambda表达式,Collection中引入了forEach方法,可以通过这个语法增加默认的实现,从而降低对这个接口进行升级的代价,不需要所有实现这个接口的类进行修改。

三、方法引用

方法引用指的是可以直接引用Java类或对象的方法,它可以被看成是一种更加简介易懂的Lambda表达式,使用方法引用后,上个例子中的排序代码可以更加简洁的编写

Arrays.sort(people, Comparator.comparing(Person::getAge));

方法引用共有下面4种形式

  • 引用构造方法:ClassName::new
  • 引用类静态方法:ClassName::methodName
  • 引用特定类 的任意对象方法:ClassName::methodName
  • 引用某个对象的方法:ClassName::methodName

下面是一个方法引用的例子:

package com.wanshi.common.bean;

import java.util.Arrays;
import java.util.Comparator;
import java.util.function.Supplier;

public class Person 

    private String name;

    private int age;

    public Person()  


    public Person(String name, int age) 
        this.name = name;
        this.age = age;
    

    public static Person getInstance(final Supplier<Person> personSupplier) 
        return personSupplier.get();
    


    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;
    

    @Override
    public String toString() 
        return "Person" +
                "name='" + name + '\\'' +
                ", age=" + age +
                '';
    

    public static int compareByAge(Person a, Person b) 
        return b.getAge() - a.getAge();
    


class CompareProvider 

    public int compareByAge(Person a, Person b) 
        return a.getAge() - b.getAge();
    




class Test 
    public static void main(String[] args) 
        //引用构造方法
        Person person = Person.getInstance(Person::new);
        person.setAge(19);
        System.out.println("测试引用构造方法:" + person.getAge());
        Person[] people = new Person("James", 25), new Person("Jack", 21);
        //引用特定类的任意对象方法
        Arrays.sort(people, Comparator.comparing(Person::getAge));
        System.out.println("测试引用特定类的任意对象方法:");
        for (Person person1 : people) 
            System.out.println(person1);
        

        Arrays.sort(people, Person::compareByAge);
        System.out.println("测试引用类静态方法:");
        for (Person person1 : people) 
            System.out.println(person1);
        

        //引用某个对象的方法
        Arrays.sort(people, new CompareProvider()::compareByAge);

        System.out.println("测试引用引用某个对象的方法:");
        for (Person person1 : people) 
            System.out.println(person1);
        

    

测试结果如下

四、注解(Annotation)

  1. JDK1.5引入了注解机制,但是有一个限制:相同的注解在同一位置只能声明一次。JDK1.8引入了重复注解机制后,相同的注解在同一个地方可以声明多次。
  2. JDK1.8 对注解进行了扩展。使得注解被使用的范围更广,例如可以给局部变量,泛型,方法异常提供注解。

五、类型推测

JDK1.8加强了类型推测机制,这种机制可以使得代码更为简洁,假如有以下类的定义

class List<E> 
    static <Z> List<Z> nil() ...;
    static <Z> List<Z> cons(Z head, List<Z> tail) ...;
    E head()...

在调用的时候,可以使用下面的代码

//通过赋值的目标类型来推测泛型的参数
List<Integer> l = List.nil();

在JDK1.7的时候,这种写法将会产生编译错误,Java7的正确写法如下

List<Integer> l = List<Integer> List.nil();

同理,在调用 cons 方法的时候写法为:

//通过方法的第一个参数来推测泛型的类型
List.cons(5, List.nil());

六、参数名字

JDK1.8通过在编译的时候增加 -parameters 选项,以及增加反射API与 Parameter,getName() 方法实现了获取方法参数名的功能。

示例代码如下

package com.wanshi.common.bean;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class Test6 

    public static void main(String[] args) 
        Method method;
        try 
            method = Test6.class.getMethod("main", String[].class);
            for (Parameter parameter : method.getParameters()) 
                System.out.println("parameter::" + parameter.getName());
            
         catch (Exception e) 
            e.printStackTrace();
        
    


如果使用指令 javac Test6.java 来编译以上程序,那么运行的结果是 parameter::args()

如果使用的是 javac Test6.java -parameters 来编译 那么结果是parameter::args

七、新增Optional类

在使用Java语言的进行编程的时候,经常需要使用大量的代码来处理空指针异常,而这种操作往往会降低程序的可读性,JDK1.8引入了Optional类来处理空指针的情况,从而提高代码的可读性,如下

public static void main(String[] args) 
    Optional<String> s1 = Optional.of("hello");
    //判断是否有值
    if (s1.isPresent()) 
        //获取值
        System.out.println(s1.get());
    

    Optional<Object> s2 = Optional.ofNullable(null);

    if (s2.isPresent()) 
        System.out.println(s2.get());
    


这里只是介绍了 Optional 简单的使用示例,更多可参考JDK帮助文档

八、新增Stream 类

JDK1.8新增了Stream类,从而把函数式编程的风格引入到Java语言中,Stream类的API提供了强大的功能,使用Stream后,可以写出更加强大,更加简洁的代码

package com.wanshi.common.bean;

import java.util.*;
import java.util.stream.Collectors;

public class Test3 

    public static void main(String[] args) 
        List<Person> l = new ArrayList<>();
        l.add(new Person("Wang", 10));
        l.add(new Person("Li", 13));
        l.add(new Person("Zhang", 10));
        l.add(new Person("Zhao", 15));
        System.out.println("找出年龄为10的第一个人类:");
        Optional<Person> s = l.stream().filter(person -> person.getAge() == 10).findFirst();
        if (s.isPresent()) 
            System.out.println(s.get().getName() + ", " + s.get().getAge());
        
        System.out.println("找出年龄为10的所有人类:");
        List<Person> personList = l.stream().filter(person -> person.getAge() == 10).collect(Collectors.toList());
        personList.forEach(person -> 
            System.out.println(person.getName() + ", " + person.getAge());
        );

        System.out.println("对人类年龄分组");
        Map<Integer, List<Person>> map = l.stream().collect(Collectors.以上是关于Java8新特性 十二大总结 (面试篇)的主要内容,如果未能解决你的问题,请参考以下文章

Java8新特性面试官问我:Java8中创建Stream流有哪几种方式?

java8新特性-默认方法

java8新特性总结

Java8新特性—— Stream集合运算流入门学习

放大招了,肝了一篇8万字的Java8新特性总结,赶快收藏

3 java8新特性总结