一。自定义注解(形体)
情形一: 单一注解
定义
public @interface MyAnnotation { //定义注解属性: //属性类型 属性名() default 默认值; //String name() default "自定义名字"; String name(); int age() default 20; //注解属性类型:基本类型,String,Class,注解,枚举,和 前面几种类型的一维数组 //Student student; error:The blank final field student may not have been initialized }
使用:
class Demo{ //给注解赋值 @MyAnnotation(name="段哥哥",sex="男") public void hello(){ System.out.println("hello"); } }
情形二:注解的嵌套
定义
public @interface MyAnnotation2 { String url() default ""; MyAnnotation[] myAnnotations(); }
使用
@MyAnnotation2(myAnnotations={@MyAnnotation(name="注解一",sex="女"),@MyAnnotation(name="注解二",sex="男")}) class Demo2{ @MyAnnotation2(myAnnotations={@MyAnnotation(name="注解一",sex="女"),@MyAnnotation(name="注解二",sex="男")}) public void hello(){ System.out.println("hello"); } }
情形三:特殊的注解属性value
定义
public @interface MyAnnotation3 { String name() default "name"; String value() default "男"; }
使用
class Demo3{ @MyAnnotation3("who") public void hello(){} }
ps:当使用注解没有指定给哪个属性赋值时,默认是value属性,所以上面的who就赋值给了value属性。
情形四:数组类型的value
定义
public @interface MyAnnotation3 { String[] value() default ""; }
使用
class Demo3{ //给数组指定一个值 @MyAnnotation3("a") public void hello(){ } //给数组指定二个值 { }的使用 @MyAnnotation3({"a","b"}) public void hello2(){ } }
我们自定义的所有注解类型都是java.lang.Annotation接口的子类,既然子类,那么Annotation接口中的方法我们都可以使用
二。注解的反射(灵魂)
java.lang.reflect.AnnotatedElement
- <T extends Annotation> T getAnnotation(Class<T> annotationClass):该方法获取指定Class类型的注解实例的引用
- Annotation[] getAnnotations():获取所有的注解,包含继承下来的
- Annotation[] getDeclaredAnnotations();获取自己直接使用的注解,不包含继承下来的
- boolean isAnnotaionPresent(Class<? extends Annotaion> annotionType):看看指定的注解在不在
谁来调用这些方法,AnnotatedElement接口的实现类,有以下这几个AnnotatedElement的实现类:
- Class:表示一个类型
- Method:表示一个方法
- Field:表示一个字段
- Constructor:表示一个构造方法
- etc
类的三种状态
注解也就是类,只要是类就有三种状态
一。SOURCE源代码(*.java)
存在磁盘上
二。CLASS字节码(*.class)
存在磁盘上
三。RUNTIME内存中的class
从磁盘上加载到内存上。这就是类加载器所做的
结论:在我们之前定义的注解,也就是类 处在CLASS字节码状态,存在于磁盘上
验证上面的结论:
Class class1=S.class; Method[] methods=class1.getMethods(); for(Method m:methods){ boolean flag=m.isAnnotationPresent(MyTest.class); System.out.println(m.getName()+"有没有"+flag); }
控制台输出的都为false
注解的生命周期-元注解
元注解:在注解上面定义的注解
元注解的种类
@Rentention
作用;改变注解的存活范围
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Retention { RetentionPolicy value(); }
public enum RetentionPolicy { /** * Annotations are to be discarded by the compiler. */ SOURCE, /** * Annotations are to be recorded in the class file by the compiler * but need not be retained by the VM at run time. This is the default * behavior. */ CLASS, /** * Annotations are to be recorded in the class file by the compiler and * retained by the VM at run time, so they may be read reflectively. * * @see java.lang.reflect.AnnotatedElement */ RUNTIME }
使用:在注解的定义上,当我们运行下面代码,控制台上就会看到true了
Class class1=S.class; Method[] methods=class1.getMethods(); for(Method m:methods){ boolean flag=m.isAnnotationPresent(MyTest.class); System.out.println(m.getName()+"有没有"+flag); }
但是单单使用了@Rentention只是改变了存活范围,该注解仅仅只能注解方法,要想在类上使用,我们需要在注解的位置上做限定
@Target
作用:注解应用的位置
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.ANNOTATION_TYPE) public @interface Target { ElementType[] value(); }
public enum ElementType { /** Class, interface (including annotation type), or enum declaration */ TYPE, /** Field declaration (includes enum constants) */ FIELD, /** Method declaration */ METHOD, /** Parameter declaration */ PARAMETER, /** Constructor declaration */ CONSTRUCTOR, /** Local variable declaration */ LOCAL_VARIABLE, /** Annotation type declaration */ ANNOTATION_TYPE, /** Package declaration */ PACKAGE }
@Documented:应用了该注解的注解的类,对应的文档中是否显示注解
@Inherited:被注解的注解的类 的子类,会自动注解子类
结论:仅仅定义注解只是定义了形体,如果想要发挥作用,就是需要反射,而反射所依赖的一些东西,需要元注解去注解
三。注解的意义
在开发中,通过一些xml配置来指挥程序的运行,
缺点:开发不直观
优点:避免硬编码
注解是来替代xml作为配置用的
优点:直观,开发简便,快速
缺点:硬编码