Java8新特性

一 、lambda表达式

1.1需求分析

public class Test01 {
    public static void main(String[] args) {
        //开启一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("新线程的名称"+Thread.currentThread().getName());
            }
        }).start();
        System.out.println("主线程的名称mian"+Thread.currentThread().getName());
    }
}

代码分析:

  1. Thread类需要一个Runnable接口作为参数,接口中的抽象方法是用来指定线程任务内容的核心
  2. 为了指定run方法体,不得不创建Runnable接口实现类并重写抽象方法run()
  3. 为了省区创建实现类单独定义一个类文件,因此引入了匿名内部类new Runnable() {需要重写的方法}
  4. 实际上我们只关心重写的方法体中的内容->因此引出了Lambda表达式进一步简化匿名内部类写法,强调方法体内容

1.2Lambda表达式

  1. 格式: (参数)->{方法体}
  2. Lambda表达式其实是一个匿名函数,类似于匿名内部类,我们不需要关心具体的函数名是什么
new Thread(()->{System.out.println("Lambda表达式实现,新线程的名称"+Thread.currentThread().getName());}).start();

1.2.1无参数的Lambda表达式

  1. 定义接口,接口中定义一个抽象方法show()无参数无返回值.
@java.lang.FunctionalInterface
public interface FunctionalInterface {
    void show();//无参抽象方法
}
  1. 定义测试类
public class Test {
    public static void main(String[] args) {
        //01、匿名内部类
        new FunctionalInterface() {
            @Override
            public void show() {
                System.out.println("匿名内部类方式");
            }
        }.show();

        //02、lambda表达式
        FunctionalInterface functionalInterface = () -> {
            System.out.println("无参构造方法Lambda表达式写法调用");
        };
        functionalInterface.show();


        //03、精简版Lambda表达式
        ((FunctionalInterface)()->{
            System.out.println("精简版Lambda表达式");
        }).show();
    }
}

1.2.2优参数的Lambda表达式

  1. 定义接口,接口中的抽象方法有参数
    案例1:

@FunctionalInterface

public interface Message {
    void Send(String information);
}
  1. 定义测试类
public class Lambda01 {
    public static void main(String[] args) {
        //Lambda表达式实现将实例化对象作为参数进行传递
        senMessage((information)->{
            System.out.println(information);
        });


    }
    //定义一个方法,参数式接口Message实例类型,由于接口无法new,所以之恶能通过匿名内部类或者Lambda形式传入实例化的参数
    public static void senMessage(Message message){
        message.Send("这是要传递的信息");//message就是实例化的对象

    }
}

案例2:

  1. 定义实体类
public class UserEntity {
    private String name;
    private int age;

    public UserEntity(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "UserEntity{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  1. 定义测试类
import java.util.ArrayList;
import java.util.function.Consumer;

public class Lambda_Sort {
    public static void main(String[] args) {
    //创建UserEntity类型的集合,并添加数据
        ArrayList<UserEntity> userEntities = new ArrayList<>();
        userEntities.add(new UserEntity("liMing",18));
        userEntities.add(new UserEntity("wangqing",28));
        userEntities.add(new UserEntity("Lucy",15));
        userEntities.add(new UserEntity("Jack",26));


        //01、按照年龄进行排序,匿名内部类方式
/*        userEntities.sort(new Comparator<UserEntity>() {
            @Override
            public int compare(UserEntity o1, UserEntity o2) {
                return o1.getAge()-o2.getAge();
            }
        });*/

        //02、Lambda实现排序,当方法体只有一条语句时不用写return,和大括号
        userEntities.sort((o1, o2) -> o1.getAge()-o2.getAge() );
        
        //03、foreach进行输出,匿名内部类方法
        /*
        System.out.println("正常forEach----------------------");
        userEntities.forEach(new Consumer<UserEntity>() {
            @Override
            public void accept(UserEntity userEntity) {
                System.out.println(userEntity);
            }
        });*/
        
        //04、lambda表达式实现forEach
        System.out.println("lambda表达式实现forEach----------------------");
        userEntities.forEach(userEntity->System.out.println(userEntity));
    }
}

1.3注解:@FunctionalInterface介绍

@FunctionalInterface是Java 8引入的一个注解,用于指示接口应该是一个函数式接口。函数式接口是指只有一个抽象方法的接口,这种接口可以用lambda表达式实现,不用再定义一个实现类。在接口上使用@FunctionalInterface注解可以帮助编译器(编译阶段)检查该接口是否满足函数式接口的条件,如果不满足,编译器会报错。

例如,下面的代码定义了一个函数式接口:

@FunctionalInterface
public interface MyFunctionalInterface {
    void doSomething();
}

在这个例子中,MyFunctionalInterface只有一个抽象方法doSomething(),因此它是一个函数式接口。我们可以使用lambda表达式来实现这个接口:

MyFunctionalInterface myFunc = () -> System.out.println("Hello, world!");
myFunc.doSomething(); // 输出 "Hello, world!"

1.3.1为什么使用@FunctionalInterface注解

在写Lambda表达式的时候,如果实现的接口中有多个方法,将无法确定方法体应该式属于哪个抽象方法
在这里插入图片描述
在接口上使用此注解后,如果接口中定义了多个抽象方法,会报错提示.

1.4Lambda表达式原理分析

1.4.1匿名内部类原理

匿名内部类底层实际上在编译阶段会生成一个Class文件

  1. 定义的接口
public interface Animal {
    void Eat();
}
  1. 实现类
public class Main {
    public static void main(String[] args) {
        InEat(new Animal() {
            @Override
            public void Eat() {
                System.out.println("小狗吃骨头");
            }
        });
    }
    public static void InEat(Animal animal){
        animal.Eat();
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4.2Lambda省略写法

在标准写法的基础上,可以使用省略写法的规则:

  1. 小括号内参数类型可以省略
  2. 如果小括号内只有一个参数,小括号也可以省略
  3. 如果大括号内有且仅有一个语句,可以同时省略大括号,return关键字,语句分号
public class Main {
    public static void main(String[] args) {
        InEat(new Animal(String name) {
            @Override
            public void Eat() {
                System.out.println("小狗吃骨头");
            }
        });
    }
    public static void InEat(Animal animal){
        animal.Eat();
    }
}

省略写法:
小括号内省略了数据类型和括号,因为只有一个参数

public class Main {
    public static void main(String[] args) {
        InEat(name->
            System.out.println("小狗吃骨头")
        );
    }
    public static void InEat(Animal animal){
        animal.Eat();
    }
}

1.4.3Lambda表达式使用前提

  1. 方法的参数会局部变量类型必须是接口才能使用Lambda
  2. 接口中有且只有一个抽象方法(@FunctionalInterface注解修饰的接口)

1.5Lambda表达式与匿名内部类的区别(面试题)

  1. 所需类型不一样:
    (1)匿名内部类可以传递的类型:类、抽象类、接口
    (2)Lambda表达式需要的类型只能是接口
  2. 抽象方法的数量不同
    (1)匿名内部类所需的接口中抽象方法数量是随意的,有多少可以实现多少
    (2)Lambda表达式所需的接口中只能由一个抽象方法
  3. 实现原理不同
    (1)匿名内部类是在编译后形成class
    (2)Lambda表达式是在程序运行过程中动态生成class

二、JDK8接口增强

2.1接口可以新增的内容

JDK8前后接口中定义的变化主要体现在默认实现、静态方法和函数接口这三个方面

  1. 默认实现方法
    (1)概念和语法:在JDK8之前,接口中只能包含抽象方法,即只有方法声明而没有方法体。JDK8开始允许在接口中定义带有具体实现的方法,称为默认实现。默认实现使用default修饰符。
    (2)应用场景:默认实现的一个重要应用场景是简化接口的实现过程,减少冗余代码。例如,在Spring框架中的HandlerInterceptor接口中,JDK8之前需要实现所有声明的方法,即使某些方法对业务逻辑无关紧要。通过默认实现,接口设计者可以提供一些方法的默认行为,实现类可以选择性地重写这些方法。
    优势:默认实现减少了抽象适配器类的需要,使得接口更加灵活和方便,同时也降低了实现类的复杂度。
  2. 静态方法
    (1)概念和语法:接口中的静态方法是指那些用static修饰的方法,可以在不创建对象实例的情况下,通过接口名直接调用。
    (2)应用场景:静态方法常用于工具类或工具方法的场景。例如,可以在接口中定义一个静态的工厂方法来创建实例。此外,静态方法也可以被用于在接口中封装与该接口相关的一些通用操作。
    注意事项:静态方法不会在接口的实现类或子接口中继承,但它们提供了一种将相关工具方法组织在一起的方式。需要注意的是,静态方法不能访问接口中的非静态成员。
  3. 函数接口
    (1)概念和语法:如果一个接口只声明了一个抽象方法(包括继承自父接口的抽象方法),那么这个接口被称为函数式接口,并且可以用@FunctionalInterface注解标注。函数式接口非常适合于使用Lambda表达式进行简洁的实现。
    (2)应用场景:函数式接口在Java 8及以后的版本中大量用于函数式编程和流式处理,如java.util.function包下的Predicate、Consumer等接口。
    优势:函数式接口的使用大大简化了代码结构,使得多线程编程、集合处理等功能变得更加直观和高效。

2.2默认方法

2.2.1为什么要在JDK8中新加默认方法?

  1. 定义接口
public interface Animal {
    //定义一个动物接口
    void Eat();
}
  1. 定义实现类与测试类
public class Imp {
    public static void main(String[] args) {
        A a = new A();
        a.Eat();
        B b = new B();
        b.Eat();
    }
}
//定义两个实现类
class A implements Animal{
    @Override
    public void Eat() {
        System.out.println("类A实现抽象方法");
    }
}
 class B implements Animal{
    @Override
    public void Eat() {
        System.out.println("类B实现抽象方法Eat");
    }
}
  1. 当接口中需要新增加一个抽象方法时,所有接口实现类都需要实现该方法
    在这里插入图片描述

2.2.2出现的问题

如果一个接口有几百个实现类,接口中新添加一个抽象方法,几百个实现必须都要实现此抽象方法,大大降低类接口的可扩展性。因此JDK8中引入了默认方法,可以在接口中定义默认的方法体,实现类可以直接调用,或者重写此方法。

default void play(){
        System.out.println("接口中定义的默认方法");
    }

2.2.3默认方法的两种使用方式

  1. 实现类直接调用默认方法
  2. 实现类重写默认方法
package 测试.JDK8默认方法;

public class Imp {
    public static void main(String[] args) {
        A a = new A();
        a.Eat();
        B b = new B();
        b.Eat();
        b.play();
    }
}
//定义两个实现类
class A implements Animal{
    @Override
    public void Eat() {
        System.out.println("类A实现抽象方法");
    }
    @Override
    public void play(){
        System.out.println("实现类A重写默认方法");
    }
}
 class B implements Animal{
    @Override
    public void Eat() {
        System.out.println("类B实现抽象方法Eat");
    }
}

在这里插入图片描述

2.2.4总结

  1. 引入默认方法的原因就是,当接口的实现类比较多的时候,如果接口中新添加一个抽象方法,所有的实现类都需要实现该方法,不管这个方法对自已有没有用,所以引入默认方法解决此问题。
  2. 定义了默认方法之后,方法体中定义一些默认的行为,如果某些实现接口的类需该方法,可以直接使用类对象进行调用,如果某些实现类觉得该方法不太适合自己,也可以对该方法进行重写,满足自己的需求。
    3.在这里插入图片描述

2.3静态方法

作用也是为了增强接口扩展,接口中的静态方法在实现类中无法像默认方法那样重写。
语法结构:

修饰符 interface 接口名{
static 返回值类型 方法名(){
方法体
}
}

在这里插入图片描述

2.4默认方法与静态方法区别

  1. 默认方法可以通过实例对象调用,静态方法只能通过接口名调用
  2. 默认方法可以被实现类继承,实现类可以直接调用默认方法或重写默认方法
  3. 静态方法不能被继承,实现类不能重写接口中静态方法,之恶能通过接口名调用

三、

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

泰勒今天想展开

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值