关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

JDK8新特性详解Lambda、StreamAPI、Optional等(一)

发布时间:2023-06-27 14:00:45
JDK8学习笔记 一、JDK8新特性 1. Lambda表达式 2. 接口的增强 3. 函数式接口 4. 方法引用 5. Stream API 6. Optional 7. 新时间日期API 二、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()); } 代码分析: Thread类需要一个Runnable接口作为参数,其中的抽象方法run是用来指定线程任务内容的核心 为了指定run方法体,不得不需要Runnable的的实现类 为了省区定义一个Runnable的实现类,不得不使用匿名内部类 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值都不得不重写一遍,而且不能出错。 而实际上,我们只在乎方法体中的代码 2. Lambda表达式初体验 Lambda表达式是一个匿名函数,可以理解为一段可以传递的代码 new Thread(()->{System.out.println("Lambda线程名字:"+ Thread.currentThread().getName());}).start(); Lambda表达式的有点:简化了匿名内部类的使用,语法更加简单。 匿名内部类语法冗余,体验了Lambda表达式和,发现Lambda表达式是简化匿名内部类的一种方式。 3. Lambda表达式语法规则 lambda省去了面向对象的条条框框,Lambda的标注格式由3部分组成 (String[] args) ->{ 代码体 } 格式说明: (参数类型、参数名称):参数列表 (代码体):方法体 ->:分割参数列表和方法体 3.1 无参无返回值的Lambda 定义一个接口 public interface UserService { public void show(); } 然后创建主方法使用 public static void main(String[] args) { goShow(new UserService() { @Override public void show() { System.out.println("show方法执行了:"+ Thread.currentThread().getName()); } }); goShow(() ->{ System.out.println("Lambda的show方法执行了:"+Thread.currentThread().getName()); }); } public static void goShow(UserService userService){ userService.show(); } 输出: 方法名为:main Lambda表达式方法名字:main 3.2 有参有返回值的Lambda 创建Person对象 @Data @NoArgsConstructor @AllArgsConstructor public class Person { private String name; private Integer age; private Integer height; } 在List集合中保存多个Person对象,然后根据这些对象做age排序操作 public static void main(String[] args) { List list = Arrays.asList( new Person("周杰伦",27,175), new Person("周星驰",32,157), new Person("周公瑾",182,188), new Person("周恩来",82,177) ); Collections.sort(list, new Comparator() { @Override public int compare(Person o1, Person o2) { return o1.getAge() - o2.getAge(); } }); for (Person p: list){ System.out.println( p .toString()); } System.out.println("----------------------------"); Collections.sort(list,(Person o1,Person o2)->{ return o2.getAge() - o1.getAge(); }); for (Person p: list){ System.out.println("lambda====" + p .toString()); } } 我们发现sort方法的第二个参数是一个Comparator接口的匿名内部类,且执行的方法有参数和返回值的表达式 输出结果 Person(name=周杰伦, age=27, height=175) Person(name=周星驰, age=32, height=157) Person(name=周恩来, age=82, height=177) Person(name=周公瑾, age=182, height=188) ---------------------------- lambda====Person(name=周公瑾, age=182, height=188) lambda====Person(name=周恩来, age=82, height=177) lambda====Person(name=周星驰, age=32, height=157) lambda====Person(name=周杰伦, age=27, height=175) 4. @FunctionalInterface注解 /** * @FunctionalInterface * 这是一个标志注解,被该注解修饰的接口只能声明一个抽象方法 */ @FunctionalInterface public interface UserService { public void show(); } 5. Lambda表达式的原理 匿名内部类会在编译的时候产生一个class文件 Lambda表达式在程序运行的 时候会形成一个类 在类中新增了一个方法,这个方法的方法体就是Lambda表达式中的代码 还会形成一个匿名内部类,实现接口,重写抽象方法 在接口中重写方法会调用新生成的方法 6. Lambda表达式省略写法 在lambda表达式的标准写法基础上,可以使用省略写法的规则为: 小括号内的参数类型可以省略 如果小括号内有且仅有一个参数,则小括号可以省略 如果大括号内有且仅有一个语句,则可以同时省略大括号,return关键字以及分号。 public static void main(String[] args) { goOrderShow((String name) ->{ System.out.println(name); return name+"666"; }); goStudyShow((String name ,Integer age) ->{ System.out.println(name + age); return name + age +"777"; }); System.out.println("Lambda简化写法"); goOrderShow(name -> name+"6666"); goStudyShow((name ,age) -> name + age + "7777"); } public static void goOrderShow(OrderService orderService){ orderService.show("张三"); } public static void goStudyShow(StudentService studentService){ studentService.show("李四",32); } 7. Lambda表达式的使用前提 Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,必须满足两个条件 方法的参数或者局部变量类型必须为接口才能使用Lambda 接口中有且仅有一个抽象方法(@FunctionalInterface) 8. Lambda和匿名内部类的对比 所需类型不一样 匿名内部类的类型可以是类、抽象类、接口 Lambda表达式需要的类型必须是接口 抽象方法的数量不一样 匿名内部类所需的接口中的抽象方法的数量是随意的 Lambda表达式所需的接口中只能有一个抽象方法 实现原理不一样 匿名内部类再编译后形成一个class Lambda表达式是在程序运行的时候动态生成class 三、 接口中新增的方法 1. 新增方法 在JDK8针对接口做了增强,在JDK8之前 interface 接口名{ 静态常量; 抽象方法; } JDK8之后 interface 接口名{ 静态常量; 抽象方法; 静态方法; 默认方法; } 2. 默认方法 2.1为什么增加默认方法 在JDK8以前接口中只有抽象方法和静态常量,会存在一下问题 如果接口中有新增抽象方法,那么实现类必须抽象这个抽象方法,非常不利与接口扩展 2.2 接口默认方法的格式 接口默认方法的语法格式是 interface 接口名{ 修饰符 default 返回值类型 方法名{ 方法体; } } 2.3 接口中默认方法的使用 接口中的默认方法有两种使用方式 实现类直接调用接口的默认方法 实现类重写接口的方法 3. 静态方法 JDK8中为接口新增了静态方法,作用也是为了接口的扩展 3.1 语法规则 interface 接口名{ 修饰符 static 返回值类型 方法名字{ 方法体; } } 3.2 静态方法的使用 public class Demo01Interface { public static void main(String[] args) { B b = new B(); System.out.println(b.test1()); System.out.println(b.test2()); System.out.println(A.test3()); } } interface A{ String test1(); public default String test2(){ return "接口新增了默认方法,可以被实现类重写,必须实例化调用"; } public static String test3(){ return "接口新增了静态方法,不能被实现类重写,类名.方法名调用"; } } class B implements A{ @Override public String test1() { return "接口抽象方法"; } } 接口中的静态方法在实现类中是不能被重写的。调用只能通过接口类型来实现:接口名.静态方法(); 4. 两者的区别 默认方法通过实例调用,静态方法通过接口名调用 默认方法可以被继承,实现类可以直接调用接口默认方法,也可以重写接口默认方法 静态方法不能被继承,实现类不能重写接口的静态方法,只能通过接口名调用 四、 函数式接口 1. 函数式接口的由来 我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名字、抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,JDK中提供了大量常用的函数式接口。 2. 函数式接口介绍 在JDK中帮我们提供的有函数式接口,主要是在Java.util.function包中 2.1 Supplier 无参有返回值的接口,对于Lambda表达式需要提供一个返回数据的类型。 @FunctionalInterface public interface Supplier { /** * Gets a result. * * @return a result */ T get(); } 使用 public static void main(String[] args) { fun1(()->{ int[] arr = {7,3,5,12,42,1}; // int max = 0; // for (int i : arr){ // if(i>max){ // max = i; // } // } // return max ; Arrays.sort(arr); return arr[arr.length -1]; }); } public static void fun1 (Supplier supplier){ //get方法是一个无参有返回值的抽象方法 Integer max = supplier.get(); System.out.println("Max ====="+ max); } 2.2 Consumer 有参数无返回值的接口,前面介绍的Supplier是接口用来生产数据的,而Consumer是用来消费数据的。使用的时候需要指定一个泛型来定义参数类型 @FunctionalInterface public interface Consumer { void accept(T var1); } 使用 public class ConsumerTest { public static void main(String[] args) { fun1(a-> a+=12); } public static void fun1 (Consumer consumer){ int a = 32; System.out.println(a); consumer.accept(a); System.out.println(a); } } 默认方法:andThen 如果一个方法的参数和返回值全部是Consumer类型,那么就可以实现效果,消费一个数据的时候,首先做一个操作,然后再做一个操作,实现组合,而这个方法就是Consumer接口中的default方法andThen方法 default Consumer andThen(Consumer var1) { Objects.requireNonNull(var1); return (var2) -> { this.accept(var2); var1.accept(var2); }; } 具体操作 public class ConsumerAndThenTest { public static void main(String[] args) { func(msg ->{ System.out.println("转换为小写》》》》》"+msg.toLowerCase(Locale.ROOT)); },msg2 ->{ System.out.println("转换为大写》》》》》"+msg2.toUpperCase(Locale.ROOT)); }); } public static void func (Consumer c1,Consumer c2){ // c1.accept("ZhangSan"); // c2.accept("ZhangSan"); // c1.andThen(c2).accept("ZhangSan"); c2.andThen(c1).accept("ZhangSan"); } } 2.3 Function 有参有返回值的接口,Function接口是根据一个类型的数据得到另一个类型的数据,前者称为前置i套件,后者成为后置条件,有参数有返回值 @FunctionalInterface public interface Function { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); } 使用:传入一个字符串返回一个数字 public class FunctionTest { public static void main(String[] args) { int a = func(msg -> Integer.parseInt(msg)); System.out.println(a); } public static Integer func(Function fun){ return fun.apply("322"); } } 默认方法:andThen,也是进行组合操作 public class FunctionAndThenTest { public static void main(String[] args) { Integer result = func(msg -> Integer.parseInt(msg),msg2-> msg2*10); System.out.println(result); } public static Integer func (Function f1,Function f2){ // int a = f1.apply("32"); // int b = f2.apply(a); // return b; return f1.andThen(f2).apply("54"); } } 默认的compose方法的作用顺序和andThen刚好相反 而静态方法identity则是,输入什么参数就返回什么参数

/template/Home/leiyu/PC/Static