我爱编程

Java注解总结(一)

2018-04-16  本文已影响0人  TheTwo

Java注解总结(一)


什么是注解

Java注解又称Java标注,是Java语言5.0版本开始支持加入源代码的特殊语法元数据。

说白了注解就是定义在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。这里的注释是给机器看的而不是给人看的。

那么如何定义一个注解

定义注解类不能使用class、enum,也不能使用interface,而是使用@interface。

public @interface MyAnnotation{}

1. 使用注解

注解可以作用在:类(接口或枚举)、属性、方法、构造器、包、参数、局部变量
具体代码如下

@MyAnnotation
public class Person {
    private static final String TAG = Person.class.getSimpleName();
    @MyAnnotation
    private String name;
    @MyAnnotation
    private int age;

    @MyAnnotation
    public Person() {
    }

    @MyAnnotation
    public Person(@MyAnnotation     String name, @MyAnnotation int age)     
    {
        this.name = name;
        this.age = age;
    }

    @MyAnnotation
    public String getName()
    {
        return name;
    }

    public int getAge() 
    {
        return age;
    }

    public void setName(String name)     
    {
        this.name = name;
    }

    public void setAge(int age) 
    {
        this.age = age;
    }

    public void test() 
    {
        @MyAnnotation
        String test = "我是测试数据";
        Log.d(TAG, test);
    }
}

2. 注解中的属性(其实这里叫属性更加简单明了一点 --- 设置属性的值获取属性的值)


public @interface MyAnnotation {

    String name() default "张三";

    int age() default 18;

    String[] clothes();
}

定义一个注解的属性时候可以有默认值,可以没有默认值。如果有指定默认值,在使用注解时,可以不指定属性的值。没有的话就必须给注解属性赋值。
定义属性类型可以是

  1. 所有基本类型(int,float,boolean,byte,double,char,long,short)
  2. String
  3. Class
  4. enum
  5. Annotation
  6. 上述类型的数组
@MyAnnotation(name = "张三", age = 18, clothesColor = {"红色", "蓝色"})
public class Person {
......
}
在定义注解和使用时应当注意
  1. 注的属性后面要有一对圆括号,而且圆括号内不能给出东西。就像是无参的方法一样;
  2. 数组的属性默认值:string[] clothesColor() default {"红色","蓝色","绿色"},这里不能使用new string[]{"红色","蓝色","绿色"},使用注解时,在给数组属性赋值时的格式:@MyAnnotation(clothesColor = {"红色", "蓝色"});

元注解

    上述定义的注解时可以在类(接口或枚举)、属性、方法、构造器、包、参数、局部变量上使用的,那么如何限定自定义注解的作用目标呢和和使用范围?那么就应使用元注解。相信元注解的作用都很清楚了,就是用来修饰自定义注解的,简称-注解的注解。

1. 元注解都有哪些?

  1. @Retention : 指定注解信息在哪个阶段存在Source Class RuntimeRetentionPolicy.SOURCE:这种类型的Annotations只在源代码级别保留,编译时就会被忽略
    RetentionPolicy.CLASS:这种类型的Annotations编译时被保 留,在class文件中存在,但JVM将会忽略
    RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,所以 他们能在 运行时被JVM或其他使用反射机制的代码所读取和使用.

  2. @Target : 指定注解修饰目标对象类型TYPE 类、接口FIELD 成员变量METHOD 方法

  3. @Documented :使用该元注解修饰,该注解的信息可以生成到javadoc 文档中

  4. @Inherited :如果一个注解使用该元注解修改,应用注解目标类的子类都会自动继承该注解

                                                                                        --以上四种类型元注摘自网络
    

Target注解

    在定义注解时可以限制注解的作用目标!例如让注解只能作用在类和方法上。这需要使用元注解:@Target。该注解有一个属性value,类型为ElementType[],它是枚举类型。具体源码如下:
public enum ElementType {
    //类、接口(包括注解类型)或enum声明
    TYPE,

    //字段(域)声明,包括enum实例
    FIELD,

    //方法声明 
    METHOD,

    //于参数声明 
    PARAMETER,

    //构造函数声明
    CONSTRUCTOR,

    //局部变量声明
    LOCAL_VARIABLE,

    //注解声明(应用于另一个注解上)
    ANNOTATION_TYPE,

    //包声明 
    PACKAGE,

    //类型参数声明(1.8新加入)
    TYPE_PARAMETER,
    
    //类型使用声明(1.8新加入)
    TYPE_USE
}

例如将MyAnnotation注解作用在方法和字段上

@Target(value = {FIELD,METHOD})
public @interface MyAnnotation {
 .....
}

那么现在如果要把MyAnnotation定义在类上呢,那是不存在的。编译器会报错,只能在你指定的作用目标上。

public class Person {
    @MyAnnotation(name = "张三")
    private String name;
    @MyAnnotation(name = "张三")
    public String getName() {
        return name;
    }
    ......
}

@Inherited

此注解的作用不是注解继承,而是当一个自定义注解应用了此注解,那么子类对象就可以通过getAnnotations()获取父类被修饰的注解。

@Inherited
@Target(value = {FIELD,METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String name() ;
    int age() default 18;
    String[] clothesColor() default {"红色","蓝色"};
}

@MyAnnotation(name = "李四",age = 20,clothesColor = {"紫色"})
public class Person {
......
}

public class ManPerson extends Person {

}

//测试
Person person = new ManPerson();
            Log.e(TAG + "--Inherited注解:", Arrays.toString(person.getClass().getAnnotations()));
//结果
MainActivity--@Inherited注解: [@com.example.annotationdemo.annotation.MyAnnotation(age=20, clothesColor=[紫色], name=李四)]

通过反射来读取注解

Java在java.lang.reflect 反射包下新增了AnnotatedElement接口,它主要用于表示目前正在 JVM 中运行的程序中已使用注解的元素,通过该接口提供的方法可以利用反射技术地读取注解的信息
返回值 方法名称 说明
Annotation getAnnotation(Class<A> annotationClass) 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getAnnotations() 返回此元素上存在的所有注解,包括从父类继承的
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。。

AnnotatedElement中相关的API方法

返回值 方法名称 说明
Annotation getAnnotation(Class<A> annotationClass) 该元素如果存在指定类型的注解,则返回这些注解,否则返回 null。
Annotation[] getAnnotations() 返回此元素上存在的所有注解,包括从父类继承的
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) 如果指定类型的注解存在于此元素上,则返回 true,否则返回 false。
Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注释。与此接口中的其他方法不同,该方法将忽略继承的注释。(如果没有注释直接存在于此元素上,则返回长度为零的一个数组。)该方法的调用者可以随意修改返回的数组;这不会对其他调用者返回的数组产生任何影响。。

使用

@Inherited
@Target(value = {FIELD}) //只能作用在类中字段上
@Retention(RetentionPolicy.RUNTIME)//运行时被JVM或其他使用反射机制的代码所读取和使用.
public @interface MyAnnotation {
    String name() default "";
}

public class Person {
    @MyAnnotation(name = "张三")
    public String name;
}

//测试
Class<Person> personClass = Person.class;
            //获取该类中所有字段
            try {
                Field namef = personClass.getDeclaredField("name");
                boolean annotationPresentName = namef.isAnnotationPresent(MyAnnotation.class);
                //如果存在此注解
                if (annotationPresentName) {
                    //获取此注解
                    MyAnnotation annotation = namef.getAnnotation(MyAnnotation.class);
                    //打印注解的值
                    Log.e(TAG + "姓名", annotation.name());
                }
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
//结果
MainActivity姓名: 张三
同理如果注解在类上可以用Class<T>.getAnnotation(ClassannotationClass)获取指定类型的注解。

Constructor,Method亦是如此。

上一篇下一篇

猜你喜欢

热点阅读