叨叨游戏网
您的当前位置:首页Java8新特性 十二大总结 (面试篇)

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

来源:叨叨游戏网



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




**运行结果如下:**



![在这里插入图片描述](https://img-blog.csdnimg.cn/c9ed97c968114804a2b6099876090678.png#pic_center)



**可见,我们采用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);

}




[](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)二、方法的默认实现和静态方法

=================================================================================



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方法,可以通过这个语法增加默认的实现,从而降低对这个接口进行升级的代价,不需要所有实现这个接口的类进行修改。



[](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)三、方法引用

=========================================================================



方法引用指的是可以直接引用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);

    }



}

}




**测试结果如下**



![在这里插入图片描述](https://img-blog.csdnimg.cn/256f96250840e6ad06eaf437ff7404.png#pic_center)



[](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)四、注解(Annotation)

===================================================================================



1.  JDK1.5引入了注解机制,但是有一个:相同的注解在同一位置只能声明一次。JDK1.8引入了重复注解机制后,相同的注解在同一个地方可以声明多次。

2.  JDK1.8 对注解进行了扩展。使得注解被使用的范围更广,例如可以给局部变量,泛型,方法异常提供注解。



[](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)五、类型推测

=========================================================================



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



class List {

static <Z> List<Z> nil() {...};

static <Z> List<Z> cons(Z head, List<Z> tail) {...};

E head(){...}

}




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



//通过赋值的目标类型来推测泛型的参数

List l = List.nil();




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



List l = List List.nil();




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



//通过方法的第一个参数来推测泛型的类型

List.cons(5, List.nil());




[](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)六、参数名字

=========================================================================



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



[](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)七、新增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帮助文档



[](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)八、新增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.groupingBy(Person::getAge));

    Iterator<Map.Entry<Integer, List<Person>>> iterator = map.entrySet().iterator();

    while (iterator.hasNext()) {

        Map.Entry<Integer, List<Person>> next = iterator.next();

        Integer age = next.getKey();

        System.out.println(age+":");

        List<Person> value = next.getValue();

        value.forEach(person -> {

            System.out.print(person.getName()+" ");

        });

        System.out.println();

    }

}

}




**运行结果为**



![在这里插入图片描述](https://img-blog.csdnimg.cn/5fd0ac610e44b7841deaab81b45de0.png#pic_center)



[](https://docs.qq.com/doc/DSmxTbFJ1cmN1R2dB)九、日期新特性

==========================================================================



在JDK1.8以前,处理日期相关的类主要有如下三个



1.  Caalendar:实现日期和时间字段之间的转换,它的属性是可变的,因此,它不是线程安全的。

2.  DateFormat:格式化和分析日期字符串

3.  Date:用来承载日期和时间信息,它的属性是可变的,因此,它不是线程安全的。



这些API使用起来都不是很方便的,而且有很多缺点,如下:



Date date = new Date(2022, 5, 1);

System.out.println(date);




在Date类传入参数,月份为5月,但输出却是 Thu Jun 01 00:00:00 CST 3922



JDK1.8 对日期相关的API进行了改造,提供了更加强大的API。新的java.time 主要包含了处理日期、时间、日期/时间、时区、时刻 (instants)、和时钟(clock) 等操作。下面是使用案例



package com.wanshi.common.bean;

import java.time.*;

public class Test4 {

public static void main(String[] args) {

    //Clock类通过指定一个时区,可以获取到当前的时刻,日期与时间

    Clock clock = Clock.system(ZoneId.of("Asia/Shanghai"));

    System.out.println("测试Clock:");

    System.out.println(clock.millis());

    System.out.println(clock.instant());



    //Instant 使用方法

    System.out.println("测试Instant:");

    Instant now = Instant.now();

因篇幅问题不能全部显示,请点此查看更多更全内容