Java8(JDK1.8)新特性
1、Lamdba表达式
2、函数式接口
3、方法引用和构造引用
4、Stream API
5、接口中的默认方法和静态方法
6、新时间日期API
7、OPtional
8、其他特性
新特性简介
1、速度快;
2、代码少、简介(新增特性:lamdba表达式);
3、强大的Stream API;
4、使用并行流和串行流;
5、最大化较少空指针异常Optional;
其中最为核心的是Lambda表达式和Stream API
新特性详细介绍
Lambda表达式
Lambda表达式是什么?
Lambda是一个匿名函数,我们可以将Lambda表达式理解为一段可以传递的代码(将代码像数据一样传递)。使用它可以写出简洁、灵活的代码。作为一种更紧凑的代码风格,使java语言表达能力得到提升。
从匿名类到Lambda转换
public class Demo01 {
private static Logger log = LoggerFactory.getLogger(Demo01.class);
public static void main(String[] args) {
Runnable t1 =new Runnable(){
@Override
public void run(){
log.info("我是没有使用Lambda表达式:不简洁");
}
};
Runnable t2 = () -> log.info("我是使用Lambda表达式:简洁、灵活");
t1.run();
t2.run();
}
}
打印的信息结果
11:41:41.239 [main] INFO org.muyu.Demo01 - 我是没有使用Lambda表达式:不简洁
11:41:41.257 [main] INFO org.muyu.Demo01 - 我是使用Lambda表达式:简洁、灵活
Lambda表达式语法
Lambda表达式在java中引入了一种新的语法元素和操作。这种操作符号为"->",Lambda操作符或箭头操作符,它将Lambda表达式分割为两部分。 左边:指Lambda表达式的所有参数 右边:指Lambda体,即表示Lambda表达式需要执行的功能。
语法格式一:无参数、无返回值,只需要一个Lambda体
new Thread(() -> log.info("我是一个线程")).start();
语法格式二:lambda有一个参数、无返回值
public class Demo03 {
private static Logger log = LoggerFactory.getLogger(Demo03.class);
public static void main(String[] args) {
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
log.info(s);
}
};
consumer.accept("爱与被爱的区别");
Consumer<String> consumer1 = (s) -> log.info(s);
consumer1.accept("接受爱不一定爱对方,爱一定付出真心爱");
}
}
语法格式三:Lambda只有一个参数时,可以省略()
Consumer<String> consumer1 = s -> log.info(s);
consumer1.accept("接受爱不一定爱对方,爱一定付出真心爱");
语法格式四:Lambda有两个参数时,并且有返回值
Comparator<Integer> comparatorMin = new Comparator<Integer>() {
@Override
public int compare (Integer o1, Integer o2) {
return o1 > o2 ? o1 : o2;
}
};
int compare = comparatorMin.compare(19, 12);
log.info("获取最大值:{}", compare);
Comparator<Integer> comparator = (Integer o1, Integer o2) -> {
return o1 > o2 ? o1 : o2;
}
log.info("获取最大值:{}", comparator.compare(12,14));
语法格式五:当Lambda体只有一条语句的时候,return和{}可以省略掉
Comparator<Integer> comparatorMin = new Comparator<Integer>() {
@Override
public int compare (Integer o1, Integer o2) {
return o1 > o2 ? o1 : o2;
}
};
int compare = comparatorMin.compare(19, 12);
log.info("获取最大值:{}", compare);
Comparator<Integer> comparator = (Integer o1, Integer o2) -> o1 > o2 ? o1 : o2;
log.info("获取最大值:{}", comparator.compare(12,14));
语法格式六:类型推断:数据类型可以省略,因为编译器可以推断得出,成为“类型推断”
Comparator<Integer> comparatorMin = new Comparator<Integer>() {
@Override
public int compare (Integer o1, Integer o2) {
return o1 > o2 ? o1 : o2;
}
};
int compare = comparatorMin.compare(19, 12);
log.info("获取最大值:{}", compare);
Comparator<Integer> comparator = (o1, o2) -> o1 > o2 ? o1 : o2;
log.info("获取最大值:{}", comparator.compare(12,14));
函数式接口
什么是函数式接口?
函数式接口:只包含一个抽象方法的接口,称为函数式接口,并且可以使用lambda表达式来创建该接口的对象,可以在任意函数式接口上使用@FunctionalInterface注解,来检测它是否是符合函数式接口。同时javac也会包含一条声明,说明这个接口是否符合函数式接口。
自定义函数式接口
@FunctionalInterface
public interface BaseMsg {
/**
* 发送信息
* @param msg
* @return
*/
public boolean send(String msg);
}
泛型函数式接口
@FunctionalInterface
public interface BaseMsg<T> {
/**
* 发送信息
* @param msg
* @return
*/
public boolean send(T msg);
}
java内置函数式接口
(Function、Consumer、Supplier、Predicate) java.util.function
Function(内置式接口的类型)
有入参有返回值
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
*
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
Consumer(消费型接口)
消费型接口:有入参,没有会有返回值
* @param <T> the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
Supplier(供给型接口)
供给型接口:没有输入参数,有返回值
*
* @param <T> the type of results supplied by this supplier
*
* @since 1.8
*/
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
Predicate(断定型接口)
断言型接口:既有输入参数也有返回值,返回类型是boolean类型
* @param <T> the type of the input to the predicate
*
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
方法引用和构造器引用
方法引用
当要传递给Lambda体的操作已经有实现方法,可以直接使用方法引用(实现抽象方法的列表,必须要和方法引用的方法参数列表一致)
方法引用:使用操作符“::”将方法名和(类或者对象)分割开来。
有下列三种情况
- 对象::实例方法
- 类::实例方法
- 类::静态方法
public static void main(String[] args) {
BaseMsg<String> strName = s -> System.out.println(s);
strName.send("Lambda表达式没有使用方法引用");
//方法引用
BaseMsg<String> strName2 = System.out::println;
strName2.send("使用方法引用");
}
方法(构造器)引用
本质上:构造器引用和方法引用相识,只是使用了一个new方法
使用说明:函数式接口参数列表和构造器参数列表要一致,该接口返回值类型也是构造器返回值类型
方法:ClassName::方法名
格式:ClassName :: new
public class Test {
/**
* 把string类型转换为int
* @param str
* @return
*/
public static Integer strToInt(String str){
return new Integer(str);
}
}
public static void main(String[] args) {
Function<String, Integer> function1 = new Function<String, Integer>() {
// apply 方法入参 -> String
// apply 方法返回值 -> Integer
@Override
public Integer apply (String s) {
// integer -> 构造函数 -> 入参 -> String / 返回值 -> Integer
return new Integer(s);
}
};
Function<String, Integer> function2 = s -> new Integer(s);
Function<String, Integer> function3 = Integer::new;
// 我们进行大胆的猜测,构造器 的入参和 apply方法的入参 保持一直
// 构造器的返回值和apply方法的出参保持一直
// 我们就可以使用 类::new 的形式
// 不管是 构造,还是有参、无参 都是属于 方法调用
// 通过这个知识推理出来 :: 是用来快捷调用 方法的
// 所以 我们进行大胆的测试 写了一个 入参和出参与apply相匹配的对象进行测试
// 测试结果 -> 符合我的猜想
Function<String, Integer> function4 = Test::strToInt;
//构造器引用
Function<String, Integer> fun1 = (num) -> new Integer(num);
Function<String, Integer> fun2 = Integer::new;
//数组引用
Function<Integer,Integer[]> fun3 = (num) ->new Integer[num];
Function<Integer,Integer[]> fun4 = Integer[]::new;
}
强大的Stream API
什么是Stream?
Java8中两个最为重要特性:第一个的是Lambda表达式,另一个是Stream API。
StreamAPI它位于java.util.stream包中,StreamAPI帮助我们更好地对数据进行集合操作,它本质就是对数据的操作进行流水线式处理,也可以理解为一个更加高级的迭代器,主要作用是遍历其中每一个元素。简而言之,StreamAP提供了一种高效且易于使用的处理数据方式。
Stream特点:
1、Stream自己不会存储数据。
2、Stream不会改变源对象。相反,它们会返回一个持有结果的新Stream对象
3、Stream操作时延迟执行的。这就意味着它们等到有结果时候才会执行。
和list不同,Stream代表的是任意Java对象的序列,且stream输出的元素可能并没有预先存储在内存中,而是实时计算出来的。它可以“存储”有限个或无限个元素。
例如:我们想表示一个全体自然数的集合,使用list是不可能写出来的,因为自然数是无线的,不管内存多大也没法放到list中,但是使用Sream就可以
Stream操作的三个步骤
- 创建Stream:一个数据源(例如:set 、list),获取一个流
- 中间操作:一个中间操作连接,对数据源的数据进行处理
- 终止操作:一个终止操作,执行中间操作连,产生结果。
创建流
集合创建
public static void createStream(){
// list
List<Clazz> clazzList = new ArrayList<>();
clazzList.add(new Clazz(1L, "2001A"));
clazzList.add(new Clazz(2L, "2002A"));
clazzList.add(new Clazz(3L, "2003A"));
clazzList.add(new Clazz(4L, "2004A"));
clazzList.add(new Clazz(5L, "2005A"));
Stream<Clazz> streamList = clazzList.stream();
// set
Set<Clazz> clazzSet = new HashSet<>();
clazzSet.add(new Clazz(1L, "2001A"));
clazzSet.add(new Clazz(2L, "2002A"));
clazzSet.add(new Clazz(3L, "2003A"));
clazzSet.add(new Clazz(4L, "2004A"));
clazzSet.add(new Clazz(5L, "2005A"));
Stream<Clazz> streamSet = clazzSet.stream();
// map
Map<Long, Clazz> clazzMap = new HashMap<>();
clazzMap.put(1L, new Clazz(1L, "2001A"));
clazzMap.put(2L, new Clazz(2L, "2002A"));
clazzMap.put(3L, new Clazz(3L, "2003A"));
clazzMap.put(4L, new Clazz(4L, "2004A"));
clazzMap.put(5L, new Clazz(5L, "2005A"));
Stream<Map.Entry<Long, Clazz>> streamMap = clazzMap.entrySet().stream();
// 数组
Clazz[] clazzArr = new Clazz[] {
new Clazz(1L, "2001A"),
new Clazz(2L, "2002A"),
new Clazz(3L, "2003A"),
new Clazz(4L, "2004A"),
new Clazz(5L, "2005A")
};
Stream<Clazz> streamArr = Arrays.stream(clazzArr);
// 自主创建
Stream<Clazz> clazzStream = Stream.of(
new Clazz(1L, "2001A"),
new Clazz(2L, "2002A"),
new Clazz(3L, "2003A"),
new Clazz(4L, "2004A"),
new Clazz(5L, "2005A")
);
//迭代
//遍历10个奇数
Stream.iterate(1,t->t+2).limit(10).forEach(System.out::println);
//生成
//生成10个随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
测试数据
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private Long id;
private String name;
private Integer age;
private Double price;
private List<Clazz> clazzList;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Clazz {
private Long id;
private String name;
}
public static List<Student> getStudentList(){
Clazz clazz1 = new Clazz(1L, "2001A");
Clazz clazz2 = new Clazz(2L, "2002A");
Clazz clazz3 = new Clazz(3L, "2003A");
Clazz clazz4 = new Clazz(4L, "2004A");
Clazz clazz5 = new Clazz(5L, "2005A");
List<Clazz> ClazzList1 = new ArrayList<Clazz>(){{
add(clazz1);
add(clazz2);
add(clazz5);
}};
List<Clazz> ClazzList2 = new ArrayList<Clazz>(){{
add(clazz1);
add(clazz2);
add(clazz3);
}};
List<Clazz> ClazzList3 = new ArrayList<Clazz>(){{
add(clazz3);
add(clazz4);
add(clazz5);
}};
Student student1 = new Student( 1L, "张三", 18, 5623.46, ClazzList1);
Student student2 = new Student( 2L, "李四", 75, 1523.16, ClazzList2);
Student student3 = new Student( 3L, "王五", 13, 46513.63, ClazzList3);
Student student4 = new Student( 4L, "赵六", 62, 95263.95, ClazzList1);
Student student5 = new Student( 5L, "张三", 52, 5134.46, ClazzList3);
Student student6 = new Student( 6L, "李四", 32, 98161.48, ClazzList2);
List<Student> studentList = new ArrayList<>();
studentList.add(student1);
studentList.add(student2);
studentList.add(student3);
studentList.add(student4);
studentList.add(student5);
studentList.add(student6);
return studentList;
}
中间操作
一个流可以后面跟随着0个或者多个中间操作,其目的是打开流,做出某种程度的数据过滤、去重、排序、映射、跳过等。然后返回一个新的流,交给下一个使用,仅仅是调用这个方法,没有真正开始遍历。
map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
过滤(filter)
public static void filter(){
List<Student> studentList = getStudentList();
// 筛选 年龄 大于 18岁的
studentList.stream()
.filter(student -> student.getAge() > 18)
.forEach(System.out::println);
}
结果
结果:
流转换(map)
public static void map(){
List<Student> studentList = getStudentList();
// 输出所有人的 姓名
studentList.stream()
.map(Student::getName)
.forEach(System.out::println);
System.out.println("-------------年龄大于20----------------");
// 输出 年龄 大于 20 岁的 姓名
studentList.stream()
.filter(student -> student.getAge() > 20)
.map(Student::getName)
.forEach(System.out::println);
}
结果
流转换(flatmap)、去重(distinct)
public static void flatMap(){
List<Student> studentList = getStudentList();
studentList.stream()
.flatMap(student -> student.getClazzList().stream())
.map(Clazz::getName)
.distinct()
.forEach(System.out::println);
}
结果:
排序(sorted)
public static void sorted(){
List<Student> studentList = getStudentList();
studentList.stream()
.sorted((student, student1) -> student1.getAge() - student.getAge())
.forEach(System.out::println);
}
结果:
终止操作
一个终止操作,执行中间操作连,产生结果。
forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator