Stream + 并行流 +Optional +接口的方法 +日期组件+重复注解及类型注解
一、Stream流
1.终止操作 - 归约
归约:将流中的元素反复结合起来,得到一个值
map+reduce的连接通常称为map_reduce模式,因Google用它进行网络搜索而出名
方法 | 描述 |
---|---|
reduce( T identity , BinaryOperator accumulator) | 参数:(初始值,结合逻辑) |
reduce(BinaryOperator accumulator) | 参数:(结合逻辑) |
public class Student implements Comparable<Student>{
private String name;
private int age;
private double salary;
private Course course;
public Student() {
}
public Student(String name, int age, double salary, Course course) {
this.name = name;
this.age = age;
this.salary = salary;
this.course = course;
}
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 Course getCourse() {
return course;
}
public void setCourse(Course course) {
this.course = course;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", salary=" + salary + ", course=" + course + "]";
}
@Override
public int hashCode(){
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((course == null) ? 0 : course.hashCode());
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;
Student other = (Student) obj;
if(age != other.age)
return false;
if(course != other.course)
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 int compareTo(Student o) {
if(this == o){
return 0;
}
if (this.age != o.age) {
return Integer.compare(this.age, o.age);
}
return 1;
}
}
public enum Course {
JAVA,HTML,PYTHON;
}
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.junit.Test;
public class Test01 {
List<Integer> numList = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01(){
//需求:获取numList集合中元素的总和
Integer reduce = numList.stream().reduce(0, (x,y)->x+y);
System.out.println(reduce);
}
@Test
public void test02(){
//需求:获取stuList集合中所有学生工资总和
Optional<Double> reduce = stuList.stream().map(Student::getSalary).reduce((salary1,salary2)->salary1+salary2);
Double sum = reduce.get();
System.out.println(sum);
}
}
2.终止操作 - 收集
收集:将流转换为其他形式。接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
方法 | 描述 |
---|---|
collect(Collector<? super T, A, R> collector) | 把元素放入Collector集合中 |
import java.util.ArrayList;
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Test;
public class Test02 {
List<Student> stuList = Arrays.asList(
new Student("张三", 28, 4800,Course.JAVA),
new Student("李四", 36, 7200,Course.JAVA),
new Student("王五", 19, 9600,Course.HTML),
new Student("赵六", 42, 6100,Course.HTML),
new Student("孙七", 23, 9600,Course.PYTHON),
new Student("吴八", 31, 3000,Course.PYTHON),
new Student("李四", 36, 7200,Course.JAVA));
@Test
public void test01(){
//需求1:把当前学生姓名提取出来,并把数据放入List集合中
List<String> list = stuList.stream().map((stu)->stu.getName()).collect(Collectors.toList());
for (String name : list) {
System.out.println(name);
}
}
@Test
public void test02(){
//需求2:把当前学生姓名提取出来,并把数据放入Set集合中
Set<String> set = stuList.stream().map((stu)->stu.getName()).collect(Collectors.toSet());
for (String name : set) {
System.out.println(name);
}
}
@Test
public void test03(){
//需求3:把当前学生姓名提取出来,并把数据放入指定集合中
ArrayList<String> arrayList = stuList.stream().map((stu)->stu.getName()).collect(Collectors.toCollection(ArrayList::new));
for (String name : arrayList) {
System.out.println(name);
}
}
@Test
public void test04(){
//需求4:收集/获取学生个数
Long count = stuList.stream().collect(Collectors.counting());
System.out.println(count);
}
@Test
public void test05(){
//需求5:收集/获取学生平均工资
Double avg = stuList.stream().collect(Collectors.averagingDouble(Student::getSalary));
System.out.println(avg);
}
@Test
public void test06(){
//需求6:收集/获取学生总工资
Double sum = stuList.stream().collect(Collectors.summingDouble(Student::getSalary));
System.out.println(sum);
}
@Test
public void test07(){
//需求7:收集/获取学生工资最大值
Optional<Double> maxOptional = stuList.stream().map((stu)->stu.getSalary()).collect(Collectors.maxBy(Double::compareTo));
Double max = maxOptional.get();
System.out.println(max);
}
@Test
public void test08(){
//需求8:收集/获取学生工资最大值
Optional<Double> minOptional = stuList.stream().map((stu)->stu.getSalary()).collect(Collectors.minBy(Double::compareTo));
Double min = minOptional.get();
System.out.println(min);
}
@Test
public void test09(){
//需求9:收集/获取工资最多的学生信息
Optional<Student> stuOptional = stuList.stream().collect(Collectors.maxBy(
(stu1,stu2)->Double.compare(stu1.getSalary(), stu2.getSalary())));
Student stu = stuOptional.get();
System.out.println(stu);
}
@Test
public void test10(){
//需求10:收集/获取工资最少的学生信息
Optional<Student> stuOptional = stuList.stream().collect(Collectors.minBy(
(stu1,stu2)->Double.compare(stu1.getSalary(), stu2.getSalary())));
Student stu = stuOptional.get();
System.out.println(stu);
}
@Test
public void test11(){
//需求11:按照学科分组
Map<Course, List<Student>> map = stuList.stream().collect(Collectors.groupingBy(Student::getCourse));
Set<Entry<Course,List<Student>>> entrySet = map.entrySet();
for (Entry<Course, List<Student>> entry : entrySet) {
System.out.println(entry);
}
}
@Test
public void test12(){
//需求12:按照学科分组,在按照年龄分组
Map<Course, Map<String, List<Student>>> map = stuList.stream().collect(Collectors.groupingBy(Student::getCourse,Collectors.groupingBy((obj)->{
Student stu = (Student)obj;
int age = stu.getAge();
if(age<28){
return "青年";
}
if(age<40){
return "中年";
}
return "老年";
})));
Set<Entry<Course,Map<String,List<Student>>>> entrySet = map.entrySet();
for (Entry<Course, Map<String, List<Student>>> entry : entrySet) {
Course key = entry.getKey();
Map<String, List<Student>> value = entry.getValue();
System.out.println(key + " -- " + value);
}
}
@Test
public void test13(){
//需求13:按照工资5000为标准分区
Map<Boolean, List<Student>> map = stuList.stream().collect(Collectors.partitioningBy((stu)->{
if(stu.getSalary() > 5000){
return true;
}
return false;
}));
Set<Entry<Boolean,List<Student>>> entrySet = map.entrySet();
for (Entry<Boolean, List<Student>> entry : entrySet) {
System.out.println(entry);
}
}
@Test
public void test14(){
//需求14:获取学生工资信息,再获取总值、平均值、最大值、最小值
//DoubleSummaryStatistics对象 -- 学生工资信息
DoubleSummaryStatistics collect = stuList.stream().collect(Collectors.summarizingDouble(Student::getSalary));
double sum = collect.getSum();
double avg = collect.getAverage();
double max = collect.getMax();
double min = collect.getMin();
System.out.println(sum);
System.out.println(avg);
System.out.println(sum);
System.out.println(max);
System.out.println(min);
}
@Test
public void test15(){
//需求15:拼接学生姓名
String str1 = stuList.stream().map(Student::getName).collect(Collectors.joining());
System.out.println(str1);
String str2= stuList.stream().map(Student::getName).collect(Collectors.joining(","));
System.out.println(str2);
String str3= stuList.stream().map(Student::getName).collect(Collectors.joining(",", "-", "-"));
System.out.println(str3);
}
}
二、并行流与串行流
并行流就是把一个内容拆分成多个数据块,并用不同的线程分别处理每个数据块的流。Java8中将并行进行了优化,我们可以很容易的对数据进行并行操作。Stream API可以声明性地通过 parallel() - 并行流 与sequential()-顺序流 之间进行切换。
注意
- 默认为顺序流/串行流
- 并行流一般在大数据搜索里使用到
- JDK1.8之前也有并行流,叫做Fork/Join并行计算框架
import java.util.stream.LongStream;
public class Test03 {
public static void main(String[] args) {
//需求:计算1-10000000L之和
long sum = LongStream.range(1, 10000000L).//获取Stream流对象,该对象中存储了1~10000000L的数字
parallel().//设置该流为并行流(底层会给我们拆分成多个线程去计算)
reduce(0, (x,y)->x+y);//归约
System.out.println(sum);
}
}
三、Optional
Optional类(java. util. Optional)是一个容器类,代表一个存在或不存在的值,原来用null表示一个值不存在,现在Optional可以更好的表达这个概念。并且可以避免空指针异常
此类的设计就是更好的避免空指针异常
方法 | 描述 |
---|---|
Optional.of(T t) | 创建一个Optional实例 |
Optional.empty() | 创建一 个空的 Optional实例 |
Optional.ofNullable(T t) | 若t不为null,创建Optional实例,否则创建空实例 |
get() | 获取Optional实例里的对象 |
isPresent() | 判断是否包含值 |
orElse(T t) | 如果调用对象包含值, 返回该值,否则返回t |
orElseGet(Supplier s) | 如果调用对象包含值,返回该值,否则返回s获取的值 |
map(Function f) | 如果有值对其处理,并返回处理后的Optional,否则返回optional. empty() |
flatMap(Function mapper) | 与map 类似,要求返回值必须是Optional |
import java.util.Optional;
import com.qf.stream.Student;
public class Test01 {
public static void main(String[] args) {
//获取Optional的对象,该对象中存储了一个学生对象
// Optional<Student> optional = Optional.of(new Student());
//获取Optional对象中的学生对象
// Student stu = optional.get();
// System.out.println(stu);
//创建Optional的对象,该对象中没有存储了学生对象
Optional<Object> optional = Optional.empty();
//获取Optional对象中的学生对象,但是Optional对象中没有学生对象,就创建个学生对象(最大化限度的减少了空指针异常)
Object stu = optional.orElse(new Student());
System.out.println(stu);
//不同情况下可以返回不同对象,orElseGet()比orElse()可变性更强
boolean bool = true;
stu = op.orElseGet(()->{
if(bool){
return new Student("刘备", 26, 8888, Course.JAVA);
}else{
return new Student("甘夫人", 26, 8888, Course.JAVA);
}
});
//获取原容器中的某个值并返回新容器中
//map(Function<? super T, ? extends U> mapper)
Optional<String> map = op.map(Student::getName);
System.out.println(map.get());
//与map 类似,要求返回值必须是Optional
//flatMap(Function<? super T, Optional<U>> mapper)
Optional<String> flatMap = op.flatMap((e)->Optional.of(e.getName()));
System.out.println(flatMap.get());
}
}
四、接口的默认方法与静态方法
从JDK1.8开始,接口中可以有默认方法,既default修饰的方法,此方法可以让接口的实现类所调用,而接口中的静态方法直接用接口名调用即可
接口默认方法的”类优先”原则:
如果一个接口中定义了一个默认方法,而接口实现类的父类定义了一个同名的方法时,选择父类中的方法
public class Test01 {
public static void main(String[] args) {
B b = new B();
b.method();
}
}
class A{
public void method(){
System.out.println("父类中的method方法");
}
}
class B extends A implements I1{}
interface I1{
default void method(){
System.out.println("接口中的默认方法");
}
}
接口冲突:如果一个父接口提供一个默认方法,而另一个接口也提供了一个具有相同名称和参数列表的方法(不管方法是否是默认方法),那么必须覆盖该方法来解决冲突
public class Test01 {
public static void main(String[] args) {
}
}
interface I1{
default void method(){
System.out.println("I1接口中的默认方法");
}
}
interface I2 extends I1{
public void method();
}
class A implements I1,I2{
@Override
public void method() {
}
}
五、日期组件
JDK1.8提供的新日期类都是不可变的,既不管怎么样的改变,都会产生一个新的实例,他们都是线程安全的
日期组件遵循与IOS-8601世界时间标准
1.组件简介
包路径 | 类名 | 描述 |
---|---|---|
java.time | 针对日期和时间操作的包 | |
LocalDate | 用于表示日期的类 | |
LocalTime | 用于表示时间的类 | |
LocalDateTime | 用于表示日期时间的类 | |
Instant | 时间戳类(1970.1.1 0:0:0 到现在的毫秒数 | |
Period | 两个日期间隔类 | |
Duration | 两个时间间隔类 | |
java.time.chrono | 针对日期时间特殊格式操作的包 | |
java.time.format | 针对时间日期时间格式化操作的包 | |
DateTimeFormatter | 格式化日期时间类 | |
java.time.temporal | 针对时间矫正操作的包 | |
java.time.zone | 针对时区操作的包 |
2.日期时间类、时间戳、间隔类
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.Period;
import java.time.ZoneOffset;
import org.junit.Test;
public class Test01 {
@Test
public void test01(){
//LocalDate LocalTime LocalDateTime
//这三个日期类的使用大致一样
//获取当前日期时间对象
LocalDateTime ldt1 = LocalDateTime.now();
System.out.println(ldt1);//2022-10-31T11:23:18:102
//获取指定日期时间对象
LocalDateTime ldt2 = LocalDateTime.of(2020, 1, 23, 8, 30, 10, 10);
System.out.println(ldt2);//2020-1-23T8:30:10:10
//获取ldt1推后的时间日期对象
LocalDateTime ldt3 = ldt1.plusYears(2);//退后两年
System.out.println(ldt3);2024-10-31T11:23:18:102
//获取ldt1提前的时间日期对象
LocalDateTime ldt4 = ldt3.minusMonths(2);//提前两个月
System.out.println(ldt4);//2024-8-31T11:23:18:102
//获取日期中的单个信息
System.out.println(ldt4.getYear());
System.out.println(ldt4.getMonthValue());
System.out.println(ldt4.getDayOfMonth());
System.out.println(ldt4.getHour());
System.out.println(ldt4.getMinute());
System.out.println(ldt4.getSecond());
}
@Test
public void test02(){
//使用时间戳(从1970年1月1日0:0:0到现在的毫秒值)
//默认创建UTC(世界标准时间)时区的时间戳对象
Instant now1 = Instant.now();
System.out.println(now1);//2022-10-31T03:27:55.921Z
//获取偏移8小时的偏移日期时间对象
OffsetDateTime odt = now1.atOffset(ZoneOffset.ofHours(8));
System.out.println(odt);//2022-10-31T11:27:55.921+08:00
//获取时间戳的毫秒值形式
System.out.println(now1.toEpochMilli());
//获取一个1970年1月1日0:0:0 往后退1秒的时间戳对象
Instant now2 = Instant.ofEpochSecond(1);
System.out.println(now2);
}
@Test
public void test03() throws InterruptedException {
//Duration:时间间隔类
Instant now1 = Instant.now();
Thread.sleep(1000);
Instant now2 = Instant.now();
//获取时间间隔类对象
Duration duration1 = Duration.between(now1, now2);
System.out.println(duration1.toMillis());
System.out.println("-----------------------------");
LocalTime lt1 = LocalTime.now();
Thread.sleep(1000);
LocalTime lt2 = LocalTime.now();
//获取时间间隔类对象
Duration duration2 = Duration.between(lt1, lt2);
System.out.println(duration2.toMillis());
}
@Test
public void test04() throws InterruptedException {
//Period:日期间隔类
LocalDate ld1 = LocalDate.of(2021, 12, 28);
Thread.sleep(1000);
LocalDate ld2 = LocalDate.of(2022, 10, 31);
//获取日期间隔类对象
Period period = Period.between(ld1, ld2);
System.out.println(period.getYears());//0
System.out.println(period.getMonths());//10
System.out.println(period.getDays());//3
}
}
3.日期时间格式化类-DateTimeFormatter
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.junit.Test;
public class Test02 {
@Test
public void test01() {
//格式化日期时间类
LocalDateTime ldt1 = LocalDateTime.now();//2022-10-31T11:35:18:122
//获取本地标准的日期时间格式化对象
DateTimeFormatter dtf1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
String strDateTime1 = ldt1.format(dtf1);//格式化时间日期
System.out.println(strDateTime1);//2022-10-31T11:35:18:122
//自定义日期时间格式化对象
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
String strDateTime2 = ldt1.format(dtf2);//格式化时间日期
System.out.println(strDateTime2);
//将指定格式的字符串解析成LocalDateTime对象
LocalDateTime parse = LocalDateTime.parse("2020年03月12日 11:04:14", dtf2);
System.out.println(parse);
}
}
4.时间矫正器类-TemporalAdjuster
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import org.junit.Test;
public class Test04 {
@Test
public void test01() {
//时间矫正器
LocalDateTime ldt1 = LocalDateTime.now();//2022-10-31T14:03:18:122
//设置指定月份
LocalDateTime ldt2 = ldt1.withMonth(12);
System.out.println(ldt2);//2022-12-31T14:03:18:122
//设置下一个周末
LocalDateTime ldt3 = ldt1.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
System.out.println(ldt3);
//自定义时间矫正器:设置下一个工作日
LocalDateTime ldt4 = ldt1.with((temporal)->{
//获取到日期时间对象
LocalDateTime ldt = (LocalDateTime) temporal;
//获取周几?
DayOfWeek week = ldt.getDayOfWeek();
if(week.equals(DayOfWeek.FRIDAY)){//周五
return ldt.plusDays(3);//往后推3天
}else if(week.equals(DayOfWeek.SATURDAY)){//周六
return ldt.plusDays(2);//往后推2天
}else{
return ldt.plusDays(1);//往后推1天
}
});
System.out.println(ldt4);
}
}
5.时区类
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Set;
import org.junit.Test;
public class Test03 {
@Test
public void test01() {
//时区类
//获取所有时区字符串
Set<String> set = ZoneId.getAvailableZoneIds();
for (String str : set) {
System.out.println(str);
}
System.out.println("-------------");
//获取指定时区的日期时间对象
LocalDateTime ldt1 = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(ldt1);
//获取指定时区的日期时间对象 + 偏移量
LocalDateTime ldt2 = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
ZonedDateTime zonedDateTime = ldt2.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println(zonedDateTime);
}
}
6.传统日期格式化的线程问题
需求:让多线程同时去解析日期
1.使用JDK1.7时的日期时间类会出现问题
错误示范
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test01 {
public static void main(String[] args) throws Exception {
//创建日期格式化类的对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
//创建带有返回值的任务类的对象
Callable<Date> task = new Callable<Date>() {
@Override
public Date call() throws Exception {
Date date = sdf.parse("20200123");
return date;
}
};
//创建线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
//提交任务
ArrayList<Future<Date>> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
Future<Date> future = pool.submit(task);
list.add(future);
}
//遍历集合
for (Future<Date> future : list) {
Date date = future.get();
System.out.println(date);
}
//关闭线程池
pool.shutdown();
}
}
2.使用JDK1.8的日期时间类
正确示范
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test02 {
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 localDate = LocalDate.parse("20200123", dtf);
return localDate;
}
};
//创建线程池
ThreadPoolExecutor pool = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
//提交任务
ArrayList<Future<LocalDate>> list = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
Future<LocalDate> future = pool.submit(task);
list.add(future);
}
//遍历集合
for (Future<LocalDate> future : list) {
LocalDate date = future.get();
System.out.println(date);
}
//关闭线程池
pool.shutdown();
}
}
六、重复注解及类型注解
jdk1.8开始可以重复注解
ps:一个类可有多个同样的注解
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
@Author(name="G")
@Author(name="Z")
public class Test01 <@Author(name="L") T>{
public static void main(String[] args) throws Exception {
Class<?> clazz = Test01.class;
//利用反射获取类上的重复注解
Author[] typeAuthors = clazz.getAnnotationsByType(Author.class);
for (Author author : typeAuthors) {
String name = author.name();
System.out.println(name);
}
//利用反射获取方法中参数上的注解
Method method = clazz.getDeclaredMethod("method", String.class);
Parameter[] parameters = method.getParameters();//获取方法上的参数对象
for (Parameter parameter : parameters) {
//获取参数上的重复注解
Author[] parameterAuthors = parameter.getAnnotationsByType(Author.class);
for (Author author : parameterAuthors) {
String name = author.name();
System.out.println(name);
}
//获取参数类型
Class<?> type = parameter.getType();
System.out.println(type);
//获取参数名(参数名不会编译到class文件)
String name = parameter.getName();
System.out.println(name);
}
}
public void method(@Author(name="GL") @Author(name="ZL")String xxx){}
}
//作者注解
import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Repeatable(Authors.class)
@Target({TYPE,PARAMETER,TYPE_PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Author {
String name();
}
//作者注解的容器
import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({TYPE,PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authors {
Author[] value();
}