Java8新特性(详细总结)

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的操作分为三个步骤:

  1. 创建Stream
  2. 中间操作(对数据进行操作)
  3. 终止操作(如果没有终止操作,中间操作是不执行的)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){

    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值