Java8新特性
Lambda表达式
简介
Lambda表达式可以取代大部分匿名内部类,可以优化代码结构。
可以取代匿名内部类?什么意思呢?
在以前如果我们需要对集合排序,我们是这样做:
Integer[] arr= {3,2,1};
Arrays.sort(arr, new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
System.out.println(Arrays.toString(arr));
使用Arrays类提供的sort方法传入一个指定排序规则的Comparator,如果我们使用匿名内部类的话,可以看到整个内部类中只用return o1-o2;语句是有用的,其他的都是多余的,为了这一句话我们多写了很多代码。那么,有了Lambda表达式后我们就可以很轻松的解决这个问题了。
java8中新增了Lambda表达式,现在我们可以这样做:
Integer[] arr= {3,2,1};
Arrays.sort(arr, (x,y)->x-y);
System.out.println(Arrays.toString(arr));
那么Lambda是如何实现的呢?我们知道sort方法需要传入一个Comparator,而Comparator是一个接口,那么我们来看看Comparator接口是怎样定义的:
@FunctionalInterface
public interface Comparator {
Comparator能够支持Lambda表达式的秘密就是类上标注的@FunctionalInterface注解,被这个注解标注的接口只能有一个抽象方法,我们知道我们写Lambda表达式时并没有指定方法,那么当使用Lambda表达式时我们重新的就是这个方法。
Lambda表达式基本语法
语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符
接口无参无返回
@FunctionalInterface
interface NoParamNoReturn {
void lambda();
}
@Test
public void test() {
NoParamNoReturn noParamNoReturn=()->{System.out.println("No param No return");};
noParamNoReturn.lambda();
}
//如果方法内只有一个语句那么{}可以省略
@Test
public void test() {
NoParamNoReturn noParamNoReturn=()->System.out.println("No param No return");
noParamNoReturn.lambda();
}
接口有一个或多个参数无返回
@FunctionalInterface
interface OneParamNoReturn{
void lambda(int x);
}
@Test
public void test() {
OneParamNoReturn oneParamNoReturn=(int x)->System.out.println(x);
oneParamNoReturn.lambda(10);
}
//如果方法只有一个参数那么()可以省略
//方法参数的类型也可以省略,编译器会根据方法参数类型推断
@Test
public void test() {
OneParamNoReturn oneParamNoReturn=x->System.out.println(x);
oneParamNoReturn.lambda(10);
}
接口无参数有返回值
@FunctionalInterface
interface NoParamHasReturn{
int lambda();
}
@Test
public void test() {
NoParamHasReturn noParamHasReturn=()->{return 10;};
noParamHasReturn.lambda();
}
//当方法只有return语句时,可以省略{}和return
@Test
public void test() {
NoParamHasReturn noParamHasReturn=()->10;
noParamHasReturn.lambda();
}
接口有一个或多个参数有返回值
@FunctionalInterface
interface HasParamHasReturn{
int lambda(int x,int y);
}
@Test
public void test() {
HasParamHasReturn hasParamHasReturn=(x,y)->x+y;
hasParamHasReturn.lambda(10, 20);
}
Lambda表达式引用方法
我们可以使用lambda表达式把接口快速指向一个已经实现了的方法
语法:方法归属者::方法名静态方法的归属者为类名,普通方法归属者为对象
@FunctionalInterface
interface HasParamHasReturn{
int lambda(int x,int y);
}
public class LambdaTest{
public int add(int x,int y) {
return x+y;
}
//lambda表达式指向对象方法
@Test public void test() {
LambdaTest lt=new LambdaTest();
HasParamHasReturn hasParamHasReturn=lt::add;
hasParamHasReturn.lambda(10, 20);
}
public static int sub(int x,int y) {
return x-y;
}
//lambda表达式引用静态方法
@Test
public void test12() {
HasParamHasReturn hasParamHasReturn=LambdaTest::sub;
hasParamHasReturn.lambda(10, 20);
}
}
//类名::实例方法 特殊情况,只有当参数列表为一个参数并且这个参数是方法调用者或多个参数并且第一个参数是调用者其他参数是参数列表
@Test
public void test() {
BiPredicate bp=(x,y)->x.equals(y);
//也可以简写为
BiPredicate bp2=String::equals;
}
lambda表达式引用构造函数创建对象
语法:类名::new;
class User{
String name;
int age;
public User() {}
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
@FunctionalInterface
interface UserCreatorBlankConstruct{
User getUser();
}
//使用lambda表达式引用构造器
@Test
public void test() {
UserCreatorBlankConstruct creator1=User::new;
creator1.getUser();
}
@FunctionalInterface
interface UserCreatorParamConstruct{
User getUser(String name,int age);
}
@Test
public void test13() {
UserCreatorParamConstruct creator2=User::new;
creator2.getUser("tom", 20);
}
java8为我们提供了4个核心的函数式接口
消费型接口
@FunctionalInterface
public interface Consumer {
void accept(T t);
}
供给型接口
@FunctionalInterface
public interface Supplier {
T get();
}
函数型接口
@FunctionalInterface
public interface Function {
R apply(T t);
}
断言型接口
@FunctionalInterface
public interface Predicate {
boolean test(T t);
}
怎么用呢?
遍历数组
@Test
public void test() {
List list=new ArrayList<>();
list.add(1);
list.add(3);
list.add(5);
list.add(7);
list.forEach(System.out::println);
}
创建对象
@Test
public void test() {
Supplier supplier=User::new;
User user = supplier.get();
}
去除前后空格并转为大写
@Test
public void test16() {
Function fun=s->{s=s.trim();s=s.toUpperCase();return s;};
String apply = fun.apply(" abCd");
}
删除集合元素
@Test
public void test() {
List list=new ArrayList<>();
list.add(new User("tom",20));
list.add(new User("jack",18));
list.add(new User("marry",22));
list.removeIf(e->e.getName()=="jack");
}
那如果我们要用内置函数式接口创建对象,怎么做呢?
@Test
public void test() {
Supplier supplier=User::new;
User user = supplier.get();
System.out.println(user);//User [name=null, age=0]
}
那到底使用的是哪个构造器呢?通过输出创建的对象可以发现调用的是无参构造器,即调用构造器参数个数对应Supplier中get方法的参数个数的构造器
那么问题又来了,如果我们要使用两个参数的构造器,那Supplier也不行啊,Function的apply方法也只有一个参数,怎么办?那我们去java.util.function包下找找有没有可以用的接口
@FunctionalInterface
public interface BiFunction {
R apply(T t, U u);
}
//使用内置函数式接口创建对象
@Test
public void test20() {
BiFunction biFunction=User::new;
User user = biFunction.apply("tom", 20);
System.out.println(user);//User [name=tom, age=20]
}
四个基本接口参数个数不够用也可以类似的去java.util.function包下找找有没有申明好的函数式接口
最后一个问题,我发现
list.forEach(System.out::println);
遍历List时,使用的forEach的参数Consumer的accept方法是这么写的,我第一个想到的是,out是System的一个内部类,但是当我点进去,发现是这样的
public final static PrintStream out = null;
out是一个静态的成员变量,那我的理解就是System.out其实是一个对象
这样System.out::println的写法其实也就是对象名::方法名
我是这样理解的,如果错了还请赐教!
Stream
介绍
java8添加了一个抽象流Stream,可以让我们像写sql一样操作集合元素。Stream将要处理的元素看做是一种流,在管道中传输,并进行处理,最后由终止操作得到处理的结果。
什么是Stream?
Stream是一个来自特定元素队列并支持聚合操作
元素是具体类型的对象,形成一个队列。
数据源是流的来源。
聚合操作是类似sql一样的操作,比如filter, map, reduce, find, match, sorted等。
Stream自己不会存储元素。
Stream不会改变源对象。
Stream操作是延迟执行的。
创建流
串行流
stream():即单线程的方式去操作流
并行流
parallelStream():即多线程方式去操作流
@Test
public void test() {
//1通过Collection提供的stream()和parallelStream()方法
List list = Arrays.asList("a","b","c");
Stream stream1 = list.stream();
Stream stream2 = list.parallelStream();
//2通过Arrays的静态方法stream()
String[] strs= {"a","b","c"};
Stream stream3 = Arrays.stream(strs);
//3通过Stream类中的静态方法of()
Stream stream4 = Stream.of("a","b","c");
//4通过Stream类的iterate方法生成无限流
Stream stream5 = Stream.iterate(0, (x)->x+1);
//5通过Stream的generate方法生成无限流
Stream.generate(()->Math.random());
}
中间操作
过滤
使用 filter(Predicate super T> predicate)来按照一定规则对流中元素进行过滤
@Test
public void test() {
List list = Arrays.asList(1,2,3,4,5);
Stream stream = list.stream();
stream = stream.filter((x)->x.compareTo(2)>0);
stream.forEach(System.out::println);
}
输出:
3
4
5
@Test
public void test2() {
List list = Arrays.asList(1,2,3,4,5);
Stream stream = list.stream();
stream = stream.filter(
(x)->{
System.out.println(x);
return x.compareTo(2)>0;}
);
}
结果:没有任何输出,这也就是前面说的Stream操作是延迟执行的,只有当终止操作这些中间操作才会依次执行
截断
使元素的个数不超过指定的数目
@Test
public void test() {
List list = Arrays.asList(1,2,3,4,5);
Stream stream = list.stream();
stream=stream.limit(3);
stream.forEach(System.out::println);
}
输出:
1
2
3
可以看到只输出了给定个元素
跳过元素
跳过流中前几个元素
@Test
public void test4() {
List list = Arrays.asList(1,2,3,4,5);
Stream stream = list.stream();
stream=stream.skip(2);
stream.forEach(System.out::println);
}
输出:
3
4
5
跳过了前两个元素
唯一筛选
两个元素通过hashCode()判断两个元素是否相同
@Test
public void test5() {
List list = Arrays.asList(1,2,3,4,5,5);
Stream stream = list.stream();
stream.distinct().forEach(System.out::println);
}
输出:
1
2
3
4
5
映射
map(method)接受一个方法,把流中的元素按照方法进行转换
@Test
public void test() {
List list = Arrays.asList("a","b","c");
Stream stream = list.stream();
stream=stream.map((x)->x.toUpperCase());
stream.forEach(System.out::println);
}
输出:
A
B
C
flatMap(method)也是接受一个函数作为参数,但是与map,不同的是如果这个函数生成的本来就是流,它会把函数生成流中的元素加到流中
//这个函数本身就生成流
public static Stream toStream(String s){
List list=new ArrayList();
char[] chs = s.toCharArray();
for (char c : chs) {
list.add(c);
}
Stream stream = list.stream();
return stream;
}
@Test
public void test() {
List list = Arrays.asList("aaa","bbb","ccc");
Stream> stream =
//由于函数本身就生成流,所以流中加入的还是流
list.stream().map(StreamTest::toStream);
//遍历的时候需要先从流中取出流,在遍历
stream.forEach((s)->s.forEach(System.out::println));
}
//然而我们可以使用flatMap进行改进
@Test
public void test() {
List list = Arrays.asList("aaa","bbb","ccc");
list.stream().flatMap(StreamTest::toStream).forEach(System.out::println);
}
输出:
a
a
a
b
b
b
c
c
c
终止操作
所有匹配
当所有元素都匹配时,allMatch(Predicate super T> predicate)才会返回true
@Test
public void test() {
List list = Arrays.asList("aaa","bbb","ccc");
boolean allMatch = list.stream().allMatch((s)->s.length()>2);
System.out.println(allMatch);
}
输出:
true
任一匹配
当Stream中任一一个元素匹配时,anyMatch(Predicate super T> predicate)返回true
@Test
public void test() {
List list = Arrays.asList("aaa","bbb","ccc");
boolean anyMatch = list.stream().anyMatch((s)->s.equals("bbb"));
System.out.println(anyMatch);
}
输出:
true
所有不匹配
当Stream中所有的元素都不匹配时,noneMatch(Predicate super T> predicate)返回true
@Test
public void test() {
List list = Arrays.asList("aaa","bbb","ccc");
boolean noneMatch = list.stream().noneMatch((s)->s.equals("ddd"));
System.out.println(noneMatch);
}
输出:
true
第一个元素
返回当前流中的第一个元素
@Test
public void test() {
List list = Arrays.asList(1,2,3,4,5);
Optional findFirst = list.stream().findFirst();
System.out.println(findFirst.get());
}
输出:
1
任一一个元素
返回当前流中的任一一个元素
@Test
public void test() {
List list = Arrays.asList(1,2,3,4,5);
Optional findAny = list.stream().findAny();
System.out.println(findAny.get());
}
输出:
1
//使用并行流试试
@Test
public void test13() {
List list = Arrays.asList(1,2,3,4,5);
Optional findAny = list.parallelStream().findAny();
System.out.println(findAny.get());
}
输出:
3
流中元素个数
返回流中的元素个数
@Test
public void test14() {
List list = Arrays.asList(1,2,3,4,5);
long count = list.stream().count();
System.out.println(count);
}
输出:
5
流中的最大值
返回流中元素的最大值
@Test
public void test15() {
List list = Arrays.asList(1,2,3,4,5);
Optional max = list.stream().max(Integer::compare);
System.out.println(max.get());
}
输出:
5
流中的最小值
返回流中的最小值
@Test
public void test16() {
List list = Arrays.asList(1,2,3,4,5);
Optional min = list.stream().min(Integer::compare);
System.out.println(min.get());
}
输出:
1
规约
将流中的元素反复结合得到一个最终值
@Test
public void test() {
List list = Arrays.asList(1,2,3,4,5);
Optional reduce = list.stream().reduce(Integer::sum);
System.out.println(reduce.get());
Integer reduce2 = list.stream().reduce(0, (x,y)->{
System.out.println(x+"->"+y);
return x+y;
});
System.out.println(reduce2);
}
输出:
15
0->1
1->2
3->3
6->4
10->5
15
可以看到当使用(T identity, BinaryOperator accumulator)时,identity即为最初和流中元素进行运算的,所以值不能为空,所以返回的不是Optional
收集
将流转换成其他形式
@Test
public void test() {
List list = Arrays.asList(1,2,3,4,5,5);
Set collect = list.stream().collect(Collectors.toSet());
System.out.println(collect);
}
输出:
[1, 2, 3, 4, 5]
@Test
public void test() {
List list = Arrays.asList(1,2,3,4,5,5);
Optional collect = list.stream().collect(Collectors.maxBy(Integer::compareTo));
System.out.println(collect.get());
}
输出:
5
class Stu{
String name;
Integer age;
String gender;
public Stu(String name, Integer age, String gender) {
super();
this.name = name;
this.age = age;
this.gender = gender;
}
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 String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Stu [name=" + name + ", age=" + age + ", gender=" + gender + "]";
}
}
//一级分组
@Test
public void test() {
List list = Arrays.asList(
new Stu("张三",20,"男"),
new Stu("李四",22,"女"),
new Stu("王五",18,"男"),
new Stu("赵六",20,"女"),
new Stu("田七",22,"女")
);
Map> collect = list.stream().collect(Collectors.groupingBy(Stu::getGender));
System.out.println(collect);
}
输出:
{女=[Stu [name=李四, age=22, gender=女], Stu [name=赵六, age=20, gender=女], Stu [name=田七, age=22, gender=女]], 男=[Stu [name=张三, age=20, gender=男], Stu [name=王五, age=18, gender=男]]}
//二级分组
@Test
public void test21() {
List list = Arrays.asList(
new Stu("张三",20,"男"),
new Stu("李四",22,"女"),
new Stu("王五",18,"男"),
new Stu("赵六",20,"女"),
new Stu("田七",22,"女")
);
Map>> collect = list.stream()
.collect(Collectors.groupingBy(Stu::getAge, Collectors.groupingBy(Stu::getGender)));
System.out.println(collect);
}
输出:
{18={男=[Stu [name=王五, age=18, gender=男]]}, 20={女=[Stu [name=赵六, age=20, gender=女]], 男=[Stu [name=张三, age=20, gender=男]]}, 22={女=[Stu [name=李四, age=22, gender=女], Stu [name=田七, age=22, gender=女]]}}
//分区
@Test
public void test22() {
List list = Arrays.asList(
new Stu("张三",20,"男"),
new Stu("李四",22,"女"),
new Stu("王五",18,"男"),
new Stu("赵六",20,"女"),
new Stu("田七",22,"女")
);
Map> collect = list.stream()
.collect(Collectors.partitioningBy((e)->((Stu)e).getAge()>20));
System.out.println(collect);
}
输出:
{false=[Stu [name=张三, age=20, gender=男], Stu [name=王五, age=18, gender=男], Stu [name=赵六, age=20, gender=女]], true=[Stu [name=李四, age=22, gender=女], Stu [name=田七, age=22, gender=女]]}
接口默认方法和静态方法
默认方法
interface MyInterface1 {
default String method1() {
return "myInterface1 default method";
}
}
class MyClass{
public String method1() {
return "myClass method";
}
}
/**
* 父类和接口中都有相同的方法,默认使用父类的方法,即类优先
* @author 莫雨朵
*
*/
class MySubClass1 extends MyClass implements MyInterface1{
}
@Test
public void test1() {
MySubClass1 mySubClass1=new MySubClass1();
System.out.println(mySubClass1.method1());//myClass method
}
如果类的父类的方法和接口中方法名字相同且参数一致,子类还没有重写方法,那么默认使用父类的方法,即类优先
interface MyInterface1 {
default String method1() {
return "myInterface1 default method";
}
}
interface MyInterface2 {
default String method1() {
return "myInterface2 default method";
}
}
/**
* 如果类实现的接口中有名字相同参数类型一致的默认方法,那么在类中必须重写
* @author 莫雨朵
*
*/
class MySubClass2 implements MyInterface1,MyInterface2{
@Override
public String method1() {
return MyInterface1.super.method1();
}
}
@Test
public void test2() {
MySubClass2 mySubClass2=new MySubClass2();
System.out.println(mySubClass2.method1());//myInterface1 default method
}
如果类实现的接口中有名字相同参数类型一致的默认方法,那么在类中必须重写
静态方法
interface MyInterface1 {
static String method2() {
return "interface static method";
}
}
@Test
public void test3() {
System.out.println(MyInterface1.method2());//interface static method
}
时间
java8以前使用的时间很多方法都已经废弃了,而且不是线程安全的,java8提供了一系列的时间类,这些时间类都是线程安全的
LocalDate、LocalTime、LocalDateTime
这三个关于时间的类在使用上都类似
/**
* LocalDate
*/
@Test
public void test1() {
LocalDate date1 = LocalDate.now();
System.out.println(date1);//2020-03-30
LocalDate plusYears = date1.plusYears(1);
System.out.println(plusYears);//2021-03-30
LocalDate minusDays = date1.minusDays(2);
System.out.println(minusDays);//2020-03-28
LocalDate date2 = LocalDate.of(2019, 3, 30);
System.out.println(date2.getYear());//2019
}
/**
* LocalTime
*/
@Test
public void test2() {
LocalTime now = LocalTime.now();
System.out.println(now);//21:15:23.418
int minute = now.getMinute();
System.out.println(minute);//15
int second = now.getSecond();
System.out.println(second);//23
LocalTime of = LocalTime.of(10, 10, 10);
System.out.println(of);//10:10:10
LocalTime minusMinutes = of.minusMinutes(2);
System.out.println(minusMinutes);//10:08:10
LocalTime plusHours = of.plusHours(2);
System.out.println(plusHours);//12:10:10
}
/**
* LocalDateTime
*/
@Test
public void test3() {
LocalDateTime now = LocalDateTime.now();
System.out.println(now);//2020-03-30T21:20:37.961
int minute = now.getMinute();
System.out.println(minute);//20
LocalDateTime plusMinutes = now.plusMinutes(20);
System.out.println(plusMinutes);//2020-03-30T21:40:37.961
LocalDateTime minusYears = now.minusYears(2);
System.out.println(minusYears);//2018-03-30T21:20:37.961
LocalDateTime of = LocalDateTime.of(2021, 3, 30, 21, 19, 50);
System.out.println(of);//2021-03-30T21:19:50
}
时间戳
/**
* Instant
*/
@Test
public void test4() {
Instant now = Instant.now();
System.out.println(now);//2020-03-30T13:26:10.640Z
Instant plusSeconds = now.plusSeconds(10);
System.out.println(plusSeconds);//2020-03-30T13:26:20.640Z
//获取时间戳相对于1970年0时0分0秒的毫秒数
long epochMilli = plusSeconds.toEpochMilli();
System.out.println(epochMilli);//1585574780640
}
Duration获取时间间隔
/**
* Duration
*/
@Test
public void test5() {
Instant start = Instant.now();
Instant end = start.plusSeconds(10);
Duration duration = Duration.between(start, end);
long seconds = duration.getSeconds();
//获取时间间隔的秒数
System.out.println(seconds);//10
//获取时间间隔的毫秒数
long millis = duration.toMillis();
System.out.println(millis);//10000
}
Peroid获取日期间隔
@Test
public void test6() {
LocalDate date1 = LocalDate.now();
LocalDate date2 = date1.plusYears(2);
Period period = Period.between(date1, date2);
//获取两时间间隔的月数,指两个月份的间隔数,并不是时间间隔的总月数
int months = period.getMonths();
System.out.println(months);//0
}
TemporalAdjuster矫正日期
@Test
public void test7() {
LocalDate date1 = LocalDate.now();
System.out.println(date1);//2020-03-31
//TemporalAdjusters类中封装了一些常用地时间矫正方法
TemporalAdjuster next = TemporalAdjusters.next(DayOfWeek.FRIDAY);
LocalDate date2 = date1.with(next);
System.out.println(date2);//2020-04-03
//自定义时间矫正器
LocalDate date3 = date1.with(x->{
LocalDate ld=(LocalDate)x;
DayOfWeek dayOfWeek = ld.getDayOfWeek();
if(dayOfWeek.equals(DayOfWeek.FRIDAY)) {
return ld.plusDays(3);
}else if(dayOfWeek.equals(DayOfWeek.SATURDAY)) {
return ld.plusDays(2);
}else {
return ld.plusDays(1);
}
});
System.out.println(date3);//2020-04-01
}
由于TemporalAdjuster是一个函数式接口,所以我们可以使用lambda表达式自定义矫正规则
@FunctionalInterface
public interface TemporalAdjuster {
Temporal adjustInto(Temporal temporal);
}
DateTimeFormatter格式化日期时间
@Test
public void test8() {
LocalDateTime dateTime1 = LocalDateTime.now();
System.out.println(dateTime1);//2020-03-31T18:28:04.256
DateTimeFormatter formatter1 = DateTimeFormatter.ISO_DATE;
String format1 = dateTime1.format(formatter1);
System.out.println(format1);//2020-03-31
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH时mm分ss秒");
String format2 = dateTime1.format(formatter2);
System.out.println(format2);//2020年03月31日18时28分04秒
LocalDateTime dateTime2 = LocalDateTime.parse(format2, formatter2);
System.out.println(dateTime2);//2020-03-31T18:28:04
}
ZoneDate
@Test
public void test9() {
//获取可用时区
ZoneId.getAvailableZoneIds().forEach(System.out::println);
LocalDate date1 = LocalDate.now(ZoneId.of("Asia/Tokyo"));
System.out.println(date1);//2020-03-31
}
ZoneTime
@Test
public void test10() {
LocalTime time1 = LocalTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(time1);//19:44:15.228
OffsetTime atOffset = time1.atOffset(ZoneOffset.ofHours(2));
System.out.println(atOffset);//19:44:15.228+02:00
}
ZoneDateTime
@Test
public void test11() {
LocalDateTime dateTime1 = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
System.out.println(dateTime1);//2020-03-31T18:51:07.136
ZonedDateTime atZone = dateTime1.atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(atZone);//2020-03-31T18:51:07.136+08:00[Asia/Shanghai]+09:00表示时间比格林尼治时间快9小时
}
重复注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MAnnotation {
String name() default "";
int age();
}
public class AnnotataionTest {
@Test
public void test() throws Exception {
Class clazz=AnnotataionTest.class;
Method method = clazz.getMethod("good", null);
MAnnotation annotation = method.getAnnotation(MAnnotation.class);
System.out.println(annotation.name()+":"+annotation.age());
}
@MAnnotation(name="tom",age=20)
public void good() {
}
}
以前我们是这样使用注解,当要在一个方法上标注两个相同的注解时会报错,java8允许使用一个注解来存储注解,可以实现一个注解重复标注
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(MAnnotations.class)//使用@Repeatable来标注存储注解的注解
public @interface MAnnotation {
String name() default "";
int age();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MAnnotations {
MAnnotation[] value();
}
public class AnnotataionTest {
@Test
public void test() throws Exception {
Class clazz=AnnotataionTest.class;
Method method = clazz.getMethod("good");
MAnnotation[] mAnnotations = method.getAnnotationsByType(MAnnotation.class);
for (MAnnotation annotation : mAnnotations) {
System.out.println(annotation.name()+":"+annotation.age());
}
}
@MAnnotation(name="tom",age=20)
@MAnnotation(name="jack",age=25)
public void good() {
}
}