jdk8新特性
一、Lambda表达式
1 问题分析
当我们需要开辟一条线程执行语句时
1 2 3 4 5 6 7
| new Thread(new Runnable() { @Override public void run() { System.out.println("新线程执行:"+Thread.currentThread().getName()); } }).start(); System.out.println("主线程执行:"+Thread.currentThread().getName());
|
代码分析:
- 当我们想要创建一个线程时需要实现一个Runnable接口
- 为了简化Runnable的实现需要写一个匿名内部类
- 而实现这个接口需要重写一个run方法,而且需要保证其返回值、类名和参数都要按照要求
- 只有在这个方法体中才能写我们最终需要执行的代码
- 然而我们最终的目的只是想要开辟一条线程执行一条语句而已
那我如何对这段代码进行简化呢?
2 初体验
为了简化匿名内部类的写法,我们可以使用Lambda表达式
1 2 3
| new Thread(() -> { System.out.println("新线程执行:"+Thread.currentThread().getName()); }).start();
|
Lambda表达式优点:解决了匿名内部类的大量代码冗余,不在拘束于其中的条条框框,极大程度上简化和美化了代码
为了更好的理解,我们可以把lambda表达式理解为一段可以传递的代码。当然带来的缺点就是可读性变差了
3 语法规则
结构解析:
- (参数类型 参数名):里面存放参数列表
- {代码块}:里面是需要执行的方法
- ->:用来分隔参数列表以及代码块
3.1 练习1(无参无返)
定义一个接口:
1 2 3
| public interface UserService { public void show(); }
|
然后创建一个主方法使用:
1 2 3 4 5 6 7 8 9 10 11
| public class LambdaDemo02 { public static void main(String[] args) { getShow(()->{ System.out.println("Lambda表达式被执行"); }); }
public static void getShow(UserService userService){ userService.show(); } }
|
输出:
3.2 练习2(有参有返)
定义一个类:
1 2 3 4 5
| public class User { private Integer id; private String username; private String password; }
|
实现一个根据uid逆序排的集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public static void main(String[] args) { List<User> list = new ArrayList<>(); list.add(new User(1,"zhangsan","123")); list.add(new User(2,"lisi","123")); list.add(new User(3,"wanhwu","123")); list.add(new User(4,"zhaoliu","123")); Collections.sort(list,(User o1,User o2) -> { return o2.getId() - o1.getId(); }); for (User user : list) { System.out.println(user); } }
|
输出:
1 2 3 4
| User{id=4, username='zhaoliu', password='123'} User{id=3, username='wanhwu', password='123'} User{id=2, username='lisi', password='123'} User{id=1, username='zhangsan', password='123'}
|
3.3 注意点(重要)
Lambda使用的接口必须只能有一个抽象方法,如果有多个时会报错,可以使用@FunctionalInterface注解来规定该接口只能有一个抽象方法
4 原理分析
匿名内部类在运行的过程中会生成一个class文件
Lambda表达式在程序运行时会形成一个类
- 在类中新建一个方法,这个方法的方法体就是lambda里面的内容
- 还会形成一个匿名内部类,实现接口,重写抽象方法
- 在接口中重写方法会调用新生成的方法
5 省略写法
在原有的语法基础上,还可以进一步的进行省略
- 参数的类型可以省略
- 当有且仅有一个参数时,括号可以省略
- 当函数体中有且仅有一条语句时,可以省略return、花括号以及分号
最简写法:
1 2 3
| public static void main(String[] args) { getShow(id -> System.out.println("zhangsan")); }
|
6 使用前提
Lambda表达式的语法非常的简洁,但是他的使用要求非常高,需要满足以下条件才能使用
- 方法的参数或局部变量必须是抽象接口才能使用
- 接口类必须只有一个抽象方法式才可以使用
7 与匿名内部类的对比
Lambda与匿名内部类的对比
- 所需的类型不一样
- 匿名内部类的类型可以是 类,抽象类,接口
- Lambda表达式需要的类型必须是接口
- 抽象方法的数量不一样
- 匿名内部类的抽象方法数量不做要求,可以无数个
- Lambda表达式所需的接口之能有一个抽象方法
- 实现原理不一样
- 匿名内部类是在编译后形成一个class
- Lambda表达式是在程序运行的时候动态生成class
二、接口中新增的方法
1 jdk 8中接口新增
jdk8在接口方面又新增了一些方法,在jdk8之前
1 2 3 4
| interface 接口名 { 静态常量; 抽象方法; }
|
在jdk8之后,新增了默认方法和静态方法
1 2 3 4 5 6
| interface 接口名 { 静态常量; 抽象方法; 默认方法; 静态方法; }
|
2 默认方法
2.1 为什么要新增默认方法
新建一个实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class InterfaceDemo01 { public static void main(String[] args) { A b = new B(); A c = new C(); } }
interface A { void test(); }
class B implements A{ @Override public void test() {} }
class C implements A{ @Override public void test() {} }
|
我们可以看到两个类B,C都继承了A接口并实现了抽象方法,可是当我们增加A的抽象方法时
B和C都会报错,那是因为B和C还没有实现A新增的抽象方法。于是我们便发现一个问题,当A被多个类继承时,其实就不利于A接口的扩展了,比如Map接口就多达164个实现类
这个时候默认方法就可以很好的解决这个问题
2.2 语法格式
接口中默认方法的语法格式:
1 2 3 4 5
| interface 接口名 { 修饰符 default 返回值类型 接口名 { 方法体; } }
|
2.3 默认方法的特点
- 默认方法不是抽象方法
- 子类也能调用该方法
- 当然也可以对他进行重写
- 最终要的是方便了接口功能的扩展
3 静态方法
静态方法的作用与默认方法一样都是为了接口的扩展
3.1 语法格式
接口中使用静态方法的语法格式:
1 2 3 4 5
| interface 接口名{ 修饰符 static 返回值类型 方法名(){ 代码块; } }
|
3.2 静态方法的特点
- 静态方法不是抽象方法
- 子类不能调用该方法
- 子类不能重写该方法
- 静态方法只能通过 接口名.静态方法名() 进行调用
默认方法和静态方法的区别很明显,但他们的目的却又都一样,只有灵活搭配才能发挥其特性
三、函数式接口
在JDK中帮我们提供的函数式接口,一般都在Java.util.function 包中
1 由来
我们知道使用Lambda表达式的前提需要有函数式接口,而Lambda并不在乎抽象方法名和接口名,只在乎他的参数列表和返回值,而JDK专门为其提供了Lambda表达式可以直接使用的接口来丰富其功能
2 介绍
2.1 Supplier
Supplier函数接口:无参有返 ,用来生产数据
1 2 3 4 5 6 7 8 9 10
| @FunctionalInterface public interface Supplier<T> {
T get(); }
|
使用:计算一到九的和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class SupplierTest { public static void main(String[] args) { test(() ->{ int[] arr = {1,2,3,4,5,6,7,8,9}; int max = 0; for (int i : arr) { max += i; } return max; }); }
public static void test(Supplier<Integer> supplier){ int max = supplier.get(); System.out.println(max); } }
|
2.2 Consurmer
Consumer函数接口:有参无返回值 故名思意是用来消耗数据的
1 2 3 4 5 6 7 8 9
| @FunctionalInterface public interface Consumer<T> { void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
|
使用:将传入的字符串转换成大写
1 2 3 4 5 6 7 8 9 10 11
| public class ConsumerTest { public static void main(String[] args) { test((msg) -> { System.out.println(msg + "转化成为大写————>"+msg.toUpperCase(Locale.ROOT)); }); }
public static void test(Consumer<String> consumer){ consumer.accept("Hello world"); } }
|
2.3 function
有参有返,不做描述
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @FunctionalInterface public interface Function<T, R> { R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); }
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); }
static <T> Function<T, T> identity() { return t -> t; } }
|
2.4 Predicate
有参返回值为Boolean类型,一般用于判断(在下面的stream流中条件语句类型多为这个)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @FunctionalInterface public interface Predicate<T> { boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); }
default Predicate<T> negate() { return (t) -> !test(t); }
default Predicate<T> or(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); }
static <T> Predicate<T> isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
|
四、方法引用
1 什么是方法引用
方法引用可以看成Lambda表达式的深层次的表达。可以说方法引用就是Lambda表达式,但我认为是Lambda表达式的另外一种写法,而他最大的作用其实还为了进一步的解决Lambda表达式的缺陷,减少Lambda表达式的代码冗余
观察使用方法引用和不使用方法引用的差别:
1.不使用方法引用
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static void main(String[] args) { int arr[] = {5,1,21,53,11}; Function<int[],Integer> function = (arr1) -> { int max = 0; for (int i : arr) { if(max < i){ max = i; } } return max; }; function.apply(arr); }
|
2.使用方法引用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static void main(String[] args) { Function<int[],Integer> function = Test::printMax; System.out.println(function.apply(arr)); }
public static int printMax(int[] arr){ int max = 0; for (int i : arr) { if(max < i){ max = i; } } return max; }
|
通过以上两段代码我们可以发现:
- 函数接口的方法方法实现被抽离出来,而不在是以内部类的形似去实现了
- 而抽离出来有一个最大的好处就是实现了代码的复用,及该代码块不仅仅作用在抽象方法的实现
- 当然也可以直接使用实现了该功能的方法,近一步减少了Lambda表达式的冗余
2 使用要求
实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的 方法的参数列表和返回值类型保持一致!(方法名可以不同),并且引用的方法必须存在
3 语法格式
符号表示:::
符号说明:双冒号为方法引用运算符,而他所在的表达式被称为方法引用表达式
使用场景:
- 当Lambda表达式想要实现的功能,在其他方法中已经实现时,我们可以去使用方法引用
- 当Lambda表达式实现的功能需要使用到多个地方时,把该功能抽离出方法实现代码的复用
常见的引用方式如下
3.1 对象名::方法名
3.2 类名::静态方法名
3.3 类名::引用实例方法名
3.4 类名::构造方法
3.5 数组::构造器
五、Stream API
1 集合处理的弊端
当我们对集合中的元素进行操作时,除了必须的添加,删除,获取外,最典型的操作就是集合的遍历
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static void main(String[] args) { List<String> list = Arrays.asList("张三","李四","王五","张二狗"); List<String> list1 = new ArrayList<>(); for (String s : list) { if(s.startsWith("张") && s.length() == 2){ list1.add(s); } } for (String s : list1) { System.out.println(s); } }
|
我们可以看到,其实对集合中的元素筛选其实很不方便,而stream给我们带来了简便的写法
1 2 3 4 5 6
| public static void main(String[] args) { list.stream() .filter(s -> s.startsWith("张")) .filter(s -> s.length() == 2) .forEach(System.out::println); }
|
实现功能:过滤留下了张性,两个字的名字并且遍历打印
2 Stream流式思想
我们要注意将Stream流于IO流区别出来,Stream流不等于IO流,所以不能使用IO流的特性去理解Stream流,Stream流有点类似于工厂的流水线,集合中的元素会像商品一样经过一次一次又一次的过滤和筛选,留下最终符合要求的元素
3 获取方法
3.1 通过Collection获取
在Collection中实现了这个默认方法
1 2 3 4 5 6 7 8
| public static void main(String[] args) { List<String> list = new ArrayList<>(); Set<String> set = new HashSet<>(); Vector<String> vector = new Vector<>(); list.stream(); set.stream(); vector.stream(); }
|
map集合的获取方式:
1 2 3 4 5 6
| public static void main(String[] args) { Map<String,Integer> map = new HashMap<>(); map.keySet().stream(); map.values().stream(); map.entrySet().stream(); }
|
3.2 通过Stream.of 获取
1 2 3 4 5 6
| public static void main(String[] args) { Integer[] arr = {1,2,3,4,5}; Stream.of(arr) .filter(val -> val < 4) .forEach(System.out::println); }
|
注意: 使用的数组必须是包装类型,基础类型的数组会被当成一个数据,无法遍历(以下是基础类型数组)
1 2 3 4 5
| public static void main(String[] args) { int[] arr2 = {1,2,3,4,5}; Stream.of(arr2) .forEach(System.out::println); }
|
输出:
4 常用方法
Stream有许多可以使用的方法,下面只介绍一些常用的API。这些方法通常可以分为两类:
方法名 |
方法作用 |
返回值类型 |
方法种类 |
count |
统计个数 |
long |
终结 |
forEach |
遍历元素 |
void |
终结 |
filter |
过滤 |
Stream |
非终结 |
limit |
取前面n个元素 |
Stream |
非终结 |
skip |
跳过前面n个元素 |
Stream |
非终结 |
map |
对数据类型转换 |
Stream |
非终结 |
concat |
将两个流合并 |
Stream |
非终结 |
终结方法:返回值不在是Stream类型,不在支持链式调用,一般用在调用链的结尾
非终结方法:返回值仍是Stream类型,支持链式调用。(除了终结方法,其他均为非终结方法)
需要注意:
- Stream只能调用一次
- Stream方法返回的是新的流
- Stream执行的过程中不可逆
- 如果Stream的调用链结尾中没有终止方法,那么整条调用链都不会执行(重点)
综合案例:
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class StreamTest03 { public static void main(String[] args) { List<String> list1 = Arrays.asList("孙小美", "阿土伯", "小明", "钱夫人", "小红", "小菜","李傻子"); List<String> list2 = Arrays.asList("王熙凤","王五","张柳","李三麻","张二狗","张天爱","张麻子");
Stream<String> stream1 = list1.stream() .filter(s -> s.length() == 3) .limit(3); Stream<String> stream2 = list2.stream() .filter(s -> s.startsWith("张")) .skip(2); Stream.concat(stream1, stream2) .map(User::new) .forEach(System.out::println); } }
|
执行结果:
1 2 3 4 5
| User{id=null, username='孙小美', password='null'} User{id=null, username='阿土伯', password='null'} User{id=null, username='钱夫人', password='null'} User{id=null, username='张天爱', password='null'} User{id=null, username='张麻子', password='null'}
|
5 结果集的收集
5.1 结果收集到集合中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void test01(){ List<String> list = Stream.of("aa", "bb", "cc", "aa") .collect(Collectors.toList()); System.out.println(list); Set<String> set = Stream.of("aa", "bb", "cc", "aa") .collect(Collectors.toSet()); System.out.println(set);
ArrayList<String> list1 = Stream.of("aa", "bb", "cc", "aa") .collect(Collectors.toCollection(ArrayList::new)); System.out.println(list1); HashSet<String> set1 = Stream.of("aa", "bb", "cc", "aa") .collect(Collectors.toCollection(HashSet::new)); System.out.println(set1); }
|
输出:
1 2 3 4
| [aa, bb, cc, aa] [aa, bb, cc] [aa, bb, cc, aa] [aa, bb, cc]
|
5.2 结果收集到数组中
toArray方法可以将结果收集到Object集合中,如果传参还可以确定数组的类型
1 2 3 4 5 6 7 8 9 10
| @Test public void test02(){ Object[] objects = Stream.of("aa", "bb", "cc", "aa") .toArray(); System.out.println(Arrays.toString(objects)); String[] strings = Stream.of("aa", "bb", "cc", "aa") .toArray(String[]::new); System.out.println(Arrays.toString(strings)); }
|
输出
1 2
| [aa, bb, cc, aa] [aa, bb, cc, aa]
|
5.3 聚合运算
跟数据库里的聚合运算一样,Stream流也提供与其一样的功能,更加方便的操作某个字段实现求最大值,最小值,和,平均值
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
| @Test public void test03(){ Optional<User> max = Stream.of( new User("zhangsan", 18) , new User("lisi", 23) , new User("wangwu", 17) , new User("zhaoliu", 11) ).collect(Collectors.maxBy((o1, o2) -> o1.getAge() - o2.getAge())); System.out.println(max);
Optional<User> min = Stream.of( new User("zhangsan", 18) , new User("lisi", 23) , new User("wangwu", 17) , new User("zhaoliu", 11) ).collect(Collectors.minBy((o1, o2) -> o1.getAge() - o2.getAge())); System.out.println(min);
Integer sum = Stream.of( new User("zhangsan", 18) , new User("lisi", 23) , new User("wangwu", 17) , new User("zhaoliu", 11) ).collect(Collectors.summingInt(User::getAge)); System.out.println(sum);
Double avg = Stream.of( new User("zhangsan", 18) , new User("lisi", 23) , new User("wangwu", 17) , new User("zhaoliu", 11) ).collect(Collectors.averagingInt(User::getAge)); System.out.println(avg);
Long count = Stream.of( new User("zhangsan", 18) , new User("lisi", 23) , new User("wangwu", 17) , new User("zhaoliu", 11) ).collect(Collectors.counting()); System.out.println(count); }
|
输出:
1 2 3 4 5
| Optional[User{name=lisi, age=23}] Optional[User{name=zhaoliu, age=11}] 69 17.25 4
|
5.4 分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
@Test public void test04(){ Map<String, List<User>> collect = Stream.of( new User("zhangsan", 17) , new User("lisi", 23) , new User("zhangsan", 21) , new User("lisi", 16) , new User("zhangsan", 22) , new User("lisi", 13) ).collect(Collectors.groupingBy(user -> user.getAge() <= 18 ? "未成年" : "成年")); collect.forEach((k,v) ->{ System.out.println(k + "=" +v); }); }
|
输出
1 2
| 未成年=[User{name=zhangsan, age=17}, User{name=lisi, age=16}, User{name=lisi, age=13}] 成年=[User{name=lisi, age=23}, User{name=zhangsan, age=21}, User{name=zhangsan, age=22}]
|
多层分组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Test public void test05(){ Map<String, Map<String, List<User>>> collect = Stream.of( new User("zhangsan", 17) , new User("lisi", 23) , new User("zhangsan", 21) , new User("lisi", 16) , new User("zhangsan", 22) , new User("lisi", 13) ).collect(Collectors.groupingBy(User::getUsername, Collectors.groupingBy(user -> user.getAge() >= 18 ? "成年" : "未成年"))); collect.forEach((k,v)->{ System.out.println(k); v.forEach((k1,v1)->{ System.out.println("\t"+k1+"="+v1); }); }); }
|
输出
1 2 3 4 5 6
| lisi 未成年=[User{name=lisi, age=16}, User{name=lisi, age=13}] 成年=[User{name=lisi, age=23}] zhangsan 未成年=[User{name=zhangsan, age=17}] 成年=[User{name=zhangsan, age=21}, User{name=zhangsan, age=22}]
|
5.5 数据分区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
@Test public void test06(){ Map<Boolean, List<User>> collect = Stream.of( new User("zhangsan", 18) , new User("lisi", 23) , new User("wangwu", 17) , new User("zhaoliu", 11) ).collect(Collectors.partitioningBy(user -> user.getAge() >= 18)); collect.forEach((k,v)->{ System.out.println(k + "=" + v); }); }
|
输出
1 2
| false=[User{name=wangwu, age=17}, User{name=zhaoliu, age=11}] true=[User{name=zhangsan, age=18}, User{name=lisi, age=23}]
|
5.6 数据拼接
Collectors.joining()可以指定分割符将所有数据拼接成一个字符串
1 2 3 4 5 6 7 8 9 10 11
| @Test public void test07(){ String collect = Stream.of( new User("zhangsan", 18) , new User("lisi", 23) , new User("wangwu", 17) , new User("zhaoliu", 11) ).map(User::getUsername) .collect(Collectors.joining("_","开始:"," 结束")); System.out.println(collect); }
|
输出
1
| 开始:zhangsan_lisi_wangwu_zhaoliu 结束
|
6 并行流
6.1 串行的Stream流
我们前面使用的都是串行流,也就是在一条线程上执行的。
6.2 并行的Stream流
parallel Stream其实就是一个并行执行的流,她通过默认的ForkjoinRool,可以提高多线程任务的效率
6.2.1 并行流的两种获取方法
通过List接口的parallelStream方法获取并行流
通过parallel方法将串行流转化成并行流
1 2 3 4 5 6 7 8 9
| @Test public void test08(){ List<Integer> list = new ArrayList<>(); Stream<Integer> integerStream = list.parallelStream();
long count = Stream.of("1", "2", "3").parallel().count(); }
|
6.2.1 并行流的执行
1 2 3 4 5 6 7 8 9 10
| @Test public void test09() { Stream.of("1","2","3","4","5","6","7","8","9") .parallel() .filter(s ->{ System.out.println(Thread.currentThread() + "s=" +s ); return Integer.valueOf(s) > 3; }) .count(); }
|
输出
1 2 3 4 5 6 7 8 9
| Thread[ForkJoinPool.commonPool-worker-15,5,main]s=4 Thread[ForkJoinPool.commonPool-worker-8,5,main]s=5 Thread[main,5,main]s=6 Thread[ForkJoinPool.commonPool-worker-11,5,main]s=2 Thread[ForkJoinPool.commonPool-worker-6,5,main]s=1 Thread[ForkJoinPool.commonPool-worker-13,5,main]s=9 Thread[ForkJoinPool.commonPool-worker-9,5,main]s=3 Thread[ForkJoinPool.commonPool-worker-4,5,main]s=7 Thread[ForkJoinPool.commonPool-worker-2,5,main]s=8
|
我们可以看到数据的输出并不是按顺序的,说明所用元素的过滤都是同时进行的,这样速度会快一些
六、Optional类
1 常规对null值的处理
1 2 3 4 5 6
| String s = null; if(s != null){ System.out.println("字符串的长度为:"+s.length()); }else{ System.out.println("该字符串为null"); }
|
在常规的开发过程中经常需要对各种各样的变量做非空判断
2 Optional
Optional是一个工具类,是jdk8中的新特性。它是一个值可以为NULL的容器对象,其主要目的是避免null检查,防止NullPointerException
3 Optional对象的基本使用
3.1 创建的三种方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
@Test public void test02(){ Optional<String> op1 = Optional.of("zhangsan");
Optional<String> op3 = Optional.ofNullable("lisi"); Optional<Object> op4 = Optional.ofNullable(null); Optional<Object> op5 = Optional.empty(); }
|
七、新时间日期API
1 旧版本的缺陷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@Test public void test01(){ Date date = new Date(2021,12,11); System.out.println(date);
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd"); System.out.println(sf.format(date));
new Thread(()->{ try { System.out.println(sf.parse("2021-12-11")); } catch (ParseException e) { e.printStackTrace(); } }).start(); }
|
- 设计不合理,在Java.util和Java.sql的包中都有日期类,java.util.Date同时包含日期和时间的,而Java.sql.Date仅仅包含日期,此外用于格式化和解析的类在Java.text包下。
- 非线程安全,java.util.Date是飞线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一
- 时区处理麻烦,日期类并不提供国际化,没有时区支持。
2 新版本的时间日期
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 52 53 54 55 56 57 58 59 60 61
|
@Test public void test2(){ LocalDate date = LocalDate.of(2022,1,1); System.out.println(date);
LocalDate now = LocalDate.now(); System.out.println(now);
System.out.println(now.getYear()); System.out.println(now.getMonth().getValue()); System.out.println(now.getDayOfMonth()); System.out.println(now.getDayOfWeek().getValue()); }
@Test public void test3(){ LocalTime localTime = LocalTime.of(12,01,01,4315); System.out.println(localTime);
LocalTime now = LocalTime.now(); System.out.println(now);
System.out.println(now.getHour()); System.out.println(now.getMinute()); System.out.println(now.getSecond()); }
@Test public void test03() { LocalDateTime date = LocalDateTime.of(2022,1,1,12,01,01,4315); System.out.println(date);
LocalDateTime now = LocalDateTime.now(); System.out.println(now);
System.out.println(now.getYear()); System.out.println(now.getMonth().getValue()); System.out.println(now.getDayOfMonth()); System.out.println(now.getDayOfWeek().getValue()); System.out.println(now.getHour()); System.out.println(now.getMinute()); System.out.println(now.getSecond()); }
|