java8新特性

                        java8新特性

                        原创者:文思

一、特性简介

速度更快

代码更少,增加了Lambda         

强大的Stream API

便于并行

最大化减少空指针异常

二、Lambda表达式

1、Lambda简介

Lambda是一个匿名函数,可以理解为一段可以传递的代码。

Lambda解决了什么?

没有Lambda之前,靠匿名内部类解决的需求

package java8;

import java.util.ArrayList;

import java.util.Arrays;

importjava.util.Comparator;

import java.util.List;

importjava.util.TreeSet;

importcom.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;

public classTestLamabda {

    Listlist= Arrays.asList(

           new Employee("zhangsan",56,19999),

           new Employee("lisi",36,169999),

           new Employee("wangwu",66,369999));

    public static void main(String[] args) {

       TestLamabdat = new TestLamabda();

       t.test();

    }

    public void test(){

       ListEmployees = getList(list,newFilterEmployeeInterface(){

           @Override

           public boolean test(Employee t) {

              return t.getSalary()>19999;

           }


       });

       for(Employee e:Employees){

           System.out.println(e);

       }

    }

    public List

getList(List list,FilterEmployeeInterfaceemp){

       ListempList= newArrayList();

       for(Employee e:this.list){

           if(emp.test(e)){

              empList.add(e);

           }

       }

       return empList;

    }

}

运行输出:

java8.Employee@15db9742

java8.Employee@6d06d69c

而用Lambda表达式:

publicListgetList(Listlist,FilterEmployeeInterfaceemp){

       ListempList = newArrayList();

       for(Employee e:this.list){

           if(emp.test(e)){

              empList.add(e);

           }

       }

       return empList;

    }

    public void test1(){

       ListempList = getList(list,(e)->((Employee)e).getSalary()>5619999);

       list.forEach(System.out::println);

    }

2、Lambda表达式

->左侧:表达式的参数列表。接口中抽象方法的的形参

->右侧:表达式中所需执行的功能。接口中抽象方法的实现

语法格式1:无参数无返回值

() -> System.out.println();

语法格式2:有参数无返回值

() -> System.out.println(e);

语法格式3:若只有一个参数,小括号可以不写

语法格式4:有两个以上参数,有返回值

Comparator com = (x,y)->{….;….

}

Lambda表达式需要函数式接口的支持。

三、函数式接口

什么是函数式接口?

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

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

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

自定义函数式接口:

@FunctionalInterface

public interfaceMyFunctionInterface {

    public T getValue(T t);

}

package java8;

import java.util.ArrayList;

import java.util.List;

import java.util.function.Consumer;

import java.util.function.Function;

import java.util.function.Predicate;

import java.util.function.Supplier;

/**

 * java8内置的四大核心函数式接口

 *

 *Consumer:消费型接口

 *     void accept(T t)

 *Supplier:供给型接口

 *     T get()

 *Function:函数型接口

 *     Rapply(T t)

 *Predicate:断言型接口

 *     booleantest(T t)

 */

public class TestLambda3 {

    //消费类型接口

    public void xiaofei(double money,Consumer con){

       con.accept(money);

    }

    public void test1(){

       xiaofei(10000,(m) -> System.out.println("旅游消费:"+m+"元"));

    }

    //供给类型接口

    public List getNumList(intnum,Suppliersup){

       List list = new ArrayList();

       for(int i=0;i

           Integer n = sup.get();

           list.add(n);

       }

       return list;

    }

    public void test2(){

Listlist= getNumList(10,()->(int)(Math.random()*100));

       for(Integer i:list){

           System.out.print(i+",");

       }

       System.out.println();

    }

    //函数型接口

    public String strHandler(String str,Functionfun){ 

       return fun.apply(str);

    }

    public void test3(){

       String newStr = strHandler("acb",(str) -> str.toUpperCase());

       System.out.println(newStr+",");

    }

    //断言型

    public boolean isTest(String str,Predicatepre){

       return pre.test(str);

    }

    public void test4(){

       boolean f = isTest("is",(m)->m.equals("is"));

       if(f){

           System.out.println(f);

       }

    }

    public static void main(String[] args) {

       System.out.println("消费型:");

       new TestLambda3().test1();

       System.out.println("供给型:");

       new TestLambda3().test2();

       System.out.println("函数型:");

       new TestLambda3().test3();

       System.out.println("断言型:");

       newTestLambda3().test4(); 

       System.out.println("自定义:");

       new TestLambda3().test5();

    }

    //自定义供给型接口

public Integer testCusomerFunctionInterface(Integer i,MyFunctionInterface customerFun ){

       return customerFun.getValue(i);

    }

    public void test5(){

       Integeri = testCusomerFunctionInterface(1,(m)->(m+1));

       System.out.println(i);

    }

}

运行输出:

消费型:

旅游消费:10000.0元

消费型:

旅游消费:10000.0元

供给型:

76,62,27,7,25,83,18,69,19,5,

函数型:

ACB,

断言型:

true

自定义:

2

java8新特性_第1张图片

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

其它子接口:

java8新特性_第2张图片


四、方法引用与构造器引用

package java8;

import java.io.PrintStream;

import java.util.Comparator;

import java.util.function.BiPredicate;

import java.util.function.Consumer;

import java.util.function.Supplier;

/*

 *一、方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可使用方法引用

 *          (可以将方法引用理解为 Lambda 表达式的另外一种表现形式)


 * 1.对象的引用 :: 实例方法名

 * 2.类名 :: 静态方法名

 * 3.类名 :: 实例方法名

 *注意:

 *  ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!

 *  ②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName

 *二、构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!

 * 1.类名 :: new

 *三、数组引用

 * 类型[] :: new;

 */

public classTestMethodRef {

    //对象::实例方法名

    public void test1(){

       PrintStream ps = System.out;

       Consumer con1= ps::println;

       con1.accept("abc");

    }

    //类::静态方法

    public void test2(){

       Comparator com = Integer::compare;

    }

    //类::实例方法名。(如果第一个参数是实例方法的调用者,且第二个参数是实例方法的参数时才能使用)

    public void test3(){

    //  BiPredicate bp = (x,y)->x.equals(y);    

       BiPredicate bp = String::equals;

    }

    //构造器引用

    public void test4(){

    //  Suppliersup = ()->new Employee();

       Supplier sup = Employee::new;

       Employee emp = sup.get();

       System.out.println(emp);

    }

}

五、Stream API

此流非彼流

Java8中有两大最为重要的改变。第一个是Lambda 表达式;另外一个则是Stream API(java.util.stream.*)。

Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

流(Stream) 到底是什么呢?

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”

注意:

①Stream 自己不会存储元素。

②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。

③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行

Stream 的操作三个步骤:

创建Stream一个数据源(如:集合、数组),获取一个流中间操作

一个中间操作链,对数据源的数据进行处理终止操作(终端操作)

一个终止操作,执行中间操作链,并产生结果

java8新特性_第3张图片

package java8;


import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.stream.Stream;

/*

 *一、Stream API 的操作步骤:

 * 1.创建 Stream

 * 2.中间操作

 * 3.终止操作(终端操作)

 */

public classTestSteamAPI {

    public void test(){

       //1、可以通过Collection系列集合提供的stream()或parallStream()

       Listlist= newArrayList<>();

       Streamstream= list.stream();

       //2通过Arrays中的静态方法stream()获取

       Employee[]emps= newEmployee[10];

       Streamstream1= Arrays.stream(emps);

       //3通过Stream类中的静态方法of()

       Streamstream2= Stream.of("a","b","c");

       //4创建无限流

       Streamstream3= Stream.iterate(1,(x->x+2));

       stream3.limit(10).forEach(System.out::println);

    }

}

Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!

而在终止操作时一次性全部处理,称为“惰性求值”。

筛选与切片:

java8新特性_第4张图片

映射:

java8新特性_第5张图片

排序:

importjava.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.stream.Stream;

public classTestStreamAPI2 {

    List list = Arrays.asList(

           new Employee("zhangsan",56,19999),

           new Employee("lisi",36,169999),

           new Employee("wangwu",66,369999));

    /**

     *中间操作

     */

    //filter举例

    public void test1(){

       //中间操作:每次循环不会执行任何操作

       Stream es = list.stream().filter((e)->

           {

              System.out.print("fasfa");

              return e.getAge()>30;

           });

       //终止操作:一次执行全部内容(惰性求值)

       es.forEach(System.out::println);

    }

    //skip举例

    public void test2(){

       list.stream().filter((e)->{

           return e.getAge()>30;

       }).skip(2).forEach(System.out::println);

    }

    //映射,map-接收lambda,将元素转换成其它形式或提取信息。并接收一个函数作为参数,该函数会作用于每一个元素上

public void test3(){       

     list.stream().map((e)->e.getName().toUpperCase()).forEach(System.out::println);         

   }

    //flatMap针对Stream,Stream嵌套Stream这种情况

    public void test4(){

       /* Stream> stream

        * stream.forEach((sm)->{sm.forEach(System.out::println)})

        */

    }

    //排序,sorted()自然排序,sorted(Comparatorcom)定制排序

    public void test5(){

       list.stream().sorted((e,ee)->{

           if(e.getAge()>ee.getAge()){

              return e.getName().compareTo(ee.getName());

           }else{

              return e.getName().compareTo(ee.getName());

           }

       }).forEach(System.out::println);

    }

}

查找与匹配:

java8新特性_第6张图片
java8新特性_第7张图片

规约:

收集:

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

java8新特性_第8张图片
java8新特性_第9张图片

import java.util.Arrays;

import java.util.List;

import java.util.Optional;

import java.util.stream.Collectors;

import org.junit.Test;

public classTestStreamAPI3 {

    List employeesList= Arrays.asList(

           new Employee("zhangsan",56,19999),

           new Employee("lisi",36,169999),

           new Employee("wangwu",66,369999));

    //规约:reduce(T identity,BinaryOperator)/reduce(BinaryOperator)

    //可以将流中元素反复结合起来得到一个值

    @Test

    public void test(){

       List list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);

       Integer sum = list.stream().reduce(0,(x,y)->x+y);

       System.out.println(sum);


       Optional op = employeesList.stream()

              .map((e)->e.getAge())

              .reduce(Integer::sum);

       System.out.println(op.get());

    }

    //收集:collect将流转换为其它形式。接收一个Collector接口的实现,用于给Stream元素做汇总的方法

    @Test

    public void test1(){

       List list = employeesList.stream().map(Employee::getName).collect(Collectors.toList());

       list.forEach(System.out::println);


       Long count = employeesList.stream().collect(Collectors.counting());

       System.out.println(count);

    }

}

六、并行流与串行流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

Java 8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API 可以声明性地通过parallel() 与sequential() 在并行流与顺序流之间进行切换。

Fork-Join框架的封装实现。

七、Optional类

非重点了解。

Optional 类(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

import java.util.Optional;

import org.junit.Test;

/*

 *一、Optional 容器类:用于尽量避免空指针异常

 * Optional.of(Tt) :创建一个 Optional 实例

 * Optional.empty():创建一个空的 Optional 实例

 * Optional.ofNullable(Tt):若 t 不为 null,创建 Optional 实例,否则创建空实例

 * isPresent():判断是否包含值

 * orElse(Tt) :  如果调用对象包含值,返回该值,否则返回t

 * orElseGet(Suppliers) :如果调用对象包含值,返回该值,否则返回 s 获取的值

 * map(Functionf):如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()

 * flatMap(Functionmapper):与 map 类似,要求返回值必须是Optional

 */

public classTestOptional {

    @Test

    public void test(){

       Optional op = Optional.of(new Employee());

       Employee e = op.get();

       System.out.println(e);

    }

}

八、接口中的默认方法与静态方法

Java 8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,默认方法使用default关键字修饰.

接口默认方法的”类优先”原则

若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时

1:选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。

2:接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

public interface TestNewInterface {

    default String getName(){

       return "this is testNewInterface";

    }

    public static void test(){

       System.out.println("thi is testNewInterface function : test");

    }

}

public interface TestNewInterface2 {

    default String getName(){

       return "this is testNewInterface2";

    }

    public static void test(){

       System.out.println("thi is testNewInterface function : test2");

    }

}

public classTestNewClass {

    public String getName(){

       return "this is testNewClass";

    }

}

    public static void main(String[] args) {

       TestSubClass t = new TestSubClass();

       String str = t.getName();

       System.out.println(str);

       TestNewInterface.test();

    }

}

运行结果:

this is testNewClass

thi is testNewInterface function : test

九、传统时间格式化安全问题与新时间格式

新时间日期API

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

public classTestLocalDateTime {

    @Test

    public void test(){

       LocalDateTime ldt = LocalDateTime.now();

       System.out.println(ldt);

    }

}

Instant 时间戳

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

@Test

    public void test1(){

       Instant inst = Instant.now();//默认获取UTC时区

       System.out.println(inst);

       System.out.println(inst.toEpochMilli());//转成毫秒时间戳

    }

Duration:用于计算两个“时间”间隔,计算两个时间之间的间隔,示例:

@Test

    public void test2(){

       Instant inst1 = Instant.now();

       Instant inst2 = Instant.now();

       Duration duration= Duration.between(inst1,inst2);

       System.out.println(duration.toMillis());


       LocalTime lt1 =LocalTime.now();

       LocalTime lt2 = LocalTime.now();

       Duration duration2 = Duration.between(lt1,lt2);

       System.out.println(duration2.toMillis());

    }

Period:用于计算两个“日期”间隔,示例:

@Test

    public void test3(){

       LocalDate ld1 = LocalDate.of(2018, 9, 23);

       LocalDate ld2 = LocalDate.now();

       Period period = Period.between(ld1,ld2);

       System.out.println(period);

       System.out.println(period.getYears());

       System.out.println(period.getMonths());

       System.out.println(period.getDays());

    }

TemporalAdjuster: 时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。

TemporalAdjusters : 该类通过静态方法提供了大量的常用TemporalAdjuster 的实现

自定义操作时间,时间校正器:

@Test

    public void test4(){

       LocalDateTime ldt = LocalDateTime.now();

       LocalDateTime ldt5 = ldt.with((l) -> {

           LocalDateTime ldt4 = (LocalDateTime)l;

           DayOfWeek dow = ldt4.getDayOfWeek();

           if(dow.equals(DayOfWeek.FRIDAY)){

              return ldt4.plusDays(3);

           }else{

              return ldt4.plusDays(1);

           }

       });

    }

解析与格式化

java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法:

1、预定义的标准格式。2、语言环境相关的格式。3、自定义的格式

@Test

    public void test5(){

       //时间解析成字符串

       DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日");

       LocalDateTime ldt = LocalDateTime.now();

       String str = ldt.format(dtf);

       System.out.println(str);

       String str1 = dtf.format(ldt);

       System.out.println(str1);

       //字符串转换成日期

       LocalDateTime ldt2 = LocalDateTime.parse(str,dtf);

       System.out.println(ldt2);

    }

十、重复注解与类型注解

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

你可能感兴趣的:(java8新特性)