当前位置:   article > 正文

JAVA8新特性

JAVA8新特性

Lambda表达式

Lambda 是一个匿名函数,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。

1.从匿名类到Lambda 的转换

匿名内部类:内部使用的变量必须是final的;如final int num = 1;

 

2.Lambda表达式语法

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为“->” ,该操作符被称为Lambda 操作符或剪头操作符。

它将Lambda 分为两个部分:
左侧:指定了Lambda 表达式需要的所有参数
右侧:指定了Lambda 体,即Lambda 表达式要执行的功能

 

上联:左右遇一括号省

下联:左侧推断类型省

横批:能省则省

 

lambda表达式对接口中抽象方法的实现:

左边是接口的参数,右边是接口的功能;

 

2.1 语法格式一:无参,无返回值,Lambda体只需一条语句

Runnable r1 = ()->System.out.println("hello World");

2.2 语法格式二:Lambda需要一个参数

Consumer<String> fun = (args)->System.out.println(args);

2.3 语法格式三:Lambda只需要一个参数时,参数的小括号可以省略

Consumer<String> fun = args -> System.out.println(args);

2.4 语法格式四:Lambda有两个参数,并且Lambda体中有多条语句

Comparator<Integer> com = (x,y)->{

   System.out.println(“函数式接口”);

   return Integer.compare(x,y);

}

2.5 语法格式五:Lambda只有一条语句,return和大括号都可以省略不写

Comparator<Integer> com = (x,y) -> Integer.compare(x,y);

2.6语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”

数据类型Long可以省略,因为可由编译器推断得出,称为“类型推断”

 

函数式接口

Lambda表达式需要接口的支持;但又不知道实现的是哪个具体的接口;所以引入“函数式接口”的支持

1.函数式接口定义

只包含一个抽象方法的接口,称为函数式接口

你可以通过Lambda 表达式来创建该接口的对象。(若Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。

我们可以在任意函数式接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口,同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。

 

2.自定义函数式接口

函数式接口中使用泛型:

3.作为参数传递Lambda 表达式

作为参数传递Lambda 表达式:

作为参数传递Lambda 表达式:为了将Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该Lambda 表达式兼容的函数式接口的类型。

 

总结:定义了接口MyFunc,实现Lambda表达式,调用时将Lambda表达式传递给接口;

 

Java 内置四大核心函数式接口

其他接口

不再用自己定义接口:Java已经为我们定义好接口

* Consumer<T>:消费型接口
 *         void accept(T t);
 * 
 * Supplier<T>:供给型接口
 *         T get();
 * 
 * Function<T,R>:函数型接口
 *     T是参数,R是返回值
 *         R apply(T t);
 * 
 * Predicate<T>:断言型接口
 *         boolean test(T t);
 * 
 */


public class TestLambda2 {
    @Test
    public void test4(){
        List<String> list = Arrays.asList("Hello", "atguigu", "ok");
        List<String> strList = filterStr(list, (s)-> s.length()>2);
        
        for(String str : strList){
            System.out.println(str);
        }    
    }

    //Predicate<T> 断言型接口
    public List<String> filterStr(List<String>list, Predicate<String> pre){
        List<String> strList = new ArrayList<>();
        
        for(String str : list){
            if(pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }
    
    
    //@Test
    public void test3(){
        String newStr = strHandler("\t\t\t ssssss", (str)->str.trim());
        System.out.println(newStr);    
    }
    
    //Function<T,R> 函数型接口:
    public String strHandler(String str, Function<String,String> fun){
        return fun.apply(str);
    }
    
    //Supplier<T> 供给型接口:
    //@Test
    public void test2(){
        List<Integer> list = getNumList(10, ()->(int)(Math.random() * 100));
        for(Integer i : list){
            System.out.println(i);
        }
    }
    
    public List<Integer> getNumList(int num, Supplier<Integer> sup){
        List<Integer> list = new ArrayList<>();
        
        for(int i = 0; i < num; i++){
            Integer n = sup.get();
            list.add(n);
        }
        return list;
    }
    
    //Consumer<T> 消费型接口:
    //@Test
    public void test1(){
        happy(10000, (m)->
                System.out.println("xxxxxx"));
    }
    
    public void happy(double money, Consumer<Double> con){
        con.accept(money);
    }
   
}

 

方法引用与构造器引用

1.方法引用

当要传递给Lambda体的操作已经有实现的方法了,可以使用方法引用!(实现抽象方法的参数列表必须与方法引用方法的参数列表保持一致!)方法引用:使用操作符“::” 将方法名和对象或类的名字分隔开来。
如下三种主要使用情况:
对象::实例方法
类::静态方法
类::实例方法

 

简单方法引用:若Lambda体中的内容有方法已经实现了,我们可使用“方法引用”;它是Lambda表达式的另一种表现形式;

注意:

1.Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。

2.当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName

 

例如:

等同于:

例如:

等同于:

例如:

等同于:

注意:当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数(或无参数)时:ClassName::methodName

 

构造器引用

格式:ClassName::new
与函数式接口相结合,自动与函数式接口中方法兼容。
可以把构造器引用赋值给定义的方法,与构造器参数列表要与接口中抽象方法的参数列表一致!

注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保存一致

例如:

等同于:

 

 

数组引用

格式:type[] :: new

例如:

等同于:

 

Stream API

Java8中有两大最为重要的改变。第一个是Lambda 表达式;另外一个则是Stream API(java.util.stream.*)。
Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

1.什么是流

流(Stream) 到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
注意:
①Stream 自己不会存储元素
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

 

2.Stream 的操作三个步骤

1.创建Stream
一个数据源(如:集合、数组),获取一个流
2.中间操作
一个中间操作链,对数据源的数据进行处理
3.终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果

3.创建Steam流

1.Java8 中的Collection 接口被扩展,提供了两个获取流的方法:
default Stream<E> stream() : 返回一个顺序流
default Stream<E> parallelStream() : 返回一个并行流

如:

List<String> list = new ArrayList();
Stream<String> stream1 = list.stream();

2.由数组创建流

Java8 中的Arrays 的静态方法stream() 可以获取数组流:
static <T> Stream<T> stream(T[] array): 返回一个流
重载形式,能够处理对应基本类型的数组:
public static IntStream stream(int[] array)
public static LongStream stream(long[] array)
public static DoubleStream stream(double[] array)

如:

Employee[] emps = new Employee[10];
Stream<Employee> stream2 = Arrays.stream(emps);

3.由值创建流

可以使用静态方法Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。
public static<T> Stream<T> of(T... values) : 返回一个流

如:

Stream<String> stream3 = Stream.of("aa","bb","cc");

4.由函数创建流:创建无限流

可以使用静态方法Stream.iterate() 和Stream.generate(), 创建无限流。
(1)迭代
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
(2)生成
public static<T> Stream<T> generate(Supplier<T> s) :

如:

// 迭代
Stream<Integer> stream4 = Stream.iterate(0, (x)->x+2);
stream4.limit(4).forEach(System.out::println);
        

//生成
Stream.generate(()->Math.random())
      .limit(5).forEach(System.out::println);

 

4.Stream 的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!
而在终止操作时一次性全部处理,称为“惰性求值”。

筛选与切片

//内部迭代,Stream中迭代

//过滤掉不符合的元素
    //@Test
    public void test1(){
        Stream<Employee> stream = emps.stream()
                .filter((e)->{
                    System.out.println("Stream API的中间操作");
                    return e.getAge() > 35;
                });
        
        //终止操作
        stream.forEach(System.out::println);
    }

//@Test
    //外部迭代
    public void test2(){
        Iterator<Employee> iter = emps.iterator();
        while(iter.hasNext()){
            System.out.println(iter.next());
        }
    }

 

//截断:当取到两个满足条件的值后,就不再处理

//@Test
    public void test3(){
        emps.stream()
            .filter((e)->e.getSalary() > 5000)
            .limit(2)
            .forEach(System.out::println);
    }

 

//跳过前2个元素

//@Test
    public void test4(){
        emps.stream()
            .filter((e)->e.getSalary() > 5000)
            .skip(2)
            .forEach(System.out::println);
    }

 

//去重,需要重新hashcode和equals方法

@Test
    public void test5(){
        emps.stream()
            .filter((e)->e.getSalary() > 5000)
            .distinct()
            .forEach(System.out::println);
    }

 

映射

@Test
    public void test7(){
        List<String> list = Arrays.asList("aa","bb",
                "cc","dd","ee");
        
        /*Stream<Stream<Character>> stream = list.stream()
                .map(TestStreamAPI2::filterCharacter);
        
        stream.forEach((sm)->{
            sm.forEach(System.out::println);
        });*/
        
        Stream<Character> sm = list.stream()
                .flatMap(TestStreamAPI2::filterCharacter);  //将流中的每一个元素添加到流中
        sm.forEach(System.out::println);
        
    }
    
    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();
    
        for(Character ch : str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }

排序

自然排序使用默认的Comparable;

自定义排序使用Comparator;

public void test8(){
        List<String> list = Arrays.asList("cc","dd","ee","aa");
        //自然排序
        list.stream().sorted().forEach(System.out::println);
        

        //自定义排序
        emps.stream().sorted((e1,e2)->{
            if (e1.getAge() == e2.getAge()){
                return e1.getName().compareTo(e2.getName());
            }else{
                return e1.getAge() - e2.getAge();
            }
        }).forEach(System.out::println);
    }

 

终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void 。

@Test
    public void test9(){
        Long count = emps.stream().count();
        System.out.println(count);
        
        Optional<Employee> op1 = emps.stream()
                .max((e1,e2)-> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(op1.get());
        
        
        Optional<Double> op2 = emps.stream()
                .map(Employee::getSalary)
                .min(Double::compare);
        System.out.println(op2.get());
    }

归约

@Test
    public void test10(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7);
        
        Integer sum = list.stream().reduce(0, (x,y)-> x + y);
        System.out.println(sum);
        
        Optional<Double> op = emps.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(op.get());
    }

收集

Collector 接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

public void test11(){
        List<String> list = emps.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());
        list.forEach(System.out::println);
        
        System.out.println("------------");
        
        HashSet<String> set = emps.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        set.forEach(System.out::println);
        
        Optional<Employee> max = emps.stream()
                .collect(Collectors.maxBy((e1,e2)->
                Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(max.get());
        
        Optional<Double> min = emps.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        System.out.println(min.get());
    }

 

并行流与串行流

了解Fork/Join 框架

Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行join 汇总.

采用“工作窃取”模式(work-stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态.而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行.那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能.

 

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel() 与sequential() 在并行流与顺序流之间进行切换。

 

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

 

新时间日期API

1.使用LocalDate、LocalTime、LocalDateTime
LocalDate、LocalTime、LocalDateTime 类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

    public void test1(){
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        
        LocalDateTime ld2 = LocalDateTime.of(2016, 11, 21, 10, 10, 10);
        System.out.println(ld2);
        
        LocalDateTime ldt3 = ld2.plusYears(20);
        System.out.println(ldt3);
        
        LocalDateTime ldt4 = ld2.minusMonths(2);
        System.out.println(ldt4);
        
        System.out.println(ldt.getYear());
        System.out.println(ldt.getMonthValue());
        System.out.println(ldt.getDayOfMonth());
        System.out.println(ldt.getHour());
        System.out.println(ldt.getMinute());
        System.out.println(ldt.getSecond());
    }

2.Instant 时间戳
用于“时间戳”的运算。它是以Unix元年(传统的设定为UTC时区1970年1月1日午夜时分)开始所经历的描述进行运算

        Instant ins = Instant.now();  //默认使用 UTC 时区
        System.out.println(ins);
        
        OffsetDateTime odt = ins.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);
        
        System.out.println(ins.getNano());
        
        Instant ins2 = Instant.ofEpochSecond(5);
        System.out.println(ins2);

3.Duration 和Period
Duration:用于计算两个“时间”间隔
Period:用于计算两个“日期”间隔

        Instant ins1 = Instant.now();
        Instant ins2 = Instant.now();
        
        System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));
        
       
        LocalDate ld1 = LocalDate.now();
        LocalDate ld2 = LocalDate.of(2011, 1, 1);
        
        Period pe = Period.between(ld2, ld1);
        System.out.println(pe.getYears());
        System.out.println(pe.getMonths());
        System.out.println(pe.getDays());

4.日期的操纵
TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。
TemporalAdjusters : 该类通过静态方法提供了大量的常用TemporalAdjuster 的实现。
例如获取下个周日:

        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);
        
        LocalDateTime ldt2 = ldt.withDayOfMonth(10);
        System.out.println(ldt2);
        
        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3);
        
        //自定义:下一个工作日
        LocalDateTime ldt5 = ldt.with((l) -> {
            LocalDateTime ldt4 = (LocalDateTime) l;
            
            DayOfWeek dow = ldt4.getDayOfWeek();
            
            if(dow.equals(DayOfWeek.FRIDAY)){
                return ldt4.plusDays(3);
            }else if(dow.equals(DayOfWeek.SATURDAY)){
                return ldt4.plusDays(2);
            }else{
                return ldt4.plusDays(1);
            }
        });
        
        System.out.println(ldt5);

 

5.解析与格式化
java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:
预定义的标准格式
语言环境相关的格式
自定义的格式

DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
        
        LocalDateTime ldt = LocalDateTime.now();
        String strDate = ldt.format(dtf);
        
        System.out.println(strDate);
        
        LocalDateTime newLdt = ldt.parse(strDate, dtf);
        System.out.println(newLdt);

6.时区的处理
Java8 中加入了对时区的支持,带时区的时间为分别为:
ZonedDate、ZonedTime、ZonedDateTime
其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式
例如:Asia/Shanghai 等
ZoneId:该类中包含了所有的时区信息
getAvailableZoneIds() : 可以获取所有时区时区信息
of(id) : 用指定的时区信息获取ZoneId 对象

 

        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println(ldt);
        
        ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
        System.out.println(zdt);

 

        Set<String> set = ZoneId.getAvailableZoneIds();
        set.forEach(System.out::println);

 

重复注解与类型注解

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/1006273
推荐阅读
相关标签
  

闽ICP备14008679号