超详细的 Java 8 新特性讲解

Java 8 新特性

  1. lambda表达式
  2. 四大内置核心函数式接口
  3. 方法引用与构造引用、数组引用
  4. steam API
  5. Optional 类
  6. 接口中的默认方法与静态方法
  7. 新时间日期
  8. 重复注解与类型注解

简介

  • 速度更快
    如:HashMap

  • Java 8之前是采用数组+链表的方式:

    根据对象的hashCode算出对象要放到数组对应的下标,不可避免会出现hash冲突,就会形成一个下标位置存放很多元素,就形成了链表,但链表中元素很多时,查询起来就很慢。

  • Java 8之后是采用数组+链表+红黑树的方式:

在这里插入图片描述
由于红黑树其中有个特点(右子树上所有结点的值均大于或等于它的根结点的值),这样在查询的时候,就比遍历一个很大的链表的速度快很多。

  • 代码更少(增加了新的语法lambda表达式)
  • 强大的Steam API
  • 便于并行
  • 最大化减少空指针异常Optional

一、Lambda表达式初式

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁灵活的代码.作为一种更紧凑的代码风格,使JAVA的语言表达能力得到了提升.

对比用与不用lambda表达式的效果:

需求:获取当前公司中年龄大于38岁的员工

//案例中所用到的员工对象
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Employee {

    private String name;
    private int age;
    private double salary;

}
	/**
 	* 函数式接口
	 */
	public interface MyPredicate<T> {
    	public Boolean filterEmployee(T t);
	}

	/**
	 * 测试数据
	 */
	List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.999),
            new Employee("李四", 45, 8888.999),
            new Employee("王五", 58, 7777.999),
            new Employee("赵六", 25, 3333.999),
            new Employee("田七", 13, 5555.999)
    );

	/**
	 * 员工过滤策略
	 */
	public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> predicate) {
        List<Employee> emps = new ArrayList<>();
        for (Employee employee : list) {
            if (predicate.filterEmployee(employee)) {
                emps.add(employee);
            }
        }
        return emps;
    }
  • 未使用Lambda表达式
 	/**
     * 原来的匿名内部类
     */
    @Test
    public void test01(){
        Comparator<Integer> com = new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1,o2);
            }
        };
        TreeSet<Integer> ts = new TreeSet<>(com);
    }

	//-------------------------方式一-------------------------------------
	
    public List<Employee> filterEmp(List<Employee> list) {//此方法扩展性不好
        List<Employee> employeeList = new ArrayList<>();
        for (Employee emp : list) {
            if (emp.getSalary() >= 6000) {
                employeeList.add(emp);
            }
        }
        return employeeList;
    }

    @Test
    public void test03() {
        List<Employee> employeeList = filterEmp(employees);
        for (Employee emp:employeeList) {
            System.out.println(emp.toString());
        }
    }

	//-------------------------方式二-------------------------------------
	@Test
    public void Test05(){
        List<Employee> employeeList = filterEmployee(this.employees, new MyPredicate<Employee>() {
            @Override
            public Boolean filterEmployee(Employee employee) {
                return employee.getSalary() >= 6000;
            }
        });
        for (Employee employee:employeeList) {
            System.out.println(employee);
        }
    }
  • 使用Lambda表达式
/**
     * Lambda表达式基础用法
     */
    @Test
    public void test02(){
        Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
        TreeSet<Integer> ts = new TreeSet<>(com);
    }
    
    //-------------------------方式一Lambda表达式-------------------------------------
    @Test
    public void test04() {
        List<Employee> list = filterEmployee(this.employees, e -> e.getSalary() >= 6000);
        list.forEach(System.out::println);
    }

	//-------------------------方式二Steam API-------------------------------------
	@Test
    public void test06(){
        this.employees.stream().filter(e -> e.getSalary() >= 6000).forEach(System.out::println);
    }

Lambda 表达式基础语法

Java8中引入了一个新的操作符“->”该操作符成为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式分成两部分:
左侧:Lambda表达式的参数列表
右侧:Lambda表达式中要执行的功能

下面介绍Lambda表达式格式:

格式一:无参数,无返回值 (如:Runnable函数式接口)
语法:()-> {System.out.println("Hello Lambda")}

    public void test1(){
        //final int num = 1; //jdk1.7之前必须显示的用final标识
        int num = 1; //jdk1.8之后不用final标识,但是是隐式的。
        
        //匿名内部类
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello Lambda"+num);
            }
        };
        r1.run();


        //Lambda表达式
        Runnable r2 = () -> {
            System.out.println("Hello Lambda"+num);
        };
        r2.run();
    }
格式二:有一个参数,无返回值 (如:Consumer函数式接口)
语法:(e)-> {System.out.println(e)}

    public void test2(){
        Consumer<String> con1 = (e) -> { System.out.println(e);};
        //或
        Consumer<String> con2 = e -> System.out.println(e);// 只有一个参数时,可以不写"()",Lambda体中只有一条语句时,“{}”也可以省略不写
        con1.accept("Hello Lambda");
    }
格式三:有多个参数,并且有返回值
语法:Comparator<Integer> com = (x,y) ->{ return Integer.compare(x,y); };
	 Comparator<Integer> com = (x,y) -> Integer.compare(x,y); //Lambda体中只有一条语句时可以省略“{}”和return
public void  test3(){
        Comparator<Integer> com = (x,y) ->return Integer.compare(x,y);
        System.out.println(com.compare(3, 2));
    }
练习:
	1. 调用Collections.sort()方法,通过定制排序比较两个Employee(先按年龄比,年龄相同按姓名比),使用Lambxa作为参数传递。
	
	public void test4() {//employees 为本文上面测试数据
        Collections.sort(employees, (x, y) -> { // 两个参数x,y就代表Employee对象,通过上下文推断而来。
            if (x.getAge() == y.getAge()) {
                return x.getName().compareTo(y.getName());
            } else {
                return Integer.compare(x.getAge(), y.getAge());
            }
        });
        for (Employee emp : employees) {
            System.out.println(emp);
        }
    }
    
	2.①声明函数式接口,接口中声明抽象方法,public String getValue(String str);
      ②声明类 TestLambda ,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值。
      ③再将一个字符串的第2个和第4个索引位置进行截取子串。
     //函数式接口
	@FunctionalInterface  
	public interface MyInterfaceFunction {
    	public String getValue(String str);
	}

	public String handlerStr(String str,MyInterfaceFunction mff){
        return mff.getValue(str);
    }

	public void test5(){
        String str = "hello,lambda";
        String s = handlerStr(str, e -> e.toUpperCase());
        System.out.println(s);

        String s1 = handlerStr(str, e -> e.substring(2, 4));
        System.out.println(s1);
    }

	3.①声明一个带两个泛型的函数式接口,泛型类型为<T,R> T为参数,R为返回值,
	  ②接口中声明对应抽象方法
      ③在 TestLambda 类中声明方法,使用接口作为参数,计算两个long型参数的和。
      ④再计算两个 long型参数的乘积。
	
	//函数式接口
	@FunctionalInterface
	public interface MyFunction<T,R> {
    	public R getValue(T t1,T t2);
	}

	public Long operation(Long a,Long b,MyFunction<Long,Long> mf){
        return mf.getValue(a,b);
    }

	public void test6(){
        Long sum = operation(20L, 30L, (e1, e2) ->  e1 + e2 );
        System.out.println(sum);

        Long mul = operation(2L, 5L, (e1, e2) -> e1 * e2);
        System.out.println(mul);
    }

二、 四大内置核心函数式接口

1、Consumer : 消费型接口 void accept(T t);

public void happy(double money, Consumer<Double> con){
        con.accept(money);
    }

public void test1(){
        happy(125.5,e -> System.out.println("消费了"+e+"元,购买了一本《Java编程思想》"));
    }

2、Supplier : 供给型接口 T get();

public List<Integer> getNumList(int num,Supplier<Integer> supplier){
        List<Integer>  list = new ArrayList<>();
        for (int i = 0;i<=num;i++){
            Integer n = supplier.get();
            list.add(n);
        }
        return list;
    }


public void test2(){
    List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
    for (Integer num:numList) {
        System.out.println(num);
    }
}

3、Function<T, R> : 函数式接口 R apply(T t);

public String strHandler(String str,Function<String,String> fun){
        return fun.apply(str);
    }

public void test3(){
        String str = strHandler("abcdefg", e -> e.substring(2, 5));
        System.out.println(str);
    }

4、Predicate : 断言型接口 boolean test(T t);

public List<String> predicate(List<String> list,Predicate<String> pre){
        List<String> strList = new ArrayList<>();
        for (String str:list) {
            if(pre.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }

    public void test4(){
        List<String> list = Arrays.asList("hello","Lambda","vue","rabbit","mysql");
        List<String> predicate = predicate(list, e -> e.length() > 4);
        for (String str:predicate) {
            System.out.println(str);
        }
    }

三、 方法引用与构造引用

方法引用:若 Lambda 体中的内容有方法已经实现了,我们可以使用"方法引用"(可以理解为方法引用是Lambda表达式的另外一种表现形式)

方法引用主要有三种语法格式:

注意点:需要实现接口中方法的参数列表和返回值类型必须和Lambda体中方法的参数列表和返回值类型保持一致。

1、对象::实例方法名

	/**
     *  void accept(T t) 函数式接口中的方法,
     *  public void println(String x) Lambda体中的方法
     */
    @Test
    public void test1(){
        Consumer<String> con1 = e -> System.out.println(e);

        Consumer<String> con2 = System.out::println;//实例方法引用

		Employee employee = new Employee();
        Supplier<String> supplier = employee::getName;//实例方法引用
        String str = supplier.get();
        System.out.println(str);
    }

2、类::静态方法名

public void test3(){
        Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);

        Comparator<Integer> com2 = Integer::compareTo;//静态方法引用
        int compare = com2.compare(3, 5);
        System.out.println(compare);
    }

3、类::实例方法名

//规则:若Lambda参数列表的第一个参数,是实例方法的调用者,第二个参数是实例方法的参数时,可以使用此类::实例方法名
public void test4(){
        BiPredicate<String,String> bp1 = (x,y) -> x.equals(y);
        BiPredicate<String,String> bp2 = String::equals;
    }

构造引用

格式:类::new
规则:需要调用的构造器参数列表要与函数式接口中方法的参数列表保持一致
public void test5(){
        Supplier<Employee> sup1 = () -> new Employee();
        Employee employee1 = sup1.get();
        System.out.println(employee1);

        Supplier<Employee> sup2 = Employee::new;
        Employee employee2 = sup2.get();
        System.out.println(employee2);

        BiFunction<Integer,Double,Employee> fun = Employee::new;
        Employee employee = fun.apply(19, 45.43);
        System.out.println(employee);
    }

数组引用

格式:Type[]::new
public void test6(){
        Function<Integer,String[]> fun1 = e -> new String[e];
        String[] arr1 = fun1.apply(10);
        System.out.println(arr1.length);

        Function<Integer,String[]> fun2 = String[]::new;
        String[] arr2 = fun2.apply(20);
        System.out.println(arr2.length);
    }

四、Steam API

  • 1、了解Stream
    Java8中有两大最为重要的改变。第一个是 Lambda表达式;另外一个则是 Stream API (java.util.stream.*)。Stream是 Java8中处理集合的关键抽象概念,它可以指疋孙们至集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用 SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。

在这里插入图片描述

  • 2、什么是Stream
    是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算!”
    注意:
    (1)Stream自己不会存储元素。
    (2)Stream不会改变源对象。相反,他们会返回一个持有结果的新Sttream。
    (3)Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

  • 3、Stream操作的三个步骤

以下操作用到的源数据
List<Employee> employees = Arrays.asList(
            new Employee("张三", 18, 9999.999, Employee.Status.BUSY),
            new Employee("李四", 45, 8888.999, Employee.Status.FREE),
            new Employee("王五", 58, 7777.999, Employee.Status.BUSY),
            new Employee("赵六", 25, 3333.999, Employee.Status.VOCAITON),
            new Employee("田七", 13, 5555.999, Employee.Status.BUSY),
            new Employee("田七", 13, 5555.999, Employee.Status.VOCAITON),
            new Employee("田七", 13, 5555.999, Employee.Status.FREE),
            new Employee("田七", 13, 5555.999, Employee.Status.BUSY)
    );

(1)创建Stream(流)
一个数据源(如:集合、数组),获取一个流。

public void test01(){
        /**
         * 创建流(Stream)方式:
         */
        //1、通过Collection 系列集合提供的Stream()或parallelStream()
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
        Stream<String> stream2 = list.parallelStream();//并行流

        //2、通过Arrays 中的静态方法stream()获取数组流
        Employee[] emps = new Employee[10];
        Stream<Employee> stream3 = Arrays.stream(emps);

        //3、通过Stream类中的静态方法of()
        Stream<String> stream4 = Stream.of("aa", "bb", "cc", "dd");

        //4、创建无限流
        //迭代
        Stream<Integer> stream5 = Stream.iterate(0, (e) -> e + 2);
        stream5.limit(10).forEach(System.out::println);

        //生成
        Stream.generate(() -> Math.random())
                .limit(5)
                .forEach(System.out::println);
    }

(2)中间操作
一个中间操作链,对数据源的数据进行处理。

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

  • 筛选与切片在这里插入图片描述
public void test1(){
        employees.stream().filter(e -> e.getAge()>10)
                .limit(8)
                .skip(1)
                .distinct()
                .filter(e -> e.getAge()>15)
                .forEach(System.out::println);
    }
  • 映射
    在这里插入图片描述
public void test2(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");

        list.stream()
                .map((e) -> e.toUpperCase())
                .forEach(System.out::println);

        System.out.println("==================================");

        employees.stream()
                .map(Employee::getName)
                .forEach(System.out::println);

        System.out.println("==================================");

        Stream<Stream<Character>> stream = list.stream()
                .map(TestSteamAPI02::filterChatacter);//{{a,a,a},{b,b,b},{c,c,c}}
        stream.forEach((sm) -> {
            sm.forEach(System.out::println);
        });

        System.out.println("==================================");

        Stream<Character> characterStream = list.stream()
                .flatMap(TestSteamAPI02::filterChatacter);//{a,a,a,b,b,b,c,c,c}
        characterStream.forEach(System.out::println);

    }

    public static Stream<Character> filterChatacter(String str){
        List<Character> list = new ArrayList<>();
        for (Character li:str.toCharArray()) {
            list.add(li);
        }
        return list.stream();
    }
  • 排序
    在这里插入图片描述
public void test3(){
        List<String> list = Arrays.asList("ddd","aaa","ccc","eee");
        list.stream()
                .sorted()//自然排序
                .forEach(System.out::println);

        System.out.println("==================================");

        employees.stream()
                .sorted((e1,e2) -> { //自定义排序
                    if(e1.getAge()==e2.getAge()){
                        return e1.getName().compareTo(e2.getName());
                    }else {
                        return Integer.compare(e1.getAge(),e2.getAge());
                    }
                })
                .forEach(System.out::println);
    }

(3)终止操作
一个终止操作,执行中间操作链,并产生结果。

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是void 。

  • 查找与匹配

    在这里插入图片描述
public void test5(){
		//allMatch()
        boolean b1 = employees.stream().allMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b1);

		//anyMatch()
        boolean b2 = employees.stream().anyMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b2);

		//noneMatch()
        boolean b3 = employees.stream().noneMatch(e -> e.getStatus().equals(Employee.Status.BUSY));
        System.out.println(b3);

		//findFirst()
        Optional<Employee> op = employees.stream().sorted((e1, e2) ->
                Double.compare(e1.getSalary(), e2.getSalary())
        ).findFirst();
        System.out.println(op.get());

		//findAny()
        Optional<Employee> any = employees.parallelStream()
                .filter((e) -> e.getStatus().equals(Employee.Status.FREE))
                .findAny();
        System.out.println(any.get());

		//count():返回流中元素的总个数
        long count = employees.stream().count();
        System.out.println(count);

        //max():返回流中最大值
        Optional<Employee> op1 = employees.stream()
                .max((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
        System.out.println(op1.get());

        //min():返回流中最小值
        Optional<Double> op2 = employees.stream()
                .map(Employee::getSalary)
                .min(Double::compare);
        System.out.println(op2.get());

    }
  • 归约
    在这里插入图片描述
public void test6(){
        //归约,可以将流中的元素反复结合起来,得到一个值
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(reduce);//55

        Optional<Double> op = employees.stream().map(Employee::getSalary).reduce(Double::sum);//对象::实例方法名
        System.out.println(op.get());
    }
  • 收集
    在这里插入图片描述
    Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
public void test7(){
        //将Employee集合对象中员工的名字收集起来,装到指定的集合中去
        List<String> list = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());//将收集到的结果装到list集合中
        list.forEach(System.out::println);

        System.out.println("=================================");

        Set<String> set = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());//将收集到的结果装到set集合中
        set.forEach(System.out::println);

        System.out.println("=================================");

        HashSet<String> hashSet = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));//将收集到的结果装到set集合中
        hashSet.forEach(System.out::println);

        System.out.println("=================================");

        //员工总数
        Long count = employees.stream()
                .collect(Collectors.counting());
        System.out.println(count);

        System.out.println("=================================");

        //员工工资平均值
        Double avg = employees.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);

        System.out.println("=================================");

        //员工工资总和
        Double sum = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);

        System.out.println("=================================");

        //工资最高的员工信息
        Optional<Employee> max = employees.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(max.get());

        System.out.println("=================================");

        //员工工资的最小值
        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));

        System.out.println(min.get());

        System.out.println("=================================");

        //按照员工状态分组
        Map<Employee.Status, List<Employee>> group = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(group);

        System.out.println("=================================");

        //多级分组 先按照员工状态分,再按员工年龄分

        Map<Employee.Status, Map<String, List<Employee>>> map = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus, Collectors.groupingBy(e -> {
                    if (e.getAge() <= 35) {
                        return "青年";
                    } else if (e.getAge() <= 50) {
                        return "中年";
                    } else {
                        return "老年";
                    }
                })));
        System.out.println(map);

        System.out.println("=================================");

        //分区 满足条件的一个区,不满足条件的一个区
        Map<Boolean, List<Employee>> collect = employees.stream()
                .collect(Collectors.partitioningBy(e -> e.getSalary() > 5000));
        System.out.println(collect);

        System.out.println("=================================");

        //连接 将收集到的元素连接起来
        String str = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(",","<",">"));//joining()中有三个参数:(1)-分隔符 (2)-前缀 (3)-后缀
        System.out.println(str);

        System.out.println("=================================");

        //总结
        DoubleSummaryStatistics summaryStatistics = employees.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(summaryStatistics.getCount()); //员工总数
        System.out.println(summaryStatistics.getMax());//最高工资
        System.out.println(summaryStatistics.getMin());//最低工资
        System.out.println(summaryStatistics.getAverage());//工资平均值
        System.out.println(summaryStatistics.getSum());//工资总和
    }

五、Optional 类

Optional<T〉类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用null表示一个值不存在,现在 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 void test1(){
        Optional<Employee> op = Optional.of(new Employee());//Optional.of(null)注意此处参数为null是汇报NullPointerException
        Employee employee = op.get();
        System.out.println(employee);
    }

    public void test2(){
        Optional<Employee> op = Optional.empty();
        Employee employee = op.get();//此处会报NullPointerException异常,Optional容器中没有值
        System.out.println(employee);
    }

    public void test3(){
        Optional<Employee> op = Optional.ofNullable(new Employee());
//        Optional<Employee> op = Optional.ofNullable(null);//跟Optional.of(null)不同,此处不会报NullPointerException异常
//        Employee employee = op.get();//此处会报NullPointerException异常,Optional容器中没有值

//        if (op.isPresent()){ //判断容器中是否包涵值,包涵返回true,否则返回false
//            System.out.println(op.get());
//        }

//        Employee employee = op.orElse(new Employee("赵六", 29, 9875.55, Employee.Status.FREE));//如果调用对象包含值,返回该值,否则返回Employee
//        System.out.println(employee);

        Employee employee =
                op.orElseGet(() -> new Employee("liji", 29, 8875.55, Employee.Status.BUSY));//如果调用对象包含值,返回该值,否则返回Supplier s 获取的值,相比orElse(),orElseGet操作更为灵活
        System.out.println(employee);
    }

    public void test4(){
        Optional<Employee> op = Optional.of(new Employee("liji", 29, 8875.55, Employee.Status.BUSY));
        Optional<String> str = op.map(Employee::getName);
        System.out.println(str.get());
    }

    public void test5(){
        Optional<Employee> op = Optional.of(new Employee("liji", 29, 8875.55, Employee.Status.BUSY));
        Optional<String> str = op.flatMap(e -> Optional.of(e.getName()));
        System.out.println(str.get());
    }

六、接口中的默认方法与静态方法

接口默认方法的”类优先”原则

  • 若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时选择父类中的方法。如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
  • 接口冲突。如果一企父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突

七、新时间日期

使用LocalDate、LocalTime、 LocalDateTime

  • LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,分别表示使用ISO-8601日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息。也不包含与时区相关的信息。

     以下通过LocalDataTime讲解Java8 新时间日期特性,另外两个操作都一样,只是LocalData是操作日期、LocalTime是操作时间。
    

1、获取LocalDataTime实例:

public void test1(){
        LocalDateTime ldt1 = LocalDateTime.now();//获取当前系统时间
        System.out.println(ldt1);//2020-11-08T16:54:15.762

        LocalDateTime ldt2 = LocalDateTime.of(2020, 11, 8, 16, 54, 30);//通过of()方法指定年月日时分秒获取LocalDataTime实例
        System.out.println(ldt2);//2020-11-08T16:54:30
    }

2、对日期时间进行计算:

public void test2(){
        public void test2(){
        LocalDateTime ldt1 = LocalDateTime.now();//获取当前系统时间
        System.out.println("加2天:"+ldt1.plusDays(2));
        System.out.println("加2小时:"+ldt1.plusHours(2));
        System.out.println("加10分钟:"+ldt1.plusMinutes(10));
        System.out.println("加1个月:"+ldt1.plusMonths(1));
        System.out.println("加100纳秒:"+ldt1.plusNanos(100));
        System.out.println("加20秒:"+ldt1.plusSeconds(20));
        System.out.println("加1星期:"+ldt1.plusWeeks(1));
        System.out.println("加2年:"+ldt1.plusYears(2));

        System.out.println("减2天:"+ldt1.minusDays(2));
        System.out.println("减2小时:"+ldt1.minusHours(2));
        System.out.println("减10分钟:"+ldt1.minusMinutes(10));
        System.out.println("减1个月:"+ldt1.minusMonths(1));
        System.out.println("减100纳秒:"+ldt1.minusNanos(100));
        System.out.println("减20秒:"+ldt1.minusSeconds(20));
        System.out.println("减1星期:"+ldt1.minusWeeks(1));
        System.out.println("减2年:"+ldt1.minusYears(2));

        System.out.println("获取当月的记号:"+ldt1.getDayOfMonth());
        System.out.println("获取当天是星期几:"+ldt1.getDayOfWeek());
        System.out.println("获得一年中的一天:"+ldt1.getDayOfYear());
        System.out.println("获取月份:"+ldt1.getMonth());//返回的是Month对象
        System.out.println("获取月份:"+ldt1.getMonthValue());
        System.out.println("获取年份:"+ldt1.getYear());
        System.out.println("获取小时:"+ldt1.getHour());
        System.out.println("获取分钟:"+ldt1.getMinute());
        System.out.println("获取秒:"+ldt1.getSecond());
        System.out.println("获取纳秒:"+ldt1.getNano());
   		 }
    }

3、Instant:时间戳(以Unix 元年:1970年1月1日 00:00:00 到某个时间的毫秒值)

public void test3(){
        Instant ins1 = Instant.now();
        System.out.println(ins1);//默认获取UTC时区

        OffsetDateTime odt = ins1.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);//2020-11-08T21:22:31.265+08:00

        long milli = ins1.toEpochMilli();//转为时间戳
        System.out.println(milli);

        Instant second = Instant.ofEpochSecond(60);
        System.out.println(second);
    }

4、Duration:计算两个“时间”之间的间隔

public void test4() throws InterruptedException {
        //Duration:计算两个“时间”之间的间隔
        Instant ins1 = Instant.now();
        Thread.sleep(1000);
        Instant ins2 = Instant.now();
        Duration duration1 = Duration.between(ins1, ins2);
        System.out.println(duration1.toMillis());

        LocalTime lt1 = LocalTime.now();
        Thread.sleep(1000);
        LocalTime lt2 = LocalTime.now();
        Duration duration2 = Duration.between(lt1, lt2);
        System.out.println(duration2.toMillis());
        System.out.println(duration2.getSeconds());//间隔分
        System.out.println(duration2.getNano());//间隔纳秒
    }

5、Period:计算两个“日期”之间的间隔

public void test5(){
        //Period:计算两个“日期”之间的间隔
        LocalDate ld1 = LocalDate.of(2020,8,2);
        LocalDate ld2 = LocalDate.now();
        Period period = Period.between(ld1, ld2);
        System.out.println(period.getYears());//间隔年
        System.out.println(period.getMonths());//间隔月
        System.out.println(period.getDays());//间隔日
    }

6、日期的操作

  • TemporalAdjuster :时间校正器。有时我们可能需要获取例如:将日期调整到“下个周日”等操作。

    TemporalAdjusters:该类通过静态方法提供了大量的常用TemporalAdjuster的实现。

public void test6() {
        //获取下周四
        LocalDate ld = LocalDate.now();
        LocalDate localDate = ld.withDayOfYear(365);//获取这一年中的第365天的日期
        System.out.println(localDate);//2020-12-30

        LocalDate localDate1 = ld.withDayOfMonth(23);//获取这一月中的第23天的日期
        System.out.println(localDate1);//2020-11-23

        LocalDate localDate2 = ld.withMonth(8);//获取当年指定月份的日期
        System.out.println(localDate2);//2020-08-08

        LocalDate localDate3 = ld.withYear(2018);//获取指定年份的日期
        System.out.println(localDate3);//2018-11-08

        LocalDate with = ld.with(TemporalAdjusters.firstDayOfMonth());//一个月中的第一天
        System.out.println(with);

        LocalDate with1 = ld.with(TemporalAdjusters.firstDayOfNextYear());//下一年中的第一天
        System.out.println(with1);

        LocalDate with2 = ld.with(TemporalAdjusters.firstDayOfNextMonth());//下一月中的第一天
        System.out.println(with2);

        LocalDate with3 = ld.with(TemporalAdjusters.firstDayOfYear());//这年中的第一天
        System.out.println(with3);

        LocalDate with4 = ld.with(TemporalAdjusters.lastDayOfMonth());//这个月的最后一天
        System.out.println(with4);

        LocalDate with5 = ld.with(TemporalAdjusters.lastDayOfYear());//这年中的最后一天
        System.out.println(with5);

        LocalDate with6 = ld.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));//获取前一个星期一
        System.out.println(with6);

        LocalDate with7 = ld.with(TemporalAdjusters.next(DayOfWeek.MONDAY));//获取下一个星期一
        System.out.println(with7);

        LocalDateTime ldt = LocalDateTime.now();
        //自定义
        LocalDateTime with8 = ldt.with(e -> {
            LocalDateTime lt = (LocalDateTime) e;
            DayOfWeek dow = lt.getDayOfWeek();
            if (dow.equals(DayOfWeek.FRIDAY)) {
                return lt.plusDays(3);
            } else if (dow.equals(DayOfWeek.SATURDAY)) {
                return lt.plusDays(2);
            } else {
                return lt.plusDays(1);
            }
        });
        System.out.println(with8);
    }
  • DateTimeFormatter :格式化时间/日期
public void test7(){
        DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        LocalDateTime ldt = LocalDateTime.now();
        String str = ldt.format(dtf);
        System.out.println(str);

        //自定义时间格式
        DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        String str2 = dtf2.format(ldt);
        System.out.println(str2);
        
		//将字符串转为:LocalDateTime
        LocalDateTime localDateTime = LocalDateTime.parse(str2, dtf2);
        System.out.println(localDateTime);
    }
  • 时区的处理
    Java8中加入了对时区的支持,带时区的时间为分别为:ZonedDate、ZonedTime、ZonedDateTime其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式,例如:Asia/Shanghai等
    Zoneld:该类中包含了所有的时区信息
    getAvailableZonelds():可以获取所有时区时区信息
    of(id):用指定的时区信息获取Zoneld对象
public void test9(){
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Chungking"));
        System.out.println(ldt);

        ZonedDateTime zdt = ldt.atZone(ZoneId.of("Asia/Chungking"));
        System.out.println(zdt);
    }

八、重复注解与类型注解

Java 8对注解处理提供了两点改进:可重复的注解及可用于类型的注解。

在这里插入图片描述


更多相关内容:请点击查看