Java8新特性
文章目录
新特性简介
- 速度更快
- Lambda表达式
- Stream API
- 便于并行
- 最大减少空指针异常 Optional
速度更快:修改底层数据结构 ,如HashMap(数组-链表-红黑树),HashSet,ConcurrentHashMap(CAS算法)
Lambda表达式
作用:
- 减少内部类的编写,提高系统可读性
- 目前技术方向转向函数式编程
语法:
// 格式一:无参数,无返回值
() -> System.out.println("Hello World")
// 格式二:一个参数,无返回值
(x) -> System.out.println(x)
// 格式三:两个以上参数
(x,y) -> {
System.out.println("业务处理");
return x+y;
}
// 格式四
(int x,int y) -> Integer.compare(x,y);
Java提供了4大核心内置 函数式接口
函数式接口:只有一个方法的接口。
/**
*
* java内置四大函数式接口
*
* Consumer<T> :消费型接口 只有输入,没有返回值
* void accept(T t);
*
* Supplier<T> :供给型接口 没有参数,只有返回值
* T get();
*
* Function<T,R> :函数型接口 有一个输入参数 参数输出
* R apply(T t);
*
* Predicate<T> :断言型接口 有一个输入参数 返回只能是布尔值
* boolean test(T t);
*
*/
// 断言型接口
void test() {
Predicate<String> pred = new Predicate(){
@Override
public boolean test(String str){
return str.isEmpty();
}
}
//简化写发
Predicate<String> p = (str)->{return str.isEmpty();};
}
方法引用:
若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”(可以理解为方法引用时Lambda表达式的另一种表现形式)
主要有三种语法格式:
- 对象::实例方法名
- 类::静态方法名
- 类::实例方法名
注意:
调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
若Lambda参数列表中的第一个参数是 实例方法的调用者,而第二个参数是实例方法的参数时,可以使用*
例子:
//方法引用
ClassName::method
//构造器引用:
ClassName::new
//数组引用:
Type::new;
Stream API
位于包: java.util.stream .*
什么是流 (Stream) ?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!
Stream的操作分为三个步骤:
- 创建Stream
- 中间操作(对数据进行操作)
- 终止操作(如果没有终止操作,中间操作是不执行的)Stream创建的方式Collection 提供了两个方法一种是stream(),还一种 parallelStream(),stream()是是创建串行流,parallelStream()创建的是并行流。
创建流:
两个获取流的方法:
default Stream<E> stream() : 返回一个顺序流
default Stream<E> parallelStream() : 返回一个并行流
在项目开发过程中,使用流式处理时,在数据量较大的情况下,通过并行流可以开启多个线程来执行处理,parallelStream与Stream的区别在于parallelStream开启了多线程的处理方式。
/* 流的创建 */
void test() {
//1.可以通过Collection 系列集合提供的stream() 、parallelStream()
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2.通过 Arrays 中的静态方法stream()获取数组流
User[] emps=new User[5];
Stream<User> stream2=Arrays.stream(emps);
//3.通过Stream 类中的静态方法of()
Stream<String> stream3=Stream.of("a","b","c");
//4.创建无限流
//迭代
Stream<Integer> stream4=Stream.iterate(0, (x) -> x+2);
stream4.limit(10).forEach(System.out::println);
}
中间操作:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性处理,成为“惰性求值”。
筛选与切片
// Employee 有三个字段: 姓名、年龄、收入
List<Employee> employeeList = Arrays.asList(
new Employee("张三",18,9999.99),
new Employee("李四",58,5555.55),
new Employee("王五",26,3333.33),
new Employee("赵六",36,6666.66),
new Employee("田七",12,8888.88),
new Employee("田七",12,8888.88)
);
/* 筛选与切片
* filter--接收Lambda,从流中排除某些元素。
* limit--截断流,使其元素不超过给定数量。
* skip(n)--跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n) 互补
* distinct--筛选,通过流所生成元素的 hashCode() 和 equals() 去掉重复元素
*/
void test() {
Stream<Employee> stream = employeeList.stream()
.filter((e) -> e.getAge()>35 )
.limit(2)
.skip(2)
.distinct(); //去重,注意:需要Employee重写hashCode 和 equals 方法
}
映射
/*
* 映射
* map--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,
* 该函数会被应用到每个元素上,并将其映射成一个新元素。
* flatMap--接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
*/
void test() {
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd");
list.stream().map((str)->str.toUpperCase())
.forEach(System.out::println);
employeeList.stream().map(Employee::getName);
//map和flatMap的关系 类似于 add(Object)和addAll(Collection coll)
}
排序
/*
* 排序
* sorted()-自然排序(按照对象类实现Comparable接口的compareTo()方法 排序)
* sorted(Comparator com)-定制排序(Comparator)
*/
void test() {
employeeList.stream()
.sorted((e1,e2)->{
if(e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getName());
}else{
return e1.getAge().compareTo(e2.getAge());
}
}).forEach(System.out::println);
// 正序排序
employeeList.stream().sorted(Comparator.comparing(Employee::getAge))
.forEach(System.out::println);
// 也可以 employeeList.sort(Comparator.comparing(Integer::intValue));
// 倒序排序
employeeList.stream().sorted(Comparator.comparing(Employee::getAge).reversed())
.forEach(System.out::println);
}
终止操作:
终止操作会从流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void。
查找与匹配
/*
* allMatch-----检查是否匹配所有元素
* anyMatch-----检查是否至少匹配一个元素
* noneMatch----检查是否没有匹配所有元素
* findFirst----返回第一个元素
* findAny------返回当前流中的任意元素
* count--------返回流中元素的总个数
* max----------返回流中最大值
* min----------返回流中最小值
*/
void test() {
//allMatch-检查是否匹配所有元素
boolean b1 = employeeList.stream()
.allMatch((e)-> e.getName().equals(”张飞“));
System.out.println(b1); //false
//anyMatch-检查是否至少匹配一个元素
boolean b2 = employeeList.stream()
.anyMatch((e)-> e.getName().equals(”张飞“));
System.out.println(b2);//true
// 以下的同理...
//findFirst-返回第一个元素
Optional<Employee> op = employeeList.stream()
.sorted((e1,e2)->Double.compare(e1.getAge(), e2.getAge()))
.findFirst();
System.out.println(op.get()); //Employee 返回第一个对象
//findAny-返回当前流中的任意元素 注意是并行流
Optional<Employee> op2 = employeeList.parallelStream()
.findAny();
//count-返回流中元素的总个数
Long count=employeeList.stream().count();
//max-返回流中最大值
Optional<Employee> op3=employeeList.stream()
.max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(op3.get()); //返回最大的对象
//min-最小的同理
Optional<Double> op4=employeeList.stream()
.map(Employee::getSalary)
.min(Double::compare); //返回最小的值
}
归约
可以将流中元素反复结合起来,得到一个值。
void test() {
List<Integer> list=Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum=list.stream()
.reduce(0, (x,y)->x+y); //0为起始值
}
收集
Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例。
/*
* 收集
* collect-将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法。
*/
void test() {
//流转List
List<String> list = employeeList.stream()
.map(Employee::getName).collect(Collectors.toList());
//转Set
Set<Employee> set = employeeList.stream().collect(Collectors.toSet());
//转HashSet
HashSet<Employee> hs = employeeList.stream()
.collect(Collectors.toCollection(HashSet::new));
//总和
Long count=employeeList.stream()
.collect(Collectors.counting());
//平均值
Double avg=employeeList.stream()
.collect(Collectors.averagingDouble(Employee::getAge));
//总和
Double sum=employeeList.stream()
.collect(Collectors.summingDouble(Employee::getAge));
//最大值
Optional<Employee> max=employeeList.stream()
.collect(Collectors.maxBy((e1,e2)->Double.compare(e1.getAge(), e2.getAge())));
System.out.println(max.get());
//最小值
Optional<Double> min=employeeList.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compare));
//分组
Map<Status,List<Employee>> map=employeeList.stream()
.collect(Collectors.groupingBy(Employee::getAge));
System.out.println(map);
//多级分组
Map<Status,Map<String,List<Employee>>> map2=employeeList.stream()
.collect(
Collectors.groupingBy(
Employee::getStatus ,
Collectors.groupingBy(
(e)->{
if(e.getAge()<=35){
return "青年";
}else if(e.getAge()<=50){
return "中年";
}else{
return "老年";
}
})
)
);
//分区--- 分成两组(满足条件和不满足条件)
Map<Boolean,List<Employee>> map3 = employeeList.stream()
.collect(Collectors.partitioningBy((e)->e.getAge()>8000));
//收集平均值、最大值、总和...
DoubleSummaryStatistics des = employeeList.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println(dss.getSum());
System.out.println(dss.getAverage());
System.out.println(dss.getMax());
//连接字符串
String strr=employees.stream()
.map(Employee::getName)
.collect(Collectors.joining(","));
}
了解 Fork/Join 框架
Fork/Join 框架:就是在必要的情况下,将一个大任务,进形拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运行的结果进行join汇总。就是把大任务拆分为小任务。
并行流与串行流
Stream API 可以声明性地通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。
long reduce = LongStream.rangeClosed(0, 1000000L)
.parallel()
.reduce(0, Long::sum);
Optional类
Optional< T>类(java.util.Optional) 是一个容器类,代表一个值存在或不存在。
原来用null表示一个值不存在,现在 Optional可以更好的表达这个概念。并且可以避免空指针异常。
常用方法:
Optional.of(T t) : 创建一个 Optional 实例
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
接口中的默认方法与静态方法
Java8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用 default 关键字修饰。
interface MyFunc{
default String getName(){
return "静态方法";
}
}
新时间日期API
以前的时间API是线程不安全的,新的时间日期LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象。分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。全新的API在time包下,他们都是不可变的,线程安全的。
// 1.LocalDate LocalTime LocalDateTime
@Test
void test() {
//当前时间
LocalDateTime now = LocalDateTime.now();
System.out.println(now);
//设置时间
LocalDateTime of = LocalDateTime.of(2018, 2, 22, 2, 22, 54);
System.out.println(of);
//加上2年
LocalDateTime of2 = of.plusYears(2);
System.out.println(of2);
//减上2年
LocalDateTime of3 = of.minusYears(2);
System.out.println(of3);
//获取 年 月 日
System.out.println(of.getYear());
System.out.println(of.getMonthValue());
System.out.println(of.getDayOfMonth());
//获取秒数时间戳(10位)
long l = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
System.out.println("获取秒数时间戳:" +l);
//获取毫秒数时间戳(13位)
long l1 = LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli();
System.out.println("获取毫秒数时间戳:" +l1);
}
对比时间之间间隔
void test() {
//时间比较
Instant i1 = Instant.now();
//延迟加载
TimeUnit.SECONDS.sleep(1);
Instant i2 = Instant.now();
long l = Duration.between(i1, i2).toMillis();
System.out.println(l);
//日期比较
LocalDate ld = LocalDate.of(2018,12,11);
//延迟加载
TimeUnit.SECONDS.sleep(1);
LocalDate ld2 = LocalDate.now();
Period between = Period.between(ld, ld2);
System.out.println(between.getYears());
System.out.println(between.getMonths());
System.out.println(between.getDays());
}
时间格式化
void test(){
LocalDateTime l = LocalDateTime.now();
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
// 时间转字符串
String format = dtf.format(l);
System.out.println(format);
// 字符串转时间
LocalDateTime parse = l.parse(format,dtf);
System.out.println(parse);
}
Data 与 LocalDateTime转换
// LocalDate 转为 Date
LocalDate localDate = LocalDate.now();
Instant instant = localDate.atStartOfDay().atZone(ZoneId.systemDefault()).toInstant();
Date date = Date.from(instant);
System.out.println("LocalDate 转为 Date: " + date);
// Date 转为 LocalDateTime
Date date3 = new Date();
LocalDateTime localDateTime3 = LocalDateTime.ofInstant(date3.toInstant(), ZoneId.systemDefault());
System.out.println("Date 转为 LocalDateTime: " + date3);
// Date 转为 LocalDate
Date date4 = new Date();
LocalDateTime localDateTime4 = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
LocalDate localDate4 = localDateTime4.toLocalDate();
System.out.println("Date 转为 LocalDate: " + localDate4);
TemporalAdjuster:时间校验器
void test(){
LocalDateTime now = LocalDateTime.now();
System.out.println(now); //2020-07-20T19:28:57.822
//把日期改成10号
LocalDateTime ldt2 = now.withDayOfMonth(10);
System.out.println(ldt2);
//把日期改成下个周末
LocalDateTime ldt3 = now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义--获取下一个工作日
LocalDateTime with = now.with((l) -> {
LocalDateTime ldt4 = (LocalDateTime) l;
DayOfWeek dayOfWeek = ldt4.getDayOfWeek();
//获取今天周几 ,如果是周五 +3天,周六 +2 ,其它 + 1
if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
return ldt4.plusDays(3);
} else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
return ldt4.plusDays(2);
} else {
return ldt4.plusDays(1);
}
});
System.out.println(with);
}
重复注解与类型注解
Java8 对注解处理提供了两点改进:可重复的注解及可用于类型的注解。
//定义注解 - MyAnnotations
@Repeatable(MyAnnotations.class) //这个代表可以定义重复注解
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})//可以修饰的目标
@Retention(RetentionPolicy.RUNTIME)//生命周期
public @interface MyAnnotations {
MyAnnotation[] value();
}
public class TestAnnotation {
//checker framework框架提供此注解
//@NonNull 不能为null, - 类型注解
private /*@NonNull*/ Object obj=null;
//配合反射,获取注解里的多个参数
@Test
public void test1() throws NoSuchMethodException, SecurityException{
Class<TestAnnotation> clazz = TestAnnotation.class;
Method m1=clazz.getMethod("show");
// 直接返回 注解的数组
MyAnnotation[] mas = m1.getAnnotationsByType(MyAnnotation.class);
for(MyAnnotation myAnnotation:mas){
System.out.println(myAnnotation.value());
}
}
@MyAnnotation("Hello")
@MyAnnotation("world")
public void show(String str){
}
}