Java8特性—流式语法 总结过Java8流式语法相关特性:函数式接口、默认方法、Lambda表达式、方法引用。
这次学一下Java8的其他特性:Stream API、Optional类、Date-Time API、Base64编码、Nashorn。
——转载自RUNOOB 、孤独烟 、回梦游先
[TOC]
Stream API Java8API添加了一个新的抽象成为流Stream,可以让你以一种声明的方式处理数据。
Stream使用一种类似于SQL语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象。
StreamAPI可以极大提高Java程序员的生产力,让程序员写出高效率,干净,简洁的代码。
这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的结点上进行处理,比如筛选,排序,聚合等。。。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
流程:stream of elements —–> filter-> sorted-> map-> collect
以上的流程转换为Java代码:
1 2 3 4 5 6 List<Integer> transactionsIds = widgets.stream() .filter(b -> b.getColor() == RED) .sorted((x,y) -> x.getWeight() - y.getWeight()) .mapToInt(Widget::getWeight) .sum();
什么是Stream? Stream(流)是一个来自数据源的元素队列并支持聚合操作
元素是特定类型的对象,形成的一个队列。Java中的Steam并不会存储元素,而是按需计算。
数据源 流的来源。可以是集合、数组、I/O channel、产生器geanerator等等。
聚合操作 类似SQL语句一样的操作,比如filter、map、reduce、find、match、sorted等。和以前的Collection操作不同,Stream操作还有两个基础的特征:
Pipelining :中间操作都会返回流对象本身。这样多个操作可以串联成一个管道,如同流式风格(fluent style)。这样做可以对操作进行优化,比如延迟执行(laziness)和短路(shor-circuiting)。
内部迭代 :以前对集合遍历都是通过Iterator或者For-Each的方式,显式的在集合外部进行迭代,这叫做外部迭代 。Stream提供了内部迭代 的方式,通过访问者模式(Visitor) 实现。
生成流 在Java8中,集合接口有两个方法来生成流:
stream() —为集合创建串行流。
parallel Stream() —为集合创建并行流。
1 2 List<String> strings = Arrays.asList("abc" , "" , "bc" , "efg" , "abcd" ,"" , "jkl" ); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
forEach Stream提供了新的方法“forEach”来迭代流中的每个数据。以下代码片段使用forEach输出了10个随机数:
1 2 Random random = new Random(); random.ints().limit(10 ).forEach(System.out::println);
map map方法用于映射每个元素到对应的结果,以下代码片段使用map输出了元素对应的平方数:
1 2 3 List<Integer> numbers = Arrays.asList(3 , 2 , 2 , 3 , 7 , 3 , 5 ); List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
filter filter方法用于通过设置的条件过滤出元素。以下代码片段使用filter方法过滤出空字符串:
1 2 3 List<String>strings = Arrays.asList("abc" , "" , "bc" , "efg" , "abcd" ,"" , "jkl" ); int count = strings.stream().filter(string -> string.isEmpty()).count();
limit limit方法用于获取指定数量的流。以下代码片段使用limit方法打印出10条数据:
1 2 Random random = new Random(); random.ints().limit(10 ).forEach(System.out::println);
sorted sorted方法用于对流进行排序。以下代码片段使用sorted方法对输出的10个随机数进行排序:
1 2 Random random = new Random(); random.ints().limit(10 ).sorted().forEach(System.out::println);
并行(parallel)程序 parallelStream是流并行处理程序的替代方法。以下实例我们使用parallelStream来输出空字符串的数量:
1 2 3 List<String> strings = Arrays.asList("abc" , "" , "bc" , "efg" , "abcd" ,"" , "jkl" ); int count = strings.parallelStream().filter(string -> string.isEmpty()).count();
Collectors Collectors类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors可用于返回列表或字符串:
1 2 3 4 5 List<String>strings = Arrays.asList("abc" , "" , "bc" , "efg" , "abcd" ,"" , "jkl" ); List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList()); System.out.println("筛选列表: " + filtered); String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(", " )); System.out.println("合并字符串: " + mergedString);
统计 一些产生统计结果的收集器也非常有用。他们主要用于int、double、long等基本类型上,他们可以用来产生类似如下的统计结果。
1 2 3 4 5 6 7 8 9 List<Integer> numbers = Arrays.asList(3 , 2 , 2 , 3 , 7 , 3 , 5 ); IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics(); System.out.println("列表中最大的数 : " + stats.getMax()); System.out.println("列表中最小的数 : " + stats.getMin()); System.out.println("所有数之和 : " + stats.getSum()); System.out.println("平均数 : " + stats.getAverage());
Optional类 Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional是个容器:他可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
Optional类的引用很好的解决空指针异常。
类声明 java.util.Optinoal<T>类的声明:
1 public final class Optional <T >extends Object
类方法 注意: 这些方法是从 java.lang.Object 类继承来的。
序号
方法 & 描述
1
*static <T> Optional<T> empty() * 返回空的 Optional 实例。
2
boolean equals(Object obj) 判断其他对象是否等于 Optional。
3
*Optional<T> filter(Predicate<? super <T> predicate) * 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。
4
Optional<T> filter(Predicate<? super <T> predicate) 如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。
5
*T get() * 如果在这个Optional中包含这个值,返回值,否则抛出异常:NoSuchElementException,使用get()方法前,最好进行isPresent()校验。
6
int hashCode() 返回存在值的哈希码,如果值不存在 返回 0。
7
void ifPresent(Consumer<? super T> consumer) 如果值存在则使用该值调用 consumer
8
boolean isPresent() 如果值存在则方法会返回true,否则返回 false。
9
<U>Optional<U> map(Function<? super T,? extends U> mapper) 如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。
10
static <T> Optional<T> of(T value) 返回一个指定非null值的Optional。
11
*static <T> Optional<T> ofNullable(T value) * 如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。
12
T orElse(T other) 如果存在该值,返回值, 否则返回 other。
13
T orElseGet(Supplier<? extends T> other) 如果存在该值,返回值, 否则触发 other,并返回 other 调用的结果。
14
*<X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) * 如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常。
15
String toString() 返回一个Optional的非空字符串,用来调试。
1、Optional(T value),empty(),of(T value),ofNullable(T value) 这四个函数之间具有相关性,因此放在一组进行记忆。
先说明一下,Optional(T value)即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供用户调用。那么Optional的本质就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空 。直接上Optional(T value)构造函数的源码,如下图:
of(T value) 的源码:
1 2 3 public static <T> Optional<T> of (T var0) { return new Optional(var0); }
其内部直接调用了构造函数,根据构造函数可以得到两个结论:
通过of(T value)函数所构造出的Optional对象,当value属性值为空时,依然会报出NPE。
通过of(T value)函数所构造出的Optional对象,当value属性值不为空是,能正常构造Optional对象。
除此之外,Optional内部还维护一个value为null的对象,大概长下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 public final class Optional <T > { private static final Optional<?> EMPTY = new Optional<>(); private Optional () { this .value = null ; } public static <T> Optional<T> empty () { @SuppressWarnings ("unchecked" ) Optional<T> t = (Optional<T>) EMPTY; return t; } }
那么,empty()的作用 就是返回EMPTY对象。
说前面三个方法都是为了铺垫,可以说ofNullable(T value)的作用 了,上源码:
1 2 3 public static <T> Optional<T> ofNullable (T var0) { return var0 == null ? empty() : of(var0); }
显而易见,相比较of(T value)的区别 就是,当value值为null时,of(T value) 会报NPE异常;而ofNullable(T value) 不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。
那是不是意味着,我们在项目中只用ofNullable(T value)函数而不需要of(T value)函数了呢?
不是的,既然存在那么自然有存在的价值。当我们在运行过程中,不想隐藏NPE,而是要立刻报告,这种情况下就用of(T value)函数。但是不得不说,这种场景很少很少,博主也仅在写junit测试用例中用到过此函数。
2、orElse(T other)、orElseGet(Supplier<? extendsT> other)、orElseThrow(Supplier<? extends X> exceptionSupplier) 这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orElse 和orElseGet 的用法如下所示,相当于value值为null时,给予一个默认值:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void test () { User user = null ; user = Optional.ofNullable(user).orElse(createUser()); user = Optional.ofNullable(user).orElseGet(() -> createUser()); } public User createUser () { User user = new User(); user.setName("zhangsan" ); return user; }
这两个函数的区别 :当user值不为null时,orElse函数依然会执行createUser()函数,而orElseGet函数并不会执行createUser()函数。
至于orElseThrow,就是value值为null时,直接抛一个异常出去,用法如下所示
1 2 User user = null ; Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在" ));
3、map(Function <? super T,? extends U> mapper)和flatMap(Function <? super T,Optional<U>> mapper) 这两个函数放在一组记忆,这两个函数做的是转换值的操作。上源码:
1 2 3 4 5 6 7 8 9 10 11 12 public final class Optional <T > { public <U> Optional<U> map (Function<? super T, ? extends U> var1) { Objects.requireNonNull(var1); return !this .isPresent() ? empty() : ofNullable(var1.apply(this .value)); } public <U> Optional<U> flatMap (Function<? super T, Optional<U>> var1) { Objects.requireNonNull(var1); return !this .isPresent() ? empty() : (Optional)Objects.requireNonNull(var1.apply(this .value)); } }
这两个函数,在函数体的上的区别不大,主要的区别 就是入参,map函数所接受的入参类型为Function<? supper T,? extends U>,而flapMap的入参类型为Function<? super T,Optional<U>>。
具体用法上,对于map 而言:
如果User结构是下面这样
1 2 3 4 5 6 public class User { private String name; public String getName () { return name; } }
这时候取name的写法如下所示:
1 String name = Optional.ofNullable(user).map(u-> u.getName()).get();
对于flatmap 而言则是这样:
如果User结构是下面这样的
1 2 3 4 5 6 public class User { private String name; public Optional<String> getName () { return Optional.ofNullable(name); } }
这时候取name的写法如下所示
1 String name = Optional.ofNullable(user).flatMap(u->u.getName()).get();
4、isPresent()和ifPresent(Consumer<? super T> consumer) 这两个函数放在一起记忆,isPresent即判断value值是否为空,而ifPresent就是在value值不为空时,做一些操作。上源码:
1 2 3 4 5 6 7 8 9 public boolean isPresent () { return this .value != null ; } public void ifPresent (Consumer<? super T> var1) { if (this .value != null ) { var1.accept(this .value); } }
注意: 不要进行下面这种鸡肋的操作
1 2 3 4 5 6 7 8 if (user != null ){ } User user = Optional.ofNullable(user); if (Optional.isPresent()){ }
因为这样写,代码结构依然丑陋。后面会给出正确写法
至于ifPresent(Consumer <?super T> consumer),用法也很简单,如下所示:
1 2 3 Optional.ofNullable(user).ifPresent(u->{ });
5、filter(Predicate<? super T> predicate) 直接上源码:
1 2 3 4 5 6 7 8 9 10 public final class Optional <T > { public Optional<T> filter (Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this ; else return predicate.test(value) ? this : empty(); } }
filter方法接受一个Predicate来对Optional中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个Optional;否则返回Optional.empty。
用法如下:
1 Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6 );
如上所示,如果user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象。
实战使用 例一 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public String getCity (User user) throws Exception { if (user!=null ){ if (user.getAddress()!=null ){ Address address = user.getAddress(); if (address.getCity()!=null ){ return address.getCity(); } } } throw new Excpetion("取值错误" ); } public String getCity (User user) throws Exception { return Optional.ofNullable(user) .map(u-> u.getAddress()) .map(a->a.getCity()) .orElseThrow(()->new Exception("取指错误" )); }
例二 1 2 3 4 5 6 7 8 9 if (user!=null ){ dosomething(user); } Optional.ofNullable(user) .ifPresent(u->{ dosomething(u); });
例三 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public User getUser (User user) throws Exception { if (user!=null ){ String name = user.getName(); if ("zhangsan" .equals(name)){ return user; } }else { user = new User(); user.setName("zhangsan" ); return user; } } public User getUser (User user) { return Optional.ofNullable(user) .filter(u->"zhangsan" .equals(u.getName())) .orElseGet(()-> { User user1 = new User(); user1.setName("zhangsan" ); return user1; }); }
还有很多应用的例子,就不一一列举了。不过采用这种链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所下降,大家项目中看情况酌情使用。
Date-Time API Java8发布新的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类,但他们同样存在上述所有问题。
Java8在java.time 包下提供了很多的新的API。以下为两个比较重要的API:
Local(本地) :简化了日期时间的处理,没有时区的问题。
Zoned(时区) :通过制定的时区处理日期时间。
新的java.time包涵盖了所有处理日期、时间、日期/时间、时区、时刻(instants)、过程(during)与时钟(clock)的操作。
常用、重要对象介绍: ZoneId:时区ID,用来确定Instant和LocalDateTime互相转换的规则。
Instant:用来表示时间线上的一个点(瞬时)。
LocalDate:表示没有时区的日期,LocalDate是不可变并且线程安全的。
LocalTime:表示没有时区的时间,LocalTime是不可变并且线程安全的。
LocalDateTime:表示没有时区的日期时间,LocalDateTime是不可变并且线程安全的。
Clock:用于访问当前时刻、日期、时间,用到时区。
Duration:用秒和纳秒表示时间的数量(长短),用于计算两个日期的“时间”间隔。
Period:用于计算两个“日期”间隔。
其中,LocalDate、LocalTime、LocalDateTime都是新API里的基础对象,绝大多数操作都是围绕这三个对象来进行的,有必要啰嗦一遍:
LocalDate:只含年 月 日的日期对象。
LocalTime:只含时 分 秒的时间对象。
LocalDateTime:同时含有年 月 日 时 分 秒的日期对象。
直接上示例: 1.获取当前时间: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDateTime localDateTime = LocalDateTime.now(); LocalDate localDate = LocalDate.now(); LocalTime localTime = LocalTime.now(); System.out.println(localDateTime); System.out.println(localDate); System.out.println(localTime); } }
2.根据指定日期/时间创建对象: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDateTime localDateTime = LocalDateTime.of(2018 ,8 ,8 ,8 ,8 ,8 ); LocalDate localDate = LocalDate.of(2018 ,8 ,8 ); LocalTime localTime = LocalTime.of(8 ,8 ,8 ); System.out.println(localDateTime); System.out.println(localDate); System.out.println(localTime); } }
3.日期时间的加减: 注意:对于LocalDate ,只有精度大于等于日的加减,如年,月,日;对于LocalTime ,只有精度小于等于时加减,如时,分,秒,纳秒;对于LocalDateTime ,则可以进行任意精度的时间相加减;否则会抛出异常:java.time.temporal.UnsupportedTemporalTypeException: Unsupported unit: XXX
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDateTime localDateTime = LocalDateTime.now(); LocalDateTime plusYearsResult = localDateTime.plusYears(2L ); LocalDateTime plusMonthsResult = localDateTime.plusMonths(4L ); LocalDateTime plusDaysResult = localDateTime.plusDays(7L ); LocalDateTime plusHoursResult = localDateTime.plusHours(2L ); LocalDateTime plusMinutesResult = localDateTime.plusMinutes(10L ); LocalDateTime plusSecondsResult = localDateTime.plusSeconds(10L ); System.out.println("当前时间是 : " + localDateTime + "\n" + "当前时间加2年后为 : " + plusYearsResult + "\n" + "当前时间加3个月后为 : " + plusMonthsResult + "\n" + "当前时间加7日后为 : " + plusDaysResult + "\n" + "当前时间加2小时后为 : " + plusHoursResult + "\n" + "当前时间加10分钟后为 : " + plusMinutesResult + "\n" + "当前时间加10秒后为 : " + plusSecondsResult + "\n" ); LocalDateTime nextMonth = localDateTime.plus(1 , ChronoUnit.MONTHS); LocalDateTime nextYear = localDateTime.plus(1 , ChronoUnit.YEARS); LocalDateTime nextWeek = localDateTime.plus(1 , ChronoUnit.WEEKS); System.out.println("now : " + localDateTime + "\n" + "nextYear : " + nextYear + "\n" + "nextMonth : " + nextMonth + "\n" + "nextWeek :" + nextWeek + "\n" ); LocalDateTime minus = localDateTime.minus(1L , ChronoUnit.YEARS); System.out.println(minus); } }
4.将年、月、日、等修改为指定的值,并返回新的日期/时间对象: 注意:在指定日期/时间时要注意取值范围,日期还要注意日期基础上是否为闰年(leap year),如果超出取值范围会报出:java.time.DateTimeException: Invalid value for DayOfYear (valid values 1 - 365/366): XXX,如果没有注意是否为闰年去特殊范围值的时候会报出:java.time.DateTimeException: Invalid date ‘DayOfYear XXX’ as ‘XXXX’ is not a leap year
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDate localDate = LocalDate.now(); LocalDate withDayOfYearResult = localDate.withDayOfYear(200 ); LocalDate withDayOfMonthResult = localDate.withDayOfMonth(5 ); LocalDate withYearResult = localDate.withYear(2017 ); LocalDate withMonthResult = localDate.withMonth(5 ); System.out.println("当前时间是 : " + localDate + "\n" + "指定本年当中的第200天 : " + withDayOfYearResult + "\n" + "指定本月当中的第5天 : " + withDayOfMonthResult + "\n" + "直接指定年份为2017 : " + withYearResult + "\n" + "直接指定月份为5月 : " + withMonthResult + "\n" ); } }
5.获取日期的年月日周时分秒: 直接用getDayOfWeek或者getMonth取得的是枚举类型(enum)的英文单词,可以用可以再用Week或者Month枚举类提供的getValue方法转换为数字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDateTime localDateTime = LocalDateTime.now(); int dayOfYear = localDateTime.getDayOfYear(); int dayOfMonth = localDateTime.getDayOfMonth(); DayOfWeek dayOfWeek = localDateTime.getDayOfWeek(); System.out.println("今天是" + localDateTime + "\n" + "本年当中第" + dayOfYear + "天" + "\n" + "本月当中第" + dayOfMonth + "天" + "\n" + "本周中星期" + dayOfWeek.getValue() + "-即" + dayOfWeek + "\n" ); int year = localDateTime.getYear(); Month month = localDateTime.getMonth(); int day = localDateTime.getDayOfMonth(); int hour = localDateTime.getHour(); int minute = localDateTime.getMinute(); int second = localDateTime.getSecond(); System.out.println("今天是" + localDateTime + "\n" + "年 : " + year + "\n" + "月 : " + month.getValue() + "-即 " + month + "\n" + "日 : " + day + "\n" + "时 : " + hour + "\n" + "分 : " + minute + "\n" + "秒 : " + second + "\n" ); } }
6.时间/日期前后的比较与判断: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDate localDate1 = LocalDate.of(2019 , 8 , 8 ); LocalDate localDate2 = LocalDate.of(2018 , 8 , 8 ); boolean date1IsBeforeDate2 = localDate1.isBefore(localDate2); System.out.println("date1IsBeforeDate2 : " + date1IsBeforeDate2); } }
7.判断是否为闰年: 1 2 3 4 5 6 7 8 9 10 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDate now = LocalDate.now(); System.out.println("now : " + now + ", is leap year ? " + now.isLeapYear()); } }
8.java8时钟 : clock() 1 2 3 4 5 6 7 8 9 10 11 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { Clock clock = Clock.systemUTC(); System.out.println(clock); } }
9.时间戳: 事实上Instant 就是Java8之前的Date,可以使用以下两个类中的方法在这两个类型之间进行转换,比如Date.from(Instant)就是用来把Instant转换成java.util.date的,而new Date().toInstant()就是将Date转换为Instant的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { Instant instant = Instant.now(); System.out.println(instant); Date date = Date.from(instant); Instant instant2 = date.toInstant(); System.out.println(date); System.out.println(instant2); } }
10.计算时间/日期间隔: Duration:用于计算两个“时间”间隔
Period:用于计算两个“日期”间隔
注意:当获取两个日期的间隔时,并不是单纯的年月日对应的数字相加减,而是会先算出具体差多少天,在折算成相差几年几月几日的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDate date1 = LocalDate.of(2018 , 2 , 13 ); LocalDate date2 = LocalDate.of(2017 , 3 , 12 ); Period period = Period.between(date1, date2); System.out.println("相差年数 : " + period.getYears()); System.out.println("相差月数 : " + period.getMonths()); System.out.println("相差日数 : " + period.getDays()); System.out.println("-------------------------------" ); long years = period.get(ChronoUnit.YEARS); long months = period.get(ChronoUnit.MONTHS); long days = period.get(ChronoUnit.DAYS); System.out.println("相差的年月日分别为 : " + years + "," + months + "," + days); System.out.println("-------------------------------" ); LocalDateTime date3 = LocalDateTime.now(); LocalDateTime date4 = LocalDateTime.of(2018 , 1 , 13 , 22 , 30 , 10 ); Duration duration = Duration.between(date3, date4); System.out.println(date3 + " 与 " + date4 + " 间隔 " + "\n" + " 天 :" + duration.toDays() + "\n" + " 时 :" + duration.toHours() + "\n" + " 分 :" + duration.toMinutes() + "\n" + " 毫秒 :" + duration.toMillis() + "\n" + " 纳秒 :" + duration.toNanos() + "\n" ); } }
11.当计算程序的运行时间时,应当使用时间戳Instant: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { Instant ins1 = Instant.now(); for (int i = 0 ; i < 1000000 ; i++) { } Instant ins2 = Instant.now(); Duration duration = Duration.between(ins1, ins2); System.out.println("程序运行耗时为 : " + duration.toMillis() + "毫秒" ); } }
12.时间日期的格式化(格式化后返回的类型是String): 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { DateTimeFormatter formatter1 = DateTimeFormatter.ISO_DATE_TIME; LocalDateTime date1 = LocalDateTime.now(); System.out.println("LocalDateTime:" +date1); String date1Str = formatter1.format(date1); System.out.println("jdk自身配置好的日期格式:" +date1Str); DateTimeFormatter dtf= DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" ); String formatDateTime = date1.format(dtf); System.out.println("自定义日期格式:" +formatDateTime); } }
注意 :格式的写法必须与字符串的形式一样:如:
2018-01-13 21:27:30 对应 yyyy-MM-dd HH:mm:ss
20180113213328 对应 yyyyMMddHHmmss
否则会有运行时异常!
看一个例子,思考原因:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { LocalDateTime ldt1 = LocalDateTime.now(); System.out.println(ldt1); DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" ); String temp = dtf1.format(ldt1); LocalDateTime formatedDateTime = LocalDateTime.parse(temp, dtf1); System.out.println(formatedDateTime); } }
记住 :得到的最终结果都是类似2019-09-22T14:34:24的格式因为在输出LocalDateTime对象时,会调用其重写的toString方法,读者可以读读源码就可知道,对于LocalDateTime对象,无论是String类型的时间日期转为LocalDatetime对象,还是LocalDateTime对象转为格式化后的LocalDateTime对象,得到的最终输出都是:yyyy-MM-ddThh:mm:ss 这样的格式!
至于为什么这样,应该是为了符合国际化吧,也正因为如此,才有了LocalDate和LocalTime,以便于开发者有更多的选择。
但上述说法仅限于LocalDateTime对象,而如果是LocalDateTime对象格式化转为String对象是可以任意格式的,如上文自定义格式示例!
13.long毫秒值转换为日期: 1 2 3 4 5 6 7 8 9 10 11 12 13 public class Demo { public static void main (String[] args) { Demo.testZonedDateTime(); } public static void testZonedDateTime () { System.out.println("---------long毫秒值转换为日期---------" ); DateTimeFormatter df= DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" ); String longToDateTime = df.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(System.currentTimeMillis()), ZoneId.of("Asia/Shanghai" ))); System.out.println(longToDateTime); } }
Base64编码 在Java8中,Base64编码已经成为Java类库的标准。
Java8内置了Base64编码的编码器和解码器。
Base64工具类提供了一套静态方法获取下面三种Base64编解码器:
基本: 输出被映射到一组字符A-Z a-z 0-9+/,编码不添加任何行标,输出的解码仅支持A-Z a-z 0-9+/。
URL: 输出映射到一组字符A-Z a-z 0-9+_,输出时URL和文件。
MIME: 输出映射到MIME友好格式。输出每行不超过76字符,并且使用’\r’并跟随’\n’作为分割。编码输出最后没有行分割。
内嵌类
序号
内嵌类 & 描述
1
static class Base64.Decoder 该类实现一个解码器用于,使用 Base64 编码来解码字节数据。
2
static class Base64.Encoder 该类实现一个编码器,使用 Base64 编码来编码字节数据。
方法
序号
方法名 & 描述
1
static Base64.Decoder getDecoder() 返回一个 Base64.Decoder ,解码使用基本型 base64 编码方案。
2
*static Base64.Encoder getEncoder() * 返回一个 Base64.Encoder ,编码使用基本型 base64 编码方案。
3
*static Base64.Decoder getMimeDecoder() * 返回一个 Base64.Decoder ,解码使用 MIME 型 base64 编码方案。
4
*static Base64.Encoder getMimeEncoder() * 返回一个 Base64.Encoder ,编码使用 MIME 型 base64 编码方案。
5
static Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator) 返回一个 Base64.Encoder ,编码使用 MIME 型 base64 编码方案,可以通过参数指定每行的长度及行的分隔符。
6
static Base64.Decoder getUrlDecoder() 返回一个 Base64.Decoder ,解码使用 URL 和文件名安全型 base64 编码方案。
7
static Base64.Encoder getUrlEncoder() 返回一个 Base64.Encoder ,编码使用 URL 和文件名安全型 base64 编码方案。
实例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Java8Tester { public static void main (String args[]) { try { String base64encodedString = Base64.getEncoder().encodeToString("runoob?java8" .getBytes("utf-8" )); System.out.println("Base64 编码字符串 (基本) :" + base64encodedString); byte [] base64decodedBytes = Base64.getDecoder().decode(base64encodedString); System.out.println("原始字符串: " + new String(base64decodedBytes, "utf-8" )); base64encodedString = Base64.getUrlEncoder().encodeToString("runoob?java8" .getBytes("utf-8" )); System.out.println("Base64 编码字符串 (URL) :" + base64encodedString); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0 ; i < 10 ; ++i) { stringBuilder.append(UUID.randomUUID().toString()); } byte [] mimeBytes = stringBuilder.toString().getBytes("utf-8" ); String mimeEncodedString = Base64.getMimeEncoder().encodeToString(mimeBytes); System.out.println("Base64 编码字符串 (MIME) :" + mimeEncodedString); }catch (UnsupportedEncodingException e){ System.out.println("Error :" + e.getMessage()); } } }
1 2 3 4 5 6 7 8 9 10 11 12 输出结果: $ javac Java8Tester.java $ java Java8Tester 原始字符串: runoob?java8 Base64 编码字符串 (URL) :VHV0b3JpYWxzUG9pbnQ_amF2YTg= Base64 编码字符串 (MIME) :M2Q4YmUxMTEtYWRkZi00NzBlLTgyZDgtN2MwNjgzOGY2NGFlOTQ3NDYyMWEtZDM4ZS00YWVhLTkz OTYtY2ZjMzZiMzFhNmZmOGJmOGI2OTYtMzkxZi00OTJiLWEyMTQtMjgwN2RjOGI0MTBmZWUwMGNk NTktY2ZiZS00MTMxLTgzODctNDRjMjFkYmZmNGM4Njg1NDc3OGItNzNlMC00ZWM4LTgxNzAtNjY3 NTgyMGY3YzVhZWQyMmNiZGItOTIwZi00NGUzLTlkMjAtOTkzZTI1MjUwMDU5ZjdkYjg2M2UtZTJm YS00Y2Y2LWIwNDYtNWQ2MGRiOWQyZjFiMzJhMzYxOWQtNDE0ZS00MmRiLTk3NDgtNmM4NTczYjMx ZDIzNGRhOWU4NDAtNTBiMi00ZmE2LWE0M2ItZjU3MWFiNTI2NmQ2NTlmMTFmZjctYjg1NC00NmE1 LWEzMWItYjk3MmEwZTYyNTdk
Nashorn 这部分仅仅是科普一下。。。知道有这么个东西,具体细节等到后序用到时再记录。。。
Nashorn一个JavaScript引擎。
从JDK1.8开始,Nashorn取代Rhino(JDK1.6,JDK1.7)成为Java嵌入式JavaScript引擎。Nashorn完全支持ECMAScript 5.1规范以及一些扩展。它使用基于JSP 292的新语言特性,其中包含在JDK7中引入的invokedynamic,将JavaScript编译成Java字节码。
与先前的Rhino实现相比,这带来了2到10倍的性能提升。
jjs jjs是个基于Nashorn引擎的命令行工具。它接受一些JavaScript源代码为参数,并且执行这些源代码。jjs可以提供交互式编程体验。
Java中调用JavaScript 使用ScriptEngineManager类,JavaScript代码可以在Java中执行。