(1)Java5开始,Java开始对元数据的支持,也就是Annotation(注解);
(2)所有的Annotation都是java.lang.annotation接口的子接口,所以 Annotation是一种特殊的接口(枚举也是特殊的类);
(3)@interface Override {} —> interface Override extends java.lang.annotation.Annotation{};
(4)注解用来为程序元素(类,方法,成员变量等)设置元数据,注解,标签,Annotation都是一体。
使用注解需要注意,必须有三方参与才有意义:
1)得有注解标签本身;
2)被贴的程序元素(类,字段,构造器,方法,参数,包等)
3)由第三方的程序使用反射的手段来赋予注解特殊的功能
import java.util.List;
public class AnnoDemo extends Object{
//重载父类方法
@Override
public String toString() {
return null ;
}
//消除警告,实际问题并未解决
@SuppressWarnings({ "rawtypes", "unused" })
public void show() {
List list = null;
}
}
Annotation贴在类,字段,构造器,方法,参数,包等上
JDK5中提供了三个注解
@Override:标记重写父类的方法
@Deprecated:标记过时/不推荐使用的方法和类
@SuppressWarings:抑制编译器发出的警告,仅仅是看不到警告(问题依然存在)
@SuppressWarings(“all”):抑制所有的警告
注意:虽然Java5才出现注解,但是Java.util.Date在Java1.1就过时了,这个时候该如何进行注解呢?
/**
* Allocates a Date
object and initializes it so that
* it represents midnight, local time, at the beginning of the day
* specified by the year
, month
, and
* date
arguments.
*
* @param year the year minus 1900.
* @param month the month between 0-11.
* @param date the day of the month between 1-31.
* @see java.util.Calendar
* @deprecated As of JDK version 1.1,
* replaced by Calendar.set(year + 1900, month, date)
* or GregorianCalendar(year + 1900, month, date)
.
*/
@Deprecated
public Date(int year, int month, int date) {
this(year, month, date, 0, 0, 0);
}
原来是通过文档注释来实现注解功能:
/**
*
* @deprecated
*/ public void show2() {
java.util.Date d = new Date();
}
在JDK7中新增了一个注解:
@SafeVarargs:抑制堆污染发出的警告(问题依然存在)一个方法中同时出现可变参数和泛型
@SafeVarargs
public static List asList(T... a){
return null;
}
(1)为什么有的注解允许接收参数,有的却不可以?
比如:@SuppressWarings(“all”),@SuppressWarnings({ “rawtypes”, “unused” }),而@Override就没有
通过阅读各自的源代码发现,@SuppressWarnings有一个value的抽象方法,其他没有。
public @interface SuppressWarnings {
/**
* The set of warnings that are to be suppressed by the compiler in the
* annotated element. Duplicate names are permitted. The second and
* successive occurrences of a name are ignored. The presence of
* unrecognized warning names is not an error: Compilers must
* ignore any warning names they do not recognize. They are, however,
* free to emit a warning if an annotation contains an unrecognized
* warning name.
*
* Compiler vendors should document the warning names they support in
* conjunction with this annotation type. They are encouraged to cooperate
* to ensure that the same names work across multiple compilers.
*/
String[] value();
}
(2)为什么有的注解可以贴在类/方法/变量上,而有的只能贴在方法上?
元注解@Target决定了该注解可以贴在什么地方(元注解中介绍)
(1)什么是元注解?
元注解:在定义注解的时候用来贴在注解上的注解,用来限定注解的用法
(2)元注解有哪些?
@Retention,@Target,@Documented,@Inherited
其中@Documented,@Inherited只需了解
@Documented:表示注解会被Javadoc指令编辑到API中
@Inherited:表示注解会遗传给子类
(3)重要元注解(@Retention,@Target)
代码示例:
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
String[] value();
}
(1)定义注解的语法:
@interface 注解名称
例如:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface VIP {
//抽象的方法 称为属性
int level();
String info();
String[] hobby();
Gender sex();
}
enum Gender{
MAN,WOMEN;
}
注解的抽象方法称为属性,如果要给这个属性赋予默认值可以在抽象方法后使用default值
(2)使用注解的语法:
@注解名[(属性名 = 属性值 , 属性名 = 属性值)]
例如:@VIP
注意:给属性赋值时如果只有一个属性要赋值且名称叫value时,可以省略不写
例如:@VIP(“XXX”)
//被贴的程序元素
@VIP(level=3, info = "高级会员", hobby = { "Java" }, sex = Gender.MAN)
public class Person {
}
注意:属性的返回值类型只能是基本类型/Class/注解/String/枚举以及它们各自的数组
不然会出错:
(1)需求:
获取类上的注解信息(以上面的Person类为例)
注解可以贴在类、字段、方法上,也就是说注解可以成为类/字段/方法的一部分
如何获取到注解上的信息呢?
Class具有获取类上成员的方法
Method具有获取方法上成员的方法
要获取的注解在谁上面,就去哪个类中去找
(2)思路:
1:获取注解所在类的字节码对象
2:在字节码中获取出注解对象
3:调用抽象方法获取注解属性的值
public class App {
public static void main(String[] args) {
//1.获取注解所在类的字节码对象
Class clz = Person.class;
//2.在字节码中获取出注解对象
//获取VIP注解
if(clz.isAnnotationPresent(VIP.class)) {
VIP vip = clz.getAnnotation(VIP.class);
//3.调用抽象方法获取注解属性的值
System.out.println(vip.level());
System.out.println(vip.info());
System.out.println(vip.hobby()[1]);
System.out.println(vip.sex());
}
//获取类上的所有注解
/*Annotation[] annos = clz.getAnnotations();
for(Annotation anno : annos)
{
System.out.println(anno);
}*/
}
}
(1)模拟JUnit4.x,必须明白它的运行效果:
先执行@Before标注的方法,再运行@Test标注的方法,最后运行@After标注的方法
(2)思路:
1.开发出3个注解
2.把注解贴在测试类中
3.开发第三方程序赋予注解功能(执行的先后顺序)
1)获取要测试的字节码对象
2)获取字节码对象中的所有方法
3)归类区分带有不同注解的方法,分三类
4)迭代所有要执行的方法,在所有要执行的方法前先执行MyBefore注解的方法,在所有要执行的方法后再执行MyAfter注解的方法
MyBefore.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyBefore {
}
MyTest.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyTest {
}
MyAfter.java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAfter {
}
MyUnitTest.java
public class MyUnitTest {
//类中的方法就是被贴的程序元素
@MyBefore
public void init() {
System.out.println("MyUnitTest.init()");
}
@MyAfter
public void destory() {
System.out.println("MyUnitTest.destory()");
}
@MyTest
public void save() {
System.out.println("MyUnitTest.save()");
}
@MyTest
public void update() {
System.out.println("MyUnitTest.update()");
}
}
App.java
public class App {
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
// TODO Auto-generated method stub
//准备三个容器装分类出来的方法
Method beforeMethod = null;
Method afterMethod = null;
List testMethods = new ArrayList<>();
//3.开发第三方程序赋予注解功能
//3.1获取到要测试的字节码对象
Class> clz = MyUnitTest.class;
//反射创建当前类的对象
Object obj = clz.newInstance();
//3.2获取字节码对象中的所有方法
Method[] ms = clz.getMethods();
//3.3归类区分带有不同注解的方法
for(Method m : ms) {
if(m.isAnnotationPresent(MyBefore.class)) {
//代码来到这里说明有MyBefore注解
beforeMethod = m;
}else if(m.isAnnotationPresent(MyAfter.class)){
//代码来到这里说明有MyAfter注解
afterMethod = m;
}else if(m.isAnnotationPresent(MyTest.class)){
//代码来到这里说明有MyTest注解
testMethods.add(m);
}
}
//3.4迭代所有要执行的方法
for (Method method : testMethods) {
//在所有要执行的方法前先执行MyBefore注解的方法
if(beforeMethod != null) {
beforeMethod.invoke(obj);
}
//通过反射调用方法
method.invoke(obj);
//在所有要执行的方法后再执行MyAfter注解的方法
if(afterMethod != null) {
afterMethod.invoke(obj);
}
}
// System.out.println(beforeMethod);
// System.out.println(afterMethod);
// System.out.println(testMethods);
}
}