zoukankan      html  css  js  c++  java
  • 作为有经验的程序员如果不懂Lambda表达式就说不过去了吧,建议收藏!!!

      最近刚好有空给大家整理下JDK8的特性,这个在实际开发中的作用也是越来越重了,本文重点讲解下Lambda表达式

    Lambda表达式

      Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
      Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
      使用 Lambda 表达式可以使代码变的更加简洁紧凑。

    1. 需求分析

       创建一个新的线程,指定线程要执行的任务

        public static void main(String[] args) {
            // 开启一个新的线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("新线程中执行的代码 : "+Thread.currentThread().getName());
                }
            }).start();
            System.out.println("主线程中的代码:" + Thread.currentThread().getName());
        }
    

    代码分析:

    1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心
    2. 为了指定run方法体,不得不需要Runnable的实现类
    3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类
    4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错,
    5. 而实际上,我们只在乎方法体中的代码

    一起来进阶提升吧:463257262

    2.Lambda表达式初体验

      Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码

    new Thread(() -> { System.out.println("新线程Lambda表达式..." +Thread.currentThread().getName()); })
                    .start();
    

      Lambda表达式的优点:简化了匿名内部类的使用,语法更加简单。

      匿名内部类语法冗余,体验了Lambda表达式后,发现Lambda表达式是简化匿名内部类的一种方式。

    3. Lambda的语法规则

      Lambda省去了面向对象的条条框框,Lambda的标准格式由3个部分组成:

    (参数类型 参数名称) -> {
        代码体;
    }
    

    格式说明:

    • (参数类型 参数名称):参数列表
    • {代码体;} :方法体
    • -> : 箭头,分割参数列表和方法体

    3.1 Lambda练习1

      练习无参无返回值的Lambda

    定义一个接口

    public interface UserService {
        void show();
    }
    

    然后创建主方法使用

    public class Demo03Lambda {
    
        public static void main(String[] args) {
            goShow(new UserService() {
                @Override
                public void show() {
                    System.out.println("show 方法执行了...");
                }
            });
            System.out.println("----------");
            goShow(() -> { System.out.println("Lambda show 方法执行了..."); });
        }
    
        public static void goShow(UserService userService){
            userService.show();
        }
    }
    

    输出:

    show 方法执行了...
    ----------
    Lambda show 方法执行了...
    

    3.2 Lambda练习2

       完成一个有参且有返回值得Lambda表达式案例

    创建一个Person对象

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class Person {
    
        private String name;
    
        private Integer age;
    
        private Integer height;
    
    }
    
    

      然后我们在List集合中保存多个Person对象,然后对这些对象做根据age排序操作

        public static void main(String[] args) {
            List<Person> list = new ArrayList<>();
            list.add(new Person("周杰伦",33,175));
            list.add(new Person("刘德华",43,185));
            list.add(new Person("周星驰",38,177));
            list.add(new Person("郭富城",23,170));
    
            Collections.sort(list, new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    return o1.getAge()-o2.getAge();
                }
            });
            for (Person person : list) {
                System.out.println(person);
            }
        }
    
    

      我们发现在sort方法的第二个参数是一个Comparator接口的匿名内部类,且执行的方法有参数和返回值,那么我们可以改写为Lambda表达式

        public static void main(String[] args) {
            List<Person> list = new ArrayList<>();
            list.add(new Person("周杰伦",33,175));
            list.add(new Person("刘德华",43,185));
            list.add(new Person("周星驰",38,177));
            list.add(new Person("郭富城",23,170));
    
            /*Collections.sort(list, new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    return o1.getAge()-o2.getAge();
                }
            });
            for (Person person : list) {
                System.out.println(person);
            }*/
            System.out.println("------");
            Collections.sort(list,(Person o1,Person o2) -> {
                return o1.getAge() - o2.getAge();
            });
            for (Person person : list) {
                System.out.println(person);
            }
        }
    

    输出结果

    Person(name=郭富城, age=23, height=170)
    Person(name=周杰伦, age=33, height=175)
    Person(name=周星驰, age=38, height=177)
    Person(name=刘德华, age=43, height=185)
    

    4. @FunctionalInterface注解

      @FunctionalInterface是JDK8中新增加的一个函数式注解,表示该注解修饰的接口只能有一个抽象方法。

    /**
     * @FunctionalInterface
     *    这是一个函数式注解,被该注解修饰的接口只能声明一个抽象方法
     */
    @FunctionalInterface
    public interface UserService {
    
        void show();
    
    }
    

    5. Lambda表达式的原理

      匿名内部类的本质是在编译时生成一个Class 文件。XXXXX$1.class

    public class Demo01Lambda {
    
        public static void main(String[] args) {
            // 开启一个新的线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("新线程中执行的代码 : "+Thread.currentThread().getName());
                }
            }).start();
            System.out.println("主线程中的代码:" + Thread.currentThread().getName());
            System.out.println("---------------");
            /*new Thread(() -> { System.out.println("新线程Lambda表达式..." +Thread.currentThread().getName()); })
                    .start();*/
        }
    }
    

    在这里插入图片描述

      还可以通过反编译工具来查看生成的代码 XJad 工具来查看

    static class Demo01Lambda$1
    	implements Runnable
    {
    
    	public void run()
    	{
    		System.out.println((new StringBuilder()).append("新线程中执行的代码 : " ).append(Thread.currentThread().getName()).toString());
    	}
    
    	Demo01Lambda$1()
    	{
    	}
    }
    

      那么Lambda表达式的原理是什么呢?我们也通过反编译工具来查看
    在这里插入图片描述
      写的有Lambda表达式的class文件,我们通过XJad查看报错。这时我们可以通过JDK自带的一个工具:javap 对字节码进行反汇编操作。

    javap -c -p 文件名.class
    
    -c:表示对代码进行反汇编
    -p:显示所有的类和成员
    

    反汇编的结果:

    E:workspaceOpenClassWorkSpaceJDK8Demo	argetclassescomobojdklambda>javap -c -p Demo03Lambda.class
    Compiled from "Demo03Lambda.java"
    public class com.bobo.jdk.lambda.Demo03Lambda {
      public com.bobo.jdk.lambda.Demo03Lambda();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":()V
           4: return
    
      public static void main(java.lang.String[]);
        Code:
           0: invokedynamic #2,  0              // InvokeDynamic #0:show:()Lcom/bobo/jdk/lambda/service/UserService;
           5: invokestatic  #3                  // Method goShow:(Lcom/bobo/jdk/lambda/service/UserService;)V
           8: return
    
      public static void goShow(com.bobo.jdk.lambda.service.UserService);
        Code:
           0: aload_0
           1: invokeinterface #4,  1            // InterfaceMethod com/bobo/jdk/lambda/service/UserService.show:()V
           6: return
    
      private static void lambda$main$0();
        Code:
           0: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
           3: ldc           #6                  // String Lambda show 方法执行了...
           5: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
           8: return
    }
    

      在这个反编译的源码中我们看到了一个静态方法 lambda$main$0(),这个方法里面做了什么事情呢?我们通过debug的方式来查看下:
    在这里插入图片描述

    上面的效果可以理解为如下:

    public class Demo03Lambda {
    
        public static void main(String[] args) {
            ....
        }
    
        private static void lambda$main$0();
            System.out.println("Lambda show 方法执行了...");
        }
    }
    

      为了更加直观的理解这个内容,我们可以在运行的时候添加 -Djdk.internal.lambda.dumpProxyClasses, 加上这个参数会将内部class码输出到一个文件中

    java -Djdk.internal.lambda.dumpProxyClasses 要运行的包名.类名
    

    命令执行

    E:workspaceOpenClassWorkSpaceJDK8Demo	argetclasses>java -Djdk.internal.lambda.dumpProxyClasses com.bobo.jdk.lambda.Demo03Lambda
    Lambda show 方法执行了...
    

    在这里插入图片描述
    反编译后的内容:
    在这里插入图片描述

      可以看到这个匿名的内部类实现了UserService接口,并重写了show()方法。在show方法中调用了Demo03Lambda.lambda$main$0(),也就是调用了Lambda中的内容。

    public class Demo03Lambda {
    
        public static void main(String[] args) {
            goShow(new UserService() {
                @Override
                public void show() {
                    Demo03Lambda.lambda$main$0();
                }
            });
            System.out.println("----------");
           
        }
    
        public static void goShow(UserService userService){
            userService.show();
        }
    
        private static void lambda$main$0();
            System.out.println("Lambda show 方法执行了...");
        }
    }
    

    小结:

    匿名内部类在编译的时候会产生一个class文件。

    Lambda表达式在程序运行的时候会形成一个类。

    1. 在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的代码
    2. 还会形成一个匿名内部类,实现接口,重写抽象方法
    3. 在接口中重写方法会调用新生成的方法

    6.Lambda表达式的省略写法

      在lambda表达式的标准写法基础上,可以使用省略写法的规则为:

    1. 小括号内的参数类型可以省略
    2. 如果小括号内有且仅有一个参数,则小括号可以省略
    3. 如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。
    public class Demo05Lambda {
    
        public static void main(String[] args) {
            goStudent((String name,Integer age)->{
                return name+age+" 6666 ...";
            });
            // 省略写法
            goStudent((name,age)-> name+age+" 6666 ...");
            System.out.println("------");
            goOrder((String name)->{
                System.out.println("--->" + name);
                return 666;
            });
            // 省略写法
            goOrder(name -> {
                System.out.println("--->" + name);
                return 666;
            });
            goOrder(name ->  666);
        }
    
        public static void goStudent(StudentService studentService){
            studentService.show("张三",22);
        }
    
        public static void goOrder(OrderService orderService){
            orderService.show("李四");
        }
        
    }
    

    7.Lambda表达式的使用前提

      Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意

    1. 方法的参数或局部变量类型必须为接口才能使用Lambda
    2. 接口中有且仅有一个抽象方法(@FunctionalInterface)

    8.Lambda和匿名内部类的对比

      Lambda和匿名内部类的对比

    1. 所需类型不一样

      • 匿名内部类的类型可以是 类,抽象类,接口
      • Lambda表达式需要的类型必须是接口
    2. 抽象方法的数量不一样

      • 匿名内部类所需的接口中的抽象方法的数量是随意的
      • Lambda表达式所需的接口中只能有一个抽象方法
    3. 实现原理不一样

      • 匿名内部类是在编译后形成一个class
      • Lambda表达式是在程序运行的时候动态生成class

    ~好了,Lambda表达式的内容就介绍到这儿,如果对你有帮助,欢迎点赞关注加收藏哦 V_V

  • 相关阅读:
    HAProxy、Keepalived 在 Ocatvia 的应用实现与分析
    Octavia 的 HTTPS 与自建、签发 CA 证书
    Octavia 创建 loadbalancer 的实现与分析
    OpenStack Rally 质量评估与自动化测试利器
    自建 CA 中心并签发 CA 证书
    Failed building wheel for netifaces
    通过 vSphere WS API 获取 vCenter Datastore Provisioned Space 置备空间
    OpenStack Placement Project
    我们建了一个 Golang 硬核技术交流群(内含视频福利)
    没有图形界面的软件有什么用?
  • 原文地址:https://www.cnblogs.com/dengpengbo/p/15146462.html
Copyright © 2011-2022 走看看