个人网站站长,中国空间站vr全景,沈阳专业网站建设公司排名,音乐类网站开发文章目录 5.Java8新特性5.1新特性列表5.2Lambda 表达式5.2.1函数式思想5.2.2举例#xff08;1#xff09;方式一#xff1a;创建对象#xff08;2#xff09;方式二#xff1a;匿名内部类#xff08;3#xff09;方式三#xff1a;Lambda 5.2.3Lambda表达式的标准格式… 文章目录 5.Java8新特性5.1新特性列表5.2Lambda 表达式5.2.1函数式思想5.2.2举例1方式一创建对象2方式二匿名内部类3方式三Lambda 5.2.3Lambda表达式的标准格式5.2.4练习1练习1——抽象方法无参无返回值①通过实现类方法②匿名内部类③lambda 2练习2——抽象方法带参无返回值①匿名内部类②lambda 3练习3——抽象方法带参带返回值 5.2.5Lambda的省略模式1举例 5.2.6lambad表达式与匿名内部类的区别 5.3方法引用5.3.1介绍5.3.2方法引用的类型1调用类的静态方法①语法②举例 2调用传入的实例参数的方法①语法②举例 3调用已经存在的实例的方法①语法②举例 4调用类的构造函数①语法②举例 5.3.3练习1练习一2练习二 5.4Stream流5.4.1定义5.4.2流的操作类型1中间操作2终端操作 5.4.3举例1创建流2操作流①过滤②映射③匹配④组合 3转换流 5.4.4Stream 与Collection 的区别 5.5函数式接口5.5.1定义5.5.2举例 5.Java8新特性 5.1新特性列表
Lambda 表达式Lambda 允许把函数作为一个方法的参数函数作为参数传递到方法中。方法引用方法引用提供了非常有用的语法可以直接引用已有Java类或对象实例的方法或构造器。与lambda联合使用方法引用可以使语言的构造更紧凑简洁减少默认方法 默认方法就是一个在接口里面有了一个实现的方法。Stream API新添加的Stream APIjava.util.stream 把真正的函数式编程风格引入到Java中。Date Time API 加强对日期与时间的处理。新工具新的编译工具如Nashorn引擎 jjs、 类依赖分析器jdeps。Optional 类 Optional 类已经成为 Java 8 类库的一部分用来解决空指针异常。Nashorn, JavaScript 引擎 Java 8提供了一个新的Nashorn javascript引擎它允许我们在JVM上运行特定的javascript应用。
5.2Lambda 表达式
5.2.1函数式思想
在数学中函数就是有输入量、输出量的一套计算方案也就是“拿数据做操作”面向对象思想 强调“必须通过对象的形式来做事情”函数式思想 则尽量忽略面向对象的复杂语法“强调做什么而不是以什么形式去做” 而我们要学习的Lambda表达式就是函数式思想的体现
5.2.2举例
需求启动一个线程在控制台输出一句话多线程程序启动了
1方式一创建对象
定义一个类MyRunnable实现Runnable接口并重写run()方法
//创建MyRunnable类的对象
class MyRunnable implements Runnable{Overridepublic void run() {System.out.println(多线程程序启动了...);}
}public class LambdaDemo {public static void main(String[] args) {//实现类的方式实现需求MyRunnable my new MyRunnable();//创建Thread类的对象把MyRunnable的对象作为构造参数传递Thread t new Thread(my);//启动线程t.start();}
}2方式二匿名内部类
匿名内部类中重写run()方法的代码分析 方法形式参数为空说明调用方法时不需要传递参数方法返回值类型为void说明方法执行没有结果返回方法体中的内容是我们具体要做的事情
public class LambdaDemo {public static void main(String[] args) {//匿名内部类的方式改进new Thread(new Runnable() {Overridepublic void run() {System.out.println(多线程程序启动了...);}}).start();}
}3方式三Lambda
public class LambdaDemo {public static void main(String[] args) {//Lambda表达式改进new Thread(() - {System.out.println(多线程程序启动了...);}).start();}
}5.2.3Lambda表达式的标准格式
组成Lambda表达式的三要素形式参数箭头代码块Lambda表达式的格式 格式(形式参数) - {代码块}形式参数如果有多个参数参数之间用逗号隔开如果没有参数留空即可-由英文中画线和大于符号组成固定写法。代表指向动作代码块是我们具体要做的事情也就是以前我们写的方法体内容 Lambda表达式的 使用前提 有一个接口接口中有且仅有一个抽象方法
5.2.4练习
1练习1——抽象方法无参无返回值
定义一个接口(Eatable)里面定义一个抽象方法void eat();
public interface Eatable {void eat();
}定义一个测试类(EatableDemo)在测试类中提供两个方法 一个方法是useEatable(Eatable e)一个方法是主方法在主方法中调用useEatable方法
①通过实现类方法
实现类实现Eatable并重写eat方法
class EatableImpl implements Eatable{Overridepublic void eat() {System.out.println(一天一苹果医生远离我);}
}public class EatableDemo {public static void main(String[] args) {//在主方法中调用useEatable方法Eatable e new EatableImpl();useEatable(e);}private static void useEatable(Eatable e){e.eat();}
}②匿名内部类
public class EatableDemo {public static void main(String[] args) {//匿名内部类useEatable(new Eatable() {Overridepublic void eat() {System.out.println(一天一苹果医生远离我);}});}private static void useEatable(Eatable e){e.eat();}
}③lambda Eatable e是一个接口并只含有一个抽象方法无参无返回值方法void eat() 以下lambda实现方式相当于实现该接口并实现该方法 public class EatableDemo {public static void main(String[] args) {//用Lambda表达式useEatable(() - {System.out.println(一天一苹果医生远离我);});}private static void useEatable(Eatable e){e.eat();}
}2练习2——抽象方法带参无返回值
定义一个接口(Flyable)里面定义一个抽象方法void fly(String s);
public interface Flyable{void fly(String s);
}定义一个测试类(FlyableDemo)在测试类中提供两个方法 一个方法是useFlyable(Flyable f)一个方法是主方法在主方法中调用useFlyable方法
①匿名内部类
public class FlyableDemo {public static void main(String[] args) {//在主方法中调用useFlyable方法//匿名内部类useFlyable(new Flyable() {Overridepublic void fly(String s) {System.out.println(s);System.out.println(飞机自驾游);}});}private static void useFlyable(Flyable f){f.fly(风和日丽晴空万里);}
}②lambda
public class FlyableDemo {public static void main(String[] args) {//LambdauseFlyable((String s)- {System.out.println(s);System.out.println(飞机自驾游);});}private static void useFlyable(Flyable f){f.fly(风和日丽晴空万里);}
}3练习3——抽象方法带参带返回值
这里就不过多赘述了和练习2差不多在最后添加返回值语句即可
5.2.5Lambda的省略模式
省略规则
参数类型可以省略。但是有多个参数的情况下不能只省略一个如果参数有且仅有一个那么小括号可以省略如果代码块的语句只有一条可以省略大括号和分号甚至是return
1举例
定义一个接口(Flyable)里面定义一个抽象方法void fly(String s);
public interface Flyable{void fly(String s);
}定义一个接口(Addable)里面定义一个抽象方法int add(int x,int y);
public interface Addable {int add(int x,int y);
}/*Lambda表达式的省略模式*/
public class LambdaDemo {public static void main(String[] args) {//useAddable((int x,int y) - {// return xy;//});//1.参数的类型可以省略,但是有多个参数的情况下不能只省略一个useAddable((x,y) - {return xy;});//useFlyable((String s) -{// System.out.println(s);//});//2.如果参数有且只有一个那么小括号可以省略useFlyable( s-{System.out.println(s);});//3.如果代码块的语句只有一条可以省略大括号和分号useFlyable(s - System.out.println(s));//4.如果代码块的语句只有一条可以省略大括号和分号;如果有return,return也要省略useAddable((x,y) - xy );}private static void useFlyable(Flyable f){f.fly(风和日丽晴空万里);}private static void useAddable(Addable a){int sum a.add(10,20);System.out.println(sum);}
}5.2.6lambad表达式与匿名内部类的区别
匿名内部类lambda创建的对象类型接口、抽象类、具体类接口使用限制接口可有一个或多个抽象方法接口有且只有一个抽象方法实现原理编译之后产生一个单独的.class字节码文件编译之后没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成
5.3方法引用
5.3.1介绍
定义方法引用是指通过方法的名字来指向一个方法优点方法引用可以使语言的构造更紧凑简洁减少冗余代码与lambda的比较方法引用MethodReference是Lambda表达式的另一种格式在某些场景下可以提高代码的可读性
5.3.2方法引用的类型
方法引用可以分为分四类调用类的静态方法、调用传入的实例参数的方法、调用已经存在的实例的方法、调用类的构造函数
以下将从四种类型的方法引用出发探讨lambda与方法引用的区分点
1调用类的静态方法
①语法
//lambda
(args) - Class.staticMethod(args)//方法引用
Class::static_method符合上面形式的调用不管有多少参数都省略掉编译器自动会帮我们传入
②举例
// Lambda 表达式
FunctionInteger, String intToStringLambda (num) - String.valueOf(num);// 方法引用
FunctionInteger, String intToString String::valueOf;FunctionInteger, String 表示一个函数式接口Functional Interface它接受一个 Integer 类型的参数并返回一个 String 类型的结果。 具体来说FunctionInteger, String 是 Java 中的函数式接口它包含一个抽象方法 apply用于将输入的 Integer 类型参数转换为输出的 String 类型结果。在这种情况下intToString 就是一个函数可以将 Integer 类型的值转换为 String 类型的值。 2调用传入的实例参数的方法
①语法
//lambda
(obj, args) - obj.instanceMethod(args)//方法引用
ObjectType::instanceMethod②举例
String str Hello World;
// Lambda 表达式
BiFunctionString, Integer, String substringLambda (str, index) - str.substring(index);// 方法引用
BiFunctionString, Integer, String substring String::substring;实现 BiFunction 接口的 apply 方法直接引用了 String 类的 substring 方法。 3调用已经存在的实例的方法
①语法
//lambda
(args) - obj.instanceMethod(args)//方法引用
obj::instanceMethod②举例
// Lambda 表达式
String str Hello World;
SupplierInteger strLengthLambda () - str.length();// 方法引用
String str Hello World;
SupplierInteger strLength str::length;Supplier 是 Java 中的一个函数式接口它不接受任何参数但返回一个结果。 4调用类的构造函数
①语法
//lambda
(args) - new ClassName(args)//方法引用
ClassName::new②举例
// Lambda 表达式
SupplierListString listSupplierLambda () - new ArrayList();// 方法引用
SupplierListString listSupplier ArrayList::new;Supplier 是 Java 中的一个函数式接口它不接受任何参数但返回一个结果。 5.3.3练习
1练习一
import java.util.List;
import java.util.ArrayList;public class Java8Tester {public static void main(String args[]){ListString names new ArrayList();names.add(Google);names.add(Runoob);names.add(Taobao);names.add(Baidu);names.add(Sina);names.forEach(System.out::println);}
}实例中我们将 System.out::println 方法作为静态方法来引用。
属于调用类的静态方法一类
结果
Google
Runoob
Taobao
Baidu
Sina2练习二
ConsumerString consumer1 new ConsumerString() {Overridepublic void accept(String s) {System.out.println(s);}
};//lambda表达式
ConsumerString consumer2 ;//方法引用
ConsumerString consumer3 ;答案
//lambda
ConsumerString consumer2 (s)-{System.out.println(s);
}//方法引用
ConsumerString consumer3 System.out::println;属于调用类的静态方法一类
5.4Stream流
5.4.1定义
Stream 就好像一个高级的迭代器但只能遍历一次就好像一江春水向东流在流的过程中对流中的元素执行一些操作比如“过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等。
5.4.2流的操作类型
要想操作流首先需要有一个数据源可以是数组或者集合。每次操作都会返回一个新的流对象方便进行链式操作但原有的流对象会保持不变。
流的操作可以分为两种类型
1中间操作可以有多个每次返回一个新的流可进行链式操作。
2终端操作只能有一个每次执行完这个流也就用光光了无法执行下一个操作因此只能放在最后。
来举个例子。
ListString list new ArrayList();
list.add(武汉加油);
list.add(中国加油);
list.add(世界加油);
list.add(世界加油);long count list.stream().distinct().count();
System.out.println(count);1中间操作
distinct() 方法是一个中间操作去重它会返回一个新的流没有共同元素。
2终端操作
count() 方法是一个终端操作返回流中的元素个数
long count();中间操作不会立即执行只有等到终端操作的时候流才开始真正地遍历用于映射、过滤等。通俗点说就是一次遍历执行多个操作性能就大大提高了。
5.4.3举例
1创建流
如果是数组的话可以使用 Arrays.stream() 或者 Stream.of() 创建流如果是集合的话可以直接使用 stream() 方法创建流因为该方法已经添加到 Collection 接口中。
public class CreateStreamDemo {public static void main(String[] args) {String[] arr new String[]{武汉加油, 中国加油, 世界加油};StreamString stream Arrays.stream(arr);stream Stream.of(武汉加油, 中国加油, 世界加油);ListString list new ArrayList();list.add(武汉加油);list.add(中国加油);list.add(世界加油);stream list.stream();}
}查看 Stream 源码的话你会发现 of() 方法内部其实调用了 Arrays.stream() 方法。
public staticT StreamT of(T... values) {return Arrays.stream(values);
}另外集合还可以调用 parallelStream() 方法创建并发流默认使用的是 ForkJoinPool.commonPool()线程池。
ListLong aList new ArrayList();
StreamLong parallelStream aList.parallelStream();2操作流
Stream 类提供了很多有用的操作流的方法
①过滤 通过 filter() 方法可以从流中筛选出我们想要的元素。 public class FilterStreamDemo {public static void main(String[] args) {ListString list new ArrayList();list.add(周杰伦);list.add(王力宏);list.add(陶喆);list.add(林俊杰);StreamString stream list.stream().filter(element - element.contains(王));stream.forEach(System.out::println);}
}filter() 方法接收的是一个 PredicateJava 8 新增的一个函数式接口接受一个输入参数返回一个布尔值结果类型的参数因此我们可以直接将一个 Lambda 表达式传递给该方法比如说 element - element.contains(王) 就是筛选出带有“王”的字符串。 forEach() 方法接收的是一个 ConsumerJava 8 新增的一个函数式接口接受一个输入参数并且无返回的操作类型的参数类名 :: 方法名是 Java 8 引入的新语法方法引用System.out 返回 PrintStream 类println 方法你应该知道是打印的。该类型为调用类的静态方法 输出结果
王力宏②映射
如果想通过某种操作把一个流中的元素转化成新的流中的元素可以使用 map() 方法。
public class MapStreamDemo {public static void main(String[] args) {ListString list new ArrayList();list.add(周杰伦);list.add(王力宏);list.add(陶喆);list.add(林俊杰);StreamInteger stream list.stream().map(String::length);stream.forEach(System.out::println);}
}map() 方法接收的是一个 FunctionJava 8 新增的一个函数式接口接受一个输入参数 T返回一个结果 R类型的参数此时参数 为 String 类的 length 方法该类型为调用传入的实例参数的方法也就是把 StreamString 的流转成一个 StreamInteger 的流。
输出结果
3
3
2
3③匹配
Stream 类提供了三个方法可供进行元素匹配它们分别是
anyMatch()只要有一个元素匹配传入的条件就返回 true。allMatch()只有有一个元素不匹配传入的条件就返回 false如果全部匹配则返回 true。noneMatch()只要有一个元素匹配传入的条件就返回 false如果全部不匹配则返回 true。
public class MatchStreamDemo {public static void main(String[] args) {ListString list new ArrayList();list.add(周杰伦);list.add(王力宏);list.add(陶喆);list.add(林俊杰);boolean anyMatchFlag list.stream().anyMatch(element - element.contains(王));boolean allMatchFlag list.stream().allMatch(element - element.length() 1);boolean noneMatchFlag list.stream().noneMatch(element - element.endsWith(沉));System.out.println(anyMatchFlag);System.out.println(allMatchFlag);System.out.println(noneMatchFlag);}
}因为“王力宏”以“王”字开头所以 anyMatchFlag 应该为 true因为“周杰伦”、“王力宏”、“陶喆”、“林俊杰”的字符串长度都大于 1所以 allMatchFlag 为 true因为 4 个字符串结尾都不是“沉”所以 noneMatchFlag 为 true。
输出结果
true
true
true④组合
reduce() 方法的主要作用是把 Stream 中的元素组合起来它有两种用法 OptionalT reduce(BinaryOperatorT accumulator) 没有起始值只有一个参数就是运算规则此时返回 Optional。 T reduce(T identity, BinaryOperatorT accumulator) 有起始值有运算规则两个参数此时返回的类型和起始值类型一致。
来看下面这个例子。
public class ReduceStreamDemo {public static void main(String[] args) {Integer[] ints {0, 1, 2, 3};ListInteger list Arrays.asList(ints);OptionalInteger optional list.stream().reduce((a, b) - a b); //lambda写法OptionalInteger optional1 list.stream().reduce(Integer::sum); //方法引用写法System.out.println(optional.orElse(0)); //6System.out.println(optional1.orElse(0)); //6int reduce list.stream().reduce(6, (a, b) - a b); //有起始值6System.out.println(reduce); int reduce1 list.stream().reduce(6, Integer::sum); //有起始值6System.out.println(reduce1);}
}运算规则可以是 Lambda 表达式比如 (a, b) - a b也可以是类名::方法名比如 Integer::sum。
程序运行的结果如下所示
0、1、2、3 在没有起始值相加的时候结果为 6有起始值 6 的时候结果为 12。
6
6
12
123转换流
既然可以把集合或者数组转成流那么也应该有对应的方法将流转换回去——collect() 方法就满足了这种需求。
public class CollectStreamDemo {public static void main(String[] args) {ListString list new ArrayList();list.add(周杰伦);list.add(王力宏);list.add(陶喆);list.add(林俊杰);String[] strArray list.stream().toArray(String[]::new);System.out.println(Arrays.toString(strArray)); //[周杰伦, 王力宏, 陶喆, 林俊杰]ListInteger list1 list.stream().map(String::length).collect(Collectors.toList());ListString list2 list.stream().collect(Collectors.toCollection(ArrayList::new));System.out.println(list1); //[3, 3, 2, 3]System.out.println(list2); //[周杰伦, 王力宏, 陶喆, 林俊杰]String str list.stream().collect(Collectors.joining(, )).toString();System.out.println(str); //周杰伦, 王力宏, 陶喆, 林俊杰}
}toArray() 方法可以将流转换成数组你可能比较好奇的是 String[]::new它是什么东东呢来看一下 toArray() 方法的源码。
A A[] toArray(IntFunctionA[] generator);也就是说 String[]::new 是一个 IntFunction一个可以产生所需的新数组的函数可以通过反编译字节码看看它到底是什么
String[] strArray (String[])list.stream().toArray((x$0) - {return new String[x$0];
});
System.out.println(Arrays.toString(strArray));也就是相当于返回了一个指定长度的字符串数组。
当我们需要把一个集合按照某种规则转成另外一个集合的时候就可以配套使用 map() 方法和 collect() 方法。
ListInteger list1 list.stream().map(String::length).collect(Collectors.toList());通过 stream() 方法创建集合的流后再通过 map(String:length) 将其映射为字符串长度的一个新流最后通过 collect() 方法将其转换成新的集合。
Collectors 是一个收集器的工具类内置了一系列收集器实现比如说 toList() 方法将元素收集到一个新的 java.util.List 中比如说 toCollection() 方法将元素收集到一个新的 java.util.ArrayList 中比如说 joining() 方法将元素收集到一个可以用分隔符指定的字符串中。
输出结果
[周杰伦, 王力宏, 陶喆, 林俊杰]
[3, 3, 2, 3]
[周杰伦, 王力宏, 陶喆, 林俊杰]
周杰伦, 王力宏, 陶喆, 林俊杰5.4.4Stream 与Collection 的区别 5.5函数式接口
5.5.1定义
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法但是可以有多个非抽象方法的接口。函数式接口可以被隐式转换为 lambda 表达式。Lambda 表达式和方法引用实际上也可认为是Lambda表达式上。
如定义了一个函数式接口如下
FunctionalInterface
interface GreetingService
{void sayMessage(String message);
}那么就可以使用Lambda表达式来表示该接口的一个实现(注JAVA 8 之前一般是用匿名类实现的)
GreetingService greetService1 message - System.out.println(Hello message);函数式接口可以对现有的函数友好地支持 lambda。
JDK 1.8 之前已有的函数式接口
java.lang.Runnablejava.util.concurrent.Callablejava.security.PrivilegedActionjava.util.Comparatorjava.io.FileFilterjava.nio.file.PathMatcherjava.lang.reflect.InvocationHandlerjava.beans.PropertyChangeListenerjava.awt.event.ActionListenerjavax.swing.event.ChangeListener
JDK1.8新增加的函数接口
java.util.function
java.util.function 它包含了很多类用来支持 Java的函数式编程该包中的函数式接口有
1BiConsumerT,U 代表了一个接受两个输入参数的操作并且不返回任何结果2BiFunctionT,U,R 代表了一个接受两个输入参数的方法并且返回一个结果3BinaryOperator 代表了一个作用于于两个同类型操作符的操作并且返回了操作符同类型的结果4BiPredicateT,U 代表了一个两个参数的boolean值方法5BooleanSupplier代表了boolean值结果的提供方6Consumer 代表了接受一个输入参数并且无返回的操作7DoubleBinaryOperator代表了作用于两个double值操作符的操作并且返回了一个double值的结果。8DoubleConsumer代表一个接受double值参数的操作并且不返回结果。9DoubleFunction 代表接受一个double值参数的方法并且返回结果10DoublePredicate代表一个拥有double值参数的boolean值方法11DoubleSupplier代表一个double值结构的提供方12DoubleToIntFunction接受一个double类型输入返回一个int类型结果。13DoubleToLongFunction接受一个double类型输入返回一个long类型结果14DoubleUnaryOperator接受一个参数同为类型double,返回值类型也为double 。15**FunctionT,R**接受一个输入参数返回一个结果。16IntBinaryOperator接受两个参数同为类型int,返回值类型也为int 。17IntConsumer接受一个int类型的输入参数无返回值 。18IntFunction 接受一个int类型输入参数返回一个结果 。19IntPredicate接受一个int输入参数返回一个布尔值的结果。20IntSupplier无参数返回一个int类型结果。21IntToDoubleFunction接受一个int类型输入返回一个double类型结果 。22IntToLongFunction接受一个int类型输入返回一个long类型结果。23IntUnaryOperator接受一个参数同为类型int,返回值类型也为int 。24LongBinaryOperator接受两个参数同为类型long,返回值类型也为long。25LongConsumer接受一个long类型的输入参数无返回值。26LongFunction 接受一个long类型输入参数返回一个结果。27LongPredicateR接受一个long输入参数返回一个布尔值类型结果。28LongSupplier无参数返回一个结果long类型的值。29LongToDoubleFunction接受一个long类型输入返回一个double类型结果。30LongToIntFunction接受一个long类型输入返回一个int类型结果。31LongUnaryOperator接受一个参数同为类型long,返回值类型也为long。32ObjDoubleConsumer 接受一个object类型和一个double类型的输入参数无返回值。33ObjIntConsumer 接受一个object类型和一个int类型的输入参数无返回值。34ObjLongConsumer 接受一个object类型和一个long类型的输入参数无返回值。35Predicate 接受一个输入参数返回一个布尔值结果。36Supplier 无参数返回一个结果。37ToDoubleBiFunctionT,U 接受两个输入参数返回一个double类型结果38ToDoubleFunction 接受一个输入参数返回一个double类型结果39ToIntBiFunctionT,U 接受两个输入参数返回一个int类型结果。40ToIntFunction 接受一个输入参数返回一个int类型结果。41ToLongBiFunctionT,U 接受两个输入参数返回一个long类型结果。42ToLongFunction 接受一个输入参数返回一个long类型结果。43UnaryOperator 接受一个参数为类型T,返回值类型也为T。
5.5.2举例
可以用注解 FunctionalInterface 自定义一个函数式接口。一旦定义了功能接口就只能有一个抽象方法。由于您只有一个抽象方法因此您可以编写多个静态方法和默认方法。
下面是为两个数字相乘而编写的 FunctionalInterface 的编程示例。
FunctionalInterface
interface FuncInterface {public int multiply(int a, int b);
}
public class Java8 {public static void main(String args[]) {FuncInterface Total (a, b) - a * b;System.out.println(Result: Total.multiply(30, 60));}
}