Java8新特性
需求: 获取年龄大于35岁的用户信息
POJO
package com.example.pojo;
public class User {
private String userName;
private String sex;
private Integer age;
private Integer salary;
public User() {
}
public User(String userName, String sex, Integer age, Integer salary) {
this.userName = userName;
this.sex = sex;
this.age = age;
this.salary = salary;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSalary() {
return salary;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", salary=" + salary +
'}';
}
}
- 获取方法
List<User> userList= Arrays.asList(
new User("张三", "男", 19, 1000),
new User("李四", "男", 36, 2000),
new User("王五", "男", 39, 3000),
new User("JACK", "男", 28, 2500),
new User("LUCY", "女", 40, 1500)
);
// 获取年龄大于35的用户信息
List<User> findAge(List<User> userList){
ArrayList<User> users = new ArrayList<>();
for (User user : userList) {
if(user.getAge() >= 35){
users.add(user);
}
}
return users;
}
@Test
void test() {
System.out.println(findAge(userList));
}
- 第一次优化(策略设计模式)
package com.example.pojo;
public interface MyPredicate<T> {
public boolean test(T t);
}
实现接口做不同的处理
package com.example;
import com.example.pojo.MyPredicate;
import com.example.pojo.User;
public class FilterUserByAge implements MyPredicate<User> {
@Override
public boolean test(User user) {
return user.getAge()>35;
}
}
package com.example;
import com.example.pojo.MyPredicate;
import com.example.pojo.User;
public class FilterUserSalary implements MyPredicate<User> {
@Override
public boolean test(User user) {
return user.getSalary()>=2000;
}
}
调用
@Test
void test() {
// 年龄大于35
System.out.println(filterUser(userList, new FilterUserByAge()));
// 薪资大于2000
System.out.println(filterUser(userList, new FilterUserSalary()));
}
// 优化方式一: 策略设计模式
List<User> filterUser(List<User> list, MyPredicate<User> mp){
ArrayList<User> users = new ArrayList<>();
for (User user : list) {
if (mp.test(user)){
users.add(user);
}
}
return users;
}
- 第二次优化(匿名内部类)
@Test
void test() {
List<User> users = filterUser(userList, new MyPredicate<User>() {
@Override
public boolean test(User user) {
return user.getAge() >= 35;
}
});
System.out.println(users);
}
- 第三次优化(Lambda表达式)
@Test
void test() {
List<User> users = filterUser(userList, (user) -> user.getAge() >= 35);
users.forEach(System.out::println);
}
- 第四次优化(Stream API)
@Test
void test() {
userList.stream()
.filter((user) -> user.getAge() >= 35)
.forEach(System.out::println);
}
1.Lambda表达式*
为什么使用Lambda表达式: Lambda是一个匿名函数,可以吧Lambda理解为一段可以传递的代码
基本用法
- 无参无返回值
@Test
public void test1(){
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello Word!");
}
};
r.run();
Runnable r2 = () -> System.out.println("Hello Lambda!");
r2.run();
}
- 一个参数, 无返回值,只有一个参数()可以不写
@Test
public void test2(){
Consumer<String> consumer = (t) -> System.out.println(t);
consumer.accept("Hello Lambda!");
}
- 两个以上参数, 有返回值,Lambda体有多条语句
@Test
public void test3(){
Comparator<Integer> com = (x, y) -> {
System.out.println("Lambda体1");
System.out.println("Lambda体2");
return x + y;
};
System.out.println(com.compare(3, 4));
}
2.函数式接口
Lambda表达式需要函数式接口的支持, 函数式接口是指接口中只有一个抽象方法的接口
可以通过注解@FunctionalInterface,检查接口是不是函数式接口
四大内置核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer消费型接口 | T | void | 对类型为T的对象应用操作:void accept(T t) |
Supplier提供型接口 | 无 | T | 返回类型为T的对象:T get() |
Function<T, R>函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t) |
Predicate断言型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t) |
- Consumer : 消费型接口
void accept(T t);
@Test
void test3(){
happy(1000, (x) -> System.out.println("消费了" + x + "元"));
}
void happy(double money, Consumer<Double> con){
con.accept(money);
}
- Supplier : 供给型接口
T get();
@Test
void test4(){
System.out.println(getNumList(6, () -> (int)(Math.random()*100)));
}
List<Integer> getNumList(int num, Supplier<Integer> sup){
ArrayList<Integer> integers = new ArrayList<>();
for (int i = 0; i < num; i++) {
integers.add(sup.get());
}
return integers;
}
- Function<T, R> : 函数型接口
R apply(T t);
@Test
void test5(){
System.out.println(strHandler("\t\t\t Hello Function!!!! ", s -> s.trim()));
}
String strHandler(String str, Function<String, String> fun){
return fun.apply(str);
}
- Predicate : 断言型接口
boolean test(T t);
List<String> strList=Arrays.asList(
"Jack",
"Back",
"AAA",
"BDC"
);
@Test
void test6(){
System.out.println(filterStr(strList, (s) -> s.length() < 4));
}
List<String> filterStr(List<String> str, Predicate<String> pre){
ArrayList<String> strings = new ArrayList<>();
for (String s : str) {
if (pre.test(s)){
strings.add(s);
}
}
return strings;
}
3.方法引用与构造引用
4.Stream API*
什么是Stream
流,是数据渠道,用于操作数据源所生成的元素序列
Stream的操作步骤
- 创建Stream
// 创建Stream
@Test
void test7(){
// 通过Collection提供的stream()
ArrayList<String> strings = new ArrayList<>();
Stream<String> stream = strings.stream();
// 通过Arrays中的静态方法stream创建数组流
User[] users = new User[10];
Stream<User> stream1 = Arrays.stream(users);
// 通过Stream类中的静态方法of()
Stream<String> a = Stream.of("a", "b", "c");
// 创建无限流
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
- 中间操作
筛选与切片
- filter 从流中排除某些元素
- limit 截断流,使其不超过指定数量
- skib 跳过流的前n个元素
- distinct 去重,通过重写equals()和hashCode()
List<User> userList= Arrays.asList(
new User("张三", "男", 19, 1000),
new User("李四", "男", 36, 2000),
new User("王五", "男", 39, 3000),
new User("JACK", "男", 28, 2500),
new User("LUCY", "女", 40, 1500),
new User("LUCY", "女", 40, 1500)
);
@Test
void test8(){
// 中间操作
Stream<User> userStream = userList.stream()
.filter((e) -> e.getAge() >= 30)
.limit(4)
.skip(2)
.distinct();
// 终止操作
userStream.forEach(System.out::println);
}
打印结果:User{userName=‘LUCY’, sex=‘女’, age=40, salary=1500}
映射
- map 接收一个lambda, 提取信息, 接收一个函数作为参数, 该函数会被应用到每个元素上。
@Test
void test9(){
// 获取所有用户的名字
userList.stream()
.map(User::getUserName)
.forEach(System.out::println);
}
- flatmap 接收一个函数作为参数,将流中的每一个值换成另一个流,然后把所有流连接成一个流。
排序
- sorted() – 自然排序
- sorted(Comparator com) – 定制排序
List<User> userList= Arrays.asList(
new User("张三", "男", 19, 2000),
new User("王五", "男", 39, 3000),
new User("JACK", "男", 28, 2500),
new User("LUCY", "女", 40, 1500),
new User("LUCY", "女", 40, 1500),
new User("李四", "男", 19, 1000)
);
List<String> list = Arrays.asList("bbb", "aaa", "ddd", "ccc");
@Test
void test10(){
list.stream()
.sorted()
.forEach(System.out::println);
userList.stream()
.sorted((e1, e2) -> {
if (e1.getAge() == e2.getAge()){
return e1.getSalary().compareTo(e2.getSalary());
}else {
return e1.getAge().compareTo(e2.getAge());
}
})
.forEach(System.out::println);
}
结果
- 终止操作
查找与匹配
- allMatch – 检查是否匹配所有元素
- anyMatch – 检查是否至少匹配一个元素
- noneMatch – 检查是否没有匹配所有元素
- findFirst – 返回第一个元素
- findAny – 返回当前流中的任意元素
- count – 返回流中元素的总个数
- max – 返回流中的最大值
- min – 返回流中的最小值
List<User> userList= Arrays.asList(
new User("张三", "男", 19, 2000, User.Status.BENCH),
new User("张三", "男", 19, 2000, User.Status.BENCH),
new User("王五", "男", 39, 3000, User.Status.VOCATION),
new User("JACK", "男", 28, 2500, User.Status.BUSY),
new User("LUCY", "女", 40, 1500, User.Status.BENCH),
new User("LUCY", "女", 40, 1500, User.Status.VOCATION),
new User("李四", "男", 19, 1000, User.Status.BUSY)
);
@Test
void test11(){
boolean b = userList.stream()
.allMatch((e) -> e.getStatus().equals(User.Status.BUSY));
System.out.println(b);
boolean b1 = userList.stream()
.anyMatch((e) -> e.getStatus().equals(User.Status.BUSY));
System.out.println(b1);
boolean b2 = userList.stream()
.noneMatch((e) -> e.getStatus().equals(User.Status.BUSY));
System.out.println(b2);
Optional<User> first = userList.stream()
.sorted((e1, e2) -> Integer.compare(e1.getSalary(), e2.getSalary()))
.findFirst();
System.out.println(first.get());
System.out.println("---------");
// parallelStream并行的方式,谁先找到是谁的
Optional<User> any = userList.parallelStream()
.filter((e) -> e.getStatus().equals(User.Status.BENCH))
.findAny();
System.out.println(any.get());
long count = userList.stream()
.count();
System.out.println(count);
Optional<User> max = userList.stream()
.max((e1, e2) -> Integer.compare(e1.getSalary(), e2.getSalary()));
System.out.println("薪资最高的用户"+max.get());
Optional<User> min = userList.stream()
.min((e1, e2) -> Integer.compare(e1.getSalary(), e2.getSalary()));
System.out.println("薪资最低的用户"+max.get());
}
规约
- reduce(T identity, BinaryOperator)/reduce(BinaryOperator) – 可以将流中的元素反复结合起来,得到一个值
List<User> userList = Arrays.asList(
new User("张三", "男", 19, 2000, User.Status.BENCH),
new User("张三", "男", 19, 2000, User.Status.BENCH),
new User("王五", "男", 39, 3000, User.Status.VOCATION),
new User("JACK", "男", 28, 2500, User.Status.BUSY),
new User("LUCY", "女", 40, 1500, User.Status.BENCH),
new User("LUCY", "女", 40, 1500, User.Status.VOCATION),
new User("李四", "男", 19, 1000, User.Status.BUSY)
);
List<Integer> intList = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
/**
* identity为初始值
*/
@Test
void test12(){
Integer intReduce = intList.stream()
.reduce(3, Integer::sum);
System.out.println(intReduce);
Integer reduce = userList.stream()
.map(User::getSalary)
.reduce(0, Integer::sum);
System.out.println(reduce);
}
收集
- collect – 将流转换成其他形式.接收一个Collector接口的实现,用于给Stream中元素做汇总
通过Collectors中的静态方法
@Test
void test13() {
Set<String> SetCollect = userList.stream()
.map(User::getUserName)
.collect(Collectors.toSet());
SetCollect.forEach(System.out::println);
// 自己new的集合
ArrayList<String> listCollect = userList.stream()
.map(User::getUserName)
.collect(Collectors.toCollection(ArrayList::new));
// 总和
Long collect = userList.stream()
.collect(Collectors.counting());
System.out.println(collect);
// 分组
Map<User.Status, List<User>> groupCollect = userList.stream()
.collect(Collectors.groupingBy(User::getStatus));
System.out.println("将用户按状态分组" + groupCollect);
// 平均薪资
Double avgSalaryCollect = userList.stream()
.collect(Collectors.averagingInt(User::getSalary));
System.out.println("用户平均薪资"+avgSalaryCollect);
// 最大
Optional<User> maxSalaryCollect = userList.stream()
.collect(Collectors.maxBy((e1, e2) -> e1.getSalary().compareTo(e2.getSalary())));
System.out.println("薪资最高用户" + maxSalaryCollect.get());
// 最小
Optional<User> minSalaryCollect = userList.stream()
.collect(Collectors.minBy((e1, e2) -> e1.getSalary().compareTo(e2.getSalary())));
System.out.println("薪资最低用户" + minSalaryCollect.get());
}
中间操作不会执行任何操作, 直到终止操作一次性执行全部内容,即惰性求值
并行流与顺序流
并行流: 把一个内容分成多个数据块,用不同的线程处理每个数据块的流
顺序流: 是单线程的
Stream API可以声明性地通过parallel()并行流与sequential()顺序流之间进行切换。
@Test
void test14() {
long parallelStartTime = System.currentTimeMillis();
LongStream.rangeClosed(0, 1000000000L)
.parallel()
.reduce(0, Long::sum);
long parallelEndTime = System.currentTimeMillis();
System.out.println("并行流" + (parallelEndTime - parallelStartTime));
System.out.println("-----------------------");
long orderStartTime = System.currentTimeMillis();
LongStream.rangeClosed(0, 1000000000L)
.sequential()
.reduce(0, Long::sum);
long orderEndTime = System.currentTimeMillis();
System.out.println("顺序流" + (orderEndTime - orderStartTime));
}
Optional类
Optional < T > 类是一个容器类, 代表一个值存在或者不存在, 能更好的避免空指针异常。
常用方法
方法 | 作用 |
---|---|
Optional.of(T t) | 创建一个Optional实例 |
Optional.empty() | 创建一个空的Optional实例 |
Optional.ofNullable(T t) | 若t不为null,创建Optional实例,否则创建空实例 |
@Test
void test15(){
Optional<User> userOptional = Optional.of(new User());
System.out.println(userOptional.get());
Optional<User> userNullOptional = Optional.empty();
System.out.println(userNullOptional.get());
Optional<User> userNullOptional3 = Optional.ofNullable(null);
System.out.println(userNullOptional3.get());
}
实质上为null的话,可以快速定位到空值的地方。
方法 | 作用 |
---|---|
isPresent() | 判断是否包含空值 |
orElse(T t) | 如果调用对象包含值,返回该值,否则返回t |
orElseGet(Supplier s) | 如果调用对象包含值, 返回该值, 否则返回s获取的值 |
map(Function f) | 如果有值对其进行处理,并返回处理后的Optional,否则返回Option.empty() |
flatMap(Function mapper) | 与map类似,要求返回值为Option |
@Test
void test16() {
Optional<User> userNullOptional1 = Optional.ofNullable(null);
if (userNullOptional1.isPresent()) {
System.out.println("userNullOptional1:" + userNullOptional1.get());
} else {
System.out.println("为空不做任何操作");
}
Optional<User> userNullOptional2 = Optional.ofNullable(new User("XQ", "女", 11, 10000, User.Status.BUSY));
if (userNullOptional2.isPresent()) {
System.out.println("userNullOptional2:" + userNullOptional2.get());
}
// 避免空指针异常
System.out.println("Optional为null时" + userNullOptional1.orElse(new User("XQ", "女", 11, 10000, User.Status.BUSY)));
System.out.println("Optional不为null时"+userNullOptional2.orElse(new User("XQ", "女", 11, 10000, User.Status.BUSY)));
Optional<String> userName = userNullOptional2.map((user -> user.getUserName()));
System.out.println(userName.get());
Optional<String> userNameOptional = userNullOptional2.flatMap((e) -> Optional.of(e.getUserName()));
System.out.println(userNameOptional.get());
}
5.接口中的默认方法与静态方法
默认方法: 在接口总用default修饰的方法可以直接实现
public interface MyFun {
default String getMsg() {
return "哈哈哈";
}
public static void show(){
System.out.println("接口中的静态方法");
}
}
接口中默认方法的类优先原则
一个接口定义了一个默认方法,而另一个父类或接口中又定义了一个同名的方法
1.选择父类中的方法
2.接口冲突,如果两个接口中提供了相同名称和参数的默认方法,那么实现类必须重写该方法解决冲突。
新建一个类,方法与接口中的默认方法一样
public class MyClass {
public String getMsg(){
return "嘿嘿嘿";
}
}
同时继承类和实现接口
public class SubClass extends MyClass implements MyFun {
}
调用
@Test
void test17(){
MyFun.show();
String msg = new SubClass().getMsg();
System.out.println(msg);
}
打印结果为 嘿嘿嘿
6.新时间日期API
传统的时间API存在线程安全的问题,多线程开发的时候必须上锁, 所以java8提供了一套全新的时间API:
1. LocalDate, LocalTime, LocalDateTime
LocalDate、 LocalTime、 LocalDateTime 类的实例是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。
@Test
void test18(){
//获取当前时间日期 now
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);
//指定时间日期 of
LocalDateTime ldt2 = LocalDateTime.of(2020, 05, 17, 16, 24, 33);
System.out.println(ldt2);
//加 plus
LocalDateTime ldt3 = ldt2.plusYears(2);
System.out.println(ldt3);
//减 minus
LocalDateTime ldt4 = ldt2.minusMonths(3);
System.out.println(ldt4);
//获取指定的你年月日时分秒... get
System.out.println(ldt2.getDayOfYear());
System.out.println(ldt2.getHour());
System.out.println(ldt2.getSecond());
}
LocalDate, LocalTime雷同
2. Instant 时间戳
以 Unix 元年 1970-01-01 00:00:00 到某个时间之间的毫秒值
@Test
void test18(){
// 默认获取 UTC 时区 (UTC:世界协调时间)
Instant ins1 = Instant.now();
System.out.println(ins1);
//带偏移量的时间日期 (如:UTC + 8)
OffsetDateTime odt1 = ins1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt1);
//转换成对应的毫秒值
long milli1 = ins1.toEpochMilli();
System.out.println(milli1);
//构建时间戳
Instant ins2 = Instant.ofEpochSecond(60);
System.out.println(ins2);
}
3. 时间/日期 间隔
- Duration:计算两个时间之间的间隔
- Period:计算两个日期之间的间隔
@Test
void test19(){
// 计算两个时间之间的间隔 between
Instant ins1 = Instant.now();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Instant ins2 = Instant.now();
Duration dura1 = Duration.between(ins1, ins2);
System.out.println(dura1.getSeconds());
System.out.println(dura1.toMillis());
}
@Test
void test20(){
LocalDate ld1 = LocalDate.of(2018, 10, 1);
LocalDate ld2 = LocalDate.now();
// ISO 标准
Period period = Period.between(ld1, ld2);
System.out.println(period.getYears());
System.out.println(period.toTotalMonths());
}
3. 时间校正器
@Test
void test21(){
//TemporalAdjusters:时间校正器
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);
//指定日期时间中的 年 月 日 ...
LocalDateTime ldt2 = ldt1.withDayOfMonth(10);
System.out.println(ldt2);
//指定时间校正器
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义时间校正器
LocalDateTime ldt5 = ldt1.with((l) -> {
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dow1 = ldt4.getDayOfWeek();
if (dow1.equals(DayOfWeek.FRIDAY)) {
return ldt4.plusDays(3);
} else if (dow1.equals(DayOfWeek.SATURDAY)) {
return ldt4.plusDays(2);
} else {
return ldt4.plusDays(1);
}
});
System.out.println(ldt5);
}
4. 时间日期格式化
- DateTimeFormatter:格式化时间 / 日期
@Test
void test22(){
//默认格式化
DateTimeFormatter dtf1 = DateTimeFormatter.ISO_DATE_TIME;
LocalDateTime ldt1 = LocalDateTime.now();
String str1 = ldt1.format(dtf1);
System.out.println(str1);
//自定义格式化 ofPattern
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime ldt2 = LocalDateTime.now();
String str2 = ldt2.format(dtf2);
System.out.println(str2);
//解析
LocalDateTime newDate = ldt1.parse(str1, dtf1);
System.out.println(newDate);
}
5. 时区
- ZonedDate
- ZonedTime
- ZonedDateTime
@Test
void test23(){
//查看支持的时区
Set<String> set = ZoneId.getAvailableZoneIds();
set.forEach(System.out::println);
//指定时区
LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
System.out.println(ldt1);
//在已构建好的日期时间上指定时区
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Europe/Tallinn"));
ZonedDateTime zdt1 = ldt2.atZone(ZoneId.of("Europe/Tallinn"));
System.out.println(zdt1);
}
格式的转换
@Test
public void test03(){
// Date 转 LocalDateTime
Date date = new Date();
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
LocalDateTime localDateTime = instant.atZone(zoneId).toLocalDateTime();
// LocalDateTime 转 Date
LocalDateTime localDateTime = LocalDateTime.now();
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zdt = localDateTime.atZone(zoneId);
Date date = Date.from(zdt.toInstant());
// 原则:利用 时间戳Instant
}
7.其他新特性
重复注解
定义注解:
@Repeatable(MyAnnotations.class) //指定容器类
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "Java 8";
}
定义容器:
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
MyAnnotation[] value();
}
public class Test01 {
//重复注解
@Test
@MyAnnotation("Hello")
@MyAnnotation("World")
public void test01() throws NoSuchMethodException {
Class<Test01> clazz = Test01.class;
Method test01 = clazz.getMethod("test01");
MyAnnotation[] mas = test01.getAnnotationsByType(MyAnnotation.class);
for (MyAnnotation ma : mas) {
System.out.println(ma.value());
}
}
}
类型注解
新增ElementType.TYPE_USE 和ElementType.TYPE_PARAMETER(在Target上)