文章目录
Java8新特性
速度快
-
优化了HashMap与HashSet集合类的数据结构
-
JDK1.7版本采用的是哈希表(数组)+链表而JDK1.8采用的是哈希表+链表+红黑树
-
哈希表+数组+红黑树的概述
-
基本原理:先得到对象的hashcode值,对数组长度取余得到数组下标,若是该数组下标的位置上无元素,直接放入,若是有使用equals方法进行比较,若是相同则覆盖,若是不同则将该对象放入链表的头部其余元素后移(1.8放入链尾)
-
如何避免Hash冲突(碰撞)
- 将重写toHashCode与equals方法保持一致:equals为true时HashCode值相同,对象相同时equals值为true
- 加载因子(0.75): 当使用的哈希表的容量达到了限定容量的百分之75就对哈希表进行扩容,扩容成原来的两倍
-
如何优化
- 当链表的长度大于8并且HashMap的总容量大于64时就将链表转为红黑树
-
红黑树除了在增加元素的效率比不上链表,查改删的效率都高于链表。而且使用了红黑树,在扩容以后,就不用再去重新计算hashcode的值进行重组,只需要将原来红黑树的某一部分取出来,将原来红黑树所在的哈希表下标加上原来哈希表的容量的新下标,放入新的哈希表的新下标即可。
-
-
优化了ConCurrentHashMap与ConcurrentHashSet
- 取消了concurrentLevel而使用CAS算法
-
优化了JVM虚拟机的内存结构了
- 将方法区彻底的从堆内存中的永久区(PremGen)分离开,以前堆内存的永久区就是方法区,分离出来的方法区使用的是物理内存,并且物理内存有多,方法区就能用多大,极大的减少了方法区被垃圾回收机制回收的可能性,新分离出来的方法区叫做(MetaSpace(元空间))
代码更少(增加了新的语法,lamda表达式)(主要)
强大的Stream API(主要)
- 使用StreamAPI操作集合的数据,将会得到极大的效率提升
便于并行
最大减少空指针异常(Optional API)
提供了线程安全的时间类
Lomda表达式(非常重要)
策略模式
定义
- 策略模式是行为模式之一,它对一系列的算法加以封装,为所有算法定义一个抽象的算法接口,并通过继承该抽象算法接口对所有的算法加以封装和实现,具体的算法选择交由客户端决定(策略)。Strategy模式主要用来平滑地处理算法的切换。
策略模式应用的背景
-
需求:将学生按照不同的条件查询出来
- 学生类如下:
@Date public class Student implements Comparable{ private String name; private Integer age; private double consumption; private Status status; public enum Status{ study, rest, sleep; }
- 学生数据如下:
public class StudentDao { public List<Student> selectAll() { ArrayList<Student> students = new ArrayList<>(); students.add(new Student("张三", 18, 1000, Student.Status.study)); students.add(new Student("王五", 21, 2000, Student.Status.rest)); students.add(new Student("赵六", 29, 3000, Student.Status.sleep)); students.add(new Student("阿七", 34, 4000, Student.Status.study)); students.add(new Student("六四", 40, 5000, Student.Status.rest)); return students; } }
-
在Java8中的解决方案:
- 定义一个函数式接口
@FunctionalInterface public interface StudentDaoFilter<T> { public boolean filter(T t); }
- 实现函数式接口完成策略的实现
//有了Java8的函数式接口后,就可以不用先实现个匿名内部类后,只需要关注于具体的策略实现 public class MainSoluton { public static void main(String[] args) { StudentDao studentDao = new StudentDao(); List<Student> students = studentDao.selectAll(); //根据消费水平查 List<Student> s1 = filterStudentDao(students, (student -> student.getConsumption() > 3000)); System.out.println("----------"); System.out.println(s1); //根据消费水平与学习状态查 List<Student> s2 = filterStudentDao(students, (student -> student.getConsumption() > 3000 && student.getStatus().equals(Student.Status.study))); System.out.println("----------"); s2.forEach(System.out::println); System.out.println("----------"); //按照状态查 List<Student> s3 = filterStudentDao(students, student -> student.getStatus().equals(Student.Status.rest)); s3.forEach(System.out::println); } public static List<Student> filterStudentDao(List<Student> oldStudents, StudentDaoFilter<Student> studentDaoFilter) { ArrayList<Student> newStudents = new ArrayList<>(); for (Student s : oldStudents) { if (studentDaoFilter.filter(s)) { newStudents.add(s); } } return newStudents; } }
具体语法
-
基本语法:
/* * 一、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); * * 上联:左右遇一括号省 * 下联:左侧推断类型省 * 横批:能省则省 * */ public class MainTest1 { //()-> System.out.println("线程运行"); @Test public void test1() { Runnable runnable = () -> System.out.println("线程运行"); runnable.run(); } //(x)->System.out.println("消费者已消费"); @Test public void test2() { Consumer consumer = (x) -> System.out.println("消费者已消费" + x + "元"); consumer.accept(500); } //(x,y)->x.comparetor(y); @Test public void test3(){ Comparator<Integer> comparator=(x,y)-> x.compareTo(y); int compare = comparator.compare(3, 2); System.out.println(compare); } //需要写多条语句时 @Test public void test4(){ Comparator<Integer> comparator=(x,y)->{ System.out.println(x+y); return x.compareTo(y); }; System.out.println(comparator.compare(1,2)); } }
-
Lamda表达式对于函数式接口的支持以及内置的四大核心函数式接口(*)
/** * Lamda表达式对于“函数式接口”的支持 * 函数式接口:接口中只有一个抽象方法的接口,称为函数式接口。 * 可以使用注解 @FunctionalInterface 修饰,该注解用于检查是否是函数式接口。 * 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 MainTest2 { //Consumer<T>:消费型接口 @Test public void test01(){ Consumer consumer=(x)->System.out.println(x); consumer.accept("I am consumer!"); } //Supplier<T>:生产者接口 @Test public void test02(){ Supplier supplier=()-> "I am Supplier"; System.out.println(supplier.get()); } //Function<T,R>:函数式型接口 @Test public void test03(){ Function<Integer,Integer> function=(x)->x; System.out.println(function.apply(1)); } //Predicate<T>:断言接口 @Test public void test4(){ Predicate<Integer> predicate=(x)->x.equals(2); System.out.println(predicate.test(1)); } }
-
Lamda表达式表现形式:方法,对象,构造器引用(可以理解为:对于lamda表达式的简写)
/* * 一、方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用 * (可以将方法引用理解为 Lambda 表达式的另外一种表现形式) * * 1. 对象的引用 :: 实例方法名 * * 2. 类名 :: 静态方法名 * * 3. 类名 :: 实例方法名 * * 注意: * ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致! * ②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,可以使用"类名::实例方法名"的语法格式: ClassName::MethodName * * 二、构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致! * 1. 类名 :: new * 三、数组引用 * 类型[] :: new; * * */ public class MainTest3 { //实例引用::实例方法名 @Test public void test01(){ //这句话相当于实现了一个Consumer的函数式接口 Consumer consumer=System.out::println; //这里会打印"nihao" consumer.accept("nihao"); Student student = new Student(); //这里相当于实现了一个Supplier的函数式接口 Supplier supplier=student::getAge; //这里打印学生的名字 System.out.println(supplier.get()); } //类名::静态方法名 @Test public void test02(){ //前两个泛型是形参,最后一个是返回值 BiFunction<Double,Double,Double> biFunction= Math::max; //输出30.00 System.out.println(biFunction.apply(1.12,30.00)); } //类名::实例方法名 @Test public void test03(){ BiPredicate<String,String> biPredicate=String::equals; //这里输出true System.out.println(biPredicate.test("nihao","nihao")); } //构造器引用 @Test public void test04(){ //前一个是形参后一个是返回值 Function<String,Student> function=Student::new; //这里会输出一个学生对象 System.out.println(function.apply("zhangsan")); BiFunction<String,Integer,Student> f2=Student::new; System.out.println(f2.apply("wangwu",25)); } //数组引用 @Test public void test05(){ Function<Integer,Student[]> function=Student[]::new; //这里创建了一个长度为10的学生对象数组。 System.out.println(function.apply(10).length); } }
Stream流(非常重要!!)
下文使用的学生对象以及数据是上文所提到的
Stream API 的必须做的操作流程
- 创建Stream流对象
- 中间步骤
- 终止步骤
- 这里需要特别注意:
- 惰性求值现象:只有当做终止操作时,所有的中间操作才会一次性执行
创建Stream流对象与中间步骤
public class MainTest01 {
private List<Student> students = new StudentDao().selectAll();
//创建流
@Test
public void test1() {
/**
* 一:
* Collection接口提供了两种抽象方法:stream():创建顺序流,parallelStream():创建并行流
* 之间的区别,这里可以简单的理解为,并行流使用了fork/join框架,并行流会在数据量大的时候处理的效率会更高
*/
Stream<Student> studentStream = students.stream();//创建顺序流
Stream<Student> parallelStream = students.parallelStream();//创建并行流
//2:通过Arrays工具类的stream()流获得一个数组的顺序流
Function<Integer, Integer[]> f1 = Integer[]::new;
Stream<Integer> integerStream = Arrays.stream(f1.apply(10));
//3:通过Stream类中的静态方法of()来获取流
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
//4:创建无限流
//迭代
Stream<Integer> integerStream1 = Stream.iterate(0, x -> x + 2).limit(10);
//limit()流的终止操作,返回一个该截取长度的流
integerStream1.forEach(System.out::println);
//生成
Stream<Double> tStream = Stream.generate(Math::random).limit(2);
tStream.forEach(System.out::println);
}
//中间步骤
@Test
public void test2() {
/*
筛选与切片
filter——接收 lambda , 从流中排除某些元素。
limit——截断流,使其元素不超过给定数量。
skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct——筛选,通过流所生映射
映射
map——接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一元素的 hashcode() 和 equals() 去除重复元素
排序
sorted()——自然排序
sorted(Comparator com)——定制排序
*/
//filter():按指定规则过滤
students.stream().filter(student -> student.getAge()>38).forEach(System.out::println);
//limit(n):截取前n个元素
System.out.println("---------------------------");
students.stream().filter(student -> student.getAge()>38).limit(2).forEach(System.out::println);
//skip(n):跳过前n个元素
System.out.println("---------------------------");
students.stream().filter(student -> student.getAge()>38).skip(2).forEach(System.out::println);
//distinct():去除重复元素根据hashcode()与equals()方法
System.out.println("---------------------------");
students.stream().distinct().forEach(System.out::println);
//映射map():为所有元素进行相同的操作
System.out.println("---------------------------");
students.stream().map(student -> student.getName()).forEach(System.out::println);
//flatMap():若map的映射是将其中元素转换成流,那么flatmap可以将映射后形成的一个个小流,合并成一个大流
//需求将每一个字符串集合的每一个字符串分开成单个字符,并且依次打印每一个字符
List<String> strings = Arrays.asList("aaa", "bbb", "ccc", "eee");
Stream<Stream<Character>> listStream = strings.stream().map(MainTest01::toChar);
listStream.forEach(clist->clist.forEach(System.out::println));
System.out.println("---------------------------");
Stream<Character> characterStream = strings.stream().flatMap(MainTest01::toChar);
characterStream.forEach(System.out::println);
//自然排序sorted():对象所属的实体类实现了Comparable接口重写了compareTo()方法
System.out.println("------------------------------");
students.stream().sorted().forEach(System.out::println);
//定制排序sorted():自己定制规则进行排序
System.out.println("------------------------------");
students.stream().sorted((s1,s2)->{
return -s1.compareTo(s2);
}).forEach(System.out::println);
}
//将字符串转换为字符数组的方法
public static Stream<Character> toChar(String s){
ArrayList<Character> characters = new ArrayList<>();
for (Character c : s.toCharArray()) {
characters.add(c);
}
return characters.stream();
}
//惰性求值现象:只有当做终止操作时,所有的中间操作才会一次性执行
@Test
public void test3(){
Stream<Student> studentStream = students.stream().filter(student -> {
System.out.println("过滤开始");
return student.getAge() > 28;
});
System.out.println("结束流操作开始");
studentStream.forEach(System.out::println);
}
}
终止步骤
//3. 终止操作
public class MainTest02 {
private List<Student> students=new StudentDao().selectAll();
/*
allMatch——检查是否匹配所有元素
anyMatch——检查是否至少匹配一个元素
noneMatch——检查是否没有匹配的元素
findFirst——返回第一个元素
findAny——返回当前流中的任意元素
count——返回流中元素的总个数
max——返回流中最大值
min——返回流中最小值
*/
@Test
public void test01(){
//allMatch()
boolean allMatch = students.stream().allMatch(student -> Student.Status.rest.equals(student.getStatus()));
System.out.println(allMatch);
//anyMatch()
boolean anyMatch = students.stream().anyMatch(student -> student.getStatus().equals(Student.Status.study));
System.out.println(anyMatch);
//noneMatch()
boolean noneMatch = students.stream().noneMatch(student -> student.getStatus().equals(Student.Status.study));
System.out.println(noneMatch);
//findFirst()返回第一个元素
Optional<Student> first = students.stream().findFirst();
System.out.println(first);
//findAny()返回当前流中任意元素
Optional<Student> any = students.stream().findAny();
System.out.println(any);
//max()返回流中最大值
Optional<Double> max = students.stream().map(Student::getConsumption).max(Double::compareTo);
System.out.println(max);
//min()返回流中最小值
Optional<Double> min = students.stream().map(Student::getConsumption).min(Double::compareTo);
System.out.println(min);
//count()返回流中元素的个数
long count = students.stream().count();
System.out.println(count);
}
/*
归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。
*/
@Test
public void test02(){
//方式一:从指定的初始值开始归约
Double sum = students.stream().map(Student::getConsumption).reduce(0.0, (x, y) -> x + y);
System.out.println(sum);
//方式二:不指定初始值的归约
Optional<Double> sum2 = students.stream().map(Student::getConsumption).reduce(Double::sum);
System.out.println(sum2);
//map与reduce的结合往往可以用于大数据的处理上
//需求:搜索学生中名字出现“六”的个数
Optional<Integer> countLiu = students.stream().map(Student::getName).map(name -> {
if (name.contains("六")) {
return 1;
} else {
return 0;
}
}).reduce(Integer::sum);
System.out.println(countLiu);
}
/*
collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法,将流转换为各种集合
*/
@Test
public void test03(){
//list集合
List<String> nameList = students.stream().map(Student::getName).collect(Collectors.toList());
System.out.println("list集合:"+nameList);
//set集合
Set<String> nameSet = students.stream().map(Student::getName).collect(Collectors.toSet());
System.out.println("Set集合:"+nameSet);
//转Set集合的第二种方式
HashSet<String> nameHashSet = students.stream().map(Student::getName).collect(Collectors.toCollection(HashSet::new));
System.out.println("HashSet集合:"+nameHashSet);
//转Map集合:map的value为整个对象,注意当流中有重复元素的时候转map会抛出异常
Map<String, Student> studentMap = students.stream().collect(Collectors.toMap(Student::getName, student -> student));
studentMap.forEach((key,value)->{
System.out.println("key="+key+":"+"map"+value);
});
//转Map集合:map的value为对象的单一属性
Map<String, Double> studentMap1 = students.stream().collect(Collectors.toMap(Student::getName, Student::getConsumption));
studentMap1.forEach((key,value)->{
System.out.println("key="+key+":"+"map"+value);
});
}
/*
将流中的元素实现数据库操作中组函数功能
*/
@Test
public void test04(){
//最大值
Optional<Double> max = students.stream().map(Student::getConsumption).collect(Collectors.maxBy(Double::compareTo));
System.out.println(max);
//最小值
Optional<Double> min = students.stream().map(Student::getConsumption).collect(Collectors.minBy(Double::compareTo));
System.out.println(min);
//求和
Double sum = students.stream().collect(Collectors.summingDouble(Student::getConsumption));
System.out.println(sum);
//平均值
Double averaging = students.stream().collect(Collectors.averagingDouble(Student::getConsumption));
System.out.println(averaging);
//计数
Long count = students.stream().collect(Collectors.counting());
System.out.println(count);
//获得一个对该组数值进行操作的包装类,该包装类包含了组函数的操作
DoubleSummaryStatistics doubleSummaryStatistics = students.stream().collect(Collectors.summarizingDouble(Student::getConsumption));
System.out.println("-----------------");
System.out.println(doubleSummaryStatistics.getMax());
System.out.println(doubleSummaryStatistics.getAverage());
}
/*
将流进行分组操作
*/
@Test
public void test5(){
Map<Student.Status, List<Student>> statusListMap = students.stream().collect(Collectors.groupingBy(Student::getStatus));
System.out.println(statusListMap);
}
/*
将流进行多级分组
*/
@Test
public void test6(){
Map<Student.Status, Map<String, List<Student>>> statusMapMap = students.stream().collect(Collectors.groupingBy(Student::getStatus
, Collectors.groupingBy((student -> {
if (student.getAge() < 20) {
return "青年";
} else if (student.getAge() < 30) {
return "中年";
} else {
return "老年";
}
}))));
System.out.println(statusMapMap);
}
/*
分区
*/
@Test
public void test7(){
Map<Boolean, List<Student>> booleanMap = students.stream().collect(Collectors.partitioningBy(student -> student.getConsumption() > 2000));
System.out.println(booleanMap);
}
/*
Collections的拼接以及归约
*/
@Test
public void test8(){
//拼接:Collections.joining()
String joinName = students.stream().map(Student::getName).collect(Collectors.joining(",", "{", "}"));
System.out.println(joinName);
//归约:Collections.reducing()
Optional<Double> reduce = students.stream().map(Student::getConsumption).collect(Collectors.reducing(Double::sum));
System.out.println(reduce);
}
}
Optional API
/*
* 一、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 {
@Test
public void test01(){
//Optional.of(T t)
Optional<Student> op1 = Optional.of(new Student("xiaoming"));
System.out.println(op1.get());
//Optional.empty(),空Optional不能使用get方法NoSuchElementException异常,因为里面没有内置对象
Optional<Object> empty = Optional.empty();
System.out.println(empty);
//Optional.ofNullable(T t)
Optional<Object> op2 = Optional.ofNullable(null);
System.out.println(op2);
//isPresent()
System.out.println(op2.isPresent()+" and "+empty.isPresent());
//orElse(T t)
System.out.println(op2.orElse(new Student("nihaoh")));
//orElseGet(Supplier s)
System.out.println(empty.orElseGet(()->{
return new Student("woshi");
}));
//map(Function f)
System.out.println(op1.map(Student::getName));
//flatMap(Function mapper)
System.out.println(op1.flatMap(student -> {
return Optional.ofNullable(student.getName());
}));
}
}
提供了线程安全的时间类
关于JDK7时间类线程不安全的测试与解决方法
/**
* 测试JDK1.7的时间类的线程安全,并在JDK1.7中解决
*/
public class TestSimpleDateFormat {
//JDk1.7时间类的线程安全
@Test
public void test1() throws Exception {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy--MM--dd");
Callable<Date> callable = new Callable() {
@Override
public Date call() throws Exception {
return dateFormat.parse("2001--09--16");
}
};
//System.out.println(callable.call());
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> result=new ArrayList<>();
for (int i = 0; i < 10; i++) {
result.add(pool.submit(callable));
}
for (Future<Date> future :result) {
System.out.println(future.get());
}
pool.shutdown();
//报错:java.util.concurrent.ExecutionException: java.lang.NumberFormatException: multiple points
}
//解决方案一
@Test
public void test02() throws ExecutionException, InterruptedException {
//使用TreadLocal类包装要共享的变量SimpleDateFormat
Callable<Date> callable = new Callable() {
@Override
public Date call() throws Exception {
return SimpleDateFormatThreadLocal.convert("2001--09--16");
}
};
//System.out.println(callable.call());
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<Date>> result=new ArrayList<>();
for (int i = 0; i < 10; i++) {
result.add(pool.submit(callable));
}
for (Future<Date> future :result) {
System.out.println(future.get());
}
pool.shutdown();
}
//解决方案二:使用Java8中的线程安全的时间类
@Test
public void test03() throws Exception{
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy--MM--dd");
Callable<LocalDate> callable = new Callable() {
@Override
public LocalDate call() throws Exception {
return LocalDate.parse("2001--09--16",dateTimeFormatter);
}
};
//System.out.println(callable.call());
//创建一个线程池
ExecutorService pool = Executors.newFixedThreadPool(10);
List<Future<LocalDate>> result=new ArrayList<>();
for (int i = 0; i < 10; i++) {
result.add(pool.submit(callable));
}
for (Future<LocalDate> future :result) {
System.out.println(future.get());
}
pool.shutdown();
}
}
/**
* TreadLocal类包装共享变量SimpleDateFormat
*/
public class SimpleDateFormatThreadLocal{
private static final ThreadLocal<DateFormat> local=new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy--MM--dd");
}
};
public static final Date convert(String srouce) throws ParseException {
return local.get().parse(srouce);
}
}
常用方法
//常用类与方法测试
public class mainTest1 {
//LocalDate,LocalTime,LocalDateTime
@Test
public void test01(){
//上述时间类都有一样的方法,分别代表的是:日期、时间、日期时间
LocalDateTime now = LocalDateTime.now();//获取当前时间
System.out.println(now);
//获取指定日期的日期时间包装类
LocalDateTime dateTime01 = LocalDateTime.of(2001, 9, 16, 0, 6);
System.out.println(dateTime01);
//对日期与时间的加减操作
LocalDateTime dateTime02 = dateTime01.plusYears(3).minusYears(2);
System.out.println(dateTime02);
//获得日期时间包装类的具体某一项时间的值
System.out.println(dateTime01.getDayOfMonth());
System.out.println(dateTime01.getHour());
System.out.println(dateTime01.getDayOfWeek());
}
//Instant : 时间戳。 (使用 Unix 元年 1970年1月1日 00:00:00 所经历的毫秒值)
//默认使用的是UTC时区
@Test
public void test02(){
Instant instant = Instant.now();
System.out.println(instant);//运行的本机时间为16:45,打印时间为2021-11-16T08:44:10.596Z
//ofHours(int hours)根据偏移时间,得到一个实例ZoneOffset。
//中国的时区与UTC时区差了八个小时
OffsetDateTime odt = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);
//得到一个纳秒数
System.out.println(instant.getNano());//914000000
//ofEpochSecond(long epochSecond)获得由1970-01-01t00:00:00z时代偏移Long秒的Instant实例。
Instant instant1 = Instant.ofEpochSecond(5);
System.out.println(instant1);//1970-01-01T00:00:05Z
}
//Duration : 用于计算两个“时间”间隔
//Period : 用于计算两个“日期”间隔(注意)
@Test
public void test03() throws InterruptedException {
Instant ins1 = Instant.now();
Thread.sleep(1000);
Instant ins2 = Instant.now();
System.out.println(Duration.between(ins1,ins2));
System.out.println("-------------------");
//Period.between只能算月份之内的数据,比如计算2020-04-21距离2021-02-22间隔的天数
// 那么用Period.between来计算就是只有一天。
// 计算间隔天数需要改成
// ChronoUnit.DAYS.between(timeLocal, LocalDate.now());
LocalDate now = LocalDate.now();
LocalDate data = LocalDate.of(2021, 9, 16);
Period period = Period.between(data,now);
System.out.println(period);
System.out.println(period.getYears());
System.out.println(period.getDays());
}
//时间校正器:TemporalAdjuster
@Test
public void test04(){
LocalDate date1 = LocalDate.now();
System.out.println(date1);
//将date1中的日期改为当月10号
LocalDate date2 = date1.withDayOfMonth(10);
System.out.println(date2);
//使用校正器将date1的日期改为下一周的星期日
LocalDate date3 = date1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(date3);
//自定义时间校正器,改为下个工作日
LocalDate date5 = date1.with((date) -> {
LocalDate date4 = (LocalDate) date;
DayOfWeek dayOfWeek = date1.getDayOfWeek();
if (dayOfWeek.equals(DayOfWeek.FRIDAY)) {
return date4.plusDays(3);
} else if (dayOfWeek.equals(DayOfWeek.SATURDAY)) {
return date4.plusDays(2);
} else {
return date4.plusDays(1);
}
});
System.out.println(date5);
}
//DateTimeFormatter:解析和格式化日期或时间
@Test
public void test5(){
//默认的解析格式是:DateTimeFormatter.ISO_LOCAL_DATE
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");
LocalDateTime ldt = LocalDateTime.now();
//按照dtf的格式格式成字符串
String strDate = ldt.format(dtf);
System.out.println(strDate);
//将字符串日期按照dtf指定的格式解析成系统日期
LocalDateTime newLdt = ldt.parse(strDate, dtf);
System.out.println(newLdt);
}
//时区:ZoneId
@Test
public void test06(){
//查看所有的时区
//Set<String> availableZoneIds = ZoneId.getAvailableZoneIds();
//availableZoneIds.forEach(System.out::println);
//将当前时间设置为指定时区的时间
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(now);
//带时区的时间
ZonedDateTime now1 = ZonedDateTime.now(ZoneId.of("US/Pacific"));
System.out.println(now1);
}
}