Java8 lambda表达式

313 阅读3分钟

Java8引入了lambda表达式,很多人可能都会误以为lambda表达式只是匿名内部类的一种优雅表达式,两者并无区别,实际上 两者有本质的区别。lambda表达式不是匿名内部类的一种简洁表达方式,除了能让代码更优雅,还可以减少匿名内部类的创建,降低JVM内存占用和回收的开销。

匿名内部类

        final CountDownLatch countDownLatch = new CountDownLatch(3);
        final SecureRandom random = new SecureRandom();
        Future<String> stringFuture1 = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(random.nextInt(10) * 1000);
                countDownLatch.countDown();
                return "stringFuture1";
            }
        });
        Future<String> stringFuture2 = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(random.nextInt(10) * 1000);
                countDownLatch.countDown();
                return "stringFuture2";
            }
        });
        Future<String> stringFuture3 = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                Thread.sleep(random.nextInt(10) * 1000);
                countDownLatch.countDown();
                return "stringFuture3";
            }
        });
        countDownLatch.await();
        System.out.println("匿名内部类的异步任务 都执行完毕.");
        System.out.println(stringFuture1.get());
        System.out.println(stringFuture2.get());
        System.out.println(stringFuture3.get());
        executorService.shutdown();

lambda表达式

        final CountDownLatch countDownLatch = new CountDownLatch(3);
        final SecureRandom random = new SecureRandom();
        Future<String> stringFuture1 = executorService.submit(() -> {
            Thread.sleep(random.nextInt(10) * 1000);
            countDownLatch.countDown();
            return "stringFuture1";
        });
        Future<String> stringFuture2 = executorService.submit(() -> {
            Thread.sleep(random.nextInt(10) * 1000);
            countDownLatch.countDown();
            return "stringFuture2";
        });
        Future<String> stringFuture3 = executorService.submit(() -> {
            Thread.sleep(random.nextInt(10) * 1000);
            countDownLatch.countDown();
            return "stringFuture3";
        });
        countDownLatch.await();
        System.out.println("lambda表达式的异步任务都执行完毕.");
        System.out.println(stringFuture1.get());
        System.out.println(stringFuture2.get());
        System.out.println(stringFuture3.get());
        executorService.shutdown();

咋一看代码,lambda表达式的代码只是匿名内部类代码的一种简化,其实不是的,我们深入到代码编译后的字节码层面去看看。

匿名内部类的字节码

  private static anonymousNestClass()V throws java/lang/InterruptedException java/util/concurrent/ExecutionException 
   L0
    LINENUMBER 8 L0
    ICONST_3
    INVOKESTATIC java/util/concurrent/Executors.newFixedThreadPool (I)Ljava/util/concurrent/ExecutorService;
    ASTORE 0
   L1
    LINENUMBER 9 L1
    NEW java/util/concurrent/CountDownLatch
    DUP
    ICONST_3
    INVOKESPECIAL java/util/concurrent/CountDownLatch.<init> (I)V
    ASTORE 1
   L2
    LINENUMBER 10 L2
    NEW java/security/SecureRandom
    DUP
    INVOKESPECIAL java/security/SecureRandom.<init> ()V
    ASTORE 2
   L3
    LINENUMBER 11 L3
    ALOAD 0
    NEW com/netflix/mock/LambdaTest$1
    DUP
    ALOAD 2
    ALOAD 1
    INVOKESPECIAL com/netflix/mock/LambdaTest$1.<init> (Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)V
    INVOKEINTERFACE java/util/concurrent/ExecutorService.submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
    ASTORE 3
   L4
    LINENUMBER 19 L4
    ALOAD 0
    NEW com/netflix/mock/LambdaTest$2
    DUP
    ALOAD 2
    ALOAD 1
    INVOKESPECIAL com/netflix/mock/LambdaTest$2.<init> (Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)V
    INVOKEINTERFACE java/util/concurrent/ExecutorService.submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
    ASTORE 4
   L5
    LINENUMBER 27 L5
    ALOAD 0
    NEW com/netflix/mock/LambdaTest$3
    DUP
    ALOAD 2
    ALOAD 1
    INVOKESPECIAL com/netflix/mock/LambdaTest$3.<init> (Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)V
    INVOKEINTERFACE java/util/concurrent/ExecutorService.submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
    ASTORE 5
   L6
    LINENUMBER 35 L6
    ALOAD 1
    INVOKEVIRTUAL java/util/concurrent/CountDownLatch.await ()V
   L7
    LINENUMBER 36 L7
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "\u533f\u540d\u5185\u90e8\u7c7b\u7684\u5f02\u6b65\u4efb\u52a1 \u90fd\u6267\u884c\u5b8c\u6bd5."
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L8
    LINENUMBER 37 L8
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 3
    INVOKEINTERFACE java/util/concurrent/Future.get ()Ljava/lang/Object;
    CHECKCAST java/lang/String
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L9
    LINENUMBER 38 L9
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 4
    INVOKEINTERFACE java/util/concurrent/Future.get ()Ljava/lang/Object;
    CHECKCAST java/lang/String
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L10
    LINENUMBER 39 L10
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 5
    INVOKEINTERFACE java/util/concurrent/Future.get ()Ljava/lang/Object;
    CHECKCAST java/lang/String
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L11
    LINENUMBER 40 L11
    ALOAD 0
    INVOKEINTERFACE java/util/concurrent/ExecutorService.shutdown ()V
   L12
    LINENUMBER 41 L12
    RETURN
   L13
    LOCALVARIABLE executorService Ljava/util/concurrent/ExecutorService; L1 L13 0
    LOCALVARIABLE countDownLatch Ljava/util/concurrent/CountDownLatch; L2 L13 1
    LOCALVARIABLE random Ljava/security/SecureRandom; L3 L13 2
    LOCALVARIABLE stringFuture1 Ljava/util/concurrent/Future; L4 L13 3
    // signature Ljava/util/concurrent/Future<Ljava/lang/String;>;
    // declaration: java.util.concurrent.Future<java.lang.String>
    LOCALVARIABLE stringFuture2 Ljava/util/concurrent/Future; L5 L13 4
    // signature Ljava/util/concurrent/Future<Ljava/lang/String;>;
    // declaration: java.util.concurrent.Future<java.lang.String>
    LOCALVARIABLE stringFuture3 Ljava/util/concurrent/Future; L6 L13 5
    // signature Ljava/util/concurrent/Future<Ljava/lang/String;>;
    // declaration: java.util.concurrent.Future<java.lang.String>
    MAXSTACK = 5
    MAXLOCALS = 6

lambda表达式的字节码

  private static lambda()V throws java/lang/InterruptedException java/util/concurrent/ExecutionException 
   L0
    LINENUMBER 44 L0
    ICONST_3
    INVOKESTATIC java/util/concurrent/Executors.newFixedThreadPool (I)Ljava/util/concurrent/ExecutorService;
    ASTORE 0
   L1
    LINENUMBER 45 L1
    NEW java/util/concurrent/CountDownLatch
    DUP
    ICONST_3
    INVOKESPECIAL java/util/concurrent/CountDownLatch.<init> (I)V
    ASTORE 1
   L2
    LINENUMBER 46 L2
    NEW java/security/SecureRandom
    DUP
    INVOKESPECIAL java/security/SecureRandom.<init> ()V
    ASTORE 2
   L3
    LINENUMBER 47 L3
    ALOAD 0
    ALOAD 2
    ALOAD 1
    INVOKEDYNAMIC call(Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)Ljava/util/concurrent/Callable; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()Ljava/lang/Object;, 
      // handle kind 0x6 : INVOKESTATIC
      com/netflix/mock/LambdaTest.lambda$lambda$0(Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)Ljava/lang/String;, 
      ()Ljava/lang/String;
    ]
    INVOKEINTERFACE java/util/concurrent/ExecutorService.submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
    ASTORE 3
   L4
    LINENUMBER 52 L4
    ALOAD 0
    ALOAD 2
    ALOAD 1
    INVOKEDYNAMIC call(Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)Ljava/util/concurrent/Callable; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()Ljava/lang/Object;, 
      // handle kind 0x6 : INVOKESTATIC
      com/netflix/mock/LambdaTest.lambda$lambda$1(Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)Ljava/lang/String;, 
      ()Ljava/lang/String;
    ]
    INVOKEINTERFACE java/util/concurrent/ExecutorService.submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
    ASTORE 4
   L5
    LINENUMBER 57 L5
    ALOAD 0
    ALOAD 2
    ALOAD 1
    INVOKEDYNAMIC call(Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)Ljava/util/concurrent/Callable; [
      // handle kind 0x6 : INVOKESTATIC
      java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      // arguments:
      ()Ljava/lang/Object;, 
      // handle kind 0x6 : INVOKESTATIC
      com/netflix/mock/LambdaTest.lambda$lambda$2(Ljava/security/SecureRandom;Ljava/util/concurrent/CountDownLatch;)Ljava/lang/String;, 
      ()Ljava/lang/String;
    ]
    INVOKEINTERFACE java/util/concurrent/ExecutorService.submit (Ljava/util/concurrent/Callable;)Ljava/util/concurrent/Future;
    ASTORE 5
   L6
    LINENUMBER 62 L6
    ALOAD 1
    INVOKEVIRTUAL java/util/concurrent/CountDownLatch.await ()V
   L7
    LINENUMBER 63 L7
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "lambda\u8868\u8fbe\u5f0f\u7684\u5f02\u6b65\u4efb\u52a1\u90fd\u6267\u884c\u5b8c\u6bd5."
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L8
    LINENUMBER 64 L8
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 3
    INVOKEINTERFACE java/util/concurrent/Future.get ()Ljava/lang/Object;
    CHECKCAST java/lang/String
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L9
    LINENUMBER 65 L9
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 4
    INVOKEINTERFACE java/util/concurrent/Future.get ()Ljava/lang/Object;
    CHECKCAST java/lang/String
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L10
    LINENUMBER 66 L10
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 5
    INVOKEINTERFACE java/util/concurrent/Future.get ()Ljava/lang/Object;
    CHECKCAST java/lang/String
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L11
    LINENUMBER 67 L11
    ALOAD 0
    INVOKEINTERFACE java/util/concurrent/ExecutorService.shutdown ()V
   L12
    LINENUMBER 68 L12
    RETURN
   L13
    LOCALVARIABLE executorService Ljava/util/concurrent/ExecutorService; L1 L13 0
    LOCALVARIABLE countDownLatch Ljava/util/concurrent/CountDownLatch; L2 L13 1
    LOCALVARIABLE random Ljava/security/SecureRandom; L3 L13 2
    LOCALVARIABLE stringFuture1 Ljava/util/concurrent/Future; L4 L13 3
    // signature Ljava/util/concurrent/Future<Ljava/lang/String;>;
    // declaration: java.util.concurrent.Future<java.lang.String>
    LOCALVARIABLE stringFuture2 Ljava/util/concurrent/Future; L5 L13 4
    // signature Ljava/util/concurrent/Future<Ljava/lang/String;>;
    // declaration: java.util.concurrent.Future<java.lang.String>
    LOCALVARIABLE stringFuture3 Ljava/util/concurrent/Future; L6 L13 5
    // signature Ljava/util/concurrent/Future<Ljava/lang/String;>;
    // declaration: java.util.concurrent.Future<java.lang.String>
    MAXSTACK = 3
    MAXLOCALS = 6

lambda表达式的字节码 很明显多出来了一行关键代码: java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)

LambdaMetafactory的最权威解释: docs.oracle.com/javase/8/do…