java8 新特性导航:
1.关于接口的改进

2.Lambda表达式

3.函数式(Functional)接口

4.方法引用与构造器引用

java8 新特性

  • 速度更快
  • 代码更少(增加了新的语法: Lambda表达式)
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针异常:Optional
  • Nashorn引擎,允许在JVM上运行JS应用

1.关于接口的改进
Java 8中,你可以为接口添加静态方法和默认方法。
从技术角度来说,这是完全合法的,
只是它看起来违反了接口作为一个抽象定义的理念。

静态方法: 使用static关键字修饰。可以通过接口直接调用静态方法,
并执行其方法体。我们经常在相互一起使用的类中使用静态方法。
你可以在标准库中找到像Collection/Collections
或者Path/Paths这样成对的接口和类。

默认方法: 默认方法使用default关键字修饰。
可以通过实现类对象来调用。
我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
比如:
java 8 API中对Collection\List\Comparator等接口提供了丰富的默认方法。

如何定义接口: 定义接口中的成员

JDK7及以前: 只能定义全局常量和抽象方法
全局常量: public static final的。 但是书写时,可以省略不写
抽象方法: public abstract的。

JDK8:
除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)

CompareA
public interface CompareA {
    //静态方法
    public static void method1(){
        System.out.println("CompareA:北京");
    }
    //默认方法
    public default void method2(){
        System.out.println("CompareA:上海 ");
    }//Public 可以省略
    default void method3(){
        System.out.println("CompareA:上海");
    }
}

CompareB
public interface CompareB {
    default void method3(){
        System.out.println("CompareB:上海");
    }
}

SuperClass
public class SuperClass {
    public void method3(){
        System.out.println("SuperClass:北京");
    }
}

SubClassTest
public class SubClassTest {
    public static void main(String[] args) {
        SubClass s = new SubClass();
//        s.method1();
//        1.接口中定义的静态方法,只能通过接口来调用  ==>利用这个可以变成工具类
        CompareA.method1();//  ==》CompareA:北京
//          2.通过实现类的对象, 可以调用接口中的默认方法
//          如果实现类重写了接口中的默认方法,调用时,依然调用的是重写的方法
        s.method2();//  ==》SubClass:上海
//          3.如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
//            那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。--》类优先原则  仅仅 针对当前有效
        s.method3();//  ==》SuperClass:北京 ==> SubClass:深圳
//            4.如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法
//            那么在实现类没有重写此方法的情况下,报错. ==>接口冲突
//              如果你把extends SuperClass去掉  就会报错 因为不知道调哪个method3()
//        结果就是: 必须在实现类中重写此方法
    }
}
class SubClass extends SuperClass implements CompareA,CompareB {
    public void method2(){
        System.out.println("SubClass:上海 ");
    }
    public void method3(){
        System.out.println("SubClass:深圳");
    }
//    5.如何在子类(实现类)的方法中调用父类\接口中被重写的方法
    public void myMethod(){
        method3();//调用自己定义的重写的方法
        super.method3();//调用的是父类中声明的
        //调用接口中的默认方法
        CompareA.super.method3();
        CompareB.super.method3();
    }
}

2.Lambda表达式

Lambda表达式的使用
函数接口才能用Lambda ,即只有一个抽象方法

2.1 举例:(o1,o2)->Integer.compare(o1,o2)

2.2 格式:
-》:Lambda操作符 或 箭头操作符
-》:左边:Lambda形参列表(其实就是接口中的抽象方法的形参列表)
-》:右边:Lambda体(其实就是重写的抽象方法的方法体)

2.3 Lambda表达式的使用:(分为6种情况介绍)
总结:
》左边: Lambda形参列表的参数类型可以省略(类型推断);
如果Lambda形参列表只有一个参数,其一对()也可以省略

》右边: Lambda 体应该使用一对{}包裹;
如果Lambda体只有一条执行语句(可能是return语句),
省略这一对{}和return关键字

2.4 Lambda表达式的本质: 作为函数式接口的实例

2.5 如果一个接口中,只声明了一个抽象方法,
则此接口就称为函数式接口,我们可以在一个接口上使用
@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口。

2.6 所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

举例:

public class LambdaTest1 {
    //语法格式一: 无参,无返回值
    @Test
    public void test1(){
        //提供实现一个Runnable接口的匿名实现类的对象
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("偶爱你们");
            }
        };
        r1.run();
        System.out.println("*****************************");

        Runnable r2 = () -> {
            System.out.println("沃真能");
        };
        r2.run();
    }
/**
偶爱你们
*****************************
沃真能
*/
    //语法格式二:Lambda需要一个参数,但是没有返回值。
    @Test
    public void test2(){
        Consumer<String> con = new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };
        con.accept("人和人的区别在哪?");
        System.out.println("*******************");
        Consumer<String> con1 = (String s) ->{
            System.out.println(s);
        };
        con1.accept("有心和没心");
    }
/**
人和人的区别在哪?
*******************
有心和没心
*/
    //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
    @Test
    public void test3(){
        Consumer<String> con1 = (String s) ->{
            System.out.println(s);
        };
        con1.accept("有心和没心");
        System.out.println("*******************");
        Consumer<String> con2 =(s) ->{
            System.out.println(s);
        };
        con2.accept("有心和没心");
    }
/**
有心和没心
*******************
有心和没心
*/
    @Test
    public void test4(){
        ArrayList<String> list = new ArrayList<>();//类型推断

        int[] arr = new int[]{1,2,3};
        int[] arr1 = {1,2,3};//类型推断
    }
    //语法格式四: Lambda 若只需要一个参数时,参数的小括号可以省略
    @Test
    public void test5(){
        Consumer<String> con1 = (s) ->{
            System.out.println(s);
        };
        con1.accept("有心和没心");
        System.out.println("*******************");
        Consumer<String> con2 = s ->{
            System.out.println(s);
        };
        con2.accept("有心和没心");
    }
/**
有心和没心
*******************
有心和没心
*/
    //语法格式五: Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
    @Test
    public void test6(){
        Comparator<Integer> com1 = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                System.out.println(o1);
                System.out.println(o2);
                return o1.compareTo(o2);
            }
        };
        System.out.println(com1.compare(12, 21));
        System.out.println("*******************");
        Comparator<Integer> com2 = (o1,o2) ->{
            System.out.println(o1);
            System.out.println(o2);
            return o1.compareTo(o2);
        };
        System.out.println(com2.compare(12, 6));
    }
/**
12
21
-1
*******************
12
6
1
*/
    //语法格式六:当Lambda体只有一条语句时,return与大括号若有,都可以省略
    @Test
    public void test7(){
        Comparator<Integer> com1 = (o1,o2) ->{
            return o1.compareTo(o2);
        };
        System.out.println(com1.compare(12, 6));
        System.out.println("*******************");
        Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
        System.out.println(com2.compare(12, 21));
    }
/**
1
*******************
-1
*/
    @Test
    public void test8(){
        Consumer<String> con1 = s ->{
            System.out.println(s);
        };
        con1.accept("有心和没心");
        System.out.println("*******************");
        Consumer<String> con2 = s -> System.out.println(s);
        con2.accept("有心和没心");
    }
}
/**
有心和没心
*******************
有心和没心
*/

3.函数式(Functional)接口

函数式接口就是一个有且仅有一个抽象方法,
但是可以有多个非抽象方法的接口。

函数式接口可以被隐式转换为 lambda 表达式。
通过 lambda 表达式来创建该接口的对象
即 Lambda表达式就是作为函数式接口的实例

自定义函数式接口
@FunctionalInterface
有注解,自己写函数式接口可以校验是否是只有一个

@FunctionalInterface
public interface MyInterface {
    void method1(); 
}

JDK 1.8 之前已有的函数式接口(不全):
java.lang.Runnable
java.util.Comparator
java.io.FileFilter
java.awt.event.ActionListener
javax.swing.event.ChangeListener

JDK 1.8 新增加的函数接口: java.util.function

在java.util.function包下定义了Java 8的丰富的函数式接口

Java内置四大核心函数式接口
1.Consumer 消费型接口
对类型为T的对象应用操作,包含方法: void accept(T t)

2.Supplier 供给型接口
返回类型为T的对象,包含方法: T get()

3.Function<T, R> 函数型接口
对类型为T的对象应用操作,并返回结果。结果是R类型的对象。
包含方法: R apply(Tt)

4.Predicate 断定型接口
确定类型为T的对象是否满足某约束,并返回boolean值。
包含方法: boolean test(T t)

还有其他函数式接口如:(不全)
BiFunction<T,U,R>
BiConsumer<T,U>
BiPredicate<T,U>

举例:(导包省略 )

public class LambdaTest2 {
    @Test
    public void test1(){
        happyTime(500, new Consumer<Double>() {
            @Override
            public void accept(Double aDouble) {
                System.out.println("学习太累了,我去吃个大餐,价格:"+aDouble);
            }
        });
        System.out.println("*****************************");
        happyTime(400,money -> System.out.println("学习太累了,我去吃个大餐,价格:"+ money));
    }
    public void happyTime(double money, Consumer<Double> con){
        con.accept(money);
    }

/**
学习太累了,我去吃个大餐,价格:500.0
*****************************
学习太累了,我去吃个大餐,价格:400.0
*/
    @Test
    public void test2(){
        List<String> list = Arrays.asList("happy","hello","blue","orange");
        List<String> filterStrs = filterString(list, new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.contains("h");
            }
        });
        System.out.println(filterStrs);
        System.out.println("*****************************");
        List<String> filterStrs1 = filterString(list,s -> s.contains("h"));
        System.out.println(filterStrs1);
    }
    //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
    public List<String> filterString(List<String> list, Predicate<String> pre){
        ArrayList<String> filterList = new ArrayList<>();
        for (String s : list) {
            if (pre.test(s)){
                filterList.add(s);
            }
        }
        return filterList;
    }
}
/**
[happy, hello]
*****************************
[happy, hello]
*/

4.方法引用与构造器引用
4.1 方法引用的使用

4.1.1 使用情境:
当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用!

4.1.2 方法引用,本质上就是Lambda表达式,
而Lambda表达式作为函数式接口的实例。
所以方法引用,也是函数式接口的实例。

4.1.3 使用格式: 类(或对象) ::方法名

4.1.4 具体分为如下的三种情况:
情况1 对象::非静态方法
情况2 类::静态方法
情况3 类::非静态方法

4.1.5 方法引用使用的要求:
要求接口中的抽象方法的形参列表
和返回值类型与方法引用的方法的形参列表和返回值类型相同!
(针对于情况1和情况2)

举例:(导包 省略)

/**
 * 方法引用的使用
*/
public class MethodRefTest {
    //情况一:对象::实例方法
    //Consumer中的void accept(T t)
    //PrintStream中的void println(T t)
//    accept方法用println方法替换 有它来实现
    @Test
    public void test1(){
        Consumer<String> con1 = str -> System.out.println(str);
        con1.accept("北京");
        System.out.println("*******************");
        PrintStream ps = System.out;
        //返回个打印流的对象  System.out对象并赋给PrintStream ps
        Consumer<String> con2 = ps::println;
        con2.accept("beijing");
    }

    /**
     * 北京
     * *******************
     * beijing
     */

    //Supplier中的T get()
    //Employee中的String getName()
//    抽象方法由另一个方法实现了 参数列表和返回值类型完全一致
    @Test
    public void test2(){
        Employee emp = new Employee(1001,"Tom",23,5600);

        Supplier<String> sup1 = () -> emp.getName();
        System.out.println(sup1.get());

        System.out.println("*******************");
        Supplier<String> sup2 = emp::getName;
        System.out.println(sup2.get());
    }
    /**
     * Tom
     * *******************
     * Tom
     */
    
    //情况二:类::静态方法
    //Comparator中的int compare(T t1,T t2)
    //Integer中的int compare(T t1,T t2)
    @Test
    public void test3(){
        Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
        System.out.println(com1.compare(12,21));
        System.out.println("*******************");
        Comparator<Integer> com2 = Integer::compareTo;
        System.out.println(com2.compare(12, 3));
    }
    /**
     * -1
     * *******************
     * 1
     */
    
    //Function中的R apply(T t)
    //Math中的Long round(Double d)
    @Test
    public void test4(){
        Function<Double,Long> func = new Function<Double, Long>() {
            @Override
            public Long apply(Double d) {
                return Math.round(d);
            }
        };
        System.out.println("*******************");
        Function<Double,Long> func1 = d -> Math.round(d);
        System.out.println(func1.apply(12.3));
        System.out.println("*******************");
        Function<Double,Long> func2 = Math::round;
        System.out.println(func1.apply(12.6));
    }
    /**
     * *******************
     * 12
     * *******************
     * 13
     */

    //情况三:类::实例方法 难度
    //Comparator中的int compare(T t1,T t2)
    // String中的int t1. compareTo(t2)
    @Test
    public void test5(){
        Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
        System.out.println(com1.compare("abc", "abd"));
        System.out.println("*******************");
        Comparator<String> com2 = String::compareTo;
        System.out.println(com2.compare("abd", "abm"));
    }
    /**
     * -1
     * *******************
     * -9
     */
    //BiPredicate中的boolean test(T t1,T t2); 两个参数是布尔型的
    //String中的boolean t1.equals(t2)  这个不是两个参数 但也是布尔型  但上面的参数t1 作为下面的调用者
    @Test
    public void test6(){
        BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
        System.out.println(pre1.test("abc", "abc"));
        System.out.println("*******************");
        BiPredicate<String,String> pre2 = String ::equals;
        System.out.println(pre2.test("abc", "abd"));
    }

    /**
     * true
     * *******************
     * false
     */
    //Function中的R apply(T t)  返回值是R    T就相当于employee 然后调用getName()
    //Employee中的String getName();
    @Test
    public void test7(){
        Employee emp = new Employee(1001,"Tom",23,5600);
        Function<Employee,String> func1 = e -> e.getName();
        System.out.println(func1.apply(emp));
        System.out.println("*******************");
        Function<Employee,String> func2 = Employee::getName;
        System.out.println(func2.apply(emp));
    }
    /**
     * Tom
     * *******************
     * Tom
     */
}

4.2 构造器引用
和方法引用类似,
函数式接口的抽象方法的形参列表和构造器的形参列表一致。
抽象方法的返回值类型即为构造器所属的类的类型

4.3 数组引用
大家可以把数组看傲是一个特殊的类,则写法与构造器引用一致。

举例:(导包 省略)

/**
 * 1.构造器引用
 *
 * 2.数组引用
 */
public class ConstructorRefTest {
//    构造器引用
//    Supplier 中的T get()       抽象方法返回值是T 这个T就是你要造的下面这个类型的对象
//    Employee的空参构造器:Employee()
    @Test
    public void test1(){
        Supplier<Employee> sup = new Supplier<Employee>() {
            @Override
            public Employee get() {
                return new Employee();
            }
        };
        System.out.println("***********************");
        Supplier<Employee> sup1 = () -> new Employee();
        System.out.println(sup1.get());
        System.out.println("***********************");
        Supplier<Employee> sup2 = Employee::new;
        System.out.println(sup2.get());
    }
    /**
     * ***********************
     * Employee()...
     * Employee{id=0, name='null', age=0, salary=0.0}
     * ***********************
     * Employee()...
     * Employee{id=0, name='null', age=0, salary=0.0}
     */
    //Function中的R apply(T t)
    @Test
    public void test2(){
        Function<Integer,Employee> func1 = id -> new Employee(id);
        Employee employee = func1.apply(1001);
        System.out.println(employee);
        System.out.println("***********************");
        Function<Integer,Employee> func2 = Employee::new;
        Employee employee1 = func2.apply(1002);
        System.out.println(employee1);
    }
    /**
     * Employee(int id)...
     * Employee{id=1001, name='null', age=0, salary=0.0}
     * ***********************
     * Employee(int id)...
     * Employee{id=1002, name='null', age=0, salary=0.0}
     */
    //BiFunction中的R apply(T t,U u)
    @Test
    public void test3(){
        BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
        System.out.println(func1.apply(1001, "Tom"));
        System.out.println("***********************");
        BiFunction<Integer,String,Employee> func2 = Employee::new;
        System.out.println(func2.apply(1002, "Tom"));
    }
/**
 * Employee{id=1001, name='Tom', age=0, salary=0.0}
 * ***********************
 * Employee{id=1002, name='Tom', age=0, salary=0.0}
 */
    //数组引用
    //Function中的R apply(T t)
    @Test
    public void test4(){
        //前一个参数是定义数组的长度的
        Function<Integer,String[]> func1 = length -> new String[length];
//      创建了参数为五的String数组
        String[] arr1 = func1.apply(5);
        System.out.println(Arrays.toString(arr1));
        System.out.println("***********************");
        Function<Integer,String[]> func2 = String[] :: new;
        String[] arr2 = func2.apply(10);
        System.out.println(Arrays.toString(arr2));
    }
    /**
     * [null, null, null, null, null]
     * ***********************
     * [null, null, null, null, null, null, null, null, null, null]
     */
}
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐