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
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
}
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.accept(money);
}
public void test1(){
xiaofei(10000,(m) -> System.out.println("旅游消费:"+m+"元"));
}
//供给类型接口
public List
List list = new ArrayList
for(int i=0;i Integer n = sup.get(); list.add(n); } return list; } public void test2(){ List for(Integer i:list){ System.out.print(i+","); } System.out.println(); } //函数型接口 public String strHandler(String str,Function return fun.apply(str); } public void test3(){ String newStr = strHandler("acb",(str) -> str.toUpperCase()); System.out.println(newStr+","); } //断言型 public boolean isTest(String str,Predicate 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 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 作为参数传递Lambda 表达式:为了将Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该Lambda 表达式兼容的函数式接口的类型。 其它子接口: 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); } } 此流非彼流 Java8中有两大最为重要的改变。第一个是Lambda 表达式;另外一个则是Stream API(java.util.stream.*)。 Stream 是Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用SQL 执行的数据库查询。也可以使用Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。 流(Stream) 到底是什么呢? 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!” 注意: ①Stream 自己不会存储元素。 ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。 ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行 Stream 的操作三个步骤: 创建Stream一个数据源(如:集合、数组),获取一个流中间操作 一个中间操作链,对数据源的数据进行处理终止操作(终端操作) 一个终止操作,执行中间操作链,并产生结果 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的中间操作 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”。 筛选与切片: 映射: 排序: 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 public void test4(){ /* 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); } } 查找与匹配: 规约: 收集: Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例: import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import org.junit.Test; public classTestStreamAPI3 { List 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.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 使用LocalDate、LocalTime、LocalDateTime、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对注解处理提供了两点改进:可重复的注解及可用于类型的注解,自行百度。
四、方法引用与构造器引用五、Stream API
六、并行流与串行流
七、Optional类
八、接口中的默认方法与静态方法
九、传统时间格式化安全问题与新时间格式
十、重复注解与类型注解