java8新特性---大总结--建议收藏、点赞、加关注!!!

x33g5p2x  于2021-12-18 转载在 其他  
字(20.0k)|赞(0)|评价(0)|浏览(410)

1、Lambda表达式------也叫做 函数式编程

Lambda是一个匿名函数,可以理解为一段可以(像数据一样)传递的代码。可以使代码更简介更灵活
使用lambda表达式有4个知识点

第一个、lambda表达式的格式()->{},只有函数式接口才能使用这种格式

函数式接口接口只能有一个抽象方法、可以有多个defaultstatic方法这样的接口才能算是函数式接口
接口中只有一个抽象方法声明,例如Runnable接口,可以使用lambda表达式
使用方式 ()->{}

new Thread(()->{System.out.println("lambda表达式测试");},"lambda").start();
//Runable来自java.lang没有返回值
Runnable runnable=()->{ System.out.println("lambda测试"); };
//Callable来自java.util.concurrent 有返回值,需要有一个泛型,例如传入Integer作为泛型
Callable<Integer> callable=()->{return 1;};
第二个

java8中,接口如果只有一个抽象方法声明,则会默认在编译的过程中会加上
@FunctionalInterface注解,相当于隐式的声明
例如

@FunctionalInterface
interface demo{
    int add(int x,int y);
}
第三个

java8中允许在接口内写实现好的方法,但需要加上default声明,实现的方法允许多个,但只能有一个方法声明没有实现。

@FunctionalInterface
interface demo{
    int add(int x,int y);
    //允许多个default声明的方法实现
    default int sub(int x,int y){
        return x - y;
    }
    default int sub_reverse(int x,int y){
        return y - x;
    }
}
第四个

java8中还允许有多个static声明的方法

@FunctionalInterface
interface demo{
    int add(int x,int y);
    //多个static
    static double div(int x,int y){
        return x / y;
    }
    static int muti(int x,int y){
        return x * y;
    }
}
java8内置的四大类型的函数接口

都在java.util.function包下

函数式接口参数列表返回类型用途
Consumer<T>Tvoid对类型T的对象应用和操作,包含方法void accept(T t)
Supplier<T>T返回类型为T的对象,包含方法T get()
Function<T, R>TR对类型T的对象应用和操作并返回结果R,包含方法R apply(T t)以及default声明的实现方法
Predicate<T>Tboolean确定类型T的对象是否满足约束,包含方法boolean test(T t)
Consumer<T>使用案例
//Consumer<T> 消费型接口 :
@Test
public void test1(){
	happy(10000, (m) -> System.out.println("每次消费:" + m + "元"));
} 

public void happy(double money, Consumer<Double> con){
	con.accept(money);
}
Supplier<T>使用案例
//Supplier<T> 供给型接口 :
@Test
public void test2(){
	List<Integer> numList = getNumList(10, () -> (int)(Math.random() * 100));
	for (Integer num : numList) {
		System.out.println(num);
	}
}

//需求:产生指定个数的整数,并放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup){
	List<Integer> list = new ArrayList<>();
	for (int i = 0; i < num; i++) {
		Integer n = sup.get();
		list.add(n);
	}
	return list;
}
Function<T, R>使用案例
//Function<T, R> 函数型接口:
@Test
public void test3(){
	String newStr = strHandler("\t\t\t 尚硅谷威武 ", (str) -> str.trim());
	System.out.println(newStr);
	String subStr = strHandler("尚硅谷威武", (str) -> str.substring(2, 5));
	System.out.println(subStr);
}

//需求:用于处理字符串
public String strHandler(String str, Function<String, String> fun){
	return fun.apply(str);
}
Predicate<T>使用案例
//Predicate<T> 断言型接口:
@Test
public void test4(){
	List<String> list = Arrays.asList("Hello", "atguigu", "Lambda", "www", "ok");
	List<String> strList = filterStr(list, (s) -> s.length() > 3);
	for (String str : strList) {
		System.out.println(str);
	}
}

//需求:将满足条件的字符串,放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre){
	List<String> strList = new ArrayList<>();
	for (String str : list) {
		if(pre.test(str)){
			strList.add(str);
		}
	}
	return strList;
}

同理java8中还内置了截图中的这些函数式接口
具体的内容查看java.util.function包下面的接口内容

2、Stream流----对数据进行操作

创建Stream

方式一、 集合类,实现了Collection类的子类都可以创建集合类,通过集合自带的stream()方法获取

常用的集合类有

1、ArrayList(初始容量10,存满了就会扩容至原先的1.5倍)

2、CopyOnWriteArrayList(解决多线程写入同一个ArrayList异常的解决方案、上个线程还没写入成功就被另一个线程写入。抛出java.util.ConcurrentModificationException异常)
为了让其出java.util.ConcurrentModificationException异常,则在线程类内部加个等待200毫秒,这样必出异常

public static void main(String[] args) {
   List<String> list = new ArrayList<>();

   for (int i = 0; i < 30; i++) {
       new Thread(() -> {
           try {
               TimeUnit.MILLISECONDS.sleep(200);//让其必出异常做的等待
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           list.add(UUID.randomUUID().toString().substring(0, 3));
           System.out.println(list.size()+""+list);
       }, String.valueOf(i)).start();
   }
}

3、LinkedList(链表初始容量0,没有最大容量)
4、Vector(初始容量10、和ArrayList一样的最大值。区别是add方法加了synchronized是线程安全的,但是这种方式对性能损耗太大!线程安全使用CopyOnWriteArrayList解决问题最好,不使用Vector,除非是个小程序不需要考虑性能问题)

5、HashSet(底层是HashMap)
6、CopyOnWriteArraySetCopyOnWriteArrayList一样的作用是为了解决多线程安全问题设置的,原理是(写时复制)

7、HashMap(初始容量1左移4位就是 24=16,最大容量230,超过了则设置为Integer.MAX_VALUE也就是231。看后面截图,容量因子0.75达到了容量因子则会扩容,会扩容到原先的2倍)

Integer.MAX_VALUE=0x7fffffff也就是2的31次方

8、ConcurrentHashMap(和前面一样为了解决java.util.ConcurrentModificationException异常)
9、TreeMap(是一种红黑树,关于TreeMap看到一篇博客,里面有详细讲解https://www.jianshu.com/p/2dcff3634326)

Collections和Collection的区别

Collections是集合的一个工具类
List排序

void reverse(List list): //反转
void shuffle(List list),  //随机排序
void sort(List list),     //按自然排序的升序排序
void sort(List list, Comparator c);  //定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j)  //交换两个索引位置的元素
void rotate(List list, int distance) /*旋转。当distance为正数时, 将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。*/

List搜索和替换

//对List进行二分查找,返回索引,注意List必须是有序的
int binarySearch(List list, Object key)

//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
int max(Collection coll),

//根据定制排序,返回最大元素,排序规则由Comparatator类控制。
//类比int min(Collection coll, Comparator c)
int max(Collection coll, Comparator c)

void fill(List list, Object obj)      //用元素obj填充list中所有元素
int frequency(Collection c, Object o) //统计元素出现次数

/* 统计targe在list中第一次出现的索引,找不到则返回-1, 类比int lastIndexOfSubList(List source, list target).*/
int indexOfSubList(List list, List target)
boolean replaceAll(List list, Object oldVal, Object newVal)// 用新元素替换旧元素。

Collection是一个接口,定义了上面ArrayList这类集合类的接口。对于实现Collection接口,子类都可以使用Collections对集合进行一系列操作。

集合类获取Stream的4种方式
//方式一、通过集合的方式获取,也就是上面记录的
List<String> list=new ArrayList<>();
Stream<String> stream1 = list.stream();

//方式二、Arrays的静态方法stream()获取
Integer[] integers=new Integer[10];
Stream<Integer> stream2 = Arrays.stream(integers);

//方式三、Stream的静态方法of
Stream<Integer> stream3 = Stream.of(12, 13, 14);

//方式四、创建无限流
//1、迭代
Stream<Integer> stream4 = Stream.iterate(0, x -> x + 2);
stream4.limit(10).forEach(System.out::println);//流进行操作,限制流的数量
//2、生成
Stream.generate(()-> Math.random()).limit(5).forEach(System.out::println);
常见的Stream操作
遍历 forEach

Stream中的定义
void forEach(Consumer<? super T> action);
传入的是一个消费型的接口,意思就是遍历集合元素,将集合的每一个元素传给接口,没有返回值。

按序遍历 forEachOrdered

void forEachOrdered(Consumer<? super T> action);

Stream.of("AAA","BBB","CCC").parallel().forEach(s->System.out.println("Output:"+s));
Stream.of("AAA","BBB","CCC").parallel().forEachOrdered(s->System.out.println("Output:"+s);

在这里会发现有一个parallel()方法,该方法是Stream继承于BaseStream接口。通过源码追踪BaseStream又是继承于AutoCloseable接口。可知Stream流是可以自动关闭的。
parallel()方法的作用就是可以对流进行并行处理,
也就是说forEach会并行处理流,那么输出的顺序很可能不会按照:"AAA","BBB","CCC"的顺序输出。
但是forEachOrdered会按照:"AAA","BBB","CCC"顺序输出,
也就是说并行处理下forEach会比forEachOrdered方法效率高

筛选和切片 filter

Stream<T> filter(Predicate<? super T> predicate);
传入一个断言型接口例如获取奇数,过滤偶数
①创建流 ②操作流 ③终止流

//1、创建流
List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);//可以将数字换成对象,然后泛型改成对应的对象即可
//2、操作流
/*可以换成对应的比较,比如 对象::getXXX==某个值 或者>,<这类比较, 使用的是Predicate断言接口,boolean test(T t);。*/
Stream<Integer> integerStream = integers.stream().filter((e) -> e % 2 == 1);

//3、终止流,进行输出
integerStream.forEach(System.out::println);//输出 1 3 5
截断流 limit

Stream<T> limit(long maxSize);
limit会造成短路现象,也就是当limit达到条件时之后的迭代操作就不会生效

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);//可以讲数字换成对象,然后泛型改成对应的对象即可
Stream<Integer> integerStream = integers.stream().filter((e) -> e % 2 == 1).limit(1);//可以换成对应的比较,比如对象.getXXX判断
integerStream.forEach(System.out::println);//只输出 1
跳过 skip

Stream<T> skip(long n);跳过,这个好理解,跳过前面的指定多少条记录

List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5);//可以讲数字换成对象,然后泛型改成对应的对象即可
Stream<Integer> integerStream = integers.stream().filter((e) -> e % 2 == 1).skip(1).limit(1);//可以换成对应的比较,比如对象.getXXX判断
integerStream.forEach(System.out::println);//只输出 3
去重 distinct

Stream<T> distinct();去重

List<Integer> integers = Arrays.asList(1, 1, 2, 3, 4, 5);//可以讲数字换成对象,然后泛型改成对应的对象即可
Stream<Integer> integerStream = integers.stream().filter((e) -> e % 2 == 1).distinct().skip(1).limit(1);//可以换成对应的比较,比如对象.getXXX判断
integerStream.forEach(System.out::println);//只输出 3
映射 map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);
传入一个函数型接口,返回的是一个流
(forEach返回值是void,用起来感觉都是获取集合中的每一个元素,但是map返回值是Stream因此如果你还想对每一个元素进一步的操作则forEach就做不到了)
会将集合的每一个元素都应用到函数式接口的参数上
如果将下面的"aaa","bbb"换成对象,可以在map传入对象::getXXX来将指定的字段提取出来

List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");//可以讲数字换成对象,然后泛型改成对应的对象即可
list.stream().map((str) -> {
            return str.toUpperCase();}
).forEach(System.out::println);
与Map类似的映射还有

通过名称也可以知道下面定义的方法分别是对Int、Long、Double类型的流做的map映射

IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

扁平化流 flatMap

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);将多个流合并成一个大流
例如集合["aaa", "bbb", "ccc", "ddd"]

map映射会变成[ ["aaa"], ["bbb"], ["ccc"], ["ddd"] ]这样的三个子流,如果对子流进行操作,
经过处理后可以变成[ ['a','a','a'], ['b','b','b'], ['c','c','c'], ['d','d','d'] ]这样的一个流,

flatMap映射会变成[ "aaa", "bbb", "ccc", "ddd" ]这样一个流,然后对这样的流进行操作,
经过处理后可以变成['a','a','a','b','b','b','c','c','c','d','d','d']这样的一个流

总结:

mapflatMap两种映射的区别就在于flatMap得到的是一个流,而map得到的是一个嵌套流

实例代码:

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class TestDemo {
    @Test
    public void methood() {
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");//可以讲数字换成对象,然后泛型改成对应的对象即可

		//不是一个嵌套流,而后面的则是一个嵌套的流,区别就在于这里,相当于返回的是['a','a','a','b','b','b','c','c','c','d','d','d']
        Stream<Character> characterStream = list.stream().flatMap(TestDemo::characterStream);
        characterStream.forEach(System.out::println);
    }

    public static Stream<Character> characterStream(String str) {
        List<Character> list = new ArrayList<>();
        for (Character aChar : str.toCharArray()) {
            list.add(aChar);
        }
        return list.stream();
    }
}

执行结果如下

如果使用的是map则需要这样写代码

import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class TestDemo {
    @Test
    public void methood() {
        List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");//可以讲数字换成对象,然后泛型改成对应的对象即可

		//返回的类型是嵌套流,相当于返回的是一个[ ['a','a','a'], ['b','b','b'], ['c','c','c'], ['d','d','d'] ]
        Stream<Stream<Character>> streamStream = list.stream().map(TestDemo::characterStream);
        streamStream.forEach((x)->x.forEach(System.out::println));
    }

    public static Stream<Character> characterStream(String str) {
        List<Character> list = new ArrayList<>();
        for (Character aChar : str.toCharArray()) {
            list.add(aChar);
        }
        return list.stream();
    }
}
和flatMap类似的方法还有

通过名称也可以知道下面定义的方法分别是对Int、Long、Double类型的流做的flatMap映射

IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);

对流中元素排序 sorted

Stream<T> sorted();返回的是一个Stream<T>,在Stream定义的方法中有两个方法,一个无参,另一个传入一个接口,可以通过改接口实现自定义排序规则

Stream<T> sorted();无参的方法是自然排序
Stream<T> sorted(Comparator<? super T> comparator);有参的方法需要传入一个接口

实例代码:
Employee对象代码

public class Employee {

	private int id;
	private String name;
	private int age;
	private double salary;
	private Status status;

	public Employee() {
	}

	public Employee(String name) {
		this.name = name;
	}

	public Employee(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public Employee(int id, String name, int age, double salary) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.salary = salary;
	}

	public Employee(int id, String name, int age, double salary, Status status) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.salary = salary;
		this.status = status;
	}

	public Status getStatus() {
		return status;
	}

	public void setStatus(Status status) {
		this.status = status;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getSalary() {
		return salary;
	}

	public void setSalary(double salary) {
		this.salary = salary;
	}

	public String show() {
		return "测试方法引用!";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + id;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		long temp;
		temp = Double.doubleToLongBits(salary);
		result = prime * result + (int) (temp ^ (temp >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Employee other = (Employee) obj;
		if (age != other.age)
			return false;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + ", status=" + status
				+ "]";
	}

	public enum Status {
		FREE, BUSY, VOCATION;
	}

}
//集合对象
 List<Employee> emps = Arrays.asList(
         new Employee(102, "李四", 59, 6666.66),
         new Employee(101, "张三", 18, 9999.99),
         new Employee(103, "王五", 28, 3333.33),
         new Employee(104, "赵六", 8, 7777.77),
         new Employee(104, "赵六", 8, 7777.77),
         new Employee(104, "赵六", 8, 7777.77),
         new Employee(105, "田七", 38, 5555.55)
 );
 emps.stream().sorted((emp1,epm2)->{
         return emp1.getAge()-epm2.getAge();//得到年龄升序
 }).forEach(System.out::println);
/** 控制台输出 得到年龄升序的流 Employee [id=104, name=赵六, age=8, salary=7777.77, status=null] Employee [id=104, name=赵六, age=8, salary=7777.77, status=null] Employee [id=104, name=赵六, age=8, salary=7777.77, status=null] Employee [id=101, name=张三, age=18, salary=9999.99, status=null] Employee [id=103, name=王五, age=28, salary=3333.33, status=null] Employee [id=105, name=田七, age=38, salary=5555.55, status=null] Employee [id=102, name=李四, age=59, salary=6666.66, status=null] */
匹配 allMatch 和 anyMatch

boolean allMatch(Predicate<? super T> predicate);所有元素都要符合条件才返回true
boolean anyMatch(Predicate<? super T> predicate);只要有一个元素符合条件就返回true
boolean noneMatch(Predicate<? super T> predicate);没有匹配到元素才返回true
传入一个断言型接口

查找 findFirst 和 findAny

Optional<T> findFirst();返回第一个元素,第一个元素可能没有,因此会将结果放在Optional中需要的时候通过Optional实例的get()方法获取
Optional<T> findAny();返回任意一个元素,具体返回的元素得通过get获取

//集合对象
List<Employee> emps = Arrays.asList(
		new Employee(102, "李四", 59, 6666.66, Status.BUSY),
		new Employee(101, "张三", 18, 9999.99, Status.FREE),
		new Employee(103, "王五", 28, 3333.33, Status.VOCATION),
		new Employee(104, "赵六", 8, 7777.77, Status.BUSY),
		new Employee(104, "赵六", 8, 7777.77, Status.FREE),
		new Employee(104, "赵六", 8, 7777.77, Status.FREE),
		new Employee(105, "田七", 38, 5555.55, Status.BUSY)
);
Optional<Employee> op = emps.stream()
			.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
			.findFirst();
Optional<Employee> op2 = emps.parallelStream()
			.filter((e) -> e.getStatus().equals(Status.FREE))
			.findAny();
总数 count

计算出流中元素的个数
long count();

实例代码:

Long count = emps.stream().count();//返回元素个数
最大值 max

得到集合中最大的元素
Optional<T> max(Comparator<? super T> comparator);

实例代码:

emps.stream().max((e1,e2)->Double.compare(e1.getSalary(),e2.getSalary()))
最小值 min

得到集合中最小的元素
Optional<T> min(Comparator<? super T> comparator);

实例代码:

emps.stream().map(Employee::getSalary).min(Double::compare)
归约 reduce

将集合的元素进行结合起来得到一个新值(可以是求和、平均数 等操作)
T reduce(T identity, BinaryOperator<T> accumulator);

Optional<T> reduce(BinaryOperator<T> accumulator);

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

收集 collect

collect的作用是将流转换成其他形式的数据,例如集合类的数据。
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);

<R, A> R collect(Collector<? super T, A, R> collector);

Collector是一个接口 、一般可以直接使用同包下的实现类Collectors完成一些简单的操作、
Collectors是一个工具类(里面有很多public static方法)、常用的操作有
流转常用集合的方法toSet()、toList()、toMap()、toConcurrentMap()、等
得到一些自己想要的对象toCollection()
求和summingInt()、summingLong()、summingDouble()
平均值averagingInt()、averagingLong()、averagingDouble()
分组groupingBy()
分区partitioningBy()
最小值minBy()
最大值maxBy()
计数counting()
过滤filtering()
map 映射mapping()
flatMap 映射flatMapping()
连接字符串joining()
聚合操作reducing()

Optional容器

封装的方法,jdk14环境的截图

通过Optional.of(T value)获取一个Optional容器实例,传入的示例不能为null
源码:

public static <T> Optional<T> of(T value) {
    return new Optional<>(Objects.requireNonNull(value));
}

通过Optional.ofNullable(T value)获取一个可以为 null的容器,如果传入一个null然后调用get()会抛出没有元素异常,而of会直接抛空指针异常 java.lang.NullPointerException

public static <T> Optional<T> ofNullable(T value) {
    return value == null ? (Optional<T>) EMPTY
                         : new Optional<>(value);
}

通过Optional.empty()获取一个空的容器

public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

ifPresent(Consumer<? super T> action)传一个消费型接口,如果容器中有元素,则执行传入的消费者函数,没有则什么也不做。

public void ifPresent(Consumer<? super T> action) {
    if (value != null) {
        action.accept(value);
    }
}

orElse(T other)如果Optional容器中有元素,则返回容器内容,如果没有元素则返回传入的对象实例;看源码很容易明白,前面也一样直接看源码就能理解方法意思

public T orElse(T other) {
    return value != null ? value : other;
}

orElse(T other)类似,只是这里传入的是一个生产者类型的接口,因此这里判断如果有元素则返回容器元素,没有则返回生产者提供的对象实例;

public T orElseGet(Supplier<? extends T> supplier) {
    return value != null ? value : supplier.get();
}

map(Function<? super T, ? extends U> mapper)如果没有值,则调用empty()返回一个空的map,如果有值则返回一个包装好的Optional容器

public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent()) {
        return empty();
    } else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

3、全新的日期时间 API

以前的日期通过java.util包下的Date类获取,该类是jdk1.1时就有的,大部分的方法都已经过时,并且对于日期格式化的类在java.text包下通过DateSimpleFormat类进行日期格式化。相当不规范。并且这个日期格式化类时线程不安全的。
java8中对日期操作的类都来自java.time包下
专门用来获取日期的用LocalDate
专门用来获取时间的用LocalTime
专门用来获取日期和时间的用LocalDateTime
专门获取时间戳的用Instant

获取自定义的日期和时间

格式化的格式时间
LocalDateTime localDateTime=LocalDateTime.now();//获取本地时间示例
DateTimeFormatter dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");//格式化成这种形式的时间
String formatedDate = dateTimeFormatter.format(localDateTime);//格式化的时间
System.out.println(formatedDate);//打印时间
获取日期

下面这种获得日期得到的格式时 yyyy-MM-dd

LocalDate localDate=LocalDate.now();//获取本地时间,格式为 yyyy-MM-dd
System.out.println(localDate);//打印时间
获取时间
LocalTime localTime=LocalTime.now();//获取时间
String timeString = localTime.toString().substring(0, 8);//默认输出的时间格式会把纳秒也会输出,所以这里进行了截取,得到HH:mm:ss
System.out.println(timeString);//打印时间
获取 秒(常做时间戳)、毫秒、纳秒
Instant instant = Instant.now();//获取时间戳
System.out.println(instant.getEpochSecond());//打印秒
System.out.println(instant.toEpochMilli());//打印时间戳毫秒
System.out.println(instant.getNano());//返回当前时间的纳秒部分 1秒=10^9纳秒 也就是说长度始终时9位以内的数字

除了上面的方法,类中还封装了对时间的加 plusXXX() 命名的方法, 减 minusXXX() 命名的方法操作。以及getYear()等方法。

计算两个时间的间隔

Duration
Duration中有加减乘除的方法,分别用plusXXX()、minuxXXX()、multipliedBy()、dividedBy()等方法。其它方法通过看源码即可。

Instant instant1=Instant.now();//获取前一个时间戳
try {
    TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
    e.printStackTrace();
}
Instant instant2=Instant.now();//获取后一个时间戳
Duration between = Duration.between(instant1, instant2);
System.out.println(between.getSeconds()); //打印间隔秒
System.out.println(between.toMillis());   //打印间隔毫秒
System.out.println(between.getNano());    //打印间隔纳秒
时间校正器

使用到了一个函数接口TemporalAdjuster
通过时间.withXXX()传入函数式接口,进行自定义时间规则。
自定义时间规则

LocalDate now = LocalDate.now();
//获取下一个工作日的时间
LocalDate nextWorkDay = now.with((l) -> {
    LocalDate ldt = (LocalDate) l;//强转
    if (ldt.equals(DayOfWeek.FRIDAY)) {
        return ldt.plusDays(3);
    } else if (ldt.equals(DayOfWeek.SATURDAY)) {
        return ldt.plusDays(2);
    } else {
        return ldt.plusDays(1);
    }
});
System.out.println(nextWorkDay);//打印下一个工作日时间

4、注解的改进

可重复注解

java8中支持重复注解,就是可以在同一个方法或者类上使用同一个注解
例如:

@MyAnnotation("aaa")
@MyAnnotation("bbb")
public void test(){
    
}

要想使用可重复注解需要有一个注解容器
例如:
这里的MyAnnotations里面存的就是注解的类型MyAnnotation数组

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[] value();
}

MyAnnotation

import java.lang.annotation.*;
@Repeatable(MyAnnotations.class)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
      String  value() default "demo";
}
类型注解

类型注解可以注解在String、Integer等任意类型。
比如对类型变量进行校验@NonNull

新增异步编程类:CompletableFuture

CompletableFuture的api介绍和案例

相关文章