1.接口中默认方法修饰为普通方法
2.函数式接口
3.lambda表达式(重要)
lambda无参方法调用
lambda有参方法调用
lambda 实例--forEach
lambda实例--Comparator
lambda实例--线程调用
4.方法构造和函数引用
什么是方法引入
方法引入的类型
静态方法引入
实例方法引入
对象方法引入 (对比实例方法引入)
构造方法引入
5.Stream流接口(重要)
Stream的创建:
Stream的常见错误
Stream -- List 转Set
Stream -- List 转Map
Stream -- reduce计算求和
Stream -- 最大值最小值
Stream -- Match 匹配
Stream -- Filiter 过滤
Stream -- Skip和Limit 分页
Stream -- Sorted 排序
6.Optional容器
isPresent() 判断参数为空
orElse() 设定默认值
filter(条件判断语句) 实现过滤
优化场景一:ifPresent (new Comsumer 消费型函数接口)
优化场景二:orElseGet (new Supplier 供给型函数接口)
优化场景三:map (new Function 函数型函数接口)
1.接口中默认方法修饰为普通方法
JDK8之前,在Interface中
- 变量必须是public、static、final的
- 方法必须是public、abstarct的,而且是默认的,不能有方法体
JDK1.8之后,接口的方法支持使用static和default修饰方法体变为普通方法,普通方法不必重写
public interface JDK8Interface {
/**
* 方法定义默认为public、abstract
* void add() = public abstract void add()
*/
void add();
/**
* 使用 default 和 static 修饰的方法可以有方法体,不使用会报错
*/
default void get(){
System.out.println("jdk8_GET");
}
static void del(){
System.out.println("jdk8_DEL");
}
}
------------------------下面是实现类----------------------
public class JDK8InterfaceImpl implements JDK8Interface {
//只需实现抽象方法即可
@Override
public void add() {
System.out.println("jdk8_ADD");
}
}
------------------------下面是测试类----------------------
public class Test01 {
public static void main(String[] args) {
JDK8InterfaceImpl jdk8Interface = new JDK8InterfaceImpl();
jdk8Interface.get();
jdk8Interface.add();
//类名.方法名调用静态方法,不能用对象名.静态方法
JDK8Interface.del();
}
}
注:刚开始可能会报以下错误,选择第二个设置jdk1.8环境即可
2.函数式接口
函数式接口的定义:
- 在接口中只能有一个抽象方法
- @FunctionalInterface 标记该接口为函数接口
- 可以用defaut修饰普通方法,允许有方法体
- 可以定义object类的抽象方法(不计入抽象方法的数目里,也不必重写)
//显式表明这是一个函数式接口,加入注解会帮助检查是否符合规范 @FunctionalInterface public interface MyInterface{ //定义一个抽象方法 void get(); //可以用default定义普通方法 default void add(){ System.out.println("add"); } /** *Object父类中的方法可以在函数接口写,不纳入只有一个抽象方法的范围 */ String toString(); //不能定义,会报错,除了Object父类的抽象方法外,只能写一个抽象方法 //String toString01(); }
3.lambda表达式(重要)
Lambda 表达式是一个匿名函数,简化我们调用匿名函数的过程,针对函数式接口
场景:有一个OrderService接口,里面有只有一个抽象方法,调用这个方法的方式有三种:
public interface OrderService {
void get();
}
1.创建一个接口实现类重写方法,利用实现类对象调用(如果只调用一次get,这个方法太麻烦)
2.利用匿名内部类.方法名
new OrderService() {
@Override
public void get() {
System.out.println("get");
}
}.get();
可以看出,如果只调用一次的话,这个代码还是比较繁琐的
注:凡是使用IDEA的,如符合使用lambda的,都有黄色的灯泡提示用lambda一键替换
3.lambda表达式(更简洁)
格式: (参数列表) -> {方法体}
精简写法:
- 方法体只有一句话的时候,不需要写{}
- 参数列表只有一个参数时,不用带括号
- 如果方法体只有一条return的情况下不需要些{} 和return
//方式1
//最外面的括号表示这是一个匿名内部类对象
//(OrderSerive) 表示类型转换
((OrderService) () -> System.out.println("get")).get();
//方式2 : 接口声明指向内部类对象
OrderService orderService = () -> System.out.println("get02");
orderService.get();
lambda有参方法调用
@FunctionalInterface
public interface MyInterface {
String get(String i);
}
//接口引用 指向 匿名内部类重写
MyInterface myInterface = i -> i;
System.out.println(myInterface.get("String"));
lambda 实例--forEach
定义一个String类型的ArrayList,添加元素,然后进行forEach遍历,分别用两种情况展示
ArrayList strings = new ArrayList<>();
strings.add("123");
strings.add("456");
strings.add("789");
//不用Lambda遍历
//源码Comsumer是一个函数式接口,只有一个accept的抽象方法,故可以用lambda
strings.forEach(new Consumer() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//使用lambda遍历
strings.forEach(s-> System.out.println(s));
lambda实例--Comparator
定义一个Interger类型的ArrayList,添加元素,然后进行升序排序,分别用两种情况展示
ArrayList ints = new ArrayList();
ints.add(9);
ints.add(6);
ints.add(10);
//方法一
//Comparator是一个 有两个参数的抽象方法 的函数式接口
ints.sort(new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
//方法二
ints.sort((o1,o2) -> o1-o2);
lambda实例--线程调用
//传统的方法.匿名内部类
//其中Runnable是函数式接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}).start();
//lambda表达式
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
4.方法构造和函数引用 什么是方法引入
方法引入:需要结合lambda表达式能够让代码变得更加精简。
代码简单程度由上到下依次为:
- 匿名内部类使用
- Lambda调用匿名内部类
- 方法引入
- 静态方法引入 类名::(静态)方法名称
- 对象方法引入 类名:: 实例方法名称
- 实例方法引入 new对象 对象实例::方法引入
- 构造函数引入 类名::new
有一个函数式接口的抽象方法,返回类型为String,参数列表为int
有一个静态方法,它属于一个类,返回类型为String,参数列表为int
倒数第一行代码:接口引用 指向 静态方法,这就叫做静态方法引入,条件是:静态方法和抽象方法必须参数列表和返回类型一致
这样做的结果就是,相当于静态方法 就是 抽象方法的重写
//函数式接口
@FunctionalInterface
public interface Int2String {
String change(int num);
}
----------------新建一个测试类---------------------
public class Test04 {
//参数为int,返回为String的静态方法
public static String staticMethod(int num){
return String.valueOf(num);
};
public static void main(String[] args) {
//lambda写法:重写了change()
Int2String int2String_0 = (num) -> String.valueOf(num);
//静态方法引入:接口引用 指向 静态方法: 静态方法相当于重写方法
Int2String int2String = Test04::staticMethod;
}
}
实例方法引入
有一个函数式接口的抽象方法,返回类型为String,参数列表为int
类里有一个普通方法。它属于对象的实例方法,返回类型为String,参数列表为int
原理与上述静态方法一致,对象的普通方法和抽象方法必须参数列表和返回类型一致
--------------------函数式接口-------------------
@FunctionalInterface
public interface Int2String {
String change(int num);
}
-------------------------新建一个测试类-----------------------
public class Test04 {
//新建一个对象的实例方法
public String method(int num){
return String.valueOf(num);
};
public static void main(String[] args) {
//创建一个对象
Test04 test04 = new Test04();
//lambda写法
Int2String int2String_0 = (num) -> test04.method(num);
//对象方法引入
Int2String int2String = test04::method;
}
对象方法引入 (对比实例方法引入)
有一个函数式接口的抽象方法,返回类型为String,参数列表第一个为Test类,第二个为int
类里有一个普通方法。它属于对象的实例方法,返回类型为String,只有一个参数为int
对象方法引入可以不需要新建对象,用 类名::非静态方法(实例方法) 即可,为什么可以?
因为抽象方法的第一个参数为指定类,第二个参数开始的参数必须与实例方法一致(可有可无)
--------------------函数式接口-------------------
@FunctionalInterface
public interface Int2String {
String change(Test tst,int num);
}
-------------------------新建一个测试类-----------------------
public class Test {
//新建一个普通方法
public String method(int num){
return "普通方法";
};
public static void main(String[] args) {
//lambda表达式
Int2String int2String = (tst,num) -> "这是lambda表达式";
//对象方法引入
Int2String int2String = test::method;
}
}
构造方法引入
有一个函数式接口,抽象方法无参数,返回Student类型
有个Student类,其实有无参构造函数
原理与上述静态方法一致,类的构造方法和抽象方法必须参数列表和返回类型一致
-------------------函数式接口------------------------
@FunctionalInterface
public interface StuInterface {
Student method();
}
---------------------学生类--------------------------
public class Student{
//无参构造
public Student(){};
}
--------------------测试类---------------------------
public class Test04 {
public static void main(String[] args) {
//lambda写法
StuInterface stuInterface0 = () -> new Student();
//构造引用的写法
StuInterface stuInterface = Student::new;
}
}
5.Stream流接口(重要)
Stream 是JDK1.8 中处理集合的关键抽象概念,它可以对集合进行的 *** 作,可以执行非常复杂的查找、过滤和映射数据等 *** 作
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
Stream的创建:- Stream分为并行流和串行流,并行流效率较高
- parallelStream为并行流采用多线程执行
- Stream采用单线程执行
//先定义一个学生集合,其中最后两个是重复的
ArrayList stus= new ArrayList<>();
stus.add(new Student("xiaoming", 20));
stus.add(new Student("xiaohong", 28));
stus.add(new Student("xiaosan", 35));
stus.add(new Stuent("xiaowen", 16));
stus.add(new Student("xiaowen", 16));
//创建集合的串行流
Stream stuStream = stus.stream();
//创建集合的并行流
Stream parStream = stus.parallelStream();
//也可以串行流转为并行流
parStream = stuStream.parallel();
Stream的常见错误
stream has already been operated upon or closed;Exception in thread "main" java.lang.IllegalStateException:
错误的原因是因为:以下提到的SteamAPI对一个Stream对象只能使用一次,使用一次以上就会报错!
---除非流对象经过API处理后返回一个新的流对象,那就可以继续 *** 作
否则需要重新创建Stream对象,再使用API *** 作
Stream -- List 转Set要解决的问题:集合的去重
//先将集合转为流
Stream stuStream = stus.stream();
//运用流的API转为Set
Set collectSet = stuStream.collect(Collectors.toSet());
此时,若遍历打印collectSet,会发现Set里还有重复的
因为两个对象的内存地址不同,equals默认是对比两个对象的地址
所以导致虽然姓名和年龄一样的对象,系统也不会判定他俩是同一个人,因此需要在Student类里重写equals()与hashCode()方可解决
Stream -- List 转Map要解决的问题:
集合stus只存放了Student对象,但是Map有Key与Value,我们先确定Map要怎么存,初步定为:Key = stuName,Value = Student对象
//先将集合转为流
Stream stuStream = stus.stream();
//key为String类型 value为Student类型
Map collect = stuStream.collect(Collectors.toMap(new Function() {
@Override
public String apply(Student student) {
return student.getName();
}
}, new Function() {
@Override
public Student apply(Student student) {
return student;
}
}));
/**第一个new Function 的apply方法返回的是key
*第二个new Function 的apply方法返回的是value
*Function<参数1,参数2>,参数一表示集合里的类型,参数二表示返回类型
*以上可在IDEA点击黄色灯泡一键变为lambda表达式
*/
//lambda超精简写法
collect = stuStream.collect(Collectors.toMap(student -> student.getName(), student -> student));
Stream -- reduce计算求和
计算stus集合的所有学生的年龄总和,返回一个Optional对象
//先将集合转为流
Stream stuStream = stus.stream();
//默认要返回Student,所以最后是一个名字为NULL,age为总数的student
//原理是迭代运算
Optional reduce = stuStream.reduce((student1, student2) -> {
Student student = new Student();
student.setAge(student1.getAge() + student2.getAge());
student.setName("s");
return student;
});
//返回结果:Optional[Student{name='null', age=99}]
System.out.println(reduce.get());
Stream -- 最大值最小值
注意API中max和min的使用,返回Optional对象
//先将集合转为流
Stream stuStream = stus.stream();
//求最大值
Optional max= stuStream.max((o1, o2) -> o1.getAge() - o2.getAge());
//求最小值,两种方法。
//方法一:把o1.getAge()和o2.getAge()交换,max方法不变
Optional min= stuStream.max((o1, o2) -> o2.getAge() - o1.getAge());
//方法二:不改变o1 - o2,直接把max方法改为min
Optional min= stuStream.min((o1, o2) -> o1.getAge() - o2.getAge());
Stream -- Match 匹配
- anyMatch表示,判断的条件里,任意一个元素成功,返回true
- allMatch表示,判断条件里的元素,所有的都是,返回true
- noneMatch跟allMatch相反,判断条件里的元素,所有的都不是,返回true
-
Stream -- Filiter 过滤//先将集合转为流 Stream
stuStream = stus.stream(); //注意,以下API其实只能使用一次,不能三行连续写,否则报错,用一次就要重新创建对象 //false boolean res1 = stuStream.noneMatch(student -> student.getAge() > 30); //true boolean res2 = stuStream.anyMatch(student -> student.getAge() > 30); //flase boolean res3 = stuStream.allMatch(student -> student.getAge() > 30); - 要求:寻找学生年龄大于或等于20的学生。返回的是一个符合条件的新的流对象
-
Stream -- Skip和Limit 分页//先将集合转为流 Stream
stuStream = stus.stream(); //返回一个符合条件的新的流 Stream studentStream = stuStream.filter(student -> student.getAge() >= 20); - 要求:从第一条开始查询,查询两条数据。可以写for循环做分页查询。返回由查询结果组成的新的流对象
-
Stream -- Sorted 排序//先将集合转为流 Stream
stuStream = stus.stream(); //skip代表跳过几条,limit表示查几条 Stream limit = stuStream.skip(1).limit(2); -
要求:按照学生年龄从小到大排序。返回一个重新排好序的流对象
-
//先将集合转为流 Stream
stuStream = stus.stream(); //从小到大排序 Stream sorted = stuStream.sorted((o1, o2) -> o1.getAge() - o2.getAge());
6.Optional容器
Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测
Optional 类的引入很好的解决空指针异常
isPresent() 判断参数为空- ofNullable(可以传递一个空对象)
- Of(不可以传递空对象):传空会报错
- isPresent(): true 不为空 / false 为空
Integer a1 = 1;
Optional a = Optional.ofNullable(a1);
System.out.println(a.isPresent());
orElse() 设定默认值
要求:如果传递的参数为空时,设置默认值;不为空则不变
Integer a1 = null;
//a = 10
Integer a = Optional.ofNullable(a1).orElse(10);
filter(条件判断语句) 实现过滤
Integer a1 = 16;
Optional a = Optional.ofNullable(a1);
//a3为null
Optional a3 = a.filter(a2 -> a2 > 17);
//a4 = 16
Optional a4 = a.filter(a2 -> a2 < 17);
//fasle
System.out.println(a3.isPresent());
//16
System.out.println(a4.get());
优化场景一:ifPresent (new Comsumer 消费型函数接口)
场景定义一个字符串,要求字符串为null就不做处理,字符串not null就做消费动作:打印
String name = "meite";
//优化前,要写三行代码
if (name != null) {
System.out.println(name);
}
//优化后
Optional name2 = Optional.ofNullable(name);
// 当value 不为空时,则不会调用
//lambda表达式,这是一个Comsumer的函数式接口
name2.ifPresent(s -> System.out.println(s));
//方法引入表达
name2.ifPresent(System.out::print);
优化场景二:orElseGet (new Supplier 供给型函数接口)
场景:现有一个Student类的stu对象,若它为空,则new对象。那么传统方法是写if语句;可以用orElseGet()做到简单
public class Test04 {
private static Student stu = null;
public static void main(String[] args) {
Student stu = Test04.getstu();
System.out.println(stu);
}
public static Student getstu() {
// 优化前:四行代码
if (stu == null) {
return createstu();
}
return stu;
// 优化后:一行代码
return Optional.ofNullable(stu).orElseGet(Test04::createstu);
}
private static Student createstu() {
return new Student("123456", 12);
}
}
优化场景三:map (new Function 函数型函数接口)
经过map处理后的返回值为Optional类型
flatMap中返回值保持不变,但必须是Optional类型,即Optional<返回值> -> Optional<返回值>
场景:有个Student类的stu对象,如果为空则new对象,不为空之后,把orderName字符串变为小写
---------------学生类--------------
public class Student {
public String name;
//构造方法
public Student(String name) {
this.name = name;
}
//初始化实例方法
public Student getInit() {
return new Student("xiaowen");
}
}
-------------------------测试类--------------------------------
public class Test04 {
public static void main(String[] args) {
String stuName = Test04.getlowName();
System.out.println(stuName);
}
public static String getlowName() {
// 优化前写法:
Student stu = new Student("xiaowen");
if (stu != null) {
stu= stu.getInit(); //若stu为空,则初始化
if (stu.name != null) {
return stu.name.toLowerCase(); //name 不为空,则转小写
}
}
//优化后写法:
return Optional.ofNullable(stu).map(Student -> Student.getInit()).map(stud -> stud.name.toLowerCase()).orElse(null);
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)