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<Employee>(){
@Override
public boolean test(Employee t) {
return t.getSalary()>19999;
}
});
for(Employee e:Employees){
System.out.println(e);
}
}
public List<Employee>
getList(List<Employee> list,FilterEmployeeInterfaceemp){
ListempList= newArrayList<Employee>();
for(Employee e:this.list){
if(emp.test(e)){
empList.add(e);
}
}
return empList;
}
}
运行输出:
java8.Employee@15db9742
而用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<Integer> com = (x,y)->{….;….
}
Lambda表达式需要函数式接口的支持。
三、函数式接口
什么是函数式接口?
只包含一个抽象方法的接口,称为函数式接口。
可以通过Lambda 表达式来创建该接口的对象。(若Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)
我们可以在任意函数式接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口,同时javadoc也会包含一条声明,说明这个接口是一个函数式接口。
自定义函数式接口:
@FunctionalInterface
public interfaceMyFunctionInterface<T> {
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<T>:消费型接口
* void accept(T t)
*Supplier<T>:供给型接口
* T get()
*Function:函数型接口
* Rapply(T t)
*Predicate<T>:断言型接口
* booleantest(T t)
*/
public class TestLambda3 {
//消费类型接口
public void xiaofei(double money,Consumer<Double> con){
con.accept(money);
}
public void test1(){
xiaofei(10000,(m) -> System.out.println("旅游消费:"+m+"元"));
}
//供给类型接口
public List<Integer> getNumList(intnum,Supplier<Integer>sup){
List list = new ArrayList<Integer>();
for(int i=0;i<num;i++){
Integer n = sup.get();
list.add(n);
}
return list;
}
public void test2(){
List<Integer>list= getNumList(10,()->(int)(Math.random()*100));
for(Integer i:list){
System.out.print(i+",");
}
System.out.println();
}
//函数型接口
public String strHandler(String str,Function<String,String>fun){
return fun.apply(str);
}
public void test3(){
String newStr = strHandler("acb",(str) -> str.toUpperCase());
System.out.println(newStr+",");
}
//断言型
public boolean isTest(String str,Predicate<String>pre){
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<Integer> 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
作为参数传递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);
}
}
五、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一个数据源(如:集合、数组),获取一个流中间操作
一个中间操作链,对数据源的数据进行处理终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
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<Stream>,Stream嵌套Stream这种情况
public void test4(){
/* Stream<Stream<String>> 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<employees> 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<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
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对注解处理提供了两点改进:可重复的注解及可用于类型的注解,自行百度。