目录
1、JDK8-新特性
- Lambda 表达式:Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)
- 函数式接口:指的是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口,这样的接口可以隐式转换为 Lambda 表达式
- 方法引用:方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码
- 默认方法:默认方法就是一个在接口里面有了一个实现的方法
- Stream API:新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- Optional 类:Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
- Date Time API:加强对日期与时间的处理。
- Nashorn, JavaScript 引擎:Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
2、Lambda 表达式
2.1 lambda的由来
public class Test01 {
public static void main(String[] args) {
//开启一个线程 该构造函数需要传递一个Runnable类型的接口参数
Thread thread = new Thread(new My());
thread.start();//开启线程
//匿名内部类
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("通过匿名内部类实现线程任务");
}
});
thread1.start();//开启线程
}
}
class My implements Runnable{
@Override
public void run() {
System.out.println("使用实现类来完成------->线程任务");
}
}
分析:
Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心
为了指定run方法体,不得不需要Runnable的实现类
为了省去定义一个Runnable 的实现类,不得不使用匿名内部类
必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错,
而实际上,我们只在乎方法体中的代码.
我们可以使用lambda表达式来完成上面的功能。
2.2 初体验lambda表达式
//lambda表达式.
Runnable runnable1=()->{
System.out.println("这是lambda表达式完成线程任务");
};
Thread thread2=new Thread(runnable1);
thread2.start();
2.3 lambda表达式的语法
(参数列表)->{}
():参数列表
->:连接符 连接的是参数以及方法体。
{}: 方法体。
2.4 无参无返回值的Lambda
public class Test02 {
public static void main(String[] args) {
//主函数调用fun方法。第一种:创建UserService接口的实现类,并创建该实现类对象。
//第二种: 匿名内部类的方式
// UserService userService=new UserService() {
// @Override
// public void show() {
// System.out.println("这是匿名内部类的show方法的实现");
// }
// };
// fun(userService);
//第三种lambda表达式:--该接口必须为函数式接口
UserService userService=()->{
System.out.println("lambda表示的show方法");
};
fun(userService);
}
public static void fun(UserService userService){//UserService userService=
userService.show();
}
}
//函数式接口-->里面有且仅有一个抽象方法。--只有这种接口才能使用lambda表达式。
interface UserService{
public void show();
}
2.5 有参有返回值
public class Test03 {
public static void main(String[] args) {
List<Person> personList=new ArrayList<>();
personList.add(new Person("张三",15));
personList.add(new Person("李四",5));
personList.add(new Person("王五",65));
personList.add(new Person("赵六",45));
personList.add(new Person("田七",35));
//对集合中的元素按照年龄排序。小到大
System.out.println(personList);
//Collections:集合工具类。--匿名内部类
// Comparator<Person> comparator=new Comparator<Person>() {
// @Override //如果是0表示相同 大于0表示o1大于02
// public int compare(Person o1, Person o2) {
// return o1.getAge()-o2.getAge();
// }
// };
// Collections.sort(personList,comparator);
// System.out.println(personList);
Comparator<Person> comparator=(o1,o2)->{
//就是对函数式接口中抽象方法的简写。
return o1.getAge()-o2.getAge();
};
Collections.sort(personList,comparator);
System.out.println(personList);
}
}
class Person{
private String name;
private Integer age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
2.6 Lambda表达式的省略写法
在lambda表达式的标准写法基础上,可以使用省略写法的规则为:
小括号内的参数类型可以省略[]
如果小括号内有且仅有一个参数,则小括号可以省略
如果大括号内有且仅有一个语句,可以同时省略大括号,return 关键字及语句分号。
public class Test05 {
public static void main(String[] args) {
USB u=(str)-> str.toUpperCase();//lambda表达式:
// USB u=new USB() {//匿名内部类对接口中抽象方法的个数没有任何要求.
// @Override
// public String toUpper(String str) {
// return null;
// }
//
// @Override
// public String toLower(String str) {
// return null;
// }
// };
fun(u);
}
public static void fun(USB usb){
String s = usb.toUpper("hello");
System.out.println(s);
}
}
interface USB{
public String toUpper(String str);
// public String toLower(String str);
}
2.7 Lambda表达式使用的前提
Lambda表达式的语法是非常简洁的,但是Lambda表达式不是随便使用的,使用时有几个条件要特别注意
方法的参数或局部变量类型必须为接口才能使用Lambda
接口中有且仅有一个抽象方法(@FunctionalInterface)
3. 内置函数式接口
要想使用lambda表达式它的前提就是必须是函数式接口。
3.1 内置函数式接口的由来
public class Test {
public static void main(String[] args) {
Operation o=arr->{
int sum=0;
for(int s:arr){
sum+=s;
}
return sum;
};
fun(o);
}
public static void fun(Operation operation){
int[] arr={1,2,3,4};
int s = operation.getSum(arr);
System.out.println("数组的和:"+s);
}
}
@FunctionalInterface
interface Operation{
public int getSum(int[] arr);
}
分析:
我们知道使用Lambda表达式的前提是需要有函数式接口,而Lambda表达式使用时不关心接口名,抽象方法名。只关心抽象方法的参数列表和返回值类型。因此为了让我们使用Lambda表达式更加的方便,在JDK中提供了大量常用的函数式接口. 大多数无需自己再定义函数式接口,而可以直接使用jdk内置的函数式接口。分成四类
3.2 消费型函数式接口Consumer
适合有参数,但是没有返回值的。
public class Test06 {
public static void main(String[] args) {
Consumer<Double> c=t->{
System.out.println("今天洗脚花费:"+t+"元");
};
fun(c,200.0);
}
public static void fun(Consumer<Double> consumer,Double money){
consumer.accept(money);
}
}
3.3 供给型函数式接口---Supplier
无参,需要返回值的接口类。
public class Test06 {
public static void main(String[] args) {
// Consumer<Double> c=t->{
// System.out.println("今天洗脚花费:"+t+"元");
// };
// fun(c,200.0);
Supplier<Integer> s=()-> new Random().nextInt(10);
fun2(s);
}
public static void fun2(Supplier<Integer> supplier){
Integer a = supplier.get();
System.out.println("结果:"+a);
}
public static void fun(Consumer<Double> consumer,Double money){
consumer.accept(money);
}
}
3.3 函数型函数式接口---Function<T,R>
T: 参数的泛型
R:返回值的泛型
public class Test {
public static void main(String[] args) {
Function<int[],Integer> o=arr->{
int sum=0;
for(int s:arr){
sum+=s;
}
return sum;
};
fun(o);
}
public static void fun(Function<int[],Integer> fun){
int[] arr={1,2,3,4};
int s = fun.apply(arr);
System.out.println("数组的和:"+s);
}
}
3.4 断言型函数式接口--Predicate
T: 参数
boolean:返回值类型
public class Test06 {
public static void main(String[] args) {
// Consumer<Double> c=t->{
// System.out.println("今天洗脚花费:"+t+"元");
// };
// fun(c,200.0);
// Supplier<Integer> s=()-> new Random().nextInt(10);
// fun2(s);
// Function<String,String> fun=t->t.toUpperCase();
// fun3(fun,"hello world");
Predicate<String> p=t->t.length()>3;
fun4(p,"欧阳锋"); //4
}
public static void fun4(Predicate<String> predicate,String name){
boolean test = predicate.test(name);
System.out.println("是否成年:"+test);
}
public static void fun3(Function<String,String> f,String str){
String apply = f.apply(str);
System.out.println("结果:"+apply);
}
public static void fun2(Supplier<Integer> supplier){
Integer a = supplier.get();
System.out.println("结果:"+a);
}
public static void fun(Consumer<Double> consumer,Double money){
consumer.accept(money);
}
}
4. 方法引用
特殊的lambda表达式,它是对lambda表达式的一种简写方式。
4.1 方法引用的由来
public class Test07 {
public static void main(String[] args) {
Consumer<int[]> c=t->{
int sum=0;
for(int a:t){
sum+=a;
}
System.out.println("数组的和:"+sum);
};
fun(c);
}
public static void fun(Consumer<int[]> consumer){
int[] arr={1,2,3,4,5};
consumer.accept(arr);
}
//求和方法
public static void sum(int[] arr){
int sum=0;
for(int a:arr){
sum+=a;
}
System.out.println("数组的和:"+sum);
}
}
如果我们在Lambda中所指定的功能,已经有其他方法存在相同方案,那是否还有必要再写重复逻辑?可以直接“引 用”过去就好了:---方法引用。::
public class Test07 {
public static void main(String[] args) {
// Consumer<int[]> c=t->{
// int sum=0;
// for(int a:t){
// sum+=a;
// }
// System.out.println("数组的和:"+sum);
// };
// Consumer<int[]> c=(t)->Test07.sum(t); //
Consumer<int[]> c= Test07::sum;
fun(c);
}
public static void fun(Consumer<int[]> consumer){
int[] arr={1,2,3,4,5};
consumer.accept(arr);
}
//求和方法
public static void sum(int[] arr){
int sum=0;
for(int a:arr){
sum+=a;
}
System.out.println("数组的和:"+sum);
}
}
4.2 方法引用的类型
4.3 静态方法引用
(args)->类名.静态方法(args). 类名::staticMethod
当lambda表达式中方法体,只有一条语句,而这条语句是类名.静态方法。而静态方法的参数和lambda的参数一致时。
类名::静态方法
public class Test {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(5);
list.add(2);
list.add(9);
list.add(8);
list.add(3);
// Comparator<Integer> comparator = (o1,o2)->o1-o2;
//Comparator<Integer> comparator = (o1,o2)->Integer.compare(o1,o2);
Comparator<Integer> comparator = Integer::compare;
Collections.sort(list,comparator);
System.out.println(list);
}
}
4.4 实例方法引用
(args) -> inst.instMethod(args) inst::instMethod
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用
public class Test1 {
public static void main(String[] args) {
//创建一个类对象
Student student = new Student("芈原",22);
//通过内置的函数接口,返回对象的名称。
// Supplier<String> supplier = ()->student.getName(); //使用供给型接口,lambada表达式
Supplier<String> supplier = student::getName;//实例方法引用
String s = supplier.get();
System.out.println(s);
//观察:lambada表达式中有且仅有一条语句,方法调用语句。 -----实力方法引用特点:()->对象.普通方法()
//函数型
Function<Student,String> function = (t)->t.getName();
String apply = function.apply(student);
System.out.println(apply);//这里就无法使用方法的引用
}
}
class Student{
private String name;
private Integer age;
public Student() {
}
public Student(String name, Integer age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public Integer getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
}
4.5 对象方法引用
lambda: (inst,args)->inst.普通方法(args): -------->类名::普通方法 类名::instMethod
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
public class Test2 {
public static void main(String[] args) {
//判断两个字符串是否一致
// BiFunction<String, String, Boolean> biFunction = (t1, t2) -> t1.equals(t2);
// Boolean apply = biFunction.apply("hello", "hello");
// System.out.println(apply);
//观察:符合对象方法引用
BiFunction<String,String,Boolean> biFunction = String::equals;
show(biFunction);
}
public static void show(BiFunction<String,String,Boolean> biFunction){
Boolean apply = biFunction.apply("hello", "hell");
System.out.println(apply);
}
}
4.6 构造方法引用
(args) -> new 类名(args)------构造方法引用: 类名::new
public class Test3 {
public static void main(String[] args) {
// Supplier<Student> supplier = ()->new Student();//lambada表达式
// Student student = supplier.get();
// System.out.println(student);
//观察:调用的构造函数
// Supplier<Student> supplier = Student::new;//构造方法引用
// Student student = supplier.get();
// System.out.println(student);
// BiFunction<String,Integer,Student> biFunction = (n,a)->new Student(n,a);
BiFunction<String,Integer,Student> biFunction = Student::new;
Student st = biFunction.apply("郭子仪", 61);
System.out.println(st);
}
}
总结:
静态方法引用 类名::静态方法 lambda表达式: (参数)->类名.静态方法(参数)
实例方法引用:对象::实例方法 lambda表达式: (参数)->对象.实例方法(参数)
对象方法引用:类名::实例方法 lambda表达式: (参数1,参数2....)->参数1.实例方法(参数2..)
构造方法引用: 类名::new lambda表达式: (参数)->new 类名(参数);