day41
JDK1.8新特性
JDK1.8新特性简介
- 速度更快 - 优化底层源码,比如HashMap、ConcurrentHashMap
- 代码更少 - 添加新的语法Lambda表达式
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常 - Optional
Lambda表达式
简介
Lambda是一个匿名函数(方法), 允许把函数作为一个方法的参数 。利用Lambda表达式可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
一般都是优化匿名内部类
基础语法
无参数、无返回值的抽象方法
java
public class Test1 {
@Test
public void test01() {
// I1 i1 = new I1() {
// @Override
// public void method() {
// System.out.println("传统使用匿名内部类的方式");
// }
// };
// i1.method();
// I1 i1 = ()->{
// System.out.println("使用lambda表达式的方式");
// };
// i1.method();
//lambda表达式里面只有一个语句情况,大括号可以省略进行简化代码
I1 i1 = ()-> System.out.println("采用Lambda表达式的方式");
i1.method();
}
}
interface I1{
public void method();//无参数、无返回值的抽象方法
}
一个参数、无返回值的抽象方法
外部new匿名内部类调用方法时,参数看接口定义的规范里的参数类型
java
public class Test01 {
public static void main(String[] args) {
// I1 i1 = new I1() {
// @Override
// public void method(String str) {
// System.out.println("使用传统匿名内部类的方式:" + str);
// }
// };
// i1.method("过往云烟");
// I1 i1 = (String str)->{
// System.out.println("使用lambda表达式的方式:" + str);
// };
// i1.method("过往云烟");
// I1 i1 = (String str)->System.out.println("使用lambda表达式的方式:" + str);
// i1.method("过往云烟");
// I1 i1 = (str)->System.out.println("使用lambda表达式的方式:" + str);
// i1.method("过往云烟");
I1 i1 = str->System.out.println("使用lambda表达式的方式:" + str);
i1.method("过往云烟");
}
}
public interface I1 {
public void method(String str);//一个参数、无返回值的抽象方法
}
多个参数、无返回值的抽象方法
java
public class Test01 {
public static void main(String[] args) {
// I1 i1 = new I1() {
// @Override
// public void method(String str, int i) {
// System.out.println("使用传统匿名内部类的方式:" + str + " -- " + i);
// }
// };
// i1.method("过往云烟", 666);
// I1 i1 = (String str,int i)->{
// System.out.println("使用lambda表达式的方式:" + str + " -- " + i);
// };
// i1.method("过往云烟", 777);
// I1 i1 = (String str,int i)->System.out.println("使用lambda表达式的方式:" + str + " -- " + i);
// i1.method("过往云烟", 888);
I1 i1 = (str, i)->System.out.println("使用lambda表达式的方式:" + str + " -- " + i);
i1.method("过往云烟", 999);
}
}
public interface I1 {
//多个参数、无返回值的抽象方法
public void method(String str,int i);
}
多个参数、有返回值的抽象方法
java
public class Test01 {
public static void main(String[] args) {
// I1 i1 = new I1() {
// @Override
// public String method(int a, int b) {
// return "使用传统匿名内部类的方式:" + (a+b);
// }
// };
// String method = i1.method(10, 10);
// System.out.println(method);
// I1 i1 = (int a,int b)->{
// return "使用lambda表达式的方式:" + (a+b);
// };
// String method = i1.method(20, 20);
// System.out.println(method);
I1 i1 = (a, b)-> "使用lambda表达式的方式:" + (a+b);
String method = i1.method(30, 30);
System.out.println(method);
}
}
public interface I1 {
//多个参数、有返回值的抽象方法
public String method(int a,int b);
}
注意点
- 重写方法的形参只有一个时,可以不加小括号
- Lambda表达式当中不允许声明一个与局部变量同名的参数或者局部变量
- Lambda表达式中访问外层的局部变量,外层的局部变量自动变成隐式常量,默认添加final
- 重写方法的形参同时加类型或同时不加类型
java
public class Test1 {
@Test
public void test01() {
int x;
int num = 10;
I1 i1 = x -> System.out.println(x + (num++));
i1.method(1000);
I2 i2 = (int x,int y) -> {
int result = x+y;
return result;
};
int result = i2.method(10, 20);
System.out.println(result);
}
}
interface I1{
public void method(int num1);
}
interface I2{
public int method(int num1,int num2);
}
案例
案例1
1.调用Collections.sort()方法,通过定制排序比较两个Student对象(先按年龄比较,年龄相同按照薪资比较),使用Lambda表达式作为参数传递
注意:比较时注意不能用减,因为doublue减后的小数会在转int损失精度,ps:0.2变为0
java
public class Test01 {
public static void main(String[] args) {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 28, 3000,Course.PYTHON));
// Collections.sort(stuList, new Comparator<Student>() {
// @Override
// public int compare(Student o1, Student o2) {
// if(o1.equals(o2)){
// return 0;
// }
//
// int compare = Integer.compare(o1.getAge(), o2.getAge());
// if(compare != 0){
// return compare;
// }
//
// return Double.compare(o1.getSalary(), o2.getSalary());
// }
// });
Collections.sort(stuList, (o1,o2)->{
if(o1.equals(o2)){
return 0;
}
int compare = Integer.compare(o1.getAge(), o2.getAge());
if(compare != 0){
return compare;
}
return Double.compare(o1.getSalary(), o2.getSalary());
});
for (Student stu : stuList) {
System.out.println(stu);
}
}
}
public enum Course{//课程枚举
JAVA,HTML,PYTHON;
}
public class Student{//学生类
private String name;
private int age;
private double salary;
private Course course;
//有参、无参、get、set方法【略】
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((course == null) ? 0 : course.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (course != other.course)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Student [name=");
builder.append(name);
builder.append(", age=");
builder.append(age);
builder.append(", salary=");
builder.append(salary);
builder.append(", course=");
builder.append(course);
builder.append("]");
return builder.toString();
}
}
案例2
2.创建I1接口,创建抽象方法:public String getValue(String str),在测试类中编写方法使用接口作为参数,将一个字符串转为大写,并作为方法的返回值
java
public class Test01 {
public static void main(String[] args) {
String str = method("abc", (x)-> {
return x.toUpperCase();
});
System.out.println(str);
}
public static String method(String str,I1 i1){
return i1.getValue(str);
}
}
interface I1{
public String getValue(String str);
}
案例3
3.创建I1<T,R>接口,泛型T为参数,R为返回值,创建抽象方法:public R add(T t1,T t2),在测试类中编写方法使用接口作为参数,计算两个long类型的和
java
public class Test01 {
public static void main(String[] args) {
long addLong = addLong(100,200,(a,b)->{
return a+b;
});
System.out.println(addLong);
}
public static long addLong(long l1,long l2,I1<Long,Long> i1){
return i1.add(l1, l2);
}
}
interface I1<T,R>{
public R add(T t1,T t2);
}
函数式接口
简介
函数式接口是指仅仅只包含一个抽象方法的接口,jdk1.8提供了一个@FunctionalInterface注解来定义函数式接口,如果我们定义的接口不符合函数式的规范便会报错。配合Lambda表达式一起使用
四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer 消费型接口 | T | void | void accept(T t); |
Supplier 供给型接口 | void | T | T get(); |
Function<T, R> 函数型接口 | T | R | R apply(T t); |
Predicate 断言型接口 | T | boolean | booelan test(T t); |
BiConsumer<T, U> | T,U | void | 对类型为T,U参数应用操作。包含方法为void accept(T t,U u); |
BiFunction<T, U, R> | T,U | R | 对类型为T,U参数应用操作,并返回R类型的结果。包含方法为R apply(T t,U u); |
UnaryOperator extends Function<T, T> | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T apply(T t); |
BinaryOperator extends BiFunction<T,T,T> | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t1,T t2); |
ToIntFunction ToLongFunction ToDoubleFunction | T | int long double | 分别计算int、long、double值的函数 |
IntFunction LongFunction DoubleFunction | int long double | R | 参数为int、long、double类型的函数 |
应用场景:当项目中需要一个接口,并且该接口中只有一个抽象方法,就没必要去创建新的接口,直接选择Java提供的使用合适的函数式接口即可
案例替换
对Lambda表达式案例,使用合适的函数式接口去替换I1
创建I1接口,创建抽象方法:public String getValue(String str),在测试类中编写方法使用接口作为参数,将一个字符串转为大写,并作为方法的返回值
注意:使用合适的函数式接口去替换I1
函数和方法:前端习惯称函数,后端习惯称方法,简单理解一个意思但非区别
函数式接口,一个抽象方法,超过就会报错
函数式接口替换接口,就案例来看更改方法里的接口,用函数调用方法,少一层
java
public class Test01 {
public static void main(String[] args) {
String str = method("abc", (x)->{
return x.toUpperCase();
});
System.out.println(str);
}
public static String method(String str,Function<String, String> fun){
return fun.apply(str);
}
}
public class Test02 {
public static void main(String[] args) {
String str = method("abc", (x)->{
return x.toUpperCase();
});
System.out.println(str);
}
public static String method(String str,UnaryOperator<String> uo){
return uo.apply(str);
}
}
创建I1<T,R>接口,泛型T为参数,R为返回值,创建抽象方法:public R add(T t1,T t2),
在测试类中编写方法使用接口作为参数,计算两个long类型的和
java
public class Test01 {
public static void main(String[] args) {
long addLong = addLong(100,200,(a,b)->{
return a+b;
});
System.out.println(addLong);
}
public static long addLong(long l1,long l2,BiFunction<Long, Long, Long> bf){
return bf.apply(l1, l2);
}
}
public class Test02 {
public static void main(String[] args) {
long addLong = addLong(100,200,(a,b)->{
return a+b;
});
System.out.println(addLong);
}
public static long addLong(long l1,long l2,BinaryOperator<Long> bo){
return bo.apply(l1, l2);
}
}
总结
1.lambda表达式(其实就有优化匿名内部类的方案)
注意:使用lambda表达式必须有多态的思想
2.函数式接口
该接口中只有一个抽象方法
@FunctionInterface这个注解表示该接口是函数式接口
经验:
如果你想玩转lambda表达式,必须先去学习匿名内部类+多态
函数式接口应用场景:需求要你写个接口,这个接口只有一个抽象方法,就用函数式接口去代替