JDK8以上提高开发效率有哪些

JDK8新特性

一,接口的默认方法和静态方法

​ 在jdk8之前,在接口中定义的普通方法都是抽象方法,方法前面默认都会添加

public abstract,不能有方法实现,必须在接口的实现类中对方法进行具体实现。

下列代码展示非jdk8新特性写法

public interface MyJdk {
    //jdk8之前写法
    String jdk8 ();
}
public class MyjdkImpl implements MyJdk {
    @Override
    public String jdk8() {
        return "jdk8之前写法!";
    }
}

public class HelloWord {
    public static void main(String[] args) {
        // 向日葵
        MyjdkImpl myjdk = new MyjdkImpl();
        System.out.println(myjdk.jdk8());
    }
}

但是JAVA8开始允许在接口中定义默认方法静态方法,对于这两个方法,可以直接在接口对其进行实现,无需再在接口实现类中进行实现。

默认方法:扩展方法,在方法前面需通过default修饰,不能直接通过接口调用,必须通过接口实现类的实例对象进行方法调用。

接口实现类对象.默认方法()

静态方法:类方法,在方法前面需通过static修饰,可以直接通过接口调用。

接口.静态方法()

代码实现:

​ 1,抽象接口

public interface MyJdk {
    //jdk8之前写法
    String jdk8 ();

    //jdk8 新特性 默认方法
    default String defaultMethod (){
        return "jdk8新特性之默认方法!";
    }

    //jdk8 新特性 静态方法
    static String staticMethod(){
        return "jdk8新特性 静态方法!";
    }
}

-- 接口实现类
public class MyjdkImpl implements MyJdk {
    @Override
    public String jdk8() {
        return "jdk8之前写法!";
    }
}

方法调用
public class HelloWord {
    public static void main(String[] args) {
        // 向日葵
        MyjdkImpl myjdk = new MyjdkImpl();
        System.out.println(myjdk.jdk8());
        System.out.println(myjdk.defaultMethod());
        System.out.println(MyJdk.staticMethod());

        MyJdk myJdk1 = new MyJdk(){
            @Override
            public String jdk8() {
                return null;
            }

            @Override
            public String defaultMethod() {
                return null;
            }
        };
    }
}

普通方法必须实现,默认方法可以选择性重写,静态方法无法重写。

面试题:在Java 接口中,接口中的方法是否支持方法体?jdk8 后支持方法体

二,Lambda 表达式

Lambda表达式是Java8中非常重要的一个新特性,其基于函数式编程的思想,支持将代码作为方法参数进行使 用。可以把Lambda表达式理解为通过一种更加简洁的方式表示可传递的匿名函数。

它本身没有名称,而且不像方法那样属于某一个类,但是可以有参数列表、代码体、返回值。使用了Lambda表达 式之后就不需要再去编写匿名类了。

1,Lambda基础格式
(参数列表)->{

	方法体

}

参数列表:即匿名方法的形参

2,Lambda运算符

方法体:用于执行业务逻辑。可以是单一语句,也可以是语句块。如果是单一语句,可以省略花括号。当需要返回 值,如果方法体中只有一条语句,可以省略return,会自动根据结果进行返回。

1)没有参数的Lambda表达式

()>new Student();

2)只有一个参数的Lambda表达式

x ‐> { 
    System.out.println(x); 
    return x; 
}

3)有多个参数的Lambda表达式

(int x,int y)>{
System.out.println(x);
System.out.println(x);
return x+y;
}

上述可以进行简写,因为在Lambda中,参数列表中参数的数据类型可以交给JVM根据上下文进行推断。所以可以 不用定义类型。

(x,y) ‐>{ 
	System.out.println(x); 
	System.out.println(y); 
	return x+y; 
}

4)一个参数和仅一条语句的Lambda表达式

x ‐> 3+x;

5)多个参数和仅一条语句的Lambda表达式

(x,y)> x+y;
举例:

1,可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
2,可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
3,可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
4,可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

public class Java8Tester {
   public static void main(String args[]){
      Java8Tester tester = new Java8Tester();
        
      // 类型声明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用类型声明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括号中的返回语句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 没有大括号及返回语句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
        
      // 不用括号
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);
        
      // 用括号
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
    
   interface MathOperation {
      int operation(int a, int b);
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
      return mathOperation.operation(a, b);
   }
}

我们也可以直接在 lambda 表达式中访问外层的局部变量:

public class Java8Tester {
    public static void main(String args[]) {
        final int num = 1;
        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
        s.convert(2);  // 输出结果为 3
    }
 
    public interface Converter<T1, T2> {
        void convert(int i);
    }
}

lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//报错信息:Local variable num defined in an enclosing scope must be final or effectively 
 final

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //编译会出错

2,Lambda 使用对比

遍历集合

public static void main(String[] args) {
String[] language = {"c", "c++",
					 "c#",
					 "java","python",
					 "go","hive",
					 "php"};
List<String> languageList = Arrays.asList(language);

//旧的循环方式
for (String s : languageList) {
	System.out.println(s+",");
}
    
//lambda循环
languageList.forEach(s‐> System.out.println(s+","));
}

使用Lambda 替换匿名内部类的使用

//匿名内部类
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello world !");
    }
};

//使用Lambda
Runnable runnable1 = ()> System.out.println("hello itcast");

实现Lambda实现集合排序

public static void main(String[] args) {

String[] language = {"c", "c++",
					 "c#",
                     "java","python",
                     "go","hive",
                     "php"};
//旧的循环比较方式
Arrays.sort(language,new Comparator<String>(){

    @Override
    public int compare(String o1, String o2) {
    	return (o1.compareTo(o2));
    }
});

//lambda循环比较
Arrays.sort(language,(o1,o2)‐>(o1.compareTo(o2)));
3, Lambda 表达式底层原理解析

写一个使用Lambda表达式的方法

public class SourceDemo {
public void demo(){
String[] language = {"c", "c++",
                     "c#",
                     "java","python",
                     "go","hive",
                     "php"};
    
List<String> list = Arrays.asList(language);
list.forEach(s‐> System.out.println(s));
	}
}

将当前.Java文件使用dos命令编译生成.class 文件,执行命令后,会在当前文件夹生成对应的.class文件

javac SourceDemo.java

将.class文件执行DOS命令进行反编译,查看文件内容

javap -p SourceDemo.java

生成内容如下:

Compiled from "SourceDemo.java"
public class com.itheima.lambda.source.SourceDemo {
public com.itheima.lambda.source.SourceDemo();
public void demo();
private static void lambda$demo$0(java.lang.String);
}

此时可以发现,代码中执行Lambda表达式的部分生成了一个静态私有函数。这个静态私有函数的函数干就是 Lambda表达式里面的内容。 那么对于这个静态私有函数,在JDK8内部是如何实现调用的呢?可以查看 LambdaMetafactory 类,该类下有一个 metafactory方法,lambda表达式每一次在执行的时候都会进入到这个方法中,并且为lambda表达式创建一个内 部类。

public static CallSite metafactory(MethodHandles.Lookup caller,
                                   String invokedName,
                                   MethodType invokedType,
                                   MethodType samMethodType,
                                   MethodHandle implMethod,
                                   MethodType instantiatedMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
mf = new InnerClassLambdaMetafactory(caller, invokedType,
                                    invokedName, samMethodType,
                                    implMethod, instantiatedMethodType,
                                    false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
mf.validateMetafactoryArgs();
return mf.buildCallSite();
}

如果想查看自己写的类里面的Lambda内容,可以在Lambda 表达式执行之前,添加

System.setProperty("jdk.internal.lambda.dumpProxyClasses", "D://");

这个方法会将运行时生成的内部类class文件进行输出。

这个方法是配置jdk 系统环境变量,

Lambda 在运行时会生成一个内部类,为了验证是否生成内部类,可以在运行时加上上面的代码,加上后,将会生成的内部类.class 输出到一个文件中.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-96JC4F25-1612259767249)(E:\JAVA学习笔记\JAVA 架构师课程\2,互联网架构及性能优化篇\第05章 JDK8以上提高开发效率有哪些3.0\JDK8以上提高开发效率有哪些\imgs\ss.png)]

当该文件生成后,可以通过 javap -c -p class文件名 查看文件中的内容

此时可以发现编译后的Lambda表达式已经被执行。

综上所述,Lambda表达式在执行的时候,会调用LambdaMetafactory.metafactory动态的生成内部类,在方法 内调用 SourceDemo$&Lambda$1 ,内部类里的调用方法块并不是动态生成的,只是在原class里已经编译生成了一个 静态的方法,内部类只需要调用该静态方法。

扩展:javac 命令是Java编译,javap是对javac编译的文件进行反编译

C:\>javap -help
用法: javap <options> <classes>
其中, 可能的选项包括:
 -help --help -?    输出此用法消息
 -version         版本信息
 -v -verbose       输出附加信息
 -l            输出行号和本地变量表
 -public         仅显示公共类和成员
 -protected        显示受保护的/公共类和成员
 -package         显示程序包/受保护的/公共类
              和成员 (默认)
 -p -private       显示所有类和成员
 -c            对代码进行反汇编
 -s            输出内部类型签名
 -sysinfo         显示正在处理的类的
              系统信息 (路径, 大小, 日期, MD5 散列)
 -constants        显示静态最终常量
 -classpath <path>    指定查找用户类文件的位置
 -bootclasspath <path>  覆盖引导类文件的位置

输出此用法消息
-version 版本信息
-v -verbose 输出附加信息
-l 输出行号和本地变量表
-public 仅显示公共类和成员
-protected 显示受保护的/公共类和成员
-package 显示程序包/受保护的/公共类
和成员 (默认)
-p -private 显示所有类和成员
-c 对代码进行反汇编
-s 输出内部类型签名
-sysinfo 显示正在处理的类的
系统信息 (路径, 大小, 日期, MD5 散列)
-constants 显示静态最终常量
-classpath 指定查找用户类文件的位置
-bootclasspath 覆盖引导类文件的位置


java -p 和Java -private 两个命令是一个效果.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

vegetari

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

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

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

打赏作者

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

抵扣说明:

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

余额充值