java8函数式编程(Lambda表达式,Optional,Stream流,时间日期api)
1.概述
Lambda表达式,也可称为闭包,是JDK8的新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中),可以使代码变的更加简洁紧凑。Lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。
名字起源是以前还没有计算机时,逻辑学家Alonzo Church想要形式化的表示能有效计算的数学函数,使用了希腊字母lambda(λ )来标记参数,从那以后,带参数变量的表达式就被称为lambda表达式。
2.lambda表达式
#### 2.1省略原则
- 参数类型可以省略
- 方法体只有一句代码时大括号return和唯一一句代码的分号可以省略
- 方法只要一个参数时小括号可以省
2.2代码
package com.example.lambda;
import java.util.function.Function;
import java.util.function.IntBinaryOperator;
import java.util.function.IntPredicate;
public class Lambda_1 {
public static void main(String[] args) {
new Thread(()->{
System.out.println("今天天气真好"+Thread.currentThread().getName());
}).start();
new Thread(()->{
System.out.println("今天天气真好"+Thread.currentThread().getName());
}).start();
//接口是函数表达式(接口中只定义了一个抽象方法)
//匿名内部类
int i=calculateNum((left, right) -> left+right);
System.out.println(i);
//函数表达式
int ii=calculateNum((int left,int right)->{
return left*right;
});
System.out.println(ii);
System.out.println("=====================");
// printNum(new IntPredicate() {
// @Override
// public boolean test(int value) {
// return value%2==0;
// }
// });
//使用lombok
printNum(value->value%2==0);
//练习四
String number=typeConver((String name)->{
return String.valueOf(name);
});
// number=typeConver(new Function<String, Integer>() {
// @Override
// public Integer apply(String s) {
// return null;
// }
// });
System.out.println(number);
//省略规则
}
//练习一
public static int calculateNum(IntBinaryOperator operator){
int a=10;
int b=20;
return operator.applyAsInt(a,b);
}
//练习二
public static void printNum(IntPredicate predicate){
int []arr={1,2,3,4,5,6,7,8,9,10};
for (int i : arr) {
if(predicate.test(i)){
System.out.println(i);
}
}
}
//练习三
public static <R> R typeConver(Function<String,R>function){
String str="34567";
R result=function.apply(str);
return result;
}
}
3.Stream流
3.1概述
java8的Stream使用的是函数式编程式,如同它的名字一样,它可以被用来对集合和数组进行链状流式的操作。可以更方法的让我们对集合或数组操作。
3.2案例数据准备
package com.example.stream;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author {
private Long id;
private String name;
private Integer age;
private String intro;
private List<Book> books;
}
package com.example.stream;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Book {
private Long id;
private String name;
private String category;
private Integer score;
private String intro;
}
package com.example.stream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamDemo {
public static List<Author> getAuthors(){
Author author=new Author(1L,"多名",33,"一个人",null);
Author author2=new Author(2L,"卡拉",12,"两个人",null);
Author author3=new Author(3L,"joker",23,"这个世界",null);
Author author4=new Author(3L,"joker",23,"这个世界",null);
//书籍列表
List<Book> books1=new ArrayList<>();
List<Book> books2=new ArrayList<>();
List<Book> books3=new ArrayList<>();
books1.add(new Book(1L,"两面三刀","爱情,哲学",88,"用一把刀划分爱恨"));
books1.add(new Book(2L,"一个人不能死在同一把刀下","个人成长,爱情",99,"讲述失败"));
books1.add(new Book(3L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
books2.add(new Book(4L,"两面三刀","爱情,哲学",88,"用一把刀划分爱恨"));
books1.add(new Book(5L,"一个人不能死在同一把刀下","个人成长,爱情",99,"讲述失败"));
books2.add(new Book(6L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
books2.add(new Book(7L,"两面三刀","爱情,哲学",88,"用一把刀划分爱恨"));
books3.add(new Book(8L,"一个人不能死在同一把刀下","个人成长,爱情",99,"讲述失败"));
books3.add(new Book(9L,"那风吹不到的地方","哲学",85,"带你用思维去领略世界的尽头"));
author.setBooks(books1);
author2.setBooks(books2);
author3.setBooks(books3);
author4.setBooks(books1);
List<Author> authors=new ArrayList<>();
authors.add(author);
authors.add(author2);
authors.add(author3);
authors.add(author3);
return authors;
}
}
3.2.1实现
//初始化数据
List<Author> authors=getAuthors();
authors
.stream()//转换成流
.distinct()//去重
.filter(author -> author.getAge()>18)//过滤
.forEach(author -> System.out.println(author));
//注意要加上终结方法才有效果
3.3常用操作
3.3.1流的创建
单列集合:集合对象.stream()
List<Author> authors=getAuthors();
Stream<Author> authorStream=authors.stream();
数组:Stream.of(arr)或Arrays.stream(arr)
Integer[]arr={1,2,3,4,5};
Stream<Integer> streams=Stream.of(arr);
Stream<Integer> stream= Arrays.stream(arr);
stream.distinct().filter(ares->ares>=3).forEach(System.out::println);
双列集合:转换成单列集合后再创建
Map<String,Integer> maps=new HashMap<>();
maps.put("张三",20);
maps.put("李四",10);
maps.put("王五",30);
Set<Map.Entry<String, Integer>> set=maps.entrySet();
Stream<Map.Entry<String, Integer>> entryStream = set.stream();
entryStream.distinct().filter(entry->entry.getValue()>=20).forEach(entry-> System.out.println(entry.getKey()+
"=========="+entry.getValue()));
3.3.2中间操作
filter
可以对流中的元素进行条件过滤,符合过滤条件的才能继续留在流中。
List<Author> authorList=getAuthors();
authorList.stream().filter(item->item.getName().length()>3).forEach(System.out::println);
map
map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数
List<Author> authorList=getAuthors();
authorList.stream().map(item->item.getAge()).map(age->age+10).forEach(age-> System.out.println(age));
distinct
可以去除流的重复元素
List<Author> authors=getAuthors();
authors.stream().distinct().forEach(System.out::println);
注意:distinct方法是依赖Object的equals方法来判断是否相同的对象的,所以需要注意重写equals方法。
sorted
可以对流中的元素进行排序
对流中的元素按照年龄进行降序排序,并且要求不能有重复元素
final List<Author> authors = getAuthors();
authors.stream().distinct().sorted((o1, o2) -> o1.getAge()-o2.getAge()).forEach(System.out::println);
注意:如果调用空参的sorted()方法需要流中实现Comparable
final List<Author> authors1 = getAuthors();
authors1.stream().distinct().sorted().forEach(System.out::println);
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class Author implements Comparable<Author>{
private Long id;
private String name;
private Integer age;
private String intro;
private List<Book> books;
@Override
public int compareTo(Author o) {
return o.getAge()-this.getAge();
}
}
limit
可以设置流的最大长度,超出的部分将被抛弃
final List<Author> authors1 = getAuthors();
authors1.stream().distinct().sorted().limit(1).forEach(System.out::println);
skip
跳过流中的前n个元素,返回剩下的元素
//去除年龄最大的人
final List<Author> authors1 = getAuthors();
authors1.stream().distinct().sorted().skip(1).forEach(System.out::println);
flatMap
map只能把一个对象转换另一个对象来作为流中的元素。而flatMap可以把一个对象转换成多个对象作为流中的元素。
列1:打印所有的书籍的名字。要求对重复的元素进行去重。
List<Author> authors = getAuthors();
authors.stream().flatMap(author -> author.getBooks().stream()).distinct().forEach(book -> System.out.println(book.getName()));
列2:打印数据的所有分类 。要求对分类进行去重。
final List<Author> authors = getAuthors();
authors.stream().flatMap(author -> author.getBooks().stream()).distinct().flatMap(book -> Arrays.stream(book.getCategory().split(",")))
.distinct().forEach(System.out::println);
3.3.3终结操作
forEach
final List<Author> authors = getAuthors();
authors.stream().forEach(System.out::println);
//System.out::println调用的就是toString()方法
count
可以用来获取当前流中元素的个数
列子:打印这些作家的所书籍的数目,注意删除重复元素
final List<Author> authors = getAuthors();
final long count = authors.stream().flatMap(author -> author.getBooks().stream()).distinct().count();
System.out.println(count);
max,min
可以用来获取流中的最高值和最低值
final List<Author> authors = getAuthors();
final Optional<Book> max = authors.stream().flatMap(author -> author.getBooks().stream()).distinct().max((o1, o2) -> o1.getScore() - o2.getScore());
System.out.println(max.get());
final Optional<Integer> min = authors.stream().flatMap(author -> author.getBooks().stream()).distinct().map(book -> book.getScore()).min((o1, o2) -> o1 - o2);
System.out.println(min.get());
collect
final List<Author> collect = authors.stream().collect(Collectors.toList());
for (Author author : collect) {
System.out.println(author);
}
final Set<Author> collect1 = authors.stream().collect(Collectors.toSet());
for (Author author : collect1) {
System.out.println(author);
}
final Map<String, Author> collect2 = authors.stream().distinct().collect(Collectors.toMap(author -> author.getName(), author -> author));
collect2.forEach((s, author) -> System.out.println(s + "++===" + author.toString()));
查找与匹配
anyMatch:可以用来判断是否有任意匹配条件的元素,结果为boolean类型
//判断是否有29岁以上的作家
List<Author> author=getAuthors();
author.forEach(System.out::println);
System.out.printf("技能抽考:%b",author.stream().anyMatch(an->an.getAge()>20));
allMatch:可以用来判断是否符合匹配条件,结果为boolean类型。如果都符合结果为true,否则结果为false
//判断是否所有的作家都是未成年
List<Author> author=getAuthors();
System.out.printf("判断是否所有的作家都是未成年:%b",author.stream().allMatch(an->an.getAge()<18));
noneMatch:判断流中的元素是否都不符合匹配条件。如果都不符合为true,否则为false
List<Author> author=getAuthors();
System.out.printf("判断所有作家是否都没有超过100岁,%b",author.stream().noneMatch(author1->author1.getAge()>100));
findAny:获取流中的任意一个元素。该方法没有办法保证获取的一定流中第一个元素
//获取任意一个满18岁的作家
System.out.println(authors.stream().filter(author -> author.getAge()>18).findAny());
findFirst:获取流中的第一个元素
System.out.printf("获取年龄最小的元素:%s",authors.stream().sorted((o1, o2) -> o1.getAge()-o2.getAge()).findFirst());
reduce归并
对流中的数据按照 你制定的方式计算出一个结果
reducce的作用是把stream中的元素给结合起来,我们可以传入一个初始化值,它会按照我们的计算方式依次拿流中的元素和初始化值的基础上进行计算,把计算结果再和后面的元素计算。
它的内部计算如下:
T result=identity;
for(T element: this.stream)
result=accumulator.apply(result,element)
return result;
其中identity就是我们通过方法参数传入的初始值accumulator的apply具体进行什么计算是我们通过方法参数来确定的
列子:
使用reduce求所有作者年龄的和
//使用reduce求所有作者年龄的和
Integer sum=authors.stream().distinct().map(author -> author.getAge()).reduce(0,(result,element)->result+element);
System.out.println(sum);
使用reduce求所有作者中年龄的最大值
Integer age=authors.stream().distinct().map(author -> author.getAge()).
reduce(Integer.MIN_VALUE,(integer, integer2) ->integer>integer2?integer:integer2);
System.out.println(age);
使用reduce求所有作者中年龄最小值
Integer age=authors.stream().distinct().map(author -> author.getAge()).
reduce(Integer.MAX_VALUE,(result,element)->result>element?element:result);
System.out.println(age);
4.Optional
4.1概述
我们在编写代码的时候出现最多的就是空指针异常。所以在很多情况下我们需要做各种非空的判读。
尤其是对象中的属性还是一个对象的情况下。这种判读会更多。
而过多的判断语句会让我们的代码显得臃肿不堪。
所以在JDK8中引入Optional,养成使用Optional的习惯后你可以写出更优雅的代码来避免空指针异常。
ofNullable
静态方法ofNullable来把数据封装成一个Optional对象。无论传入的参数是否为null都不会出现问题
Optional<Book> optionalBook=bookOptional();
optionalBook.ifPresent(System.out::println);
public static Optional<Book> bookOptional(){
Book book=new Book();
book.setAge("12");
return Optional.ofNullable(null);
}
of
确定一个对象不是空的则可以使用Optional的静态方法of来把数据封装成Optional对象
public static Optional<Book> getAuthorOptional(){
Book author=new Book("1","2",10);
author=null;
return author==null?Optional.empty():Optional.of(author);
}
final Optional<Book> authorOptional = getAuthorOptional();
authorOptional.ifPresent(System.out::println);
Optional.empty()
如果发现某个对象为null,这个时候需要把null封装成Optional对象。则可以使用Optional.empty()
4.2安全消费
我们获取到一个Optional对象后肯定需要对其中的数据进行使用,这时候我们可以使用其ifPresent方法对消费其中的值。这个方法会判断其内封装的数据是为空,不为空时会执行具体的消费代码,这样使用起来就更加安全了。列如以下写法就优雅的避免了空指针异常,
final Optional<Book> optionalBook = bookOptional();
optionalBook.ifPresent(book-> System.out.println(book.getName()));
4.3获取值
如果我们想获取值自己进行处理可以使用get方法获取,但不推荐因为Optional内部的数据为空的时候会出现异常
4.4安全获取值
如果我们期望安全的获取值,我们不推荐使用get()方法,而是使用Optional提供的以下方法。
orElseGet:
获取数据并且数据为空时的默认值。如果数据不为空就能获取到该数据。如果为空则根据你传入的参数来创建对象作为默认值返回
final Optional<Book> optionalBook = bookOptional();
final Book book = optionalBook.orElseGet(()->new Book("张三","20",10));
System.out.println(book.getName());
orElseThrow:
获取数据,如果数据不为空就获取该数据。如果为空则根据你传入的参数来创建异常抛出
final Optional<Book> optionalBook = bookOptional();
try {
final Book book = optionalBook.orElseThrow(() -> new RuntimeException("数据为空"));
System.out.println(book);
} catch (Throwable e) {
throw new RuntimeException(e);
}
4.5过滤
我们可以使用filter方法对数据进行过滤。如果原本是有数据的,但是不符合判断,也会变成一个无数据的optional对象。
final Optional<Book> optionalBook = bookOptional();
optionalBook.filter(book -> book.getNum()>20).ifPresent(book -> System.out.println(book));
4.6判断
我们可以使用isPresent方法进行是否存在数据的判断。如果为空返回false,如果不为空返回true。但是这种方式并不能提现Optional的好处,更推荐使用ifPresent方法。
>final Optional<Book> optionalBook = bookOptional();
if(optionalBook.isPresent()){
System.out.println(optionalBook.get().getName());
}
4.7数据流转换
Optional还提供了map可以让我们对数据进行转换,并且转换得到的数据还是被Optional包装好的,保证了我们的使用安全。
final Optional<Books> books = bookOptional();
final Optional<List<Book>> books1 = books.map(book -> book.getBooks());
books1.ifPresent(b->b.forEach(System.out::println));
5.函数接口
5.1概念
只有一个抽象方法的接口我们称为函数接口
JDK的函数接口都加上了@Functionallnterface注解进行标识。但是无论是否加上该注解只要接口中只有一个抽象方法,都是函数式接口。
5.2常见函数式接口
Consumer消费接口
根据其中抽象方法列表和返回类型知道,我们可以在方法中对传入的参数进行消费。
@FunctionalInterface
public interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
Function计算转换接口
根据其中抽象方法和参数列表的返回值类型知道,我们可以在方法传入的参数计算或转换,把结果返回
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
Predicate判断接口
根据其中抽象方法的参数列表和返回值类型知道,我们可以在方法中传入的参数条件判断,返回判断结果
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
Supplier生产接口
根据其中抽象方法的参数列表和返回值类型,我们可以在方法中创建对象,把创建好的对象返回
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
常用的默认方法
and
我们在使用Predicate接口时候可能需要进行判断条件。而and方法相当于是使用&&来拼接条件
//年龄大于18并且名字长度大于3
final Stream<Author> stream = getAuthors().stream();
final List<Author> collect = stream.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge()>20;
}
}.and(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length()>3;
}
})).collect(Collectors.toList());
collect.forEach(System.out::println);
or
我们使用Predicate接口时候可能需要进行判断条件的拼接而使用or相当于是使用||来拼接判读
//年龄大于18或名字长度大于3
final Stream<Author> stream = getAuthors().stream();
final List<Author> collect = stream.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge()>20;
}
}.or(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getName().length()>3;
}
})).collect(Collectors.toList());
collect.forEach(System.out::println);
negate
Predicate接口中使用的方法,negate(取消,否认)方法相当于在判断前添加!表示取反
//年龄非大于18
final Stream<Author> stream = getAuthors().stream();
final List<Author> collect = stream.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge()>20;
}
}.negate()).collect(Collectors.toList());
collect.forEach(System.out::println);
6.方法引用
我们在使用lambda时,如果方法体中只有一个方法的调用的话(包括构造方法),我们可以用方法引用进一步简化代码。
6.1推荐方法
我们在使用lambda是不需要考虑什么时候方法引用,用哪种方法引用,方法引用格式是什么,我们只需要在写lambda方法发现方法只有一行代码,并且是方法的调用时使用快捷键尝试是否能够转换为方法引用即可。
当我们方法引用使用的多了慢慢也可以直接写方法引用。
6.2基本格式
类名或者对象名::方法名
6.3 语法详细(了解)
6.3.1 引用静态方法
其实就是引用类的静态方法
格式
类名::方法名
使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用某个类的静态方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个静态方法中,这个时候我们就可以引用类的静态方法。
final Stream<Author> stream = getAuthors().stream();
stream.map(author -> author.getAge()).map(String::valueOf).forEach(age-> System.out.println(age+1));
6.3.2 引用对象的实例方法
格式
对象名::方法名
使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用某个对象的成员方法,并且我们把要重写的抽象方法中所有的参数都按照顺序传入了这个成员方法中,这个时候我们就可以引用对象的实例方法。
列如:
final List<Author> authors = getAuthors();
final Stream<Author> stream = authors.stream();
StringBuffer buffer=new StringBuffer();
stream.map(author -> author.getName()).forEach(name->buffer.append(name));
System.out.println(buffer.toString());
优化后:
final List<Author> authors = getAuthors();
final Stream<Author> stream = authors.stream();
StringBuffer buffer=new StringBuffer();
stream.map(author -> author.getName()).forEach(buffer::append);
System.out.println(buffer.toString());
6.3.3 引用类的实例方法
格式
类名::方法名
使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码是调用了第一个参数的成员方法,并且我们把要重写的抽象方法中剩余的所有参数都按照顺序传入这个成员方法中,这个时候我们就可以引用类的实例方法
列如:
final String name = subAuthorName("王灼梅是猪", new UseString() {
@Override
public String use(String str, int start, int length) {
return str.substring(start, length);
}
});
System.out.println(name);
优化后:
final String name = subAuthorName("王灼梅是猪",String::substring);
System.out.println(name);
6.3.4构造器引用
如果方法体中的一行代码是构造器的话就可以使用构造器引用。
格式
类型::new
使用前提
如果我们在重写方法的时候,方法体中只有一行代码,并且这行代码调用了某个类的构造器方法,并且我们把要重写的抽象方法中的所有的参数都按照顺序传入了这个构造器方法中,这个时候我们就可以引用构造器。
列如:
final List<Author> authors = getAuthors();
final List<String> collect = authors.stream().map(author -> author.getName()).map(new Function<String, StringBuffer>() {
@Override
public StringBuffer apply(String s) {
return new StringBuffer(s);
}
}).map(sb -> sb.append("-三更").toString()).collect(Collectors.toList());
collect.forEach(System.out::println);
优化后:
final List<Author> authors = getAuthors();
final List<String> collect = authors.stream().map(Author::getName).map(StringBuffer::new).map(sb -> sb.append("-三更").toString()).collect(Collectors.toList());
collect.forEach(System.out::println);
7.高级用法
基本数据类型优化
我们之前用到的Stream的方方由于都使用了泛型。所以涉及到的参数的返回值都是引用数据类型。
即使我们操作的都是整数小数,但是实际的都是他们的包装类。jdk5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时就好像使用基本数量类型一样方便。但是你一定知道装箱和拆箱肯定是要销毁时间的。虽然这个时间消耗很小。但是大量的数据重复的装箱拆箱操作时候,你就不能无视这个时间的损耗了。
所以为了能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。
列如:mapToInt,mapToLong,mapToDouble,flatMapToInt,FlatMapToDouble
final List<Author> authors = getAuthors();
authors.stream().map(Author::getAge).map(age->age+10).filter(age->age>18).map(age->age+2).forEach(age-> System.out.println(getType(age)));
System.out.println("====================");
authors.stream().mapToInt(author->author.getAge()).map(age->age+10).filter(age->age>18).map(age->age+2).forEach(age-> System.out.println(getType(age)));
8.并行流
当流中有大量元素时,我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完成。如果我们自己去用代码实现的话其实非常的复杂,并且要求你对并发编程有足够的理解和认识。而如果我们使用Stream的话,我们只需要修改一个方法的调用就可以并行流帮我们实现,从而提高效率。
parallel方法可以把串行的流转换成并行流。
final Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 11, 6, 7, 8, 9, 0);
stream.parallel().forEach(zhi-> System.out.println(zhi+Thread.currentThread().getName()));
parallelStream直接获取并行流对象
final List<Author> authors = getAuthors();
authors.parallelStream().flatMap(author -> author.getBooks().stream()).forEach(zhi-> System.out.println(zhi+Thread.currentThread().getName()));
9.时间API
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
Local(本地) − 简化了日期时间的处理,没有时区的问题。
Zoned(时区) − 通过制定的时区处理日期时间。
新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
jdk8以一个新的开始为创建优秀的API.新的日期时间API包含:
java.time
-包含值对象的基础包
java.time.chrono
-提供不同的日历系统的访问
java.tiem.format
-格式化和解析时间和日期
java.tiem.temporal
-包括底层框架和扩展特性
java.tiem.zone
-包含时区支持类
9.1本地日期时间LocalDate、LocalTime、LocalDateTime—>类似于Calender
实例化:now()/of(xxx,xx,xx)n
方法:getXxx()/withXxx()修改/plusXxx()曾加/minusXxx()减少…
9.2 Instant:瞬时—>类似Date
实例化:now()/ofEpochMilli()设置毫秒
方法:toEpochMilli()获取毫秒时间;
9.3 DateTimeFormatter----->类似SimpleDateFormat
用于解析格式化和解析LocalDate、LocalTime、LocalDateTime
//localDate
LocalDate localDate=LocalDate.now();
System.out.println(localDate);
System.out.println(localDate.getYear());
System.out.println(localDate.getMonthValue());
System.out.println(localDate.getDayOfMonth());
final LocalDate localDate2 = localDate.withMonth(12);
final LocalDate localDate3 = localDate.plusDays(10);
System.out.println(localDate3);
final LocalDate localDate4 = localDate.minusDays(10);
System.out.println(localDate4);
LocalDate localDate1=LocalDate.of(2020,10,2);
System.out.println(localDate1);
LocalTime localTime=LocalTime.now();
System.out.println(localTime);
LocalDateTime localDateTime=LocalDateTime.now();
System.out.println(localDateTime);
//瞬间
Date date=new Date();
Instant instant=Instant.ofEpochMilli(date.getTime());
final long l = instant.toEpochMilli();
System.out.println(l);
Date date1=new Date(l);
System.out.println(date1+"======");
final OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);
//格式化
DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy年MM月dd日");
LocalDateTime dateTime=LocalDateTime.now();
final String format = formatter.format(dateTime);
System.out.println(format);
final TemporalAccessor parse = formatter.parse("2020年10月10日");;
System.out.println(parse);
System.out.println(parse.get(ChronoField.YEAR));
总结
学习从来没捷径,循序渐进登高峰