JDK1.8 新特性(一)【默认方法、静态方法和Lambda表达式】

简介: JDK1.8 新特性(一)【默认方法、静态方法和Lambda表达式】

1、接口的默认方法与静态方法

什么是普通方法?

我们可以把 Java 中的方法看成两类:普通方法(有方法体的)和抽象方法(没有方法体的,需要子类去实现的,比如接口、抽象类)。

JDK8 之前,Java 中接口 Interface 之中可以定义变量和方法:

  • 变量 必须是 public、static、final 的
  • 方法 必须是 public、abstract 的

而且这些修饰符都是默认的,也就是不需要我们写。

从JDK8 开始 支持使用 static 和 default 来修饰方法可以写方法体不需要子类重写被 static 和 default修饰的方法

接口中定义的默认方法只有子类对象才能调用,而接口中定义的静态方法只有接口类才能调用。

编写接口

public interface JDK8Interface {
    /*
     * 默认就是 public abstract 的,不需要加修饰符
    */
    void add();
    /*
     * JDK8 提供 默认的方法
     */
    default void get(){
        System.out.println("get");
    }
    /*
     * JDK8 提供的静态方法 只能通过 接口名.方法名来 调用
     */
    static void del(){
        System.out.println("del");
    }
}

编写接口的实现类

public class JDK8InterfaceImpl implements JDK8Interface {
    @Override
    public void add() {
        System.out.println("add");
    }
}

测试

public class Test01 {
    public static void main(String[] args) {
        JDK8InterfaceImpl jdk8Interface = new JDK8InterfaceImpl();
        jdk8Interface.add();
        jdk8Interface.get();
        JDK8Interface.del();    
    }
}

我们可以得到这样一个结论:接口中定义的默认方法只有子类对象才能调用,而接口中定义的静态方法只有接口类才能调用。

2、Lambda表达式(重点)

什么是 Lambda 表达式

Lanmbda 表达式是一个匿名函数,即没有函数名的函数,基于数学中的 λ 而得名。

优点:简化匿名内部类的调用,减少代码量。

缺点:不好调试

编写接口:

public interface OrderService {
    void get();
}

测试:

public class Test02 {
    public static void main(String[] args) {
        // 1. 通过匿名内部类创建一个实现OrderService接口的子类
        OrderService service = new OrderService() {
            @Override
            public void get() {
                System.out.println("get");
            }
        };
        service.get();
        // 2. lambda 表达式  括号代表参数列表
        ((OrderService) ()-> System.out.println("get")).get();
        // 3. 同样是lambda表达式
        new Thread(()-> System.out.println("get")).start();
    }
}

注意:这里的接口只能有一个抽象方法!具体原因看下面。

2.1、Lambda 表达式规范

2.1.1、函数接口的定义

Java 中使用 Lambda 表达式依赖于函数接口。我们在 new 一个对象,需要传入的参数是接口类型时,可以使用该 接口的实现类,也可以直接构造一个匿名内部类(这里我们就可以通过 lambda 表达式来简化代码,提高开发效率,主要是看起来是真的舒服)。

  • 在接口当中只允许存在一个抽象方法。
  • 允许使用 default 和 static 修饰的方法。
  • 使用注解 @FunctionalInterface 来标记该接口为函数接口(接口中只包含一个方法的叫做函数接口),我们可以发现,如果接口中出现了超过1个的抽象方法代码就会爆红。
  • 可以定义 Object 类中的方法

下面是我自定义的一个函数接口。

/**
 * 函数接口 只允许包含一个抽象方法
 * 4. 使用注解 @FunctionalInterface 来标注着是一个函数接口
 */
@FunctionalInterface
public interface MyFunctionInterface {
    // 1. 只允许存在一个方法
    void get();
    // 2. 允许存在 default 和 static 修饰的方法
    default void add(){
        System.out.println("add");
    }
    static void ss(){
        System.out.println("ss");
    }
    // 3. 可以存在 Object 类中的方法
    String toString();
    boolean equals(Object obj);
    int hashCode();
}

2.1.2、Java 内置的函数接口

new Thread(new Runnable() {
            @Override
            public void run() {
               // 方法体 
            }
        }).start();

正因为i Runnable 是一个函数接口,所以我们可以这样简写:

new Thread(()->{
          // 方法体  
        }).start();

2.2、Lambda 基础语法

语法:

() -> {}
  • ():参数列表
  • ->:分隔符
  • {}:方法体

2.2.1、无参方法调用

(1)编写函数接口

@FunctionalInterface
public interface ParamLambdaInterface {
    void get();
}

(2)使用 lambda

public class Test03 {
    public static void main(String[] args) {
        // 1. 匿名内部类
        new ParamLambdaInterface(){
            @Override
            public void get() {
                System.out.println("get");
            }
        };
        // 2. lambda 表达式
        ParamLambdaInterface pl = () -> {
            System.out.println("get");
        };
        pl.get();
    }
}

2.2.2、有参方法调用

(1)编写函数接口

@FunctionalInterface
public interface OrderService {
    void get(String name);
}

2)使用 lambda

public class Test04 {
    public static void main(String[] args) {
        // 1. 通过匿名内部类创建一个实现OrderService接口的子类
        OrderService service = new OrderService() {
            @Override
            public void get(String name) {
                System.out.println(name);
            }
        };
        service.get("tom");
        // 2. lambda 表达式 参数类型可以省略
        OrderService od = (name)->{
            System.out.println(name);
        };
        od.get("get");
        // 简化
        ((OrderService) System.out::println).get("tom");
    }
}

2.3、Lambda 的精简写法

2.3.1、无参方法

上面 2.2.1 也可以这么写:

// 一行代码的情况下 可以省去花括号
((ParamLambdaInterface) () -> System.out.println("get")).get();

2.3.2、有参方法

2.2.2 可以这么写:

如果参数只有一个可以省去括号

((OrderService) name -> System.out.println(name)).get("get");

练习

定义一个用于计算和的函数接口

@FunctionalInterface
public interface AddInterface {
    int add(int a,int b);
}

测试,求 1 和 2 的和

int result = ((AddInterface) (a, b) -> a + b).add(1, 2);

2.4、Lambda 实战

forEach

定义一个存放姓名的 List :

List<String> list = new ArrayList<>();
        list.add("mike");
        list.add("tom");
        list.add("bob");

使用 List 的 forEach 方法打印所有元素:

可以看到 forEach 的参数也是一个函数接口,而且只有一个抽象方法 accept。

    // 1. 普通写法
    list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });    
    // 2. lambda
    list.forEach(name-> System.out.println(name) );
    // 简化
    list.forEach(System.out::println);

将输出结果全部大写输出:

// 将名字转为大写再输出
        list.forEach(name-> System.out.println(name.toUpperCase(Locale.ROOT)));

集合排序

数据准备

public class User {
    private String name;
    private Integer age;
    public User(){}
    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}

创建一个 User 集合

List<User> list = new ArrayList<>();
        list.add(new User("mike",20));
        list.add(new User("bob",18));
        list.add(new User("tom",25));

使用 lambda 进行排序:

    // 1. 普通写法
    list.sort(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return 0;
            }
        });
    // 2. lambda 表达式
    list.sort((user1,user2)-> user1.getAge()-user2.getAge());   // 升序排列
    // 输出结果
    list.forEach(System.out::println);

运行结果:

User{name='bob', age=18}
User{name='mike', age=20}
User{name='tom', age=25}


相关文章
|
1月前
|
Java
让星星⭐月亮告诉你,jdk1.8 Java函数式编程示例:Lambda函数/方法引用/4种内建函数式接口(功能性-/消费型/供给型/断言型)
本示例展示了Java中函数式接口的使用,包括自定义和内置的函数式接口。通过方法引用,实现对字符串操作如转换大写、数值转换等,并演示了Function、Consumer、Supplier及Predicate四种主要内置函数式接口的应用。
27 1
|
2月前
|
容器
jdk8新特性-详情查看文档
jdk8新特性-详情查看文档
46 3
|
1月前
|
存储 安全 Java
JDK1.8 新的特性
JDK1.8 新的特性
21 0
|
2月前
|
编解码 安全 Java
jdk8新特性-接口和日期处理
jdk8新特性-接口和日期处理
|
2月前
|
Java 编译器 API
JDK8新特性--lambda表达式
JDK8的Lambda表达式是Java语言的一大进步。它为Java程序提供了更多的编程方式,让代码更加简洁,也让函数式编程的概念在Java中得到了体现。Lambda表达式与Java 8的其他新特性,如Stream API、新的日期时间API一起,极大地提高了Java编程的效率和乐趣。随着时间的流逝,Java开发者对这些特性的理解和应用将会越来越深入,进一步推动Java语言和应用程序的发展。
15 0
|
2月前
|
Java
安装JDK18没有JRE环境的解决办法
安装JDK18没有JRE环境的解决办法
346 3
|
3月前
|
Java 关系型数据库 MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【8月更文挑战第19天】在Linux上搭建Java Web应用环境,需安装JDK 1.8、Tomcat及MariaDB。本指南详述了使用apt-get安装OpenJDK 1.8的方法,并验证其版本。接着下载与解压Tomcat至`/usr/local/`目录,并启动服务。最后,通过apt-get安装MariaDB,设置基本安全配置。完成这些步骤后,即可验证各组件的状态,为部署Java Web应用打下基础。
59 1
|
3月前
|
Oracle Java 关系型数据库
Mac安装JDK1.8
Mac安装JDK1.8
717 4
|
4月前
|
Java Linux
Linux复制安装 jdk 环境
Linux复制安装 jdk 环境
111 3
|
1月前
|
Oracle Java 关系型数据库
jdk17安装全方位手把手安装教程 / 已有jdk8了,安装JDK17后如何配置环境变量 / 多个不同版本的JDK,如何配置环境变量?
本文提供了详细的JDK 17安装教程,包括下载、安装、配置环境变量的步骤,并解释了在已有其他版本JDK的情况下如何管理多个JDK环境。
868 0