Java8新特性(又称为 jdk1.8)是Java语言开发的一个主要版本。Oracle公司于2014年3月18日发布Java8,它支持函数式编程,新的JavaScript引擎,新的日期API,新的Stream API等。——摘自菜鸟教程
Java8 之前
interface之中可以定义变量和方法,变量必须是 public、static、final 修饰的,方法必须是 public、abstract 修饰的。( 其中 public 类型和抽象方法需要子类实现。
)
/** * @author QHJ * @date 2021/11/17 11:39 * @description: Java8 接口 */ public interface JDK8Interface { // 定义变量 public String userName = "zhangsan"; static String password= "123456"; final int age = 0; // 定义方法 void get(); } /** * @author QHJ * @date 2021/11/17 11:42 * @description: Java8 实现类 */ public class JDK8InterfaceImpl implements JDK8Interface { @Override public void get() { System.out.println("这是public抽象方法的实现类..."); } }
Java8 开始
interface中的方法支持使用 static 和 default 修饰,可以写方法体,但不需要子类重写。( public类型和抽象方法仍然需要子类实现。
)
/** * @author QHJ * @date 2021/11/17 11:39 * @description: Java8 接口 */ public interface JDK8Interface { // 定义变量 public String userName = "zhangsan"; static String password= "123456"; final int age = 0; // 定义方法 void get(); /** * 默认方法,可以写方法体 */ default void getDefault(){ System.out.println("这是默认方法,可以写方法体..."); } /** * 静态方法,可以写方法体 */ static void getStatic(){ System.out.println("这是静态方法,可以写方法体..."); } } /** * @author QHJ * @date 2021/11/17 11:42 * @description: Java8 实现类 */ public class JDK8InterfaceImpl implements JDK8Interface { @Override public void get() { System.out.println("这是重写的public方法..."); } /** * 默认和静态方法不需要重写· */ /*@Override public void getDefault() { System.out.println("这是重写的默认方法..."); }*/ }
小贴士:
Java8 前
接口定义的方法:使用public修饰的方法和抽象方法需要子类实现;
接口定义的变量:使用public、static、final修饰。
Java8 开始
接口定义的方法:static、default修饰的为普通方法,可以有方法体,不需要子类重写;
public、抽象方法没有方法体,需要子类重写;
接口定义的变量:使用public、static、final修饰。
Lambda 是一个 匿名函数 ,我们可以把 Lambda 表达式理解为是 一段可以传递的代码 (将代码像数据一样进行传递),从而可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使 Java 的语言表达能力得到了提升。
Lambda 表达式,也可称为闭包,它是推动 Java8 发布的最重要的新特性。
Lambda 可以以非常简洁的形式调用我们的匿名函数接口。
有这样一个场景:
在 java 中我们很容易对一个变量进行赋值,比如:int a = 1;String b = “zs”;
但是如何将一段代码或者一个函数赋值给一个变量呢?这个变量应该是什么类型的?
Java8 中的新特性很容易就解决了这个问题。
/** * Test--OrderService */ System.out.println("======OrderService======"); // 1、使用new的实现类的形式调用接口 OrderService orderService = new OrderServiceImpl(); orderService.addOrder(); // 2、使用匿名内部类接口调用 new OrderService(){ @Override public void addOrder() { System.out.println("使用匿名内部类的形式调用接口..."); } }.addOrder(); // 3、使用lambda调用接口 OrderService orderService1 = () -> System.out.println("使用lambda调用OrderService接口..."); orderService1.addOrder();
package cn.qhj.lambda; import java.util.Comparator; import java.util.TreeSet; /** * @author QHJ * @date 2021/11/21 11:36 * @description: */ public class ToLambda { public static void main(String[] args) { // 匿名内部类 Runnable runnable1 = new Runnable() { @Override public void run() { System.out.println("Hello!这是匿名内部类..."); } }; // Lambda表达式 Runnable runnable2 = () -> System.out.println("Hello!这是Lambda表达式..."); // 使用匿名内部类作为参数传递 TreeSettreeSet1 = new TreeSet<>(new Comparator () { @Override public int compare(String o1, String o2) { return Integer.compare(o1.length(), o2.length()); } }); // 使用 Lmabda 表达式作为参数传递 TreeSet treeSet2 = new TreeSet<>( ((o1, o2) -> Integer.compare(o1.length(), o2.length())) ); } }
Lambda 表达式在 Java 语言中引入了一个新的语法元素和操作符。这个操作符为 "->"
,该操作符被称为 Lambda 操作符或箭头操作符
。它将 Lmabda 分为两个部分:
左侧:指定了 Lambda 表达式需要的所有参数;
右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
根据参数传递情况和是否有返回值,Lambda 表达式有五种语法格式:
下述例子中的相关接口:
/** * @author QHJ * @date 2021/11/21 15:44 * @description: */ @FunctionalInterface public interface LambdaInterface { void get(); void get(int i); void get(int i, int j); String get(int i); String get(int i, int j); }
() -> System.out.println("....")
/** * Test--LamdaInterface */ // 1、使用lambda调用无参方法 System.out.println("======LamdaInterface======"); LambdaInterface lambdaInterface = () -> { System.out.println("使用lambda表达式调用无参方法..."); }; lambdaInterface.get(); // 2、精简写法:如果方法体中只有一条语句的情况下,可以不需要() LambdaInterface lambdaInterface1 = () -> System.out.println("使用lambda调用无参方法的精简写法..."); lambdaInterface1.get();
(args) -> System.out.println(args)
/** * Test--LamdaInterface(一个参数) */ System.out.println("======LambdaInterface======"); // 1、使用匿名内部类调用有参方法 String result = new LambdaInterface(){ @Override public void get(int i) { System.out.println(i); } }.get(1); System.out.println("使用匿名内部类调用:" + result); // 2、使用lambda调用有参方法 LambdaInterface lambdaInterface1 = (i) -> { System.out.println(i); }; System.out.println("使用lambda表达式调用有参方法:" + lambdaInterface1.get(1));
args -> System.out.println(args)
/** * Test--LamdaInterface(传递一个参数时可省略()) */ System.out.println("======LambdaInterface======"); // 2、使用lambda调用有参构造函数(一个参数时,可省略小括号) LambdaInterface lambdaInterface = i -> { System.out.println(i); }; System.out.println("使用lambda表达式调用有参方法:" + lambdaInterface.get(1));
(i, j) -> { return i + "--" + j; }
/** * Test--LamdaInterface */ System.out.println("======LambdaInterface======"); // 1、使用匿名内部类调用有参方法 String result = new LambdaInterface(){ @Override public String get(int i, int j) { return i + "--" + j; } }.get(1, 2); System.out.println("使用匿名内部类调用:" + result); // 2、使用lambda调用有参构造函数 LambdaInterface lambdaInterface = (i, j) -> { return i + "--" + j; }; System.out.println("使用lambda表达式调用有参方法:" + lambdaInterface.get(1, 2));
(i, j) -> return i + "--" + j;
// 3、精简写法:如果方法体中只有一条return的情况下不需要写{}和return LambdaInterface lambdaInterface = (i, j) -> i + "--" + j; System.out.println("使用lambda调用有参方法:" + lambdaInterface.get(1, 2));
类型推断:
上述 Lambda 表达式中的参数类型都是由编译器推断得出的。 Lambda 表达式中无需指定类型,程序依然可以编 译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断”。
只包含一个抽象方法的接口,称为函数式接口
。/** * @author QHJ * @date 2021/11/7 15:01 * @description: 普通的函数式接口 */ // 使用Lambda表达式,依赖于函数式接口。当接口中只有一个抽象方法时,默认此接口就是函数式接口 @FunctionalInterface // 当有@FunctionalInterface注解时,表示标记该接口为函数接口 public interface MyFunctionalInterface { void add(); /** * 使用default修饰为普通方法 * 注意:在接口中只能有一个抽象方法,通过使用default关键字可将其修饰为普通方法 */ default void get(){ System.out.println("这是默认的方法..."); } /** * 可以定义object类中的方法 * object父类中的方法可以在函数接口中重写 * @return */ String toString(); }
/** * Test--MyFunctionalInterface */ System.out.println("======MyFunctionalInterface======"); // 1、使用匿名内部类的方式调用接口 new MyFunctionalInterface(){ @Override public void add() { System.out.println("这是使用匿名内部类时MyFunctionalInterface重写的add()方法..."); } }.add(); // 2、使用lambda调用接口 MyFunctionalInterface myFunctionalInterface = () -> System.out.println("使用lambda调用MyFunctionalInterface接口..."); myFunctionalInterface.add();
/** * @author QHJ * @date 2021/11/21 16:24 * @description: 函数式接口中使用泛型 */ @FunctionalInterface public interface MyFunction{ public T getValue(T t); }
public static String toUpperString(MyFunctionmyFunction, String str){ return myFunction.getValue(str); } /** * 作为参数传递 Lambda 表达式 * 为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 * Lambda 表达式兼容的函数式接口的类型。 */ System.out.println("======作为参数传递 Lambda 表达式======"); String newStr = toUpperString(s -> s.toUpperCase(), "abCDfse"); System.out.println(newStr);
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer< T > 消费型接口 | T | void | 对类型为T的对象应用操作。包含方法:void accept(T t); |
Supplier< T > 供给型接口 | 无 | T | 返回类型为T的对象。包含方法:T get(); |
Function |
T | R | 对类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t); |
Predicate< T > 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean 值。包含方法boolean test(T t); |
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
BiFunction |
T, U | R | 对类型为 T, U 参数应用操作, 返回 R 类型的结 果。包含方法为R apply(T t, U u); |
UnaryOperator< T > (Function子接口) | T | T | 对类型为T的对象进行一元运算, 并返回T类型的结果。包含方法为T apply(T t); |
BinaryOperator< T >(BiFunction 子接口) | T, T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1, T t2); |
BiConsumer |
T, U | void | 对类型为T, U 参数应用操作。包含方法为void accept(T t, U u) |
ToIntFunction< T >、ToLongFunction< T >,、ToDoubleFunction< T > | T | int,、long,、double | 分 别 计 算 int 、 long 、double、值的函数 |
IntFunction< R >、LongFunction< R > DoubleFunction< R > | int、long、double | R | 参数分别为int、long、double 类型的函数 |
使用 Lambda 表达式,就依赖于函数式接口:
1、在接口中只能够允许有一个抽象方法;
2、在函数式接口中定义 object 类中的方法;
3、使用默认或者静态方法;
4、@FunctionalInterface 表示该接口为函数式接口。
Java 中使用 Lambda 表达式的规范,必须是为函数式接口!
JDK 中自带的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
java.nio.file.PathMatcher
java.lang.reflect.InvocationHandler
java.beans.PropertyChangeListener
java.awt.event.ActionListener
javax.swing.event.ChangeListener
出了这些自带的函数式接口,我们也可以使用 @FunctionalInteface 修饰为函数式接口!
结合 Lambda 表达式能够让代码变的更加精简。
当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用!
方法引用规则: 实现抽象方法的参数列表和返回类型,必须与方法引用方法的参数列表和返回类型保持一致!
方法引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。
方法引用主要有三种使用情况:
静态方法引用
类 :: 静态方法
public class Test{ /** * 静态方法引用 */ public static void getStaticMethod(){ System.out.println("这是静态方法引用..."); } public static void main(String[] args){ // 1、使用lambda的方式调用get()方法 JDK8Interface jdk8Interface1 = () -> { JDK8Interface.getStatic(); }; jdk8Interface1.get(); // 2、使用方法引用调用方法 // 必须满足:方法引用的方法必须和函数接口中的方法参数列表/返回值保持一致(也就是get()方法和getStaticMethod()方法要一致) JDK8Interface jdk8Interface2 = Test::getStaticMethod; jdk8Interface2.get(); } }
对象方法引用
类 :: 实例方法
public class Test{ /** * 对象方法引用 * @return */ public String objGet(UserEntity userEntity) { return "qhj"; } public static void main(String[] args){ /** * Test--对象方法引用 */ System.out.println("======对象方法引用======"); // 1、使用匿名内部类的形式 ObjectService objectService = new ObjectService() { @Override public String get(UserEntity userEntity) { return userEntity.getName() + ":" + userEntity.getAge(); } }; System.out.println(objectService.get(new UserEntity("qhj", 23))); // 2、使用lambda的形式 ObjectService objectService1 = (userEntity -> userEntity.getName() + ":" + userEntity.getAge()); System.out.println(objectService1.get(new UserEntity("qhj", 22))); // 3、对象方法引用 这里的Test::objGet--->test.objGet() ObjectService objectService2 = Test::objGet; System.out.println(objectService2.get(new UserEntity("qhj", 22))); } }
实例方法引用
对象 :: 实例方法
public class Test{ /** * 实例方法引用 * @param a */ public void get(Integer a) { System.out.println("实例方法引用get方法:" + a); } public static void main(String[] args){ /** * Test--实例方法引用 */ System.out.println("======实例方法引用======"); Test test = new Test(); // 1、匿名内部类的方式 MessageInterface messageInterface = new MessageInterface() { @Override public void get(Integer a) { test.get(a); } }; messageInterface.get(1); // 2、lambda的方式 MessageInterface messageInterface1 = (a) -> { test.get(a); }; messageInterface1.get(2); // 3、实例方法引用的方式 MessageInterface messageInterface2 = test::get; messageInterface2.get(3); } }
className :: new
与函数式接口相结合,自动与函数式接口中的方法兼容。可以把构造器引用赋值给定义的方法,构造器参数列表和返回类型要与接口中抽象方法的参数列表和返回类型完全一致!
/** * Test--构造器引用 * 遵循规范:函数接口返回类型::new */ System.out.println("======构造器引用====="); // 1、lambda方式 UserInterface userInterface = () -> new UserEntity("qhj", 22); System.out.println(userInterface.getUser()); // 2、构造函数引入 UserInterface userInterface = UserEntity::new; System.out.println(userInterface.getUser());
type[] :: new
/** * 数组引用 */ System.out.println("======数组引用======"); // 1、匿名内部类的方式 Functionfunction1 = new Function () { @Override public String[] apply(Integer t) { return new String[t]; } }; String[] array1 = function1.apply(5); System.out.println(Arrays.toString(array1)); // 2、Lambda 表达式的方式 Function function2 = length -> new String[length]; String[] array2 = function2.apply(5); System.out.println(Arrays.toString(array2)); // 3、数组引用的方式 Function function3 = String[] :: new; String[] array3 = function3.apply(5); System.out.println(Arrays.toString(array3));