java8新特性

java8发布已经有几年了,如今java11已经出来了,在这里谈java8有点过时,不过java8确实是一个改变比较大的版本,在这里简单分析一下。
JDK8新增内容大致分为如下内容:

1:接口的static,default方法
2:函数式接口
3:labmda语法
4:方法引用
5:重新定义了时间类
6:stream操作数据集合类
7:其他内容


1:接口里的static,default方法

我们都知道,在1.8之前的jdk版本中,接口里面只有有抽象方法。但是现在我说接口里面有具体的实现方法,你会不会觉得我说的有问题?

但是,请看JDK1.8中如下代码:

java.util.list 接口的方法

    default void replaceAll(UnaryOperator operator) {
        Objects.requireNonNull(operator);
        final ListIterator li = this.listIterator();
        while (li.hasNext()) {
            li.set(operator.apply(li.next()));
        }
    }

java.util.Map 接口的方法:

        public static , V> Comparator> comparingByKey() {
            return (Comparator> & Serializable)
                (c1, c2) -> c1.getKey().compareTo(c2.getKey());
        }

有人看到这里会说:”我X,JDK1.8是不是有bug,接口里面有实现方法,这不是颠覆我的三观吗?”

是的,JDK1.8中,确实在接口中加入了static和default的实现方法。
1:static方法属于接口,不能被子类重写。
2:default可以被子类重写。

抛出问题
1:JDK1.8是一个新增内容比较多的版本,如果在接口里面新增抽象方法,那么他们的所有子类都需要重写该方法,那么多子类需要去实现,这个工作量是不是很大?
2:即使接口加了抽象方法,但是有些子类不想重写接口方法,有些子类需要重写接口方法,这可怎么办才好?

解决方案
好吧,既然这样的话
重写定义接口规范
1:接口里面加入static实现方法,那么子类都不需要重写了。
2:加入default实现方法(这里相当于钩子方法,钩子方法在前文的模板方法里有说明),需要的重写的子类去重写接口就好。

是不是非常省事呢?这样static方法和default方法就诞生了。。。


2:函数式接口

接口大家都知道,但是什么是函数式接口呢?请看下面例子:

    /**
     * 只有一个抽象方法的接口:函数是接口
     */
    interface FunctionInterface{
        
        public void abstractMethod();
    }

上面代码很明确:只有一个抽象方法的接口是函数式接口。

这属于jdk8的新特性,大家有没有回忆道我们之前用过的接口,如:

java.lang.Runnable:

@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface Runnable is used
     * to create a thread, starting the thread causes the object's
     * run method to be called in that separately executing
     * thread.
     * 

* The general contract of the method run is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }

java.util.concurrent.Callable:

@FunctionalInterface
public interface Callable {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

这两个我们经常用的线程类接口,都有且仅有一个抽象方法,所以他们都为函数式接口

注意:
JDK1.8中新加了如下接口的注解,用来标注此接口为函数式接口(也可以不用):

@FunctionalInterface

3:lambda语法

1.8之前创建线程代码:

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("异步线程");
            }
        }).start();

一共6行代码才能进行创建。

1.8创建线程代码

new Thread(() -> {System.out.println("异步线程");}).start();

一行代码就能进行线程的创建,这里就用到了lambda语法。是不是很方便。

lambda语法:
(参数体) -> {方法体}

注意
1:只有函数式接口才能用lambda
2:lambda语法只做抽象方法的申明(相当于子类去实现重写接口),并没有真正去执行

我们这里有个几个例子加深说明:

a:无参数无返回值的函数式接口的lambda写法

    @FunctionalInterface
    interface FunctionInterface{

        public void abstractMethod();
    }
    
    /**
     * 测试
     */
    public static void main(String []args){
        /*
        第一种写法
         */
        FunctionInterface functionInterface = () -> {System.out.println("测试");};
        functionInterface.abstractMethod();
        
        /*
        第二种写法
         */
        FunctionInterface functionInterface1 = () -> System.out.println("测试");
        functionInterface1.abstractMethod();
    }

这里lambda语法中()并没有写参数,和abstractMethod的参数对应,{}里面没有返回和抽象方法的返回对应。

b:有一个参数有返回值的lambda语法:

    @FunctionalInterface
    interface FunctionInterface{

        public void abstractMethod(int a);
    }

    /**
     * 测试
     */
    public static void main(String []args){
        /*
        第一种写法
         */
        FunctionInterface functionInterface = (a) -> {System.out.println("测试");};
        functionInterface.abstractMethod(1);

        /*
        第二种写法
         */
        FunctionInterface functionInterface2 = (a) -> System.out.println("测试");
        functionInterface2.abstractMethod(1);

         /*
        第三种写法
         */
        FunctionInterface functionInterface1 = a -> System.out.println("测试");
        functionInterface1.abstractMethod(1);

    }

c:有一个参数有返回值的lambda的语法:

    @FunctionalInterface
    interface FunctionInterface{

        public int abstractMethod(int a);
    }

    /**
     * 测试
     */
    public static void main(String []args){
        /*
        第一种写法
         */
        FunctionInterface functionInterface = (a) -> {return 1;};
        functionInterface.abstractMethod(1);

        /*
        第二种写法
         */
        FunctionInterface functionInterface2 = (a) -> 1;
        functionInterface2.abstractMethod(1);

                /*
        第三种写法
         */
        FunctionInterface functionInterface1 = a ->{return 1;};
        functionInterface1.abstractMethod(1);

    }

d:多个参数有返回值的lambda的语法:

    @FunctionalInterface
    interface FunctionInterface{

        public int abstractMethod(int a,int b);
    }

    /**
     * 测试
     */
    public static void main(String []args){
        /*
        第一种写法
         */
        FunctionInterface functionInterface = (a,b) -> {return 1;};
        functionInterface.abstractMethod(1,2);

        /*
        第二种写法
         */
        FunctionInterface functionInterface2 = (a,b) -> 1;
        functionInterface2.abstractMethod(1,2);

    }

可以看出来只要有一个参数,()是可以省略的,如果有返回值{}是可以省略的,这个看个人喜好吧。


4:方法引用

方法引用,只是一种lambda书写的技巧,可以让我们的lambda语法更加的简洁。

需要遵循以下规则就可以使用方法引用:

1:方法引用只能用在lambda的方法体{}中
2:方法体只有一个方法才可以使用方法引用
3:使用 :: 双冒号连接符

方法引用分为:

1:静态方法引用
2:对象方法引用
3:实例方法引用
4:构造方法引用

a:静态方法引用:

    static void methodReference(){
        System.out.println("测试");
    }


    @FunctionalInterface
    interface FunctionInterface{

        public void abstractMethod();
    }
    
    public static void main(String []args){
        FunctionInterface functionInterface = Demo6::methodReference;
        functionInterface.abstractMethod();

      //相当于下面的lambda语法
        FunctionInterface functionInterface1 = () -> { Demo6.methodReference(); };
        functionInterface1.abstractMethod();

    }

注意:方法引用中,接口方法和参数要和被使用的静态方法的参数对应。,不然不能使用方法引用

比如:

public class Demo6 {
    
    
    static void methodReference(int a,int b){
        System.out.println("测试");
    }


    @FunctionalInterface
    interface FunctionInterface{

        public void abstractMethod(int a,int b);
    }
    
    public static void main(String []args){
        FunctionInterface functionInterface = Demo6::methodReference;
        //相当于lambda语法:   FunctionInterface functionInterface = (a,b) -> { Demo6.methodReference(a,b); };
        functionInterface.abstractMethod(1,2);
    }
    
}

静态类的方法参数和函数式接口的方法参数一一对应

b:对象方法引用

public class Demo6 {
    
    
     void methodReference(){
        System.out.println("测试");
    }


    @FunctionalInterface
    interface FunctionInterface{

        public void abstractMethod();
    }
    
    public static void main(String []args){
        FunctionInterface functionInterface = new Demo6()::methodReference;
        //相当于lambda语法:  FunctionInterface functionInterface = () - > { new Demo6().methodReference(); };
        functionInterface.abstractMethod();
    }
    
}

想要对象去引用,肯定需要先new出对象,注意点同上。

c:实例方法引用

public class Demo6 {
    
     void methodReference(int a){
        System.out.println("测试");
    }

    @FunctionalInterface
    interface FunctionInterface{

        public void abstractMethod(Demo6 demo6,int a);
    }
    
    public static void main(String []args){
        FunctionInterface functionInterface = Demo6::methodReference;
         //相当于lambda语法:  FunctionInterface functionInterface = () - > { new Demo6().methodReference(); };
        Demo6 demo6 = new Demo6();
        functionInterface.abstractMethod(demo6,1);
    }
}

注意:
1:实例方法引用,函数式接口的抽象方法的第一个参数,必须是需要引用的对象
(public void abstractMethod(Demo6 demo6,int a); 中的 Demo6 demo6)

2:其他参数注意点和静态方法引用一样,都要一一对应。

d:构造方法引用

public class Demo6 {

    public Demo6() {
    }

    @FunctionalInterface
    interface FunctionInterface{

        public void abstractMethod();
    }
    
    public static void main(String []args){
        FunctionInterface functionInterface = Demo6::new;
        //相当于lambda语法:  FunctionInterface functionInterface = () - > { new Demo6(); };
        functionInterface.abstractMethod();
    }
}

用new关键字
注意点同静态方法引用,参数需要一一对应

上面的四种方法引用,其实我们完全也可以用lambda语法实现,但是使用了方法引用是不是更加方便呢?这个需要多多练习。

5:重新定义了时间类

Java 8引入了新的Date-Time API(JSR 310)来改进时间、日期的处理。时间和日期的管理一直是最令Java开发者痛苦的问题。java.util.Date和后来的java.util.Calendar一直没有解决这个问题(甚至令开发者更加迷茫)。

因为上面这些原因,诞生了第三方库Joda-Time,可以替代Java的时间管理API。Java 8中新的时间和日期管理API深受Joda-Time影响,并吸收了很多Joda-Time的精华。新的java.time包包含了所有关于日期、时间、时区、Instant(跟日期类似但是精确到纳秒)、duration(持续时间)和时钟操作的类。新设计的API认真考虑了这些类的不变性(从java.util.Calendar吸取的教训),如果某个实例需要修改,则返回一个新的对象。

新增内容如下,你们可以自行翻阅查询api,这里就不介绍其用法了。

java.time.Duration
java.time.LocalDate
java.time.LocalDateTime
java.time.LocalTime
java.time.ZonedDateTime
java.time.Clock

6:stream操作数据集合类

Stream 是用函数式编程方式在集合类上进行复杂操作的工具,其集成了Java 8中的众多新特性之一的聚合操作,开发者可以更容易地使用Lambda表达式,并且更方便地实现对集合的查找、遍历、过滤以及常见计算等。

比如我们先要取出list对象里面的各个字符,并且去掉重复项:

使用stream(注意: filter,map,flatMap里面的参数都为函数式接口,所以我们直接使用lambda语法):

    public static void main(String []args){
        List list = new ArrayList();
        list.add("hello");
        list.add("world");
        list.add(null);
        list.add("!!!");

        String result = list.stream()
                .filter(Objects::nonNull)
                .map((a) -> a.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.joining(",","【","】"));

        System.out.println(result);
    }

结果:

【h,e,l,o,w,r,d,!】

Steam操作数据肯定是有一种状态,比如操作中(过滤,排序等)或者操作结束(返回结果,遍历等等)
stream状态分为三种:Intermediate,Terminal,Short-circuiting,具体为:

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

Terminal:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、iterator

Short-circuiting
anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit

stream操作比较多,篇幅有点长,小编会在后期进行具体操作演示。

7:其他内容

1:Optional类,空指针在代码中非常头疼,用optional非常有好的解决了这个空指针判断的问题。
2:Nashorn JavaScript引擎,我们可以再JVM中编写和运行JS应用。
3:对于Base64的引入,不需要我们再使用第三方库。
4:其他。

你可能感兴趣的:(java8新特性)