Java 8 (又称为 jdk 1.8) 是 Java 语言开发的一个主要版本。 Oracle 公司于 2014 年 3 月 18 日发布 Java 8 ,它支持函数式编程,新的 JavaScript 引擎,新的日期 API,新的Stream API 等。
新特性
Java8 新增了非常多的特性,我们主要讨论以下几个:
- Lambda 表达式 − Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中。
- 强大的Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
- 便于并行
- 最大化减少空指针异常,引入 Optional容器类
Java8的好处
- 速度更快[^1]
- 代码更少(增加了新的语法Lambda表达式)
- 强大的Stream API
- 便于并行
- 最大化减少空指针异常,提供一个Optional容器类
1 Lambda表达式
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(讲代码像数据一样传递)。可以写出更简洁、更灵活的代码。作为一种更进奏的代码风格,使Java的语言表达能力得到了提升。
package lambda.java8;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.junit.Test;
/*
* Lambda表达式必须由函数式接口的支持(一个抽象类中只有一个方法)
*
* 一、Lambda 表达式的基础语法:Java8中引入了一个新的操作符 "->" 该操作符称为箭头操作符或 Lambda 操作符
* 箭头操作符将 Lambda 表达式拆分成两部分:
*
* 左侧:Lambda 表达式的参数列表
* 右侧:Lambda 表达式中所需执行的功能, 即 Lambda 体
*
* 语法格式一:无参数,无返回值
* () -> System.out.println("Hello Lambda!");
*
* 语法格式二:有一个参数,并且无返回值
* (x) -> System.out.println(x)
*
* 语法格式三:若只有一个参数,小括号可以省略不写
* x -> System.out.println(x)
*
* 语法格式四:有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
* Comparator<Integer> com = (x, y) -> {
* System.out.println("函数式接口");
* return Integer.compare(x, y);
* };
*
* 语法格式五:若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写
* Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
*
* 语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
* (Integer x, Integer y) -> Integer.compare(x, y);
*
* 上联:左右遇一括号省 (尖括号-> 左侧只有一个参数时,参数的括号可以省略。 右侧lambda体中只有一条语句的时候,{};也可以省略)
* 下联:左侧推断类型省 (尖括号 -> 左侧引入了类型推断的概念,可以通过目标上下文推断出参数类型, 因此参数列表的参数类型可以省略)
* 横批:能省则省
*
* 二、Lambda 表达式需要“函数式接口”的支持
* 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。
* 可以使用注解 @FunctionalInterface 修饰该接口 ,来检查该接口是否是函数式接口
*/
public class TestLambda2 {
@Test
public void test1(){
int num = 0;//jdk 1.7 前,必须是 final
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!" + num);
}
};
r.run();
System.out.println("-------------------------------");
Runnable r1 = () -> System.out.println("Hello Lambda!");
r1.run();
}
@Test
public void test2(){
Consumer<String> con = x -> System.out.println(x);
con.accept("我大尚硅谷威武!");
}
@Test
public void test3(){
Comparator<Integer> com = (x, y) -> {
System.out.println("函数式接口");
return Integer.compare(x, y);
};
}
@Test
public void test4(){
// 省略括号
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
}
@Test
public void test5(){
// 使用JDK8的类型推断 来实现参数类型省略
show(new HashMap<>());
}
public void show(Map<String, Integer> map){
}
//需求:对一个数进行运算
@Test
public void test6(){
System.out.println(operation(10,5, (x,y) -> x * y));
System.out.println(operation(10,5, (x,y) -> x + y));
}
public Integer operation(Integer num,Integer num2, MyFun mf){
return mf.getValue(num,num2);
}
}
/**
* Lambda 表达式需要“函数式接口”的支持
* 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。
* 可以使用注解 @FunctionalInterface 修饰该接口 ,来检查该接口是否是函数式接口
*/
@FunctionalInterface
public interface MyFun {
public Integer getValue(Integer num , Integer num2);
}
下面通过fastjson中的一个函数式接口来演示多个参数和多个lambda体的例子。
首先创建一个javaBean,来实现序列化和反序列化
package fastJson.bean;
import java.util.Date;
import com.alibaba.fastjson.annotation.JSONField;
/**
* @author tomatocc
* @email 553866242@qq.com
* @desc 创建JaveBean 将其序列化为json 必須提供get set 方法
*
* format 配置date序列化和反序列使用yyyyMMdd日期格式
* serialize 指定字段不序列化 使用 ordinal 参数指定字段的顺序
* ordinal : 配置序列化和反序列化的顺序,1.1.42版本之后才支持
* deserialize:是否反序列化
*
*注意:FastJson 在进行操作时,是根据 getter 和 setter 的方法进行的,并不是依据 Field 进行。
*注意:若属性是私有的,必须有 set 方法。否则无法反序列化。反之,如果属性是public的则可以省略set/get方法
*/
public class Person {
public Person() {}
@JSONField(name = "AGE", serialize = false , deserialize = false)
public int age;
@JSONField(name = "LAST NAME", ordinal = 3)
public String lastName;
@JSONField(name = "FIRST NAME", ordinal = 2)
public String firstName;
@JSONField(name = "DATE OF BIRTH", format = "dd/MM/yyyy", ordinal = 1)
public Date dateBirth;
public Person(int age, String firstName, String lastName ,Date dateBirth) {
this.age = age;
this.firstName = firstName;
this.lastName = lastName;
this.dateBirth = dateBirth;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getDateBirth() {
return dateBirth;
}
public void setDateBirth(Date dateBirth) {
this.dateBirth = dateBirth;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
下面分别通过普通匿名类方式和lambda方式来完成对javaBean序列化的数据过滤。
package fastJson;
import java.text.ParseException;
import java.util.Date;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.BeanContext;
import com.alibaba.fastjson.serializer.ContextValueFilter;
import fastJson.bean.Person;
/**
* @author tomatocc
* @email 553866242@qq.com
* @desc Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。
* Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。
*
* Fastjson 特性 提供服务器端、安卓客户端两种解析工具,性能表现较好。 提供了 toJSONString() 和
* parseObject() 方法来将 Java 对象与 JSON 相互转换。调用toJSONString方 法即可将对象转换成 JSON
* 字符串,parseObject 方法则反过来将 JSON 字符串转换成对象。 允许转换预先存在的无法修改的对象(只有class、无源代码)。
* Java泛型的广泛支持。 允许对象的自定义表示、允许自定义序列化类。 支持任意复杂对象(具有深厚的继承层次和广泛使用的泛型类型)。
*/
public class Test {
public static void main(String[] args) throws ParseException {
// 将javaBean序列化为json
javaBeanToJson();
}
/**
* 序列化和反序列化
*
* @throws ParseException
*/
public static void javaBeanToJson() throws ParseException {
// 反序列化操作(将json转对象)
Person person = new Person(20, "John", "Doe", new Date());
// 过滤序列化后的JSON
ContextValueFilter valueFilter = new ContextValueFilter() {
@Override
public Object process(BeanContext context, Object object, String name, Object value) {
if (name.equals("DATE OF BIRTH")) {
return "NOT TO DISCLOSE";
}
if (value.equals("John")) {
return ((String) value).toUpperCase();
} else {
return null;
}
}
};
String matOutPut = JSON.toJSONString(person, valueFilter);
System.out.println("过滤序列化后的JSON : " + matOutPut);
System.out.println("-----------------------------------------------------------------");
// 两个参数 则lambda表达式必须有{};
ContextValueFilter lambda = (context, object, name, value) -> {
if (name.equals("DATE OF BIRTH")) {
return "NOT TO DISCLOSE";
}
if (value.equals("John")) {
return ((String) value).toUpperCase();
} else {
return null;
}
};
String matOutPutLam = JSON.toJSONString(person, lambda);
System.out.println("lambda表达式 : 过滤序列化后的JSON : " + matOutPutLam);
}
}
四大内置核心函数式接口
通过上面的例子,我们发现只要我们使用Lambda表达式就必须要创建一个函数式接口,然后才能使用。强大的jaa其实已经为我们内置了很多常用的函数式接口,下面我们来学习这些内置函数式接口。
- Consumner : 消费型接口
- Supplier :供给型接口
- Function:函数式接口
- Predicate:断言型接口
1) 四大核心函数演示代码
package lambda.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.junit.Test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc Java8 内置的四大核心函数式接口
*
* Consumer<T> : 消费型接口
* void accept(T t);
*
* Supplier<t> : 供给型接口
* T get();
*
* Function<T,R> : 函数型接口
* R apply(T t);
*
* Predicate<T> : 断言型接口
* boolean test(T t);
*
*/
public class TestLambda3 {
//Consumer<T> : 消费型接口 (有去无回)///
@Test
public void test1() {
happy(1000 , m -> System.out.println("消费金额: " + m + "元"));
}
/**
* 消费方法
* @param money
* @param con
*/
public void happy(double money , Consumer<Double> con) {
con.accept(money);
}
//Supplier<t> : 供给型接口///
@Test
public void test2() {
// 生成10个100以内的随机数
List<Integer> list = getNumList(10 , () -> (int)(Math.random() * 100 ));
for(Integer num : list) {
System.out.println("num : " + num);
}
}
// 需求 : 产生一些整数,并放入集合中
/**
*
* @param num 生成整数的数量
* @param supplier
* @return
*/
public List<Integer> getNumList(int num , Supplier<Integer> supplier){
List<Integer> list = new ArrayList<>();
// 通过supplier产生一系列的随机数,并将这些随机数放到集合中
for(int i= 0 ; i<num ;i++) {
Integer n = supplier.get();
list.add(n);
}
return list;
}
//Function<T,R> : 函数型接口///
// 处理字符串
@Test
public void test3() {
Integer newStr = strHanlder("tomatocc", (str) -> str.length());
System.out.println("字符串的长度:" + newStr);
Integer hashCode = strHanlder("tomatocc", (str) -> str.hashCode());
System.out.println("字符串的hashCode:" + hashCode);
}
// 需求: 用于处理字符串
/**
*
* @param str 需要处理的字符串
* @param fun 第一个String表示传进来的类型 第二个String表示返回的类型
* @return
*/
public Integer strHanlder(String str , Function<String ,Integer> fun) {
return fun.apply(str);
}
//Predicate<T> : 断言型接口///
@Test
public void test4() {
List<String> list = Arrays.asList("lambda" , "tom","Simion" ,"tomatocc" , "jack");
// 将原集合中的长度大于3的字符串筛选出来
List<String> strList = filterStr(list, (s) -> s.length() > 3);
for(String str : strList) {
System.out.println("断言接口筛选结果: " + str);
}
}
//需求:将满足条件的字符串放入到另外一个集合中
/**
*
* @param list 需要处理的字符换集合
* @param pre 断言接口所需要的参数:String
* @return
*/
public List<String> filterStr(List<String> list , Predicate<String> pre){
List<String> strList = new ArrayList<>();
// 增强for循环遍历传进来的集合,并进行筛选,将满足条件的字符换放到新的集合中,并返回
for(String str : list) {
if(pre.test(str)) {
strList.add(str);
}
}
return strList;
}
}
除了上述四大核心接口之外,java8还提供了一些子接口,如下。
2) 方法引用和构造器引用
首先创建一个实体类,方便后面使用
package lambda.java8;
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public Employee() {};
public Employee(String name,int age) {
this.age = age;
this.name = name;
}
public Employee(int age) {
this.age = age;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String show() {
return "测试方法引用!";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
long temp;
temp = Double.doubleToLongBits(salary);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Employee other = (Employee) obj;
if (age != other.age)
return false;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
return false;
return true;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
}
}
演示代码
package lambda.test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.Test;
import lambda.java8.Employee;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc
*
*一、
* 方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用“方法引用”
* (可以理解为方法引用是Lambda表达式的另外一种表现形式)
*
* 主要有三种语法格式:
* 对象::实例方法名
* 类::静态方法名
* 类::实例方法名
*
* 注意: Lambda体中调用方法的参数列表返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致
*
*
* 二、构造器引用:
* 格式
* ClassName::new
* 注意:需要调用的构造器的参数列表要与函数式接口中的抽象方法的参数列表保持一致
*
* 三、数组引用
* Type::new
*/
public class TestMethodRef {
/**
* 对象::实例方法名
*/
@Test
public void test1() {
Consumer<String> con = (x) -> System.out.println("x:" + x);
con.accept("tomatocc");
// 这种方式是直接通过PrintStream对象中的方法实现输出
PrintStream ps = System.out;
Consumer<String> con1 = (x) -> ps.println("x:" + x);
con1.accept("tomatocc1");
// 下面两种使用的前提是,对于consumer函数式接口,传进去的参数值必须和接口返回的类型完全一致
Consumer<String> con2 = ps::println;
con2.accept("tomatocc2");
Consumer<String> con3 = System.out::println;
con3.accept("tomatocc3");
}
/**
* 对象::实例方法名
*/
@Test
public void test2() {
// 初始化一个对象
Employee emp = new Employee(1, "tom" ,25,8000);
// 通过调用供给型接口获得数组第一个位置的值
Supplier<String> sup = () ->emp.getName();
String str = sup.get();
System.out.println(str);
// 通过对象名::实例方法名 获得值
Supplier<Integer> sup1 = emp::getAge;
Integer num = sup1.get();
System.out.println("age " + num);
}
/**
* 类::静态方法
*/
@Test
public void test3() {
// 通过调用Integer的compare函数式接口来比较两个数
Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
int res = com.compare(5, 10);
System.out.println("lambda实现比较结果" + res);
Comparator<Integer> com1 = Integer::compare;
int res1 = com1.compare(10, 5);
System.out.println("类::静态方法实现比较结果" + res1);
}
/**
* 类::实例方法名
*/
@Test
public void test4() {
// 通过BiPredicate函数式接口,比较两个字符串是否一致
BiPredicate<String, String> bp = (x,y) -> x.equals(y);
System.out.println(bp.test("tom", "tomt"));
// 通过 类::实例方法名 实现
// (使用前提:lambda表达式中的第一个参数是实例方法的调用者,
// 而第二个参数是实例方法的参数时,则可以用 类名::实例方法名 进行调用)
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("tom", "tom"));
}
/**
* 构造器引用
*/
@Test
public void test5() {
// 通过调用supplier供给型接口,无参数,直接返回Employee对象 这里调用的是无参构造器
Supplier<Employee> sup = () -> new Employee();
Employee emp = sup.get();
System.out.println("通过调用Supplier函数式接口 无参构造器 实现年龄获取: " + emp.getAge());
// 构造器引用方式 无参构造器
Supplier<Employee> sup1 = Employee::new;
Employee emp1 = sup1.get();
System.out.println("通过调用Supplier函数式接口 和 无参构造器引用 实现年龄获取: " + emp1.getAge());
// 通过调用function函数式接口(传一个参数,返回一个参数),来完成一个参数列表构造器的引用
Function<Integer, Employee> fun1 = (x) -> new Employee(x);
Employee emp2 = fun1.apply(20);
System.out.println("通过调用function函数式接口 带参构造器 实现年龄获取: " + emp2.getAge());
// 构造器引用方式 带一个参数的构造器
Function<Integer , Employee> fun2 = Employee::new;
Employee emp3 = fun2.apply(20);
System.out.println("通过调用function函数式接口和 构造器引用 调用带参构造器实现年龄获取: " +emp3.getAge());
// 通过调用BiFunction函数式接口(传两个参数,返回一个参数),构造器引用方式 来实现对象获取
BiFunction<String, Integer, Employee> fun3 = Employee::new;
Employee emp4 = fun3.apply("tomatocc", 25);
System.out.println("通过调用BiFunction函数式接口和 构造器引用 调用带参构造器实现年龄获取: " + "name: " + emp4.getName() + " age : " + emp4.getAge());
}
/**
* 数组引用
*/
@Test
public void test7() {
// 通过调用Function函数式接口,传一个Integer类型的数组长度,返回该数组
Function<Integer, String[]> fun = (x) -> new String[x];
String [] strs = fun.apply(10);
System.out.println("数组长度为: " + strs.length);
Function<Integer, String []> fun1 = String[]::new;
String [] strs1 = fun1.apply(10);
System.out.println("数组引用方式 数组长度为: " + strs1.length);
}
}
2、强大的Streamp API
StreamAPI是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据的等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。
什么是Stream
Stream(流)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”
注意:
- Stream 自己不会存储元素
- Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream
- Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行
操作Stream的三个步骤
- 创建Stream
一个数据源(如:集合、数组),获取一个流 - 中间操作
一个中间操作链,对数据源的数据进行处理 - 终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果。
1)创建操作 演示代码
package lambda.test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc
* 一、Stream的三个操作步骤:
* 1.创建Stream
* 2.中间操作
* 3.终止操作(终端操作)
*/
public class TestStreamAPI1 {
/**
* 创建操作
*/
@Test
public void test1() {
// 1.可以通过Collection 系列集合提供的stream() 或者 parallelStream()
// Java8中的Collection 接口被扩展,提供了两个获取流的方法
// stream() 返回一个串行流
// parallelStream() 返回一个并行流
List<String> list= new ArrayList<String>();
Stream<String> stream = list.stream();
// 2. 通过Arrays 中的静态方法stream() 获取数组流
Integer [] ints = {1,6,10,2,30};
Stream<Integer> stream1 = Arrays.stream(ints);
// 3.通过Stream 类中的静态方法of()
Stream<String> stream2 = Stream.of("aa","bb","cc");
// 4.创建无限流
// 通过迭代
Stream<Integer> stream3 = Stream.iterate(0, (x) -> x+2); // 起始值为0,参数为x 返回x+2
stream3
.limit(10) // 中间操作:只获得前十个
.forEach(System.out::println);// 输出结果
// 通过生成 generate() 需要一个供给型函数式接口
Stream.generate(() -> Math.random()) // 生成随机数
.limit(5) // 中间操作:只获得前五个
.forEach(System.out::println);
}
}
2)中间操作
多个
中间操作
可以连接起来形成一个流水线
,除非流水线上触发种植操作,否则中间操作不会执行任何的处理!
而在终止操作时一次性全部处理,称为"惰性求值"
。
常见的中间操作:
- 筛选与切片
演示代码
package lambda.test;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc
* 一、Stream的三个操作步骤:
* 1.创建Stream
* 2.中间操作
* 3.终止操作(终端操作)
*/
public class TestStreamAPI2 {
// 中间操作
/**
* 筛选与切片
* filter - 接收Lambda,从流中排除某些元素
* limit - 截断流,使其元素不超过给定数量。
* skip(n) - 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n)互补
* distinct -筛选,通过流所产生元素的hashCode() 和equals()去除重复元素
*/
// 创建一个集合
List<String> list = Arrays.asList("tom","jack","tomatocc","admin","simion","tomatocc","tomatocc");
/**
* filter 中间操作
*/
// 内部迭代:迭代操作由Sream API完成
@Test
public void test1() {
// 中间操作:不会执行任何操作
Stream<String> stream =
list.stream()// 调用filter中间操作,参数为Predicate() 断言型函数式接口
.filter( e -> {
System.out.println("中间操作: 判断当前字符串长度是否大于3 ." + (e.length()> 3));
return e.length()> 3;
}); // 在断言函数式接口中将参数长度大于3的进行筛选
// 终止操作:一次性执行远不内容,即“惰性求值”
stream.forEach(System.out::println); // 设置终止操作 (如果没有终止操作则不会有任何输出)
}
// 外部迭代
@Test
public void test2() {
// 调用iterator
Iterator<String> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
/**
* limit 中间操作
*/
@Test
public void test3() {
list.stream()
.filter( e -> {
return e.length()> 3; // 先筛选
})
.distinct()//去除重复
.forEach(System.out::println);
}
/**
* skip操作
*/
@Test
public void test4() {
list.stream()
.filter( e -> {
return e.length()> 3; // 先筛选
})
.skip(2)// 跳过结果集中的前两个元素
.forEach(System.out::println);
}
/**
* distinct操作
*/
@Test
public void test5() {
list.stream()
.filter( e -> {
return e.length()> 3; // 先筛选
})
.skip(2)// 跳过结果集中的前两个元素
.forEach(System.out::println);
}
- 映射
演示代码
/**
* 映射
* map - 接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
* flatMap - 接收一个函数作为参数,将流中的每个值都换成另外一个流,然后把所有流连接成一个流
*/
/**
* map 操作
*/
@Test
public void test6() {
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
list.stream()
.map( str -> str.toUpperCase())// 调用map映射,将结合所有元素转成大写
.forEach(System.out::println);
List<String> list2 = Arrays.asList("aaa","bbbb","ccc","dddd","eee");
list2.stream()
.map(String::length )// 调用map映射,并使用对象::实例方法名 打印每个元素的长度
.forEach(System.out::println);
}
/**
* flatMap 操作
*/
@Test
public void test7() {
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
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<Character>();
for(Character ch : str.toCharArray()) {
list.add(ch);
}
return list.stream();
}
- 排序
演示代码
/**
* 排序
* sorted() - 自然排序(Comparable)
* sorted(Comparator com) - 定制排序(Comparator)
*/
@Test
public void test8() {
List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
list.stream()
.sorted() // 自然排序
.forEach(System.out::println);
System.out.println("------------------------------------");
// 员工集合 因为Employee类没有实现Comparable 因此可以定制排序
List<Employee> emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99),
new Employee(102, "李四", 59, 6666.66),
new Employee(103, "王五", 38, 3333.33),
new Employee(104, "赵六", 8, 7777.77),
new Employee(105, "田七", 38, 5555.55));
// 定制排序
emps.stream()
.sorted((e1,e2) -> {
if(e1.getAge() == e2.getAge()) { // 先按照年龄排序,年龄一样的话按照姓名排序
return e1.getName().compareTo(e2.getName());
} else {
return Integer.compare(e1.getAge(), e2.getAge());
}
}).forEach(System.out::println);
}
3)终止操作
终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void。
- 查找与匹配
演示代码 (先创建一个对象)
package lambda.java8;
public class Employee {
private int id;
private String name;
private int age;
private double salary;
private Status status;
public Employee() {};
public Employee(String name,int age) {
this.age = age;
this.name = name;
}
public Employee(int id, String name, int age, double salary, Status status) {
super();
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
this.status = status;
}
public Employee(int age) {
this.age = age;
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
public String show() {
return "测试方法引用!";
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + ", status=" + status
+ "]";
}
public enum Status{
FREE,
BUSY,
VOCATION;
}
}
package lambda.test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
import lambda.java8.Employee;
import lambda.java8.Employee.Status;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc 终止操作
*/
public class TestStreamAPI3 {
// 员工集合
List<Employee> emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99 ,Status.VOCATION),
new Employee(102, "李四", 59, 6666.66,Status.BUSY),
new Employee(103, "王五", 38, 3333.33,Status.FREE),
new Employee(104, "赵六", 8, 7777.77,Status.FREE),
new Employee(105, "田七", 38, 5555.55,Status.BUSY));
/**
* 查找与匹配
* allMatch - 检查是否匹配所有元素
* anyMatch - 检查是否至少匹配一个元素
* noneMatch - 检查是否没有匹配所有元素
* findFirst - 返回第一个元素
* findAny - 返回当前流中的任意元素
* count - 返回当前流中的总个数
* max - 返回流中的最大值
* min - 返回流中的最小值
*/
@Test
public void test() {
boolean b1 = emps.stream()
.allMatch( e -> e.getStatus().equals(Status.BUSY));
System.out.println("allMatch : 该集合的所有员工是不是都处于某个状态:" + b1);
boolean b2 = emps.stream()
.anyMatch( e -> e.getStatus().equals(Status.BUSY));
System.out.println("anyMatch: 该集合的所有员工状态是否至少有一个处于某种状态:" + b2);
boolean b3= emps.stream()
.noneMatch( e -> e.getStatus().equals(Status.VOCATION));
System.out.println("noneMatch: 该集合的所有员工是否没有某种状态:" + b3);
// 当你的返回结构有可能为空,则java会返回一个Optional容器 (最大化避免空指针)
Optional<Employee> op = emps.stream()
.sorted( (e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())) // 按照工资排序
.findFirst();
System.out.println("findFirst: 经过工资排序后显示第一个:" + op.get());
Optional<Employee> op2 = emps.stream()// stream式串行流
.filter( e -> e.getStatus().equals(Status.FREE))// 将所有free状态的筛选出来
.findAny();
System.out.println("findAny: 返回当前流中的任意元素:" + op2.get());
long num = emps.stream()
.count();
System.out.println("count : 当前流中集合总长度为" + num);
Optional<Employee> op3 = emps.stream()
.max((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary()));
System.out.println("max: 返回当前流中工资最高的 : " + op3.get());
Optional<Double> op4 = emps.stream()
.map(Employee::getSalary) //对象::实例方法名
.min(Double::compare); // 类::静态方法名
System.out.println("min : 当前流中工资最低为:" + op4.get());
}
}
- 归约
演示代码
// 员工集合
List<Employee> emps = Arrays.asList(
new Employee(101, "张三", 18, 9999.99 ,Status.VOCATION),
new Employee(102, "李四", 59, 6666.66,Status.BUSY),
new Employee(103, "王五", 38, 3333.33,Status.FREE),
new Employee(104, "赵六", 8, 7777.77,Status.FREE),
new Employee(105, "田七", 38, 5555.55,Status.BUSY));
/**
* 归约
* reduce(T indentity ,BinaryOperator) / reduce(BinaryOperator) —— 可以将流中元素反复结合起来,得到一个值
*/
@Test
public void test3() {
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作为起始值(x),进行x+y的操作,然后将x+y的操作结果作为x新的起始值(x),进行x+y的操作。。。。
System.out.println("总和:" + sum);
// 计算集合中的工资总和
Optional<Double> sum1 = emps.stream()
.map(Employee::getSalary)
.reduce(Double::sum);
System.out.println("工资总和为:" + sum1);
}
- 收集
演示代码
/**
* 收集
* collect —— 将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
*
*/
@Test
public void test4() {
// 员工集合
List<Employee> emplist = Arrays.asList(
new Employee(101, "张三", 18, 9999.99 ,Status.VOCATION),
new Employee(102, "李四", 59, 6666.66,Status.BUSY),
new Employee(103, "王五", 38, 3333.33,Status.FREE),
new Employee(104, "赵六", 8, 7777.77,Status.FREE),
new Employee(105, "田七", 38, 5555.55,Status.BUSY));
// 获取姓名集合
emplist.stream()
.map(Employee::getName)// 筛选集合中的所有名字
.collect(Collectors.toList())// 将该流中的数据进行收集,返回一个list
.forEach(System.out::println);// 进行打印
// 获取所有工资的平均值
Double avg = emplist.stream()
.collect(Collectors.averagingDouble(Employee::getSalary));
System.out.println("平均工资为:" + avg);
// 获取工资总和
Double sum = emplist.stream()
.collect(Collectors.summingDouble(Employee::getSalary));
System.out.println("工资总和为:" + sum);
// 工资最大值
Optional<Employee> maxEmp = emplist.stream()
.collect(Collectors.maxBy((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())));
System.out.println("工资最大值的员工:" + maxEmp);
// 工资最大值
Optional<Double> max = emplist.stream()
.map(Employee::getSalary)
.collect(Collectors.maxBy(Double::compare));
System.out.println("工资最大值:" + max);
// 工资最小值
Optional<Employee> minEmp = emplist.stream()
.collect(Collectors.minBy((e1,e2) -> Double.compare(e1.getSalary(),e2.getSalary())));
System.out.println("工资最小值的员工:" + minEmp);
// 工资最小值
Optional<Double> min = emplist.stream()
.map(Employee::getSalary)
.collect(Collectors.minBy(Double::compareTo));
System.out.println("工资最小值:" + min);
}
- 分组
演示代码
/**
* 分组
*/
@Test
public void test5() {
// 员工集合
List<Employee> emplist = Arrays.asList(
new Employee(101, "张三", 18, 9999.99 ,Status.VOCATION),
new Employee(102, "李四", 59, 6666.66,Status.BUSY),
new Employee(103, "王五", 38, 3333.33,Status.FREE),
new Employee(104, "赵六", 8, 7777.77,Status.FREE),
new Employee(105, "田七", 38, 5555.55,Status.BUSY));
// 将集合进行按照状态进行分组
Map<Status, List<Employee>> gp = emplist.stream()
.collect(Collectors.groupingBy(Employee::getStatus));// 按照状态进行分组
System.out.println(gp);
// 多级分组
// 1.将集合进行按照状态进行分组
// 2.然后按照年龄分组
Map<Status,Map<String,List<Employee>>> map = emplist.stream()
.collect(Collectors.groupingBy(Employee::getStatus , Collectors.groupingBy( (e) -> {
if(((Employee) e).getAge() <= 35) {
return "青年";
}else if(((Employee) e).getAge() <= 50) {
return "中年";
} else {
return "老年";
}
})));
System.out.println(map);
}
}
- 分区
演示代码
/**
* 分区
*/
@Test
public void test6() {
// 员工集合
List<Employee> emplist = Arrays.asList(
new Employee(101, "张三", 18, 9999.99 ,Status.VOCATION),
new Employee(102, "李四", 59, 6666.66,Status.BUSY),
new Employee(103, "王五", 38, 3333.33,Status.FREE),
new Employee(104, "赵六", 8, 7777.77,Status.FREE),
new Employee(105, "田七", 38, 5555.55,Status.BUSY));
// 将集合进行按照工资大于8000进行分区,满足的分到一个区,不满足的分到另一个区
Map<Boolean, List<Employee>> collect = emplist.stream()
.collect(Collectors.partitioningBy( e -> e.getSalary()>8000));
System.out.println(collect);
}
- 其他操作
演示代码
/**
* 其他操作
*/
@Test
public void test8() {
// 员工集合
List<Employee> emplist = Arrays.asList(
new Employee(101, "张三", 18, 9999.99 ,Status.VOCATION),
new Employee(102, "李四", 59, 6666.66,Status.BUSY),
new Employee(103, "王五", 38, 3333.33,Status.FREE),
new Employee(104, "赵六", 8, 7777.77,Status.FREE),
new Employee(105, "田七", 38, 5555.55,Status.BUSY));
DoubleSummaryStatistics collect = emplist.stream()
.collect(Collectors.summarizingDouble(Employee::getSalary));
System.out.println("最大值:" + collect.getMax());
System.out.println("最小值:" + collect.getMin());
System.out.println("平均值:" + collect.getAverage());
System.out.println("总数:" + collect.getCount());
System.out.println("总和:" + collect.getSum());
String join = emplist.stream()
.map(Employee::getName)
.collect(Collectors.joining(",","===","==="));// 件流中的所有姓名进行连接
System.out.println("join进行数据连接:");
System.out.println(join);
}
3、并行流和串行流
- Fork/Join框架
Fork/Join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行join汇总。
Fork/Join框架与传统线程池的区别
采用“工作窃取”模式(work-stealing):
当执行新的任务时,他可以将其拆分分成更小的任务执行,并将小任务添加到线程队列中,然后子啊从一个随机线程的队列中头一个并把它放到自己的队列中。
相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上,在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态,而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行,那么处理该子问题的线程会自动寻找其他尚未运行的子问题来执行,这种方式减少了线程的等待事见,提高了性能。
- 并行流与顺序流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。
Java8 中将并行进行了优化,我们可以很容易的对数据进行并行操作。StreamAPI可以声明性的通过parallel() 与 sequential() 在并行流与顺序流之间进行切换。
// 计算1-1亿累加求和所消耗的时间
Instant start = Instant.now();// 获得当前时间
long sum = LongStream.rangeClosed(0, 100000000L)
.parallel()
.reduce(0,Long::sum);
Instant end = Instant.now();// 获得当前时间
System.out.println("java8循环 结果: " + sum + " 耗时:" + Duration.between(start, end).toMillis()+ "ms");
4、Optional类
Optional类(java.utin.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
package lambda.test;
import java.util.Optional;
import org.junit.Test;
import com.sun.swing.internal.plaf.metal.resources.metal;
import lambda.java8.Employee;
import lambda.java8.Employee.Status;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc
> Optional<T>类(java.utin.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
*/
public class TestOptional {
/**
* 通过Optional.of() 创建实例
*/
@Test
public void test1() {
// 创建一个Employee对象,用Optional容器进行封装
Optional<Employee> of = Optional.of(new Employee());
// Optional<Employee> of = Optional.of(null); 可以快速等位空指针位置
// 获得这个对象
Employee emp = of.get();
// 打印对象
System.out.println(emp);
}
/**
* 通过Optional.empty();创建一个空的实例
*/
@Test
public void test2() {
Optional<Employee> op = Optional.empty();
System.out.println(op.get());
}
/**
* 通过Optional.ofNullable(T t);创建一个实例
*/
@Test
public void test3() {
Optional<Employee> op = Optional.ofNullable(new Employee());
System.out.println(op.get());
}
/**
* 通过Optional.ofNullable(T t);创建一个实例
* isPresent() : 判断是否包含值
* orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
* orElseGet(Supplier s):如果调用对象包含值,则返回该值,否则返回s获取的值
*/
@Test
public void test4() {
// 先构建一个对象
Optional<Employee> op = Optional.ofNullable(new Employee());
// 如果有值,则进行处理
if(op.isPresent()) {
System.out.println(op.get());
} else {
System.out.println("空值");
}
//
Optional<Employee> op1 = Optional.ofNullable(null);
// 如果上面的new Employee() 对象为null 则使用下面的默认值
Employee emp = op1.orElse(new Employee("张三",18));
System.out.println(emp);
Optional<Employee> op2 = Optional.ofNullable(null);
Employee emp2 = op2.orElseGet(() -> new Employee("李四",18));
System.out.println(emp2);
}
/**
* map(Function f) : 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
* flatMap(Function mapper):与map类似,要求返回值必须是Optional
*/
@Test
public void test5() {
// 先构建一个对象
Optional<Employee> op = Optional.ofNullable(new Employee("张三",18));
Optional<String> str = op.map((e) -> e.getName());
System.out.println(str.get());
///
Optional<Employee> op1 = Optional.ofNullable(new Employee("张三",18));
Optional<String> str2 = op1.flatMap((e) -> Optional.of(e.getName()));
System.out.println(str2.get());
}
}
5、接口中的默认方法与静态方法
Java8中,允许接口中包含具有具体实现的方法,该方法称为“默认方法,默认方法可以使用default关键字修饰。
package lambda.test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc default修饰符
*/
public interface MyFun {
default String getName(String name) {
if(name.isEmpty()) {
return "哈哈";
}
return name;
}
}
如果,我们在创建一个类MyClss,同样创建一个名为getName(String name)的方法,并有一个String类型的返回值。然后新创建一个类来继承MyClss类,并实现MyFun 接口。
package lambda.test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc MyClss类,并创建和MyFun接口中的同名方法
*/
public class MyClss {
public String getName(String name) {
if(name.isEmpty()) {
return "嘿嘿";
}
return name;
}
}
//
package lambda.test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc TestClass类继承MyClss类,实现MyFun接口
*/
public class TestClass extends MyClss implements MyFun{
public static void main(String[] args) {
System.out.println(new TestClass().getName(""));
}
}
上面的输出结果是“嘿嘿”,也就是说TestClass 这个类调用的getName()方法其实上是MyClss类中的方法,而并不是MyFun接口中的方法。
接口默认方法的“类优先”原则*
若一个接口中定义了一个默认方法,而另外一个父类或者接口中又定义了个同名的方法时
- 选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
- 接口冲突。如果一个付类接口提供一个默认方法,而另一个接口业提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突。
除此之外,java8中还允许出现静态方法。
package lambda.test;
/**
* @author tomatocc
* @email 553866242@qq.com
* @desc default修饰符
*/
public interface MyFun {
public static String show(){
return "java 8 静态方法";
}
}
///
package lambda.test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc TestClass类中直接调用接口中的静态方法
*/
public class TestClass {
public static void main(String[] args) {
System.out.println(MyFun.show());
}
}
6、新时间日期API
java8之前,Date类是非线程安全的,因此如果在多线程情况下使用Date时间类进行操作,就必须使用到ThreadLocal,而在java8之后,引入了LocalDate类,来帮我们解决线程不安全的问题。以下是示例代码。
package lambda.date;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.junit.Test;
/**
* @author tomatocc
* @email 553866242@qq.com
* @desc 类描述
*/
public class TestSimpleDateFormat {
/**1.8之前的Date类都是线程不安全的 演示Demo
* @throws ExecutionException
* @throws InterruptedException **/
@Test
public void test1() throws InterruptedException, ExecutionException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
// 创建Callable 去解析时间
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return sdf.parse("20161218");
}
};
// 创建一个长度为10的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
// 循环10次
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
// 直接输出的话就会报错
for(Future<Date> future : results) {
System.out.println("1.8之前,线程不安全的问题:结果:: " + future.get());
}
}
/**1.8之前的Date类都是线程不安全的 演示Demo
* @throws ExecutionException
* @throws InterruptedException **/
@Test
public void test2() throws InterruptedException, ExecutionException {
/**通过ThreadLocal 进行改造 */
// 创建Callable 去解析时间
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
return DateFormatThreadLocal.convert("20161218");
}
};
// 创建一个长度为10的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> results = new ArrayList<>();
// 循环10次
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
// 直接输出的话就会报错
for(Future<Date> future : results) {
System.out.println("1.8之前改用ThreadLocal处理线程不安全的问题:结果: " + future.get());
}
// 关闭线程池
pool.shutdown();
}
/**1.8之后使用LocalDate解决线程不安全 演示Demo
* @throws ExecutionException
* @throws InterruptedException **/
@Test
public void test3() throws InterruptedException, ExecutionException {
/**通过ThreadLocal 进行改造 */
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");// 按照指定格式
// 创建Callable 去解析时间
Callable<LocalDate> task = new Callable<LocalDate>() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("20161218", dtf);
}
};
// 创建一个长度为10的线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> results = new ArrayList<>();
// 循环10次
for (int i = 0; i < 10; i++) {
results.add(pool.submit(task));
}
// 直接输出的话就会报错
for(Future<LocalDate> future : results) {
System.out.println("1.8之前改用ThreadLocal处理线程不安全的问题:结果: " + future.get());
}
// 关闭线程池
pool.shutdown();
}
}
java8之前处理Date线程不安全的方法。
package lambda.date;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc 1.8以前处理线程不安全的SimpleDateFormat方法
*/
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateFormatThreadLocal {
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyyMMdd");
}
};
public static Date convert(String source) throws ParseException {
return df.get().parse(source);
}
}
其它时间类API
LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期活时间,并不包含当前的时间信息。也不包含时区相关的信息。
注:ISO-8601日历系统是国际标准化组织制定的现代化公民的日期和时间的表示方法。
package lambda.date;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.ZoneOffset;
import org.junit.Test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc LocalDate(处理日期 年月日) LocalTime(处理时间 时分秒) LocalDateTime(处理日期和时间)
*/
public class TestLocalDateTime {
@Test
public void test1() {
// 获得当前时间
LocalDateTime ldt = LocalDateTime.now();
System.out.println("当前时间:" + ldt);
// 获得指定时间
LocalDateTime of = LocalDateTime.of(2015, 10,19,13,22,30);
System.out.println("获得指定时间 : " + of);
// 当前时间+2年
LocalDateTime plusYears = ldt.plusYears(2);
System.out.println("当前时间+2年 " + plusYears);
// 当前时间-2年
LocalDateTime minusYears = ldt.minusYears(2);
System.out.println("当前时间-2年 " + minusYears);
// 得到当前年
System.out.println(ldt.getYear());
// 得到当前月
System.out.println(ldt.getMonthValue());
System.out.println(ldt.getMonth());
// 得到当前日
System.out.println(ldt.getDayOfMonth());
// 得到当前小时
System.out.println(ldt.getHour());
// 得到当前分
System.out.println(ldt.getMinute());
// 得到当前秒
System.out.println(ldt.getSecond());
}
/**
* Instant : 时间戳(以Unix 元年: 1970年1月1日 00:00:00到某个时间之间的毫秒值)
*/
@Test
public void test2() {
// 默认获取的是 UTC 时区 和 中国有8个小时时差
Instant inst = Instant.now();
System.out.println("默认当前时间(UTC时区) : " + inst );
OffsetDateTime odt = inst.atOffset(ZoneOffset.ofHours(8));// 设置和 UTC 8个小时时差
System.out.println("当前时间(中国时区)" + odt);
// 获得时间戳
System.out.println("毫秒值 : " + inst.toEpochMilli());// 毫秒值
// 基于Unix 元年时间基础上,增加60秒
Instant it = Instant.ofEpochSecond(60);
System.out.println(it);
}
/**
* Duration : 计算两个“时间”之间的间隔
* Period : 计算两个“日期”之间的间隔
* @throws InterruptedException
*/
@Test
public void test3() throws InterruptedException {
/**Duration : 计算两个“时间”之间的间隔**/
Instant now1 = Instant.now();// 当前时间1
Thread.sleep(1000);// 休眠10毫秒
Instant now2 = Instant.now();// 当前时间2
// 计算两个时间的间隔
Duration duration = Duration.between(now1, now2);
System.out.println("两个时间间隔毫秒数 : " + duration.toMillis());// 相差的毫秒数
LocalTime lt = LocalTime.now();// 当前时间1
Thread.sleep(1000);// 休眠10毫秒
LocalTime lt1 = LocalTime.now();// 当前时间2
System.out.println("两个时间间隔毫秒数 : " + Duration.between(lt, lt1).toMillis());// 相差的毫秒数
/**Period : 计算两个“日期”之间的间隔**/
LocalDate of = LocalDate.of(2015, 2, 10);// 指定时间 20150210
LocalDate of1 = LocalDate.now();// 当前时间 20190119
Period period = Period.between(of, of1);
System.out.println("两个日期间隔: " + period);// P3Y11M9D : 3年 11月9 天
System.out.println("两个日期间隔 年: " + period.getYears());
System.out.println("两个日期间隔 月: " + period.getMonths());
System.out.println("两个日期间隔 日: " + period.getDays()) ;
}
}
- TemporalAdjusters 时间矫正器、
TemporalAdjuster :时间矫正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。就需要时间矫正器。
TemporalAdjusters : 该类通过静态方法提供了大量的常用TemporalAdjuster的实现。
package lambda.date;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import org.junit.Test;
/**
*@author tomatocc
*@email 553866242@qq.com
*@desc TemporalAdjusters 时间矫正器
*/
public class TestTempor {
@Test
public void test1() {
// 获得当前时间
LocalDateTime now = LocalDateTime.now();
System.out.println("当前时间:" + now);
LocalDateTime ldt = now.withDayOfMonth(1);// 返回当前月份1号的当前时间
System.out.println("校正后时间:" + ldt);
// 返回下一个周日的时间
LocalDateTime with = now.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println("下个周日的时间 : " + with);
// 自定义时间
// 下一个工作日(周一--周五)
LocalDateTime with2 = now.with((l) -> {
LocalDateTime ldt1 = (LocalDateTime) l;
DayOfWeek dow = ldt1.getDayOfWeek();
if(dow.equals(DayOfWeek.FRIDAY)) {// 如果是周五,则当前日期+3天
return ldt1.plusDays(3);
} else if(dow.equals(DayOfWeek.SATURDAY)) {// 如果是周六,则当前日期+2天
return ldt1.plusDays(2);
} else { // 其他情况 + 1天
return ldt1.plusDays(1);
}
});
System.out.println("下一个工作日:" + with2);
}
}
- 时区的处理
Java8中加入了对时区的支持,带时区的时间分别为:
ZonedDate、ZonedTime、ZonedDateTime
其中每个时区都对应着ID,地区ID都为 “{区域}/{城市}” 的格式。例如:Asia/Shanghai
ZoneId: 该类中包含了所有的时区信息
getAvailableZoneIds():可以获取所有时区信息
of(id) : 用指定的时区信息获取ZoneId对象。
/**
* 时区的操作
* ZonedDate、ZonedTime、ZonedDateTime
*/
@Test
public void test2() {
Set<String> set = ZoneId.getAvailableZoneIds();
// 打印所有时区
//set.forEach(System.out::println);
// 获得America/Montreal时区下的当前时间
LocalDateTime now = LocalDateTime.now(ZoneId.of("America/Montreal"));
System.out.println("America/Montreal 时区下当前时间 : " + now);
// 获得Asia/Shanghai时区下的当前时间
LocalDateTime now1 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
// 用Asia/Shanghai时区去构建now1
ZonedDateTime zdt = now1.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("用Asia/Shanghai时区去构建now1 当前时间 : " + zdt);// 2019-01-19T05:17:26.409+08:00[Asia/Shanghai] +08表示Asia/Shanghai比UTC有8个小时时差
}
7、重复注解和类型注解
java8对注解处理提供了两点改进:可重复的注解及可用于类型的注解
[^1]: 1)对底层数据结构进行调整(HashMap、HashSet、ConcurrentHashMap(CAS算法)的改动,由数组+链表改为数组+链表+红-黑树[^2],这样的好处是除了添加以外,所有的操作速度均大幅度提高)。
2)同时,对底层的内存结构也进行了调整,由原来的堆、栈和方法区(属于堆中永久区的一部分)
,改为堆、栈和元空间[^3](MetaSpace,直接使用的是物理内存).
[^2]: 关于【红-黑树】的详细介绍
[^3]: 关于【元空间】的详细介绍
欢迎关注本人个人公众号,交流更多技术信息