java11的新特性
java 11的新特性主要体现在:
函数式接口、Lambda表达式、方法引用/构造器引用、StreamAPI、接口的增强
1、Lambda表达式
Lambda表达式是一个匿名函数,我们可以把lambda表达式理解为是一段可以传递的代码。使用lambda表达式 可以写出更简洁 更灵活的代码。
1.1 Lambda 表达式的语法
(s1,s2) -> s1.compareTo(s2)
语法中:
-> 称为lambda操作符或箭头操作符
左边 指定了lambda表达式的参数列表
右边 指定lambda体。是抽象方法的实现逻辑 。也是lambda表达式的实现的功能
语法一: 无参无返回值
Runnable r2 = ()-> System.out.println("hello world-----");
语法二:有一个参数 但是没有返回值
@Test
public void lambdaTest3(){
Consumer<String> c = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
// lambda表达式的写法 由泛型推断出参数的类型--称为类型推断
Consumer<String> c1 = s->System.out.println(s);
c.accept("aaaaaaaa");
}
语法三:有多个参数 多条执行语句 并且可以有返回值
@Test
public void lambdaTest4(){
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("比较两个整数的大小");
int res = o1.compareTo(o2);
return res;
}
};
//lambda表达式 当有多条语句时 大括号不能省略
Comparator<Integer> comp = (o1, o2)->{
System.out.println("比较两个整数的大小");
int res = o1.compareTo(o2);
return res;
};
}
语法四:有多个参数 只有一条返回语句
//lambda表达式 有多个参数,只有一条返回语句 此时大括号可以省略 return 也可以省略
Comparator<Integer> comp = (o1, o2)-> o1.compareTo(o2);
System.out.println(comp.compare(10,20));
lambda表达式和匿名内部类的区别
所需的类型不同 :
匿名内部类中。可以是接口 也可以是抽象类 还可以是具体的类
lambda表达式 只能是接口
使用的限制不同:
如果接口中仅有一个抽象方法 可以使用lambda表达式 也可以是用匿名内部类
如果接口中有多个抽象方法 只能使用匿名内部类 不能使用lambda表达式
实现的原理不同:
匿名内部类编译之后 产生一个单独的字节码文件
lambda表达式 编译之后 没有单独的字节码class文件 对应的字节码在运行时产生。
2、函数式接口
2.1 什么是函数式接口
只包含一个抽象方法的接口 称为函数式接口
@FunctionalInterface 可以用来检测接口是否是函数式接口。
java.util .function包下定义了大量的函数式接口
因为在java中,lambda表达式是一个对象,而不是函数,他们必须依附于一类特别的对象类型—函数式接口
在java中,lambda表达式就是一个函数式接口的实现
2.2 自定义函数式接口
@FunctionalInterface
public interface InterFunction {
int getValue();//函数式接口只能有一个抽象方法
}
函数式接口中使用泛型
@FunctionalInterface
public interface InterFunction<T> {
T getValue(T t);
}
@Test
public void lambdaTest5(){
InterFunction<String> fun = s-> s.toUpperCase();
System.out.println(fun.getValue("abc"));
}
2.3 作为参数传递Lambda表达式
public String toUpperCaseString(InterFunction<String> fun,String str){
return fun.getValue(str);
}
@Test
public void lambdaTest6(){
String newString = toUpperCaseString(s-> s.toUpperCase(),"abcdef");
System.out.println(newString);
}
2.4 JAVA 内置的四大核心函数式接口
Interface Consumer<T>
void accept(T t) 对类型T的对象应用操作。
Interface Supplier<T>
T get() 返回类型为T的对象
Interface Function<T,R>
R apply(T t) 将类型为T的对象进行相应的操作处理 之后返回一个R类型的对象
Interface Predicate<T>
boolean test(T t) 确定类型为T的对象是否满足要求
3、接口的组成更新
3.1 ## 7.1 接口组成更新
常量 public static final
抽象方法 public abstract
jdk8引入
默认方法 public default
静态方法
私有方法
3.2 默认方法
格式:
public default 返回值类型 方法名(){
}
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
注意事项:
默认方法不能是抽象方法 所以他不强制重写,但是也可以重写 重写的时候需要去掉default修饰符
public可以省略 但是default不能省略
3.3 静态方法
public static 返回值类型 方法名(){
}
注意事项:
静态方法只能通过接口名调用, 不能通过实现类或者对象名调用
public可以省略 static 不能省略
3.4 私有方法
private 返回值类型 方法名(){
}
注意事项:
默认方法可以调用私有的静态方法和非静态方法
静态方法只能调用私有的静态方法
4、方法引用
4.1 :: 方法引用符
改符号引用运算符 而他所在的表达式称为方法引用
4.2 引用类方法
类名 :: 静态方法名
static int parseInt(String s) 将字符串参数解析为带符号的十进制整数。
@FunctionalInterface
public interface Converter {
int converter(String s);
}
public class ConverterDemo {
public static void main(String[] args) {
// lambda
userConverter(s->Integer.parseInt(s),"123");
//方法引用
userConverter(Integer :: parseInt,"456");
}
public static void userConverter(Converter c,String s){
int i = c.converter(s);
System.out.println(i);
}
}
4.3 对象的实例方法的引用
对象::成员方法
@FunctionalInterface
public interface Printer {
void printUpCase(String str);
}
public class PrintString {
public void printUpper(String s){
String res = s.toUpperCase();
System.out.println(res);
}
}
public class PrinterDemo {
public static void main(String[] args) {
userPrinter(str -> System.out.println(str.toUpperCase()),"hello");
PrintString ps = new PrintString();
userPrinter(ps::printUpper,"world");
}
public static void userPrinter(Printer p ,String s){
p.printUpCase(s);
}
}
4.4 构造引用
格式:
类名 :: new
@FunctionalInterface
public interface StudentInterface {
Student build(String name,int age);
}
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class StudentDemo {
public static void main(String[] args) {
//lambda表达式
userStudent((name,age)->new Student(name,age));
//构造引用
userStudent(Student::new);
}
public static void userStudent(StudentInterface si){
Student stu = si.build("张三",21);
System.out.println(stu.getName() +"---"+stu.getAge());
}
}
5、StreamAPI
5.1 Stream 流
java. util.stream. Stream是Java.8新加入的最常用的流接口。(这并不是一 一个函数式接口。)
Stream带来的好处:
直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤流、打印
5.2 常见的方法
1、 获取流(生成流)
通过数据源(集合,数组)生成流 list.stream
获取一个流非常简单,有以下几种常用的方式:
所有的Collection集合都可以通过stream默认方法获取流;
default Stream<E> stream ()
stream接口的静态方法of可以获取数组对应的流。
static <T> Stream<T> of (T... values )
参数是一个可变参数。那么我们就可以传递一个数组
2、 中间操作
一个流的后面可以跟随零个或多个中间操作,目的只要是打开流,对其中数据进行过滤/映射,然后返回一个新的流。交给下一个操作使用
Stream流中的常用方法**_ forEach**
void forEach(Consumer<? super T> action);
该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
Consumer接口是一个消费型的函数式接口,可以传递Lambda表达式,消费数据
简单记:
forEach方法, 用来遍历流中的数据
是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
Stream流中的常用方法filter:
用于对Stream流中的数据进行过滤
Stream<T> filter(Predicate<? super. T> predicate);
filter方法的参数Predicote是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤
Predicote中的抽象方法:
boolean test(T t);
Stream流中的常用万法limit: 用于截取流中的元素
limit方法可以对流进行截取,只取用前n个。
方法签名:Stream<T> limit(long maxSize);
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
limit方法是-一个延迟方法,只是对流中的元素进行截取,返回的是-一个新的流,所以可以继续调用Stream流中的其他方法
如果需要将流中的元素映射到另一个流中,可以使用map方法.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的7类型数据转换为另- -种类型的流。
Function中的抽象方法:R apply(T t);
stream流中的常用方法_ skip:用于跳过元素
如果希望跳过前几个元素,可以使用skip方法获取一一个截取之后的新流:Stream<T>. skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一一个长度为的空流。
Stream流中的常用方法_ concat :用于把流组合到一起
如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法
static <T> Stream<T> concat (Stream<? extends T> 0, Stream<? extends T> b)
3、终止操作
一个流只能有一个终结操作,当这个流执行了终结操作时,此时流就会被关闭。无法进行后续操作。
终结方法:
Stream流中的常用方法count :用于统计Stream流中元素的个数
函数式接口、Lambda表达式、方法引用/构造器引用、StreamAPI、接口的增强
1、Lambda表达式
Lambda表达式是一个匿名函数,我们可以把lambda表达式理解为是一段可以传递的代码。使用lambda表达式 可以写出更简洁 更灵活的代码。
1.1 Lambda 表达式的语法
(s1,s2) -> s1.compareTo(s2)
语法中:
-> 称为lambda操作符或箭头操作符
左边 指定了lambda表达式的参数列表
右边 指定lambda体。是抽象方法的实现逻辑 。也是lambda表达式的实现的功能
语法一: 无参无返回值
Runnable r2 = ()-> System.out.println("hello world-----");
语法二:有一个参数 但是没有返回值
@Test
public void lambdaTest3(){
Consumer<String> c = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
// lambda表达式的写法 由泛型推断出参数的类型--称为类型推断
Consumer<String> c1 = s->System.out.println(s);
c.accept("aaaaaaaa");
}
语法三:有多个参数 多条执行语句 并且可以有返回值
@Test
public void lambdaTest4(){
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println("比较两个整数的大小");
int res = o1.compareTo(o2);
return res;
}
};
//lambda表达式 当有多条语句时 大括号不能省略
Comparator<Integer> comp = (o1, o2)->{
System.out.println("比较两个整数的大小");
int res = o1.compareTo(o2);
return res;
};
}
语法四:有多个参数 只有一条返回语句
//lambda表达式 有多个参数,只有一条返回语句 此时大括号可以省略 return 也可以省略
Comparator<Integer> comp = (o1, o2)-> o1.compareTo(o2);
System.out.println(comp.compare(10,20));
lambda表达式和匿名内部类的区别
所需的类型不同 :
匿名内部类中。可以是接口 也可以是抽象类 还可以是具体的类
lambda表达式 只能是接口
使用的限制不同:
如果接口中仅有一个抽象方法 可以使用lambda表达式 也可以是用匿名内部类
如果接口中有多个抽象方法 只能使用匿名内部类 不能使用lambda表达式
实现的原理不同:
匿名内部类编译之后 产生一个单独的字节码文件
lambda表达式 编译之后 没有单独的字节码class文件 对应的字节码在运行时产生。
2、函数式接口
2.1 什么是函数式接口
只包含一个抽象方法的接口 称为函数式接口
@FunctionalInterface 可以用来检测接口是否是函数式接口。
java.util .function包下定义了大量的函数式接口
因为在java中,lambda表达式是一个对象,而不是函数,他们必须依附于一类特别的对象类型—函数式接口
在java中,lambda表达式就是一个函数式接口的实现
2.2 自定义函数式接口
@FunctionalInterface
public interface InterFunction {
int getValue();//函数式接口只能有一个抽象方法
}
函数式接口中使用泛型
@FunctionalInterface
public interface InterFunction<T> {
T getValue(T t);
}
@Test
public void lambdaTest5(){
InterFunction<String> fun = s-> s.toUpperCase();
System.out.println(fun.getValue("abc"));
}
2.3 作为参数传递Lambda表达式
public String toUpperCaseString(InterFunction<String> fun,String str){
return fun.getValue(str);
}
@Test
public void lambdaTest6(){
String newString = toUpperCaseString(s-> s.toUpperCase(),"abcdef");
System.out.println(newString);
}
2.4 JAVA 内置的四大核心函数式接口
Interface Consumer<T>
void accept(T t) 对类型T的对象应用操作。
Interface Supplier<T>
T get() 返回类型为T的对象
Interface Function<T,R>
R apply(T t) 将类型为T的对象进行相应的操作处理 之后返回一个R类型的对象
Interface Predicate<T>
boolean test(T t) 确定类型为T的对象是否满足要求
3、接口的组成更新
3.1 ## 7.1 接口组成更新
常量 public static final
抽象方法 public abstract
jdk8引入
默认方法 public default
静态方法
私有方法
3.2 默认方法
格式:
public default 返回值类型 方法名(){
}
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
注意事项:
默认方法不能是抽象方法 所以他不强制重写,但是也可以重写 重写的时候需要去掉default修饰符
public可以省略 但是default不能省略
3.3 静态方法
public static 返回值类型 方法名(){
}
注意事项:
静态方法只能通过接口名调用, 不能通过实现类或者对象名调用
public可以省略 static 不能省略
3.4 私有方法
private 返回值类型 方法名(){
}
注意事项:
默认方法可以调用私有的静态方法和非静态方法
静态方法只能调用私有的静态方法
4、方法引用
4.1 :: 方法引用符
改符号引用运算符 而他所在的表达式称为方法引用
4.2 引用类方法
类名 :: 静态方法名
static int parseInt(String s) 将字符串参数解析为带符号的十进制整数。
@FunctionalInterface
public interface Converter {
int converter(String s);
}
public class ConverterDemo {
public static void main(String[] args) {
// lambda
userConverter(s->Integer.parseInt(s),"123");
//方法引用
userConverter(Integer :: parseInt,"456");
}
public static void userConverter(Converter c,String s){
int i = c.converter(s);
System.out.println(i);
}
}
4.3 对象的实例方法的引用
对象::成员方法
@FunctionalInterface
public interface Printer {
void printUpCase(String str);
}
public class PrintString {
public void printUpper(String s){
String res = s.toUpperCase();
System.out.println(res);
}
}
public class PrinterDemo {
public static void main(String[] args) {
userPrinter(str -> System.out.println(str.toUpperCase()),"hello");
PrintString ps = new PrintString();
userPrinter(ps::printUpper,"world");
}
public static void userPrinter(Printer p ,String s){
p.printUpCase(s);
}
}
4.4 构造引用
格式:
类名 :: new
@FunctionalInterface
public interface StudentInterface {
Student build(String name,int age);
}
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class StudentDemo {
public static void main(String[] args) {
//lambda表达式
userStudent((name,age)->new Student(name,age));
//构造引用
userStudent(Student::new);
}
public static void userStudent(StudentInterface si){
Student stu = si.build("张三",21);
System.out.println(stu.getName() +"---"+stu.getAge());
}
}
5、StreamAPI
5.1 Stream 流
java. util.stream. Stream是Java.8新加入的最常用的流接口。(这并不是一 一个函数式接口。)
Stream带来的好处:
直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:获取流、过滤流、打印
5.2 常见的方法
1、 获取流(生成流)
通过数据源(集合,数组)生成流 list.stream
获取一个流非常简单,有以下几种常用的方式:
所有的Collection集合都可以通过stream默认方法获取流;
default Stream<E> stream ()
stream接口的静态方法of可以获取数组对应的流。
static <T> Stream<T> of (T... values )
参数是一个可变参数。那么我们就可以传递一个数组
2、 中间操作
一个流的后面可以跟随零个或多个中间操作,目的只要是打开流,对其中数据进行过滤/映射,然后返回一个新的流。交给下一个操作使用
Stream流中的常用方法**_ forEach**
void forEach(Consumer<? super T> action);
该方法接收一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
Consumer接口是一个消费型的函数式接口,可以传递Lambda表达式,消费数据
简单记:
forEach方法, 用来遍历流中的数据
是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
Stream流中的常用方法filter:
用于对Stream流中的数据进行过滤
Stream<T> filter(Predicate<? super. T> predicate);
filter方法的参数Predicote是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤
Predicote中的抽象方法:
boolean test(T t);
Stream流中的常用万法limit: 用于截取流中的元素
limit方法可以对流进行截取,只取用前n个。
方法签名:Stream<T> limit(long maxSize);
参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作
limit方法是-一个延迟方法,只是对流中的元素进行截取,返回的是-一个新的流,所以可以继续调用Stream流中的其他方法
如果需要将流中的元素映射到另一个流中,可以使用map方法.
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
该接口需要一个Function函数式接口参数,可以将当前流中的7类型数据转换为另- -种类型的流。
Function中的抽象方法:R apply(T t);
stream流中的常用方法_ skip:用于跳过元素
如果希望跳过前几个元素,可以使用skip方法获取一一个截取之后的新流:Stream<T>. skip(long n);
如果流的当前长度大于n,则跳过前n个;否则将会得到一一个长度为的空流。
Stream流中的常用方法_ concat :用于把流组合到一起
如果有两个流,希望合并成为一个流,那么可以使用Stream接口的静态方法
static <T> Stream<T> concat (Stream<? extends T> 0, Stream<? extends T> b)
3、终止操作
一个流只能有一个终结操作,当这个流执行了终结操作时,此时流就会被关闭。无法进行后续操作。
终结方法:
Stream流中的常用方法count :用于统计Stream流中元素的个数