欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JDK1.8 新特性

程序员文章站 2022-05-01 15:12:54
...

jdk1.8新特性知识点:

  • Lambda表达式
  • 函数式接口
  • 方法引用和构造器调用
  • Stream API
  • 接口中的默认方法和静态方法
  • 新时间日期API

在jdk1.8中对hashMap等map集合的数据结构优化。hashMap数据结构的优化,原来的hashMap采用的数据结构是哈希表(数组+链表),hashMap默认大小是16,一个0-15索引的数组,如何往里面存储元素,首先调用元素的hashcode方法,计算出哈希码值,经过哈希算法算成数组的索引值,如果对应的索引处没有元素,直接存放,如果有对象在,那么比较它们的equals方法比较内容;如果内容一样,后一个value会将前一个value的值覆盖,如果不一样,在1.7的时候,后加的放在前面,形成一个链表,形成了碰撞,在某些情况下如果链表。无限下去,那么效率极低,碰撞是避免不了的;加载因子:0.75,数组扩容,达到总容量的75%,就进行扩容,但是无法避免碰撞的情况发生;在1.8之后,在数组+链表+红黑树来实现hashmap,当碰撞的元素个数大于8时 & 总容量大于64,会有红黑树的引入;除了添加之后,效率都比链表高,1.8之后链表新进元素加到末尾。ConcurrentHashMap在jdk1.7锁分段机制,并发级别默认16,concurrentLevel,jdk1.8采用CAS算法(无锁算法,不再使用锁分段),数组+链表中也引入了红黑树的使用。
JDK1.8 新特性
JVM内存结构
JDK1.8 新特性

新特性简介:

  • 速度更快
  • 代码更少(Lambda)
  • 强大的StreamAPI
  • 便于并行
  • 最大化减少空指针异常Optional

Lambda表达式

lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
Lambda 表达式可以使代码变的更加简洁紧凑。
lambda 表达式的语法格式如下:

(parameters) -> expression
或
(parameters) ->{ statements; }

以下是lambda表达式的重要特征:

  • **可选类型声明:**不需要声明参数类型,编译器可以统一识别参数值。
  • **可选的参数圆括号:**一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • **可选的大括号:**如果主体包含了一个语句,就不需要使用大括号。
  • **可选的返回关键字:**如果主体只有一个表达式返回值则编译器会自动返回值,大括号指定明表达式返回一个数值。

Lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5  
() -> 5  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

先来体验一下lambda最直观的优点:简洁代码

package com.hanker.java8;
import java.util.Comparator;
import java.util.TreeSet;
public class LambdaDemo1 {
	public static void main(String[] args) {
		  //匿名内部类
		  Comparator<Integer> cpt = new Comparator<Integer>() {
		      @Override
		      public int compare(Integer o1, Integer o2) {
		          return Integer.compare(o1,o2);
		      }
		  };

		  TreeSet<Integer> set = new TreeSet<>(cpt);
		  System.out.println("=========================");
		  //使用lambda表达式
		  Comparator<Integer> cpt2 = (x,y) -> Integer.compare(x,y);
		  TreeSet<Integer> set2 = new TreeSet<>(cpt2);
	}
}

只需要一行代码,极大减少代码量!!
这样一个场景,在商城浏览商品信息时,经常会有条件的进行筛选浏览,例如要选颜色为红色的、价格小于8000千的….
综合案例:
①需求:获取当前公司中年龄大于35岁的员工信息
②需求: 获取公司中员工工资大于5000的员工信息

package com.hanker.java8;
public class Employee {
	private String name;
	private Integer age;
	private Double salary;
	
	public Employee(String name, Integer age, Double salary) {
		super();
		this.name = name;
		this.age = age;
		this.salary = salary;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Integer getAge() {
		return age;
	}
	public void setAge(Integer age) {
		this.age = age;
	}
	public Double getSalary() {
		return salary;
	}
	public void setSalary(Double salary) {
		this.salary = salary;
	}
	@Override
	public String toString() {
		return "Employee [name=" + name + ", age=" + age + ", salary=" + salary + "]";
	}
}
//==========普通实现方法============
package com.hanker.java8;
import java.util.ArrayList;
import java.util.List;
public class LambdaDemo2 {
	private static List<Employee> list = new ArrayList<>();
	static {
		list.add( new Employee("张三",38,5500.00) );
		list.add( new Employee("李四",30,4500.00) );
		list.add( new Employee("王五",48,6500.00) );
		list.add( new Employee("赵六",28,8500.00) );
	}
	
	public static void main(String[] args) {
		List<Employee> emps = filterEmp1(list);
		for (Employee e : emps) {
			System.out.println(e);
		}
		System.out.println("===============");
		emps = filterEmp2(list);
		for (Employee e : emps) {
			System.out.println(e);
		}
		
	}
	//需求: 获取公司中员工工资大于5000的员工信息
	private static List<Employee> filterEmp2(List<Employee> list){
		List<Employee> emps = new ArrayList<>();
		for(Employee emp : list) {
			if(emp.getSalary() > 5000) {
				emps.add(emp);
			}
		}
		return emps;
	}
	
	//需求:获取当前公司中年龄大于35岁的员工信息
	private static List<Employee> filterEmp1(List<Employee> list){
		List<Employee> emps = new ArrayList<>();
		for(Employee emp : list) {
			if(emp.getAge() > 35) {
				emps.add(emp);
			}
		}
		return emps;
	}
}

上面两个方法的核心代码就一行:比较大小的代码;但是我们需要写很多代码。
四种代码优化方式:

package com.hanker.java8;
//编写一个策略接口
public interface MyPredicate<T> {
	boolean test(T t);
}
//======第一个实现类:根据年龄过滤=======
package com.hanker.java8;
public class FilterEmployeeByAge implements MyPredicate<Employee> {
	@Override
	public boolean test(Employee t) {
		return t.getAge()>=35;
	}
}
//======第二个实现类:根据工资过滤=======
package com.hanker.java8;
public class FilterEmployeeBySalary implements MyPredicate<Employee> {
	@Override
	public boolean test(Employee t) {
		return t.getSalary()>=5000;
	}
}
//==========测试类===============
package com.hanker.java8;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
//需求:获取当前公司中年龄大于35岁的员工信息
//需求: 获取公司中员工工资大于5000的员工信息
public class LambdaDemo3 {
	private static List<Employee> list = new ArrayList<>();
	static {
		list.add( new Employee("张三",38,5500.00) );
		list.add( new Employee("李四",30,4500.00) );
		list.add( new Employee("王五",48,6500.00) );
		list.add( new Employee("赵六",28,8500.00) );
	}
	
	public static void main(String[] args) {
		List<Employee> emps = filterEmp(list,new FilterEmployeeByAge());
		for (Employee e : emps) {
			System.out.println(e);
		}
		System.out.println("================");
		List<Employee> emps2 = filterEmp(list,new FilterEmployeeBySalary());
		for (Employee e : emps2) {
			System.out.println(e);
		}
	}
	//优化方式一:使用策略模式
	private static List<Employee> filterEmp(List<Employee> list,MyPredicate<Employee> predicate){
		List<Employee> emps = new ArrayList<>();
		for(Employee emp : list) {
			if(predicate.test(emp)) {
				emps.add(emp);
			}
		}
		return emps;
	}
	//优化方式二: 使用匿名内部类优化代码
	@Test
	public void test1() {
		List<Employee> emps =  filterEmp(list, new MyPredicate<Employee>() {
			@Override
			public boolean test(Employee t) {
				return t.getSalary()>5000;
			}
		});
		for (Employee employee : emps) {
			System.out.println(employee);
		}
	}
	//优化方式三:使用Lambda表达式
	@Test
	public void test2() {
		List<Employee> emps = filterEmp(list, e -> e.getSalary()>5000);
		emps.forEach(System.out::println);
	}
	//优化方式四:使用Stream API
	@Test
	public void test3() {
		list.stream().filter(e -> e.getSalary()>6000)
		             .limit(2)
		             .forEach(System.out::println);
		System.out.println("------------------------");
		list.stream().map(Employee::getName)
					.forEach(System.out::println);
	}
}

Lmabda表达式的语法总结: ( ) -> ( );

前置 语法
无参数无返回值 () -> System.out.println(“Hello World”)
有一个参数无返回值 (x) -> System.out.println(x)
有且只有一个参数无返回值 x -> System.out.println(x)
有多个参数,有返回值,有多条lambda体语句 (x,y) -> {System.out.println(“xxx”);return xxxx;};
有多个参数,有返回值,只有一条lambda体语句 (x,y) -> xxxx

口诀:左右遇一省括号,左侧推断类型省
注:当一个接口中存在多个抽象方法时,如果使用lambda表达式,并不能智能匹配对应的抽象方法,因此引入了函数式接口的概念。

package com.hanker.java8;
import java.util.Comparator;
import java.util.function.Consumer;
import org.junit.Test;
//Lambda表达式的基础语法:
public class LambdaDemo4 {
    //语法格式1:无参数无返回值
	@Test
	public void test1() {
		int num = 0;//jdk1.7前必须是final
		Runnable r = new Runnable() {
			public void run() {
				System.out.println("Welcome to Beijing " + num);
			}
		};
		r.run();
		System.out.println("--------------------");
		Runnable r1 = () -> System.out.println("Hello Lambda!!");
		r1.run();
	}
	//语法格式2:有一个参数无返回值, 注意参数的小括号可以省略
	@Test
	public void test2() {
		Consumer<String> con = (x) -> System.out.println("欢迎访问:"+x);
		con.accept("淘宝");
	}
	//语法格式3:有两个以上的参数,有返回值,并且Lambda体中有多条语句
	@Test
	public void test3() {
		Comparator<Integer> com = (x,y) -> {
			System.out.println(":::函数式接口:::");
			return Integer.compare(x, y);
		};
	}
	//语法格式4:若Lambda体中只有一条语句,return和大括号都可以省略不写
	@Test
	public void test4() {
		Comparator<Integer> com = (x,y) -> {
			System.out.println(":::函数式接口:::");
			return Integer.compare(x, y);
		};
		Comparator<Integer> com2 = (x,y) -> Integer.compare(x, y);
	}
	//语法格式5:Lambda表达式的参数列表的数据类型可以省略,因为JVM编译器通过上下文推断出数据类型,即“类型推断”
	@Test
	public void test5() {
		Comparator<Integer> com2 = (Integer x,Integer y) -> Integer.compare(x, y);
		Comparator<Integer> com3 = (x,y) -> Integer.compare(x, y);
	}
}

函数式接口

函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。什么是函数式接口?
简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@FunctionalInterface. 函数式接口练习:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PZiO1iUV-1605148026323)(images/2.png)]

代码:

package com.hanker.java8;
@FunctionalInterface
public interface MyFunction {
	public String getValue(String str);
}
//================
package com.hanker.java8;
@FunctionalInterface
public interface MyFunction2<T,R> {
	public R getValue(T t1,T t2);
}
//=============
package com.hanker.java8;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Test;

public class FunctionInterfaceDemo {
	private static List<Employee> list = new ArrayList<>();
	static {
		list.add( new Employee("张三",38,5500.00) );
		list.add( new Employee("李四",30,4500.00) );
		list.add( new Employee("王五",48,6500.00) );
		list.add( new Employee("赵六",28,8500.00) );
	}
	@Test
	public void test2() {
		Collections.sort(list, (e1,e2) ->{
			if(e1.getAge() == e2.getAge()) {
				return e1.getName().compareTo(e2.getName());
			}else {
				return - Integer.compare(e1.getAge(), e2.getAge());
			}
		});
		for(Employee e:list) {
			System.out.println(e);
		}
	}
	@Test
	public void test3() {
		String s = strHandler("   欢迎光临    ", str -> str.trim());
		System.out.println(s);
		s = strHandler("abcdef", str -> str.toUpperCase());
		System.out.println(s);
		s = strHandler("13803711122",str->str.substring(3, 7));
		System.out.println(s);
	}
	//需求:用于处理字符串
	public String strHandler(String str, MyFunction mf) {
		return mf.getValue(str);
	}
	
	@Test
	public void test4() {
		op(100L,200L,(x,y) -> x + y);
		op(100L,200L,(x,y) -> x * y);
	}
	
	//需求: 对于两个Long型数据进行处理
	public void op(Long l1,Long l2,MyFunction2<Long, Long> mf) {
		System.out.println(mf.getValue(l1, l2));
	}
	@Test
	public void test1() {
		Integer num  = operation(100, (x) -> x * x);
		System.out.println("num==="+num);
	}
	public Integer operation(Integer num, MyFun mf) {
		return mf.getValue(num);
	}
}

常见的四大函数式接口

  • Consumer:消费型接口,有参无返回值

    //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:供给型接口,无参有返回值

    //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<Integer>();
        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 s = strHandler("  \t\t大家好\t\t    ", str ->str.trim());
        System.out.println(s);
    }
    //需求: 用于处理字符串
    public String strHandler(String str,Function<String, String> fun) {
        return fun.apply(str);
    }
    
  • Predicate: 断言型接口,有参有返回值,返回值是boolean类型

    //Predicate<T> 断言型接口:
    @Test
    public void test4() {
        List<String> list = Arrays.asList("张无忌","周芷若","赵敏","殷离","小昭");
        List<String> newlist = filterStr(list, str -> str.length()>=3);
        for (String string : newlist) {
            System.out.println(string);
        }
    }
    
    //需求:将满足条件的字符串放入集合中
    public List<String> filterStr(List<String> list,Predicate<String> pre){
        List<String> retlist = new ArrayList<String>();
        for(String str : list) {
            if(pre.test(str)) {
                retlist.add(str);
            }
        }
        return retlist;
    }
    

    在四大核心函数式接口基础上,还提供了诸如BiFunction、BinaryOperation、toIntFunction等扩展的函数式接口,都是在这四种函数式接口上扩展而来的,不做赘述。
    JDK1.8 新特性

    总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口,直接拿来用就好了.

方法引用

若lambda体中的内容有方法已经实现了,那么可以使用“方法引用”
也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单.
注意:
1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
2.若lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method

(a) 方法引用
三种表现形式:
①对象::实例方法名
②类::静态方法名
③类:实例方法名 (lambda参数列表中第一个参数是实例方法的调用 者,第二个参数是实例方法的参数时可用)

package com.hanker.java8;

import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.junit.Test;

public class TestMethodRef {

	//对象::实例方法名
	@Test
	public void test1() {
		PrintStream ps1 = System.out;
		Consumer<String> con = x -> ps1.println(x);
		
		PrintStream ps = System.out;
		Consumer<String> con1 = ps::println;
		
		Consumer<String> con2 = System.out::println;
		con2.accept("张无忌");
	}
	
	@Test
	public void test2() {
		Employee emp = new Employee("赵敏", 18, 8899D);
		Supplier<String> sup = () -> emp.getName();
		System.out.println(sup.get());
		
		Supplier<Integer> sup2 = emp::getAge;
		System.out.println(sup2.get());
	}
	
	//类::静态方法
	@Test
	public  void test3() {
		Comparator<Integer> com = (x,y) -> Integer.compare(x, y);
		Comparator<Integer> com1 = Integer::compare;
		int x = com1.compare(100, 200);
		System.out.println(x);
	}
	
	//类::实例方法
	@Test
	public void test4() {
		BiPredicate<String, String> bp = (x,y) -> x.equals(y);
		//若Lambda参数列表中的第一个参数是实例方法的调用者,
		//而第二个参数是实例方法的参数时,可以使用ClassName::method
		BiPredicate<String, String> bp2 = String::equals;
	}
}

(b)构造器引用
格式:ClassName::new
注意: 需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致。

//构造方法引用:ClassName::new
@Test public void test5() {
    Supplier<Employee> sup = () -> new Employee();
    Supplier<Employee> sup2 = Employee::new;
    Employee emp = sup2.get();
    System.out.println(emp);

    //调用带参数的构造器
    Function<String, Employee> fun2 = Employee::new;
    Employee emp2 = fun2.apply("小昭");
    System.out.println(emp2);

    BiFunction<String, Integer, Employee> bf = Employee::new;
    Employee emp3 = bf.apply("周芷若", 24);
    System.out.println(emp3);

}

©数组引用
格式:Type[]::new

//数组引用
@Test public void test6() {
    Function<Integer, String[]> fun = x -> new String[x];
    String [] strs = fun.apply(10);
    System.out.println(strs.length);

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

Stream API

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

Stream操作的三个步骤

  • 创建stream: 一个数据源(如:集合,数组),获取一个流
  • 中间操作(过滤、map):一个中间操作链,对数据源的数据进行处理
  • 终止操作(终端操作):一个终止操作,执行中间操作链,并产生结果
    JDK1.8 新特性

Stream的创建

package com.hanker.java8;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
public class TestStreamAPI {
	//创建Stream
	@Test public void test1() {
		//1.可以通过Collection系列集合提供的stream()或parallelStream()
		List<String> list = new ArrayList<>();
		Stream<String> stream1 = list.stream();
		//2.通过Arrays中的静态方法stream()获取数组流
		Employee [] emps = new Employee[10];
		Stream<Employee> stream2 = Arrays.stream(emps);
		//3.通过Stream类中的静态方法of()
		Stream<String> stream3 = Stream.of("aaa","bbb","ccc");
		//4.创建无限流-迭代
		Stream<Integer> stream4 = Stream.iterate(0, x -> x + 2);
		stream4.limit(10).forEach(System.out::println);
		//4.创建无限流-生成
		Stream.generate( ()->Math.random() ).limit(5).forEach(System.out::println);
	}
}

Stream的中间操作

package com.hanker.java8;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.junit.Test;
public class TestStreamAPI {
	private static List<Employee> list = new ArrayList<>();
	static {
		list.add( new Employee("张三",38,5500.00) );
		list.add( new Employee("李四",30,4500.00) );
		list.add( new Employee("王五",48,6500.00) );
		list.add( new Employee("赵六",28,8500.00) );
		list.add( new Employee("赵六",28,8500.00) );
		list.add( new Employee("赵六",28,8500.00) );
		list.add( new Employee("赵六",28,8500.00) );
	}
	//中间操作:迭代操作由Stream API完成,也称为内部迭代,
	@Test public void test2() {
		//filter中间操作:接收Lambda,从流中过滤某些元素,不会只想任何操作
		Stream<Employee> stream = list.stream().filter(e -> e.getAge() > 35);
		//终止操作:一次性执行全部内容,即:“惰性求值”
		stream.forEach(System.out::println);
	}
	
	//中间操作:limit
	@Test public void test3() {
		list.stream().filter(e -> {
			System.out.println("短路。。");
			return e.getSalary()>5000;})
		.limit(2)
		.forEach(System.out::println);
	}
	//中间操作:skip(n)跳过元素,返回一个扔掉前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
	@Test public void test4() {
		list.stream().filter(e -> e.getSalary()>5000)
		.skip(2)
		.forEach(System.out::println);
	}
	//中间操作:distinct去重,通过流所生成元素的hashCode()和equals()去除重复
	@Test public void test5() {
		list.stream().filter(e -> e.getSalary()>5000)
		.distinct()
		.forEach(System.out::println);
	}
	//中间操作:map映射,接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
	@Test public void test6() {
		List<String> strList = Arrays.asList("aaa","bbb","ccc","ddd");
		strList.stream()
		.map(str -> str.toUpperCase())
		.forEach(System.out::println);
		System.out.println("==============");
		list.stream().map(Employee::getName)
		.forEach(System.out::println);
	}
	//中间操作:flatMap接收一个函数作为参数,讲流中的每个值都转换成一个另一个流,然后把所有流连接成一个流
	@Test public void test7() {
		List<String> strList = Arrays.asList("aaa","bbb","ccc","ddd");
		Stream<Character> sm = strList.stream()
		.flatMap(TestStreamAPI::filterCharacter);
		sm.forEach(System.out::println);
		
	}
	public static Stream<Character> filterCharacter(String str){
		List<Character> list = new ArrayList<Character>();
		for (Character character : str.toCharArray()) {
			list.add(character);
		}
		return list.stream();
	}
	//中间操作:排序, sorted自然排序(Comparable),sorted(Comparator com)定制排序
	@Test public void test8() {
		List<String> strList = Arrays.asList("ccc","bbb","aaa","ddd");
		strList.stream().sorted().forEach(System.out::println);
		System.out.println("---------------");
		list.stream().sorted((e1,e2) ->{
			if(e1.getAge().equals(e2.getAge())) {
				return e1.getName().compareTo(e2.getName());
			}else {
				return -e1.getAge().compareTo(e2.getAge());
			}
		}).forEach(System.out::println);
	}
}

Stream的终止操作

实体类

package com.dubbo.demo;
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;
	}
}

案例

package com.dubbo.demo;
import com.dubbo.demo.Employee.Status;
import org.junit.Test;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
public class AppTest {
    List<Employee> emps = Arrays.asList(
            new Employee(102, "李四", 79, 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)
    );

    /**
     * allMatch--检查是否匹配所有元素
     * anyMatch--检查是否至少匹配一个元素
     * noneMatch--检查是否没有匹配所有元素
     * findFirst--返回第一个元素
     * findAny--返回当前流的任一对象
     */
    @Test
    public void test1(){
        boolean b1 = emps.stream().allMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b1);
        boolean b2 = emps.stream().anyMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b2);
        boolean b3 = emps.stream().noneMatch(e -> e.getStatus().equals(Status.BUSY));
        System.out.println(b3);
        //Optional容器,封装一个有可能为空的对象
        Optional<Employee> optional = emps.stream()
                .sorted((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
                .findFirst();
        System.out.println(optional.get());

        Optional<Employee> op2 = emps.stream()
                .filter(e -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(op2.get());
    }

    /**
     * count---总数
     * max-----最大值
     * min-----最小值
     */
    @Test
    public void test2(){
        long count = emps.stream().count();
        System.out.println(count);
        Optional<Employee> max = emps.stream()
                                     .max(Comparator.comparingDouble(Employee::getSalary));
        System.out.println(max);

        Optional<Double> min = emps.stream().map(Employee::getSalary).min(Double::compare);
        System.out.println(min.get());
    }
}

Stream的归约操作

    /**
     * 归约: reduce(T identity,BinaryOpertor) reduce(BinaryOperator)
     * 可以将流中元素反复结合起来,得到一个值
     */
    @Test
    public void test3(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        //先把 0 作为x,从流中取出数据赋给 y 累加求和
        Integer sum = list.stream().reduce(0, (x, y) -> x + y);
        System.out.println(sum);
        //需求: 计算所有员工的工资总和
        Optional<Double> optional = emps.stream().map(Employee::getSalary).reduce(Double::sum);
        System.out.println(optional.get());
    }

Stream的收集操作

/**
     * 收集:collect--将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
     * Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但是Collectors实用类提供了很多静态方法
     * 可以方便地创建常见收集器实例
     */
    @Test
    public void test4(){
        List<String> list = emps.stream().map(Employee::getName).collect(Collectors.toList());
        list.forEach(System.out::println);
        System.out.println("====================");
        Set<String> set = emps.stream().map(Employee::getName).collect(Collectors.toSet());
        set.forEach(System.out::println);
        System.out.println("====================");
        HashSet<String> hashSet = emps.stream().map(Employee::getName).collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::println);
    }
    @Test
    public void test5(){
        //总数
        Long count = emps.stream().collect(Collectors.counting());
        System.out.println(count);
        System.out.println("====================");
        //平均值
        Double avg = emps.stream().collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);
        //总和
        Double sum = emps.stream().collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);
        //最大值
        Optional<Employee> max = emps.stream()
            .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
        System.out.println(max.get());
        //最小值
        Optional<Double> min = emps.stream().map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        System.out.println(min.get());
        //分组
        Map<Status, List<Employee>> collect = emps.stream()
                       .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(collect);
        //多级分组
        Map<Status, Map<String, List<Employee>>> mapMap = emps.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(mapMap);
    }
    @Test
    public void test6(){
        //分区
        Map<Boolean, List<Employee>> map = emps.stream().collect(Collectors.partitioningBy(e -> e.getSalary() > 8000));
        System.out.println(map);
        //统计
        DoubleSummaryStatistics dss = emps.stream().collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(dss.getMax());
        System.out.println(dss.getSum());
        System.out.println(dss.getAverage());
        System.out.println(dss.getCount());
        //连接
        String names = emps.stream().map(Employee::getName).collect(Collectors.joining(","));
        System.out.println(names);
    }

Stream API 练习

见后续博客文章

并行流与顺序流

并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流。

Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。StreamAPI可以声明性地通过parallel与sequential在并并流与顺序流之间进行切换。

了解Fork/Join框架

Fork/Join框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行join汇总。
JDK1.8 新特性
Fork/Join框架与传统线程池的区别

采用“工作窃取”模式(work=stealing):当执行新的任务是它可以将其拆分成更小的任务执行,并将小任务加到线程队列中,然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。

相对于一般的线程池实现,Fork/Join框架的优势体现在对其中包含的任务的处理方式上,在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续执行,那么该线程会处于等待状态,而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续执行,那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行,这种方式减少了线程的等待时间,提高了性能。

案例:

package com.dubbo.demo;
import java.util.concurrent.RecursiveTask;
public class ForkJoinCalculate extends RecursiveTask<Long> {
    private long start;
    private long end;
    private static final long THRESHOLD = 10000;

    public ForkJoinCalculate(long start,long end){
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        long length = end - start;
        if(length <= THRESHOLD){
            long sum  = 0;
            for (long i = start; i <= end; i ++){
                sum += i;
            }
            return sum;
        }else{
            long middle = (start + end) / 2;
            ForkJoinCalculate left = new ForkJoinCalculate(start,middle);
            left.fork();//拆分子任务,同时亚入线程队列

            ForkJoinCalculate right = new ForkJoinCalculate(middle + 1, end);
            right.fork();

            return left.join() + right.join();
        }
    }
}

测试类:

package com.dubbo.demo;
import org.junit.Test;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.LongStream;

public class ForkJoinTest {
    /**
     * fork/join框架
     */
    @Test
    public void test1(){
        Instant start = Instant.now();
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinCalculate task = new ForkJoinCalculate(0,10000000);
        Long sum = pool.invoke(task);
        System.out.println(sum);

        Instant end = Instant.now();
        System.out.println("耗费时间:"+ Duration.between(start,end).toMillis());
    }

    /**
     * 普通for循环
     */
    @Test
    public void test2(){
        Instant start = Instant.now();
        long sum = 0;
        for (long i=0; i < 10000000; i++){
            sum += i;
        }
        System.out.println(sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+ Duration.between(start,end).toMillis());
    }

    /**
     * java8并行流
     */
    @Test
    public void test3(){
        Instant start = Instant.now();
        LongStream.rangeClosed(0,10000000).parallel().reduce(0,Long::sum);
        Instant end = Instant.now();
        System.out.println("耗费时间:"+ Duration.between(start,end).toMillis());
    }
}

Optional类

Optional类(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(Funcaiton f): 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
  • flatMap(Function mapper):与map类似,要求返回值必须是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
     *
     *      总结:Optional.of(null)  会直接报NPE
     */
@Test
public void test1(){
    Optional<Employee> op = Optional.of(
    new Employee("zhansan", 11, 12.32, Employee.Status.BUSY));
    System.out.println(op.get());

    // NPE
    Optional<Employee> op2 = Optional.of(null);
    System.out.println(op2);
}
@Test
public void test2(){
    Optional<Object> op = Optional.empty();
    System.out.println(op);

    // No value present
    System.out.println(op.get());
}
@Test
public void test3(){
    Optional<Employee> op = Optional.ofNullable(
        new Employee("lisi", 33, 131.42, Employee.Status.FREE));
    System.out.println(op.get());

    Optional<Object> op2 = Optional.ofNullable(null);
    System.out.println(op2);
    // System.out.println(op2.get());
}
@Test
public void test5(){
    Optional<Employee> op1 = Optional.ofNullable(
        new Employee("张三", 11, 11.33, Employee.Status.VOCATION));
    System.out.println(op1.orElse(new Employee()));
    System.out.println(op1.orElse(null));
}

@Test
public void test6(){
    Optional<Employee> op1 = Optional.of(
        new Employee("田七", 11, 12.31, Employee.Status.BUSY));
    op1 = Optional.empty();
    Employee employee = op1.orElseGet(() -> new Employee());
    System.out.println(employee);
}

@Test
public void test7(){
    Optional<Employee> op1 = Optional.of(
        new Employee("田七", 11, 12.31, Employee.Status.BUSY));
    System.out.println(op1.map( (e) -> e.getSalary()).get());
}

接口中可以定义默认实现方法和静态方法

在接口中可以使用default和static关键字来修饰接口中定义的普通方法

public interface Interface {
    default  String getName(){
        return "zhangsan";
    }

    static String getName2(){
        return "zhangsan";
    }
}

在JDK1.8中很多接口会新增方法,为了保证1.8向下兼容,1.7版本中的接口实现类不用每个都重新实现新添加的接口方法,引入了default默认实现,static的用法是直接用接口名去调方法即可。当一个类继承父类又实现接口时,若后两者方法名相同:

  • 则优先继承父类中的同名方法,即“类优先”,那么接口中具有相同名称和参数的默认方法会被忽略
  • 接口冲突:如果实现两个同名方法的接口,则要求实现类必须手动声明默认实现哪个接口中的方法。

类优先案例

package com.interview.ujiuye5;

interface MyFun {
    default String getName(){
        return "哈哈哈";
    }
}
class MyClass {

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

}
class SubClass extends MyClass implements MyFun{

}
public class TestInterface {
    public static void main(String[] args) {
        SubClass subClass = new SubClass();
        System.out.println(subClass.getName());//嘿嘿嘿
    }
}

接口冲突案例

package com.interview.ujiuye5;

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

    @Override
    public String getName() {
        return MyInterface.super.getName();//指定调用哪个接口的方法
    }
}
public class TestInterface {
    public static void main(String[] args) {
        SubClass2 subClass = new SubClass2();
        System.out.println(subClass.getName());
        MyInterface.show();//静态方法直接调用
    }
}

新的日期API LocalDate | LocalTime | LocalDateTime

新的日期API都是不可变的,更使用于多线程的使用环境中

老版本存在线程安全问题

package com.interview.ujiuye5;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

/**
 * Copyright (C), 2018-2020
 */
public class TestSimleDateFormat {
    public static void main(String[] args) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		Callable<Date> task = new Callable<Date>() {
			public Date call() throws Exception {
				return sdf.parse("20201121");
			}

		};
		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();
    }
}

解决线程安全问题:

添加一个工具类

package com.interview.ujiuye5;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatThreadLocal {
	
	private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>(){
		
		protected DateFormat initialValue(){
			return new SimpleDateFormat("yyyyMMdd");
		}
		
	};
	
	public static final Date convert(String source) throws ParseException{
		return df.get().parse(source);
	}

}

使用工具类:

public class TestSimleDateFormat {
    public static void main(String[] args) throws Exception {	
	//解决多线程安全问题
    Callable<Date> task = new Callable<Date>() {

        @Override
        public Date call() throws Exception {
            return DateFormatThreadLocal.convert("20201121");
        }

    };

    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();
	}
}

使用jdk1.8的时间API

package com.interview.ujiuye5;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * Copyright (C), 2018-2020
 */
public class TestDateFormat {
    public static void main(String[] args) throws Exception {
		DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMdd");
		Callable<LocalDate> task = new Callable<LocalDate>() {
			@Override
			public LocalDate call() throws Exception {
				LocalDate ld = LocalDate.parse("20201121", dtf);
				return ld;
			}
		};

		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日历系统的日期、时间、日期和时间。它们提供了简单的日期或时间,并不包含当前的时间信息,也不包含与时区相关的信息。

案例:

@Test
public void test(){
    // 从默认时区的系统时钟获取当前的日期时间。不用考虑时区差
    LocalDateTime date = LocalDateTime.now();
    //2020-07-15T14:22:39.759
    System.out.println(date);

    System.out.println(date.getYear());
    System.out.println(date.getMonthValue());
    System.out.println(date.getDayOfMonth());
    System.out.println(date.getHour());
    System.out.println(date.getMinute());
    System.out.println(date.getSecond());
    System.out.println(date.getNano());

    // 手动创建一个LocalDateTime实例
    LocalDateTime date2 = LocalDateTime.of(2020, 12, 17, 9, 31, 31, 31);
    System.out.println(date2);
    // 进行加操作,得到新的日期实例
    LocalDateTime date3 = date2.plusDays(12);
    System.out.println(date3);
    // 进行减操作,得到新的日期实例
    LocalDateTime date4 = date3.minusYears(2);
    System.out.println(date4);
}

时间戳类型:

@Test
public void test2(){
    // 时间戳  1970年1月1日00:00:00 到某一个时间点的毫秒值
    // 默认获取UTC时区
    Instant ins = Instant.now();
    System.out.println(ins);
	System.out.println(LocalDateTime.now().
                   toInstant(ZoneOffset.of("+8")).toEpochMilli());
    System.out.println(System.currentTimeMillis());

    System.out.println(Instant.now().toEpochMilli());
 	System.out.println(Instant.now()
                        .atOffset(ZoneOffset.ofHours(8))
                        .toInstant().toEpochMilli());
}

时间间隔、日期间隔:

@Test
public void test3(){
    // Duration:计算两个时间之间的间隔
    // Period:计算两个日期之间的间隔
    Instant ins1 = Instant.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    Instant ins2 = Instant.now();
    Duration dura = Duration.between(ins1, ins2);
    System.out.println(dura);
    System.out.println(dura.toMillis());

    System.out.println("======================");
    LocalTime localTime = LocalTime.now();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    LocalTime localTime2 = LocalTime.now();
    Duration du2 = Duration.between(localTime, localTime2);
    System.out.println(du2);
    System.out.println(du2.toMillis());
}

日期间隔:

@Test
public void test4(){
    LocalDate localDate =LocalDate.now();

    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    LocalDate localDate2 = LocalDate.of(2016,12,12);
    Period pe = Period.between(localDate, localDate2);
    System.out.println(pe);
}

日期的操作

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

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

  • 例如获取下个周日:

    LocalDate nextSunday = LocalDate.now()
        .with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
    

案例:

@Test
public void test5(){
    // temperalAdjust 时间校验器
    // 例如获取下周日  下一个工作日
    LocalDateTime ldt1 = LocalDateTime.now();
    System.out.println(ldt1);

    // 获取一年中的第一天
    LocalDateTime ldt2 = ldt1.withDayOfYear(1);
    System.out.println(ldt2);
    // 获取一个月中的第一天
    LocalDateTime ldt3 = ldt1.withDayOfMonth(1);
    System.out.println(ldt3);

    LocalDateTime ldt4 = ldt1.with(TemporalAdjusters.next(DayOfWeek.FRIDAY));
    System.out.println(ldt4);

    // 获取下一个工作日
    LocalDateTime ldt5 = ldt1.with((t) -> {
        LocalDateTime ldt6 = (LocalDateTime)t;
        DayOfWeek dayOfWeek = ldt6.getDayOfWeek();
        if (DayOfWeek.FRIDAY.equals(dayOfWeek)){
            return ldt6.plusDays(3);
        }
        else if (DayOfWeek.SATURDAY.equals(dayOfWeek)){
            return ldt6.plusDays(2);
        }
        else {
            return ldt6.plusDays(1);
        }
    });
    System.out.println(ldt5);
}

日期格式化:

@Test
public void test6(){
    // DateTimeFormatter: 格式化时间/日期
    // 自定义格式
    LocalDateTime ldt = LocalDateTime.now();
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    String strDate1 = ldt.format(formatter);
    String strDate = formatter.format(ldt);
    System.out.println(strDate);
    System.out.println(strDate1);

    // 使用api提供的格式
    DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE;
    LocalDateTime ldt2 = LocalDateTime.now();
    String strDate3 = dtf.format(ldt2);
    System.out.println(strDate3);

    // 解析字符串to时间
    DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    LocalDateTime time = LocalDateTime.now();
    String localTime = df.format(time);
    LocalDateTime ldt4 = LocalDateTime.parse("2021-09-28 17:07:05",df);
    System.out.println("LocalDateTime转成String类型的时间:"+localTime);
    System.out.println("String类型的时间转成LocalDateTime:"+ldt4);
}

时区的处理:

Java8中加入了对时区的支持,带时区的时间分别为:ZonedDate,ZonedTime,ZonedDateTime

@Test
public void test7(){
    LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
    System.out.println(now);

    LocalDateTime now2 = LocalDateTime.now();
    ZonedDateTime zdt = now2.atZone(ZoneId.of("Asia/Shanghai"));
    System.out.println(zdt);

    Set<String> set = ZoneId.getAvailableZoneIds();
    set.stream().forEach(System.out::println);
}

重复注解与类型注解

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

@Repeatable(MyAnnotations.class)
@Target({ElementType.TYPE_PARAMETER.....}) //用在类型参数
@Retention...
public @interface MyAnnotation{
    String value;
}
//容器注解
@Target
@Retention...
public @interface MyAnnotations{
    MyAnnotation [] value();
}


public class TestAnnotation{
    @Test
    public void test1()throws Exception{
        Class<TestAnnotation> clazz = TestAnnotation.class;
        Method m1 = clazz.getMethod("show");
        MyAnnotation [] mas = m1.getAnnotationsByType(MyAnnotation.class);
        
        for(MyAnnotation m : mas){
            sout(m.value())
        }
    }
    
    @MyAnnotation("hello")
    @MyAnnotation("world")
    public void show(@MyAnnotation("123")  String str){   //注解用在类型参数
        
    }
}
相关标签: JAVASE