Java8新特性

本文深入探讨了Java8的重要更新,包括Lambda表达式、Stream API、Optional类、新时间日期API等关键特性,以及接口中的默认方法与静态方法、方法引用、构造器引用等内容。

1、简介

  • 速度更快
  • 代码更少(增加了新的语法Lambda表达式)
  • 强大的Stream API
  • 便于并行
  • 最大化减少空指针异常 Optional
其中最为核心的为Lambda表达式与Stream API

2、Lambda表达式

为什么使用Lambda表达式

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

从匿名类到 Lambda 的转换

public class TestLambda {
    @Test
    public void test1() {
    	//匿名内部类
    	Runnable r1=new Runnable() {
            @Override
            public void run() {
            	System.out.println("hello Lambda");
            }
    	};
    	r1.run();//TestLambda
    	
    	//Lambda
    	Runnable r2=()->System.out.println("hello Lambda");
    	r2.run();//TestLambda
    }
    
    @Test
	public void test2() {
		//原来使用匿名内部类作为参数传递
		TreeSet<String> ts1=new TreeSet<>(new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return Integer.compare(o1.length(), o2.length());
			}
		});
		
		//Lambda表达式作为参数传递
		TreeSet<String> ts2=new TreeSet<>((o1,o2)->Integer.compare(o1.length(), o2.length()));	
	}
}
复制代码

Lambda表达式的基本语法

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “->” , 该操作符被称为 Lambda 操作符或剪头操作符。
它将 Lambda 分为两个部分:
    左侧:指定了 Lambda 表达式需要的所有参数
    右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
复制代码
语法格式一:无参,无返回值,Lambda体只有一条语句
Runnable runnable = new Runnable() {
    @Override
    public void run() {
    	System.out.println("Hello World!" + num);
    }
};
//使用lambda表达式	
Runnable runnable = () -> System.out.println("Hello Lambda!");
复制代码
语法格式二:有一个参数时,无返回值,Lambda体只有一条语句
Consumer consumer=new Consumer() {
    @Override
    public void accept(Object o) {
        System.out.println(o);
    }
};
//使用lambda表达式	
Consumer consumer=(o)-> System.out.println(o);
复制代码
语法格式三:有一个参数时,参数的小括号可以省略,无返回值,Lambda体只有一条语句
Consumer<String> consumer = x -> System.out.println(x);
复制代码
语法格式四:有两个及两个以上参数,有返回值,并且Lambda体中有多条语句
Comparator<Integer> comparator=new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return Integer.compare(o1,o2);
    }
};
//使用lambda表达式	
Comparator<Integer> c=(o1,o2)->{
    System.out.println("函数式接口");
    return Integer.compare(o1,o2);
};
复制代码
语法格式五:若Lambda体中只有一条语句,return和大括号都可以省略不写
Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
复制代码
语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即"类型推断"
Comparator<Integer> comparator = (Integer x, Integer y) -> Integer.compare(x, y);
复制代码

3、函数式接口

什么是函数式接口?

  • lambda表达式需要“函数式接口”的支持
  • 接口中只有一个抽象方法的接口,成为函数式接口
  • 可以使用Lambda 表达式来创建该接口的对象
  • 可以在任意函数式接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包 含一条声明,说明这个接口是一个函数式接口。

自定义函数式接口

@FunctionalInterface
public interface MyNumber {
    public double getValue();
}
复制代码

函数式接口中使用泛型

@FunctionalInterface
public interface MyFunc<T> {
    public T getValue(T t);
}
复制代码

作为参数传递Lambda表达式:

为了将Lambda表达式作为参数传递,接收Lambda表达式的参数类型必须是与该Lambda表达式兼容的函数式接口的类型一致。
复制代码
public class MyTest {
    public void test1() {
    	String newStr=toUpperString((str)->str.toUpperCase(), "abcdef");
    }
    
    public String toUpperString(MyFunc<String> mf,String str) {
    	return mf.getValue(str);
    }			
}
复制代码

java内置四大核心函数及其应用

1、Consumer:消费型接口,参数类型T,返回类型void,方法accept(T t)
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

@Test
public void test1() {
	happy(10000,(m)->System.out.println(m));
}

public void happy(double money,Consumer<Double> consumer) {
	consumer.accept(money);
}
复制代码
2、Supplier:供给型接口,没有参数,返回类型T,方法get()
@FunctionalInterface
public interface Supplier<T> {
    T get();
}

@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> supplier){
    List<Integer> list=new ArrayList<>();
    for(int i=0;i<num;i++) {
        Integer n=supplier.get();
        list.add(n);
    }
    return list;
}
复制代码
3、Function<T, R>:函数型接口,参数类型T,返回类型R,方法apply(T t)
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

@Test
public void test3() {
    String newStr=strHandler("\t\t\t bjlemon",(str)->str.trim());
    System.out.println(newStr);
    
    newStr=strHandler("bjlemon",(str)->str.substring(2, 5));
    System.out.println(newStr);
}

//需求:用于处理字符串
public String strHandler(String str,Function<String, String> function) {
	return function.apply(str);
}
复制代码
4、Predicate:断言型接口,参数类型T,返回类型boolean,方法test(T t)
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

@Test
public void test4() {
    List<String> list=Arrays.asList("Hello","bjlemon","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> predicate){
    List<String> strList=new ArrayList<>();
    for(String str:list) {
        if(predicate.test(str)) {
            strList.add(str);
        }
    }
    return strList;
}
复制代码

4、方法引用与构造器引用

方法的引用

当要传递给Lambda体的操作,已经有方法提供了实现,可以使用方法引用
(可以将方法引用理解为 Lambda 表达式的另外一种表现形式)
实现抽象方法的参数列表,必须与方法引用的参数列表保持一致
方法引用:使用操作符::,将方法名和对象或类的名字分隔开来
注意:
    方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
    若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName
复制代码
public class TestMethodRef {

    //1、对象的引用 :: 实例方法名
    @Test
    public void test1(){
        Consumer<String> con = (str) -> System.out.println(str);
        con.accept("Hello World!");
        
        System.out.println("--------------------------------");
        
        //PrintStream ps = System.out;
        Consumer<String> con2 = System.out::println;
        con2.accept("Hello Java8!");
    }

    @Test
    public void test2() {
    	Employee emp = new Employee(101, "张三", 18, 9999.99);
    	Supplier<String> sup = () -> emp.getName();
    	System.out.println(sup.get());
    
    	System.out.println("----------------------------------");
    
    	Supplier<String> sup2 = emp::getName;
    	System.out.println(sup2.get());
    }
    
    //2、类名 :: 静态方法名
    @Test
    public void test3() {
    	BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);
    	System.out.println(fun.apply(1.5, 22.2));
    
    	System.out.println("--------------------------------------------------");
    
    	BiFunction<Double, Double, Double> fun2 = Math::max;
    	System.out.println(fun2.apply(1.2, 1.5));
    }
    
    @Test
    public void test4() {
    	Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
    
    	System.out.println("-------------------------------------");
    
    	Comparator<Integer> com2 = Integer::compare;
    }
    
    //3、类名 :: 实例方法名
    @Test
    public void test5() {
    	BiPredicate<String, String> bp = (x, y) -> x.equals(y);
    	System.out.println(bp.test("abcde", "abcde"));
    
    	System.out.println("-----------------------------------------");
    
    	BiPredicate<String, String> bp2 = String::equals;
    	System.out.println(bp2.test("abc", "abc"));
    
    	System.out.println("-----------------------------------------");
    
    	Function<Employee, String> fun1 = (e) -> e.show();
    	System.out.println(fun1.apply(new Employee()));
    
    	System.out.println("-----------------------------------------");
    
    	Function<Employee, String> fun2 = Employee::show;
    	System.out.println(fun2.apply(new Employee()));
    }
}	
复制代码

构造器的引用

构造器的参数列表,需要与函数式接口中参数列表保持一致!  
格式:ClassName::new
复制代码
public class TestMethodRef {
    // 构造器引用:格式:ClassName::new
    @Test
    public void test6() {
    	Supplier<Employee> sup = () -> new Employee();
    	System.out.println(sup.get());
    
    	System.out.println("------------------------------------");
    
    	Supplier<Employee> sup2 = Employee::new;
    	System.out.println(sup2.get());
    }
    
    @Test
    public void test7() {
    	Function<String, Employee> fun1=(name)->new Employee(name);
    	System.out.println(fun1.apply("青子"));
    	
    	Function<String, Employee> fun2 = Employee::new;
    	System.out.println(fun2.apply("青子"));
    	
    	System.out.println("---------------------------------");
    	
    	BiFunction<String, Integer, Employee> fun3=(name,age)->new Employee(name,age);
    	System.out.println(fun3.apply("青子",16));
    	
    	BiFunction<String, Integer, Employee> fun4 = Employee::new;
    	System.out.println(fun4.apply("青子",16));
    }
    
    // 数组引用:格式:type[]::new
    @Test
    public void test8() {
    	Function<Integer, String[]> fun1 = (args) -> new String[args];
    	String[] strs1 = fun1.apply(10);
    	System.out.println(strs1.length);
    
    	Function<Integer, String[]> fun2 =String[]::new;
    	String[] strs2 = fun2.apply(15);
    	System.out.println(strs2.length);
    
    	
    	System.out.println("--------------------------");
    
    	Function<Integer, Employee[]> fun3 = (args)->new Employee[args];
    	Employee[] emps1 = fun3.apply(20);
    	System.out.println(emps1.length);
    	
    	Function<Integer, Employee[]> fun4 = Employee[]::new;
    	Employee[] emps2 = fun4.apply(30);
    	System.out.println(emps2.length);
    }
}
复制代码

5、Stream API

简介

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。
可以使用 Stream API 来并行执行操作
简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式
复制代码

什么是Stream?

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

Stream操作的三个步骤

  • 创建Stream:一个数据源(如:集合、数组),获取一个流
public class TestStreamaAPI {
    //创建Stream
    @Test
    public void test1(){
    	//方式一:Java8 中的 Collection 接口被扩展,提供了两个获取流的方法
    	//default Stream<E> stream() : 返回一个顺序(串行)流
    	//default Stream<E> parallelStream() : 返回一个并行流
    	List<String> list = new ArrayList<>();
    	Stream<String> stream = list.stream();
    	Stream<String> parallelStream = list.parallelStream();
    	
    	//方式二:Java8 中的 Arrays 的静态方法 stream()可以获取数组流
    	//static <T> Stream<T> stream(T[] array): 返回一个流
    	//public static IntStream stream(int[] array)
    	//public static LongStream stream(long[] array)
    	//public static DoubleStream stream(double[] array)
    	Integer[] nums = new Integer[10];
    	Stream<Integer> stream1 = Arrays.stream(nums);
    	
    	//方式三:可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数
    	//public static<T> Stream<T> of(T... values) : 返回一个流
    	Stream<Integer> stream2 = Stream.of(1,2,3,4,5,6);
    	
    	//方式四:可以使用静态方法 Stream.iterate()和Stream.generate(),创建无限流。
    	//迭代:public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) 
    	Stream<Integer> stream3 = Stream.iterate(0, (x) -> x + 2).limit(10);
    	
    	//生成:public static<T> Stream<T> generate(Supplier<T> s)
    	Stream<Double> stream4 = Stream.generate(Math::random).limit(2);
    }
}
复制代码
  • 中间操作:一个中间操作链,对数据源的数据进行处理
    多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,
    否则中间操作不会执行任何的处理!
    而在终止操作时一次性全部处理,称为“惰性求值”。
public class TestStreamaAPI {
    //2. 中间操作
    List<Employee> emps = Arrays.asList(
        new Employee(101, "张三", 18, 9999.99),
        new Employee(102, "李四", 58, 5555.55),
        new Employee(103, "王五", 26, 3333.33),
        new Employee(104, "赵六", 8, 7777.77),
        new Employee(105, "田七", 12, 8888.88),
    );
    
    //筛选与切片一:filter(Predicate predicate)接收Lambda,从流中排除某些元素
    //内部迭代:迭代操作 Stream API 内部完成
    @Test
    public void test2(){
        //所有的中间操作不会做任何的处理
        Stream<Employee> stream = emps.stream()
            .filter((e) -> {
            	System.out.println("测试中间操作");
            	return e.getAge() > 35;
            });
        //只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
        stream.forEach(System.out::println);
    }
    
    //外部迭代
    @Test
    public void test3(){
        Iterator<Employee> it = emps.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
    }
    
    //筛选与切片二:limit(Long maxSize)截断流,使其元素不超过给定数量
    @Test
    public void test4(){
        emps.stream()
            .filter((e) -> {
            	System.out.println("短路!"); 
            	return e.getSalary() > 5000;
            }).limit(2)
            .forEach(System.out::println);
    }
	
    //筛选与切片三:skip(Long n)跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit(n) 互补
    @Test
    public void test5(){
        emps.stream()
            .filter((e) -> e.getSalary() > 5000)
            .skip(2)
            .forEach(System.out::println);
    }
    
    //筛选与切片四:distinct筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
    @Test
    public void test6(){
        emps.stream()
            .distinct()
            .forEach(System.out::println);
    }

    
    //映射一:map<Function f>--接收lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,
    //该函数会被应用到每个元素上,并将其映射成一个新的元素。
    //映射二:flatMap(Function f)--接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
    @Test
    public void test7(){
        List<String> list= Arrays.asList("aaa","bbb","ccc","ddd","eee");
        list.stream()
                .map((str)->str.toUpperCase())
                .forEach(System.out::println);

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

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

        //{{a,a,a},{b,b,b},...}:将Stream<Character>添加到流中
        Stream<Stream<Character>> stream=list.stream()
                .map(LambdaTest::filterCharacter);
        stream.forEach((sm)->{
            sm.forEach(System.out::print);
        });

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

        //{a,a,a,b,b,b,...}:将Stream<Character>中的Character元素添加到流中
        Stream<Character> sm=list.stream()
                .flatMap(LambdaTest::filterCharacter);
        sm.forEach(System.out::print);
    }

    public static Stream<Character> filterCharacter(String str){
        List<Character> list=new ArrayList<>();
        for(Character ch:str.toCharArray()){
            list.add(ch);
        }
        return list.stream();
    }
    
    //map与flatMap的关于类似于ArrayList中的add(Object obj)与addAll(Collection c)的区别
    //add(Object obj):将obj对象添加到集合中
    //addAll(Collection c):将集合中的元素添加到集合中
    
    //排序
    //sorted()--自然排序(Comparable)
    //sorted(Comparator comparator)--定制排序

    @Test
    public void test8(){
        List<String> list= Arrays.asList("aaa","bbb","ccc","ddd","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);

    }
}
复制代码
  • 终止操作(终端操作):一个终止操作,执行中间操作链,并产生结果
    终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
public class TestStreamaAPI {
    //3. 终止操作
    //allMatch(Predicate predicate):检查是否匹配所有元素
    //anyMatch(Predicate predicate):检查是否至少匹配一个元素
    //noneMatch(Predicate predicate):检查是否没有匹配的元素
    @Test
    public void test1(){
        boolean bl = emps.stream()
        	.allMatch((e) -> e.getStatus().equals(Status.BUSY));
        
        System.out.println(bl);
        
        boolean bl1 = emps.stream()
        	.anyMatch((e) -> e.getStatus().equals(Status.BUSY));
        
        System.out.println(bl1);
        
        boolean bl2 = emps.stream()
        	.noneMatch((e) -> e.getStatus().equals(Status.BUSY));
        
        System.out.println(bl2);
    }
    
    //findFirst():返回第一个元素
    //findAny():返回当前流中的任意元素
    @Test
    public void test2(){
    	Optional<Employee> op = emps.stream()
    		.sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
    		.findFirst();
    	
    	System.out.println(op.get());
    	
    	System.out.println("--------------------------------");
    	
    	Optional<Employee> op2 = emps.parallelStream()
    		.filter((e) -> e.getStatus().equals(Status.FREE))
    		.findAny();
    	
    	System.out.println(op2.get());
    }
    
    //count():返回流中元素的总个数
    //max(Comparator comparator):返回流中最大值
    //min(Comparator comparator):返回流中最小值
    //forEach(Consumer consumer):内部迭代
    @Test
    public void test3(){
    	long count = emps.stream()
                 .filter((e) -> e.getStatus().equals(Status.FREE))
                 .count();
    	System.out.println(count);
    	
    	Optional<Double> op = emps.stream()
    		.map(Employee::getSalary)
    		.max(Double::compare);
    	System.out.println(op.get());
    	
    	Optional<Employee> op2 = emps.stream()
    		.min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
    	System.out.println(op2.get());
    	
    	emps.stream().forEach(System.out::println);
    }
    
    //reduce(T identity, BinaryOperator):可以将流中元素反复结合起来,得到一个值,返回 T
    //reduce(BinaryOperator):可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
    @Test
    public void test4(){
    	List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
    	
    	Integer sum = list.stream()
    		.reduce(0, (x, y) -> x + y);
    	
    	System.out.println(sum);
    	
    	System.out.println("----------------------------------------");
    	
    	Optional<Double> op = emps.stream()
    		.map(Employee::getSalary)
    		.reduce(Double::sum);
    	
    	System.out.println(op.get());
    }
}
复制代码
备注:map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名。
复制代码

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

Java8中允许接口中包含具有具体实现的方法,该方法称为“默认方法”,
默认方法使用 default 关键字修饰
复制代码
public interface MyFun {
    default String getName(){
    	return "哈哈哈";
    }
}

public interface MyInterface {
    default String getName(){
    	return "呵呵呵";
    }
    public static void show(){
    	System.out.println("接口中的静态方法");
    }
}

public class MyClass {
    public String getName(){
    	return "嘿嘿嘿";
    }
}

public class SubClass extends MyClass implements MyFun, MyInterface{
	@Override
	public String getName() {
		return MyInterface.super.getName();
	}
	public static void main(String[] args) {
		SubClass sc = new SubClass();
		System.out.println(sc.getName());
		MyInterface.show();
	}
}
复制代码
接口默认方法的”类优先”原则:若一个接口中定义了一个默认方法,而另外一个父类或接口中又定义了一个同名的方法时
1、选择父类中的方法:如果一个父类提供了具体的实现,那么接口中具有相同名称和参数的默认方法会被忽略。
2、接口冲突。如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),
那么必须覆盖该方法来解决冲突
复制代码

7、新时间日期API

旧时间日期的线程安全性问题

public class TestSimpleDateFormat {
    public void test1()throws Exception{
        SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd");
        Callable<Date> task=new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return sdf.parse("20190120");
            }
        };
    
        ExecutorService pool= Executors.newFixedThreadPool(10);
    
        List<Future<Date>> results=new ArrayList<>();
    
        for(int i=0;i<10;i++){
            results.add(pool.submit(task));
        }
    
        for(Future<Date> future:results){
            System.out.println(future.get());
        }
        pool.shutdown();
    }
}
复制代码
改进方案
复制代码
public class DateFormatThreadLocal {

    private static final ThreadLocal<DateFormat> threadLocal=new ThreadLocal<DateFormat>(){
        @Override
        protected DateFormat initialValue() {
            return new SimpleDateFormat("yyyyMMdd");
        }
    };

    public static Date convert(String source)throws Exception{
        return threadLocal.get().parse(source);
    }
}

public class TestSimpleDateFormat {
    @Test
    public void test1()throws Exception{
        Callable<Date> task=new Callable<Date>() {
            @Override
            public Date call() throws Exception {
                return DateFormatThreadLocal.convert("20190120");
            }
        };

        ExecutorService pool= Executors.newFixedThreadPool(10);

        List<Future<Date>> results=new ArrayList<>();

        for(int i=0;i<10;i++){
            results.add(pool.submit(task));
        }

        for(Future<Date> future:results){
            System.out.println(future.get());
        }
        pool.shutdown();
    }
}
复制代码

新时间日期类线程安全

public class TestSimpleDateFormat {
    @Test
    public void test2()throws Exception{
        DateTimeFormatter dtf=DateTimeFormatter.ofPattern("yyyyMMdd");
        Callable<LocalDate> task=new Callable<LocalDate>() {
            @Override
            public LocalDate call() throws Exception {
                return LocalDate.parse("20190120",dtf);
            }
        };

        ExecutorService pool= Executors.newFixedThreadPool(10);

        List<Future<LocalDate>> results=new ArrayList<>();

        for(int i=0;i<10;i++){
            results.add(pool.submit(task));
        }

        for(Future<LocalDate> future:results){
            System.out.println(future.get());
        }
        pool.shutdown();
    }
}
复制代码

使用 LocalDate、LocalTime、LocalDateTime

LocalDate、LocalTime、LocalDateTime类的实例是不可变的对象,
分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。
注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法
复制代码
public class TestLocalDateTime {

    //1. LocalDate、LocalTime、LocalDateTime
    @Test
    public void test1(){
        //根据当前时间创建对象
        LocalDateTime localDateTime=LocalDateTime.now();
        System.out.println(localDateTime);

        //根据指定时间创建对象
        localDateTime=LocalDateTime.of(2019,01,20,20,00,01);
        System.out.println(localDateTime);

        //增加年月日时分秒
        localDateTime=localDateTime.plusYears(1);
        System.out.println(localDateTime);

        //减少年月日时分秒
        localDateTime=localDateTime.minusYears(2);
        System.out.println(localDateTime);

        System.out.println(localDateTime.getYear());
        System.out.println(localDateTime.getMonthValue());
        System.out.println(localDateTime.getDayOfMonth());
        System.out.println(localDateTime.getHour());
        System.out.println(localDateTime.getMinute());
        System.out.println(localDateTime.getSecond());
    }

    //2. Instant : 时间戳。 (使用 Unix 元年  1970年1月1日 00:00:00 所经历的毫秒值)
    @Test
    public void test2(){
        Instant instant=Instant.now();//默认使用 UTC 时区
        System.out.println(instant);

        OffsetDateTime odt=instant.atOffset(ZoneOffset.ofHours(8));
        System.out.println(odt);
    }

    //3.Duration : 用于计算两个“时间”间隔
    //Period : 用于计算两个“日期”间隔
    @Test
    public void test3(){
        Instant ins1 = Instant.now();

        System.out.println("--------------------");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }

        Instant ins2 = Instant.now();

        System.out.println("所耗费时间为:" + Duration.between(ins1, ins2));

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

        LocalDate ld1 = LocalDate.now();
        LocalDate ld2 = LocalDate.of(2011, 1, 1);

        Period pe = Period.between(ld2, ld1);
        System.out.println(pe.getYears());
        System.out.println(pe.getMonths());
        System.out.println(pe.getDays());
    }

    //4. TemporalAdjuster : 时间校正器
    @Test
    public void test4(){
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);

        LocalDateTime ldt2 = ldt.withDayOfMonth(10);
        System.out.println(ldt2);

        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3);

        //自定义:下一个工作日
        LocalDateTime ldt5 = ldt.with((l) -> {
            LocalDateTime ldt4 = (LocalDateTime) l;

            DayOfWeek dow = ldt4.getDayOfWeek();

            if(dow.equals(DayOfWeek.FRIDAY)){
                return ldt4.plusDays(3);
            }else if(dow.equals(DayOfWeek.SATURDAY)){
                return ldt4.plusDays(2);
            }else{
                return ldt4.plusDays(1);
            }
        });
        System.out.println(ldt5);
    }

    //5. DateTimeFormatter : 解析和格式化日期或时间
    @Test
    public void test5(){
//		DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE;

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss E");

        LocalDateTime ldt = LocalDateTime.now();
        String strDate = ldt.format(dtf);

        System.out.println(strDate);

        LocalDateTime newLdt = ldt.parse(strDate, dtf);
        System.out.println(newLdt);
    }

    @Test
    public void test6(){
        Set<String> set = ZoneId.getAvailableZoneIds();
        set.forEach(System.out::println);
    }

    //6.ZonedDate、ZonedTime、ZonedDateTime : 带时区的时间或日期
    @Test
    public void test7(){
        LocalDateTime ldt = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
        System.out.println(ldt);

        ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("US/Pacific"));
        System.out.println(zdt);
    }
}
复制代码

8、Optional 类

Optional<T> 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,
现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
复制代码
public class TestOptional {

    @Test
    public void test1(){
        //Optional.of(T t):创建一个 Optional 实例
        Optional<Employee> optional0=Optional.of(new Employee());//默认值
        System.out.println(optional0.get());

        //Optional<Employee> optional1=Optional.of(null);//空指针

        //Optional.empty():创建一个空的 Optional 实例
        Optional optional2=Optional.empty();//调用get()会报空指针

        //Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
        Optional<Employee> optional3=Optional.ofNullable(new Employee());
        System.out.println(optional3.get());

        Optional<Employee> optional4=Optional.ofNullable(null);//调用get()会报空指针
        //isPresent():判断是否包含值
        if(optional4.isPresent()){
            System.out.println(optional4.get());
        }

        //orElse(T t):如果调用对象包含值,返回该值,否则返回t
        Employee employee=optional4.orElse(new Employee(1,"张三",18,888.88));

        //orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值
        Employee employee1=optional4.orElseGet(()->new Employee());

        //map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
        Optional<Employee> optional5=Optional.ofNullable(new Employee(1,"张三",18,888.88));
        Optional<String> str=optional5.map(Employee::getName);

        //flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
        Optional<String> str2=optional5.flatMap((e)->Optional.of(e.getName()));
    }
}
复制代码

9、注解

JDK内置的基本注解类型

@Override: 限定重写父类方法, 该注释只能用于方法
@Deprecated: 用于表示某个程序元素(类, 方法等)已过时
@SuppressWarnings: 抑制编译器警告
复制代码

JDK的元Annotation

用于修饰Annotation
@Retention:指定注解的生命周期
@Target:指定注解可以用在哪些地方
@Documented:指定注解是否被javadoc工具提取成文档
@Inherited:指定注解是否具有继承性
复制代码

自定义注解

@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationDemo {
    String value();
}

@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "default";
    String[] datas() default {"zhangsan", "lisi", "wangwu"};
    Gender gender() default Gender.MALE;
    AnnotationDemo annotationDemo() default @AnnotationDemo("annotation");
}

@MyAnnotation(
        value = "张三",
        datas = {"abc","ABC"},
        gender = Gender.FEMALE,
        annotationDemo = @AnnotationDemo(value = "xxx")
)
public class Demo {}

public class TestAnnotation {
    @Test
    public void test1() {
        MyAnnotation myAnnotation = null;
        // 判断MyAnnotation注解是否在Demo类上,如果在则取出
        if (Demo.class.isAnnotationPresent(MyAnnotation.class)) {
            myAnnotation = Demo.class.getDeclaredAnnotation(MyAnnotation.class);
            System.out.println(myAnnotation.value());
            System.out.println(Arrays.toString(myAnnotation.datas()));
            System.out.println(myAnnotation.gender().name());
            System.out.println(myAnnotation.annotationDemo().value());
        }
    }
}
复制代码

重复注解

@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "default";
}

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[] value();
}

public class TestAnnotation {

    @MyAnnotation("hello")
    @MyAnnotation("world")
    public void show(@MyAnnotation("abc") String str)throws Exception{}

    @Test
    public void test()throws Exception{
        Class<TestAnnotation> clazz=TestAnnotation.class;
        Method method=clazz.getDeclaredMethod("show",String.class);
        MyAnnotations ms=method.getDeclaredAnnotation(MyAnnotations.class);

        for(MyAnnotation myAnnotation :ms.value()){
            System.out.println(myAnnotation.value());
        }

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

        MyAnnotation[] myAnnotations=method.getDeclaredAnnotationsByType(MyAnnotation.class);
        for(MyAnnotation m:myAnnotations){
            System.out.println(m.value());
        }
    }
}
复制代码

转载于:https://juejin.im/post/5be51e95e51d454b1a2f7f60

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值