Java8新特性
1.目录
- Lambda表达式/函数式接口/方法引用与构造器引用
- Stream API(操作数据如同操作SQL语句一样简单)
- Optional类
- 接口中的默认方法与静态方法
- 新时间日期AP
- 其他新特性
2.Java8新特性简介
1.速度更快
2.代码更少(增加了新的语法 Lambda表达式)
3.强大的 Stream APl
4.便于并行
5.最大化减少空指针异常 Optional
讲解:
1.速度更快:
比如说对于底层的数据结构做了一些更新和改动,对垃圾回收机制也就是内存结构做了一些改动,以及对于并行流做了扩展和支持
比如hasMap之前数据重复了之后用链表接,后来改成红黑树除了插入值慢了一点其他都很快
比如ConcurrentHashMap在1.7并发级别默认是16,在jdk1.7的时候采用的锁分段机制,在1.8以后改成了CAS算法采用的是无锁算法,因为以
前是16个段少了浪费,多了容易卡
第二能也对底层的内存结构也不一样了:原来栈、堆、方法区(方法区属于堆的永久区的一部分,但为什么画图为什么把他画在堆的外面)(永久区存的都是类信息,加载类的信息或者核心类库,且永久区的数据几乎不会被垃圾回收机制回收,因为回收的条件比较苛刻),为什么把方法区画在外面:在早期除了Oracle-SUM Hotspot 以外、Oracle JRocket 、IBM J9JVM、Taobao JVM早就没有永久区了,把方法区从永久区剥离了出来。在JDK1.8以后就没有永久区了,取而代之的是MetaSpace 元空间,它与jvm不同的是元空间使用的是物理内存,元空间被垃圾回收机制回收其中一条就是:当内存快要满的时候就被回收,永久区的PremGenSize和MaxPemGenSize就消失了取而代之的是Metaspcesize与MaxMetaSpaceSize,默认上上是你的物理内存多大你的元空间就多大,但是也可以通过这俩值设置
1.Lambad表达式/函数式接口/方法引用与构造器引用
1.简介:
Lambda是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代 码。作为一种更紧凑的代码风格←使Java的语言表达能力得到了提升
Lambda表达式的基础语法:Java8中引入了一个新的操作符" -> ”该操作符称为箭头操作符或 Lambda操作符
箭头操作符将 Lambda表达式拆分成两部分:
左侧:Lambda表达式的参数列表
右侧:Lambda表达式中所需执行的功能,即 Lambda体
实际上Lambad表达式就是对接口的实现,但Lambad表达式只能对函数式接口:即接口找那个只有一个抽象方法的接口,称为函数式接口。可以使用注解@FunctionalInterface修饰,可以检查是否是函数式接口,如果多写了就报错
2.Lambad表示的语法
Lambad表达式也有很多语法格式:
1.无参数,无返回值:()-> System.out.println(“Hello”); //因为左侧对应参数,既然没有参数直接就写个空的小括号就行
Runnable runnable = ()-> System.out.println("xxx");
runnable.run();
2.有一个参数,无返回值的
Consumer<String> a = (x) -> System.out.println(x);
a.accept("jinx");
3.入参只有一个参数时小括号可以不写:
Consumer<String> a = x -> System.out.println(x);
a.accept("jinx");
4.有两个以上的参数,有返回值,并且Lambad体重有多条语句//多条语句Lambad体必须用大括号
Comparator<Integer> c = (x,y)->{
System.out.println("函数式接口");
return Integer.compare(x,y);
};
5.有两个以上的参数,有返回值,Lambad体重有一条语句,return 和大括号都可以省略不写
6.Lambad表达式的参数列表类型可以省略不写,因为JVM编译器通过上下文推断出:数据类型,即"类型推断"
Comparator<Integer> c = (x,y)-> Integer.compare(x,y);
//虽然我们不写,但是jvm会去实现的接口compare里面寻找参数是什么类型
//这种方式我们以前也见过:比如
List<String> list = new ArrayList<>();
//为什么后面的不用写,因为会根据前面尖括号的内容进行推断类型
//这个是JDK1.7之后才有的
//在jdk1.8里:
new Lambad().show(new HashMap<>());
//尖括号里面的内容都可以省略不写了
//因为可以根据你的目标推断出你是什么类型,这放到JDK1.7中是不行的
public void show(Map<String,Integer> map){
}
//原来的匿名内部类
public void test1(){
Comparator<Integer> com = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1,o2);
}
};
TreeSet<Integer> treeSet = new TreeSet<>(com);
}
public void test2(){
//上面的匿名内部类我们真正需要的就return Integer.compare(o1,o2);这一行代码
//所以可以简化成
//就像上面说的将匿名内部类转换成Lambad表达式左侧就是上面匿名内部类的参数,右边是要执行的操作就是你对接口的实现
Comparator<Integer> com = (x,y)->Integer.compare(x,y);
TreeSet<Integer> treeSet = new TreeSet<>(com);
}
List<employess> list = Arrays.asList(
new employess("张三1",18,9999.8),
new employess("张三2",19,999.99),
new employess("张三3",20,200.99),
new employess("张三4",35,9699.99),
new employess("张三5",36,979.99)
);
//需求:获取当前公司中年龄大于35工资大于900的员工信息
//如果要以前获取这些想到的肯定就是foreach循环筛选,但是效率差,优化的话可以写设计模式
//但是我不想写,直接用Stream API就行
public List<employess> filterEmployess(List<employess> list){
return list.stream().filter(employess -> employess.getAge()>35 && employess.getMoney()>900).collect(Collectors.toList());
}
public static void main(String[] args) {
new Lambad().filterEmployess(new Lambad().list).stream().forEach(e->{
System.out.println(e.getName());
});
new Lambad().list.stream().map(employess::getName).forEach(System.out::println);
@FunctionalInterface
public interface operation {
public Integer getValue(Integer num);
}
--在其他方法中
左边是()是参数 右边是要执行的操作
System.out.println(new Lambad().opera(100,(x)-> x*x));
System.out.println(new Lambad().opera(100,(x)-> x + x+200));
//注意这个lambad是一个类可不是lambad啊
public Integer opera(Integer num,operation operation){
return operation.getValue(num);
}
/**
* 平时用实现一个功能就得写一个接口很麻烦就可以用
* 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 lambad1 {
//消费性接口 Consumer<T>
@Test
public void test1(){
new lambad1().happy(100.0,(x)-> System.out.println("消费了"+x+"元"));
}
public void happy(Double money, Consumer<Double> tConsumer){
tConsumer.accept(money);
}
//Supplier<T> 供给型接口
@Test
public void test2(){
//需求:产生指定个数的整数,并放入集合中
//为什么写()-> 因为supplier.get()方法没有参数
//随机产生100以内的整数传入到集合中,你要产生什么对象就传什么
getNumberList(10,()-> (int)(Math.random() * 10)).forEach(System.out::println);
}
public List<Integer> getNumberList(int size, Supplier<Integer> supplier){
List<Integer> list = new ArrayList<>();
for (int i = 0; i < size; i++) {
//产生size个supplier类型的数据放入集合中返回
Integer n = supplier.get();
list.add(n);
}
return list;
}
//Function<T,R> 函数型接口,传入一个参数,返回一个值
@Test
public void test3(){
//需求:用于处理字符串
System.out.println(strHandler("jinx ",(x)->x.trim().toUpperCase()));
}
public String strHandler(String str, Function<String,String> fun){
return fun.apply(str);
}
//Predicate<T> 断言型接口
@Test
public void test4(){
//需求:将满足条件的字符串,放入集合中
List<String> li = new ArrayList<>();
li.add("a");
li.add("ac");
li.add("asa");
li.add("accc");
li.add("aa");
li.add("aaaaaaa");
filterList(li,(x)->x.length()>2).forEach(System.out::println);
}
public List<String> filterList(List<String> list, Predicate<String> predicate) {
List<String> a = new ArrayList<>();
//predicate.test(String)里面的条件实现就是x.length()>2 塞选长度大于2的
list.stream().filter(String ->(predicate.test(String))).forEach(item ->{
a.add(item);
});
return a;
}
}
这个么多还感觉不够用?里面还有一些其他子接口
/**
* 一、方法引用:若 Lambda体中的内容有方法已经实现了,我们可以使用"方法引用
* (可以理解为方法引用是 Lambda表达式的另外一种表现形式)
* 主要有三种语法格式:
* 对象:实例方法名
* 类::静态方法名
* 类:实例方法名
* 注意:
* 1.lambdad体中调用方法的参数列表与返回值类型要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
* 2.若Lambda参数列表中的第一参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用C1 assUme::methoe
*
* 二、构造器引用
* 格式: ClassName :: new
* 注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致!
*
* 三、数组引用:
* Type::new
*
*/
public class Lambad2 {
public static void main(String[] args) {
//类实例方法名 --比较两个参数是否一样
//这样写有条件,是你(x,y)第一个参数x是第一个方法的调用者,就是->x.equals()
//第二个参数是这个实例方法的参数就是(x,y)->x.equals(y) 里面的第一个y和第二个y
BiPredicate<String,String> bp = (x,y)->x.equals(y);
System.out.println(bp.test("jinx","jinx"));
//可以简写成
BiPredicate<String,String> b2 = String::equals;
System.out.println(b2.test("jinx","jinx"));
//类静态方法名
Comparator<Integer> c = (x,y) ->Integer.compare(x,y);
//可以简写成
Comparator<Integer> a = Integer::compare;
//构造器引用 -自动匹配无参构造器
Supplier<employess> supplier = employess::new;
employess emp = supplier.get();
System.out.println(emp.getName());
//有参的
/**
* employess类那边那边是这样写的
* public employess(int age) {
* this.setAge(age);
* }
*/
Function<Integer,employess> function = employess::new;
employess e = function.apply(10);
System.out.println(e.getAge());
//--------------数组引用
Function<Integer,String[]> fun = (x)->new String[x];
String[] sts = fun.apply(10);
System.out.println(sts.length);
Function<Integer,String[]> fun1 = String[] :: new;
String[] sts1 = fun.apply(20);
System.out.println(sts1.length);
}
2.Stream API
Java8中有两大最为重要的改变。第一个是 Lambda表达式;另外个则是 Stream API(java.uti1. stream.*)
Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作使用 Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。也可以使用 Stream API来并行执行操作。简而言之Stream API提供了一种高效且易于使用的处理数据的方式。只要是流基本都是对数据操作的
我们可以将一个数据源转化成流,然后对这个流进行一系列流水线式的操作如过滤,截取等,组最后产生一个新的流,新的流和原来的数据源没有任何关系,原来的数据源不会受到影响。
流( Stream)到底是什么呢?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算”
注意:
① Stream自己不会存储元素。
② Stream不会改变源对象。相反,他们会返回一个持有结果的新 Stream
③ Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream的操作三个步骤
1.创建 Stream
一个数据源(如:集合、数组),获取一个流
2.中间操作
一个中间操作链,对数据源的数据进行处理
3.终止操作(终端操作)
一个终止操作,执行中间操作链,并产生结果
1.创建流的方法有:
//1.可以通过Collection系列集合提供的stream()(串行流)或
//parallelStream()(并行流)
List list= new ArrayList<>();
Stream s = list.stream();
//2.通过Arrays中的静态方法stream()获取数组流
employess[] e = new employess[10];
Stream st =Arrays.stream(e);
//3.通过Stream类中的静态方法of()
Stream stream3 = Stream.of(“aa”,“bb”,“cc”);
//4.创建无限流 没有穷尽的流
//迭代 就是从0开始每次+2的操作
Stream stream4 = Stream.iterate(0,(x)->x+2);
stream4.limit(10).forEach(System.out::println);
2.中间操作有:
Stream的中间操作:
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”如:
而每次循环都会迭代迭代不是由我们来做的而是由 StreamAPI里的一个内部迭代来做
/*将集合映射为 String类型的再进行一个筛选,筛选条件为 name = jinx7的*/
arrayList.stream().map(test5 :: getName).filter(getName -> getName.equals("jinx7")).collect(Collectors.toList()).stream().forEach(item ->{
System.out.println(item);
});
//最后的forEach就是终止操作的一种
limit():只执行指定次数的迭代,比如limit(2),就只会迭代两次指定数据
distinct筛选,通过流所生成元素的 hashCode()和equa1s()去除重复元素
flatMap :如果遇到嵌套流可以用这个来进行循环,否则
stream.forEach((sm)->{
sm.forEach(System.out::println)
})
查找与匹配
allMatch–检查是否匹配所有元素
anyMatch—检查是否至少匹配一个元素
noneMatch—检查是否没有匹配所有元素
findFirst一返回第一个元素
findAny—返回当前流中的任意元素
count一返回流中元素的总个数
max—返回流中最大值
min—返回流中最小值 //如果是最小值建议先map转一下再求值
boolean b1 = employees.stream().allMatch((e)->
e.getStatus().equals(status.BUSY)) //检查元素中是不是都是这个状态的
并行流与顺序流
并行流:就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流,使我们的程序很容易得切换成多线程而且更能利用CPU的资源
Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作,StreamAPI可以声明性地通过parallel()与sequential()在并行流与顺序流直接进行切换
我们首先得了解一个框架 Fork/Join框架,因为parallel()底层就是一个Fock/Join
举例:
比如我现在有个大任务要进行计算如果用单线程计算就很消耗时间,比如要计算1到一百亿之间的数的累加如果是单线程效率是极低的。我们就可以把两个大任务拆分成子任务,子任务还能拆分子任务,我们可以设置一个临界值什么时候不分了,一直分到不能再分,然后吧拆分出来的一个个的子任务压入到CPU的线程队列当中开始执行,执行之后每个子任务的值再执行Join合并,最终合并成一个最终的结果。
特点:
数据量越大体现越明显
说完之后是否发现Fork/Join与多线程没啥区别?其实是有区别的
首先:
传统多线程:
所谓的多线程就是把任务分配到CPU不同的核上也就是不同的线程上,比如以4核CPU为例,如果是传统线程完成是会将任务拆分到4个核上在CPU不同的核心上执行,但是传统线程存在一个问题那就是"阻塞",如果在1 4核心执行的其中一个任务阻塞了那么在这个核上的其他线程就会等待这个任务执行完成之后才能执行,但是这个时候在2 3核心上执行的任务顺利执行完了,当1核心阻塞过去之后继续执行剩下的任务时,这时候我们会发现 2 3 核的线程是处于空闲状态,而你阻塞的1核上面的线程队列还在排着,1 4核处于繁忙状态而 2 3处于空闲状态 造成的就是没有很好的利用CPU的资源这两个cpu的资源被我们白白浪费了
Fork/Join:
它是把大任务拆分成小任务压入到每个线程中形成一个个线程队列,默认CPU有几核就利用几个线程,形成线程队列之后它有一个核心叫做:”工作窃取模式“
“工作窃取模式(work- stealing):
当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列(比如说:其中一个线程先执行完了,但是它不会处于空闲状态它会从其他线程的最末尾拿取一个任务执行,这样能更大程度利用CPU资源)中相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上。在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行这种方式减少了线程的等待时间,提高了性能。
传统的多线程与Fork/Join的主要区别:
1.在CPU资源的利用上
传统的线程是会阻塞,导致其他线程执行完处于空闲状态,不能很好的利用CPU的资源
Fork/Join有工作窃取模式线程不会处于空闲状态
Fork/Join在1.7的时候就有了,但是使用想对来说比较麻烦应用就不是特别广泛,但是Fork/Join的核心还是特别好的。下面是JDK1.8中的写法
Instant instant = Instant.now();//开始时间
//顺序流 也就是单线程
// LongStream.rangeClosed(0,100000000000L).reduce(0,Long::sum);//生成一系列的数 花费46573毫秒
//怎么变成并行流?加一个parallel()并行流,顺序流sequential()
LongStream.rangeClosed(0,100000000000L).parallel().reduce(0,Long::sum);//花费12489毫秒
System.out.println("共花费"+ Duration.between(instant,Instant.now()).toMillis());
3.Optional类
Optiona类(java.uti1.Option1)是一个容器类里面封装了很多的对象,代表一个值存在或不存在,原来用null表示一个值不存在,现在 Optional可以更好的表达这个概念。并且可以避免空指针异常。
Optional类是为了更好的避免空指针,,说白了就是达到快速定位
常用方法:
Optional.of(T t):创建一个Optional实例
Optional. empty():创建一个空的Optional实例
Optional. ofNullable(T t):若t不为nu11,创建 Optionall实例,否则创建空实例
isPresent():判断是否包含值
orElse(T t):如果调用对象包含值,返回该值,否则返回t
orElseGet( Supplier s)∵如果调用对象包含值,返回该值,否则返回s获取的值
map( Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional. empty()
flatMap( Function mapper):与map类似,要求返回值必须是 Optional
//就是把一个对象封装到Optional里他给你提供相应的方法
//1.Optional.of(T t):创建一个Optional实例
Optional<employess> optional = Optional.of(new employess());
employess e = optional.get(); //可以用get方法获取这个实例
System.out.println(e);
//注意of方法为null会报错,Optional不是能避免空指针么?换一种思想,如果对象是其他地方传递过来的,并且传递过来一个null
//通过of创建的时候是不是就能直接定位到报空指针的位置了?TMD绝了!!!
//2.有时候没值就想构建一个null 有两种方式:1.Optional.empty() 2.Optional. ofNullable(T t):若t不为nu11,创建 Optionall实例,否则创建空实例
//2.1 Optional.empty();
Optional<employess> op = Optional.empty();
//System.out.println(op.get()); //这个会报错
//2.2 Optional.ofNullable(new employess()); //ofnullable 是 empty 与 of 的集合 传过来值就构建,传不过来就为空
Optional<employess> op2 = Optional.ofNullable(new employess());
System.out.println(op2.get());
Optional<employess> op3 = Optional.ofNullable(null);
// System.out.println(op3.get()); //这个也会报错
//3.在实际中肯定不能这么用,那怎么判断构建的对象是否有值
Optional<employess> isnull = Optional.ofNullable(new employess());
if(isnull.isPresent())
System.out.println(isnull.get().getAge());
else
System.out.println("aaa");
//4.orElse(T t):如果调用对象包含值,返回该值,否则返回t
Optional<employess> op4 = Optional.ofNullable(null);
employess e1 =op4.orElse(new employess("jinx",16,20.0));//为空就执行这个
System.out.println(e1.getAge());
//5.orElseGet( Supplier s)∵如果调用对象包含值,返回该值,否则返回s获取的值
//它里面是一个供给型接口,用来生成对象用的,与上面不一样的地方在于可以在里面写判断等操作了
op4.orElseGet(() -> new employess());
//6.map( Function f):如果有值对其处理,并返回处理后的 Optional,否则返回 Optional. empty()
Optional<employess> a = Optional.ofNullable(new employess("jinx",16,20.0));
Optional<String> b = a.map((c)-> c.getName());
System.out.println(b.get());
//7.flatMap( Function mapper):与map类似,要求返回值必须是 Optional
Optional<String> f = a.flatMap((g)-> Optional.of(g.getName()));
System.out.println(f.get());
4.接口中的默认方法与静态方法
从Java8开始支持在接口中拥有实现的方法,即为默认方法,用defalut修饰。
那么现在有个问题,如果接口和父类里面的方法重名了那会发生什么?
//接口
public interface MyFun {
default String getName(){
return "JINX";
}
}
//父类
public class MyFun1 {
public String getName(){
return "JINX1";
}
}
//实现类
public class extImp extends MyFun1 implements MyFun {
}
//执行
public static void main(String[] args) {
extImp t = new extImp();
System.out.println(t.getName());
//执行过后会发现输出的是“JINX1” 为什么?
}
是因为“类优先”原则
接口中的默认方法的”类优先”原则
若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
●选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
●接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突
那么如果又多出一个呢?
//接口1
public interface MyFun {
default String getName(){
return "JINX";
}
}
//接口2
public interface MyInterface {
default String getName(){
return "jinx3";
}
}
//父类
public class MyFun1 {
public String getName(){
return "JINX1";
}
}
//实现类
public class extImp /*extends MyFun1 */implements MyFun,MyInterface {
//实现类只能二选一,否则默认返回为null
@Override
public String getName() {
return MyFun.super.getName();
//return null; 不选择默认返回为null
}
}
//执行
public static void main(String[] args) {
extImp t = new extImp();
System.out.println(t.getName());
//执行过后会发现输出的是“JINX”
}
5.新时间日期API
java中不管使用的Data,TimeZone,Calender都不是线程安全的,含义是它们都是可变的,意思是多个线程同时操作日期就会引发线程安全问题,全新的一套API是不可变的,不可变是什么意思?拿字符串举个例子:String被称为不可变的字符序列,不管数据怎么改变都将产生一个新的实例这就叫做不可变,全新的时间是在time这个包里面
java.time包:都是针对时间与日期操作的,java1.8时间日期类是不可变,是线程安全的,大部分遵守的是ISO-8601标准,这个是世界针对于时间日期格式的统一标准
java.time.chrono:用于对于时间做一些特殊格式的操作
java.time.format:用于时间日期格式化
java.time.temporal:对于时间日期做运算的
java.time.rone:对于时期做一些运算的
//1.localDate localTime localDateTime
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
System.out.println(ldt.getYear());
System.out.println(ldt.getMonthValue());
System.out.println("也可以返回月对象获取月份"+ldt.getMonth().getValue());
System.out.println(ldt.getDayOfMonth());
System.out.println(ldt.getHour());
System.out.println(ldt.getMinute());
System.out.println(ldt.getSecond());
//2.加上两年 每次操作都会产生一个新的实例
LocalDateTime ldt1 = ldt.plusYears(2);
System.out.println(ldt1);
//减去两个月
System.out.println("减去俩月:"+ldt1.minusMonths(2));
LocalDateTime ldt2 = ldt1.minusMonths(2);
System.out.println("减去俩月:"+ldt2);
/**
* Instant:时间戳以(Unix 元年:1970年1月1日 00:00:00到某个时间之内的毫秒值)
*/
System.out.println("------------------------------");
Instant instant = Instant.now();
System.out.println(instant);
//会发现时间不对,这个默认获取的是UTC时区为准,
//UTC就是本初子午线那边的与我们中国差了8个小时
OffsetDateTime odt = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println("偏移后"+odt);
System.out.println("转为毫秒"+instant.toEpochMilli());
/**
* Duration : 计算两个“时间”之间的间隔
* Period:计算两个”日期“之间的间隔
*/
System.out.println("------------------------------");
Instant ins1 = Instant.now();
/*try {
Thread.sleep(1000);
}catch (Exception e){
System.out.println(e);
}*/
Instant ins2 = Instant.now();
Duration duration = Duration.between(ins1,ins2);//求出两个时间之间的间隔
System.out.println("两个相差"+duration.getSeconds()+"秒");
/**
* DateTimeFormatter ;格式化时间/日期
*/
System.out.println("*---------------------------");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//时间转日期
String date = LocalDateTime.now().format(dtf);
System.out.println(date);
System.out.println(dtf.format(LocalDateTime.now()));
//日期转时间
LocalDateTime l = LocalDateTime.parse(date,dtf);
/**
* 对ZonedDate,ZonedTime,ZonedDateTime 对时区进行处理
*/
System.out.println("------------------------");
ZoneId.getAvailableZoneIds().forEach(System.out::println);
LocalDateTime zone =LocalDateTime.now(ZoneId.of("America/Argentina/Buenos_Aires"));
//获取美国时区
System.out.println(zone);
ZonedDateTime z = zone.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println("上海时间"+z);
6.重复注解与类型注解
Java 8对注解处理提供了两点改进:可重复的注解以及可用于类型的注解