Java注解

2017-07-28  本文已影响0人  欢欢喜喜AA

元数据

元数据是指用来描述数据的数据(“data about data”),简单的说,就是描述代码间关系,代码本身,资源数据等的数据。一般是结构化数据(如存储在数据库里的数据,规定了字段的长度、类型等)。元数据是指从信息资源中抽取出来的用于说明其特征、内容的结构化的数据。
比如,关于一本书(信息资源),我们在图书馆系统中检索可以得到题名,版本、出版数据、相关说明,包括检索点等,这些就是元数据,用于描述资源的。

Java注解

JDK5.0引入了Annotation的概念来描述元数据,在java中,元数据是以标签的形式存在,元数据并不会影响程序代码的编译和执行。JDK5.0出来后,java语言中有四种类型,class(类)、enum(枚举)、interface(接口)和@interface(注解),他们在java中处于同一级别。元数据在java中,既是由注解来表示的。注解可以在编译、类加载和运行时被读取。
Java的注解本质上是一个接口,继承了接口Annotation的接口,这里的继承用的是extends。

注解既然和接口类似,就会包含成员:

@interface隐含继承Annotation,但是自定义注解不能直接继承Annotation。直接继承的是接口,非注解。

注解的作用

  1. 编写文档(通过代码里面标识的元数据编写,例如@param,@return)
  2. 代码分析(可以替代配置文件)
  3. 编译检查(通常配合lint使用,如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。)

Java中自带的注解

(基本)

  1. @Override(限定重写父类的方法)
  2. @SuppressWarning(用于抑制编译器产生警告信息)
  3. @Deprecated(表示"已过时/不建议使用/在后期的API中可能被删除")
  4. @SafeVarargs(java7新增,和堆污染有关,具体不是很了解)
    (用于标识注解的注解)
  5. @Retention(决定注解存活和读取时间,它包含一个RetationPolicy的value成员变量,用于指定它所修饰的注解的读取和存活时间)
    一般的值有:
    • Retationpolicy.CLASS:在类加载的时候解读,执行的时候,jvm就会抛弃掉。
    • Retationpolicy.ROURCE:存在于源码中,在编译的时候解读,之后就抛弃,不进入类加载和运行环节。
    • Retationnpolicy.RUNTIME:运行时解读,可以在运行时通过反射获取注解,执行操作。
  6. @Target (指定修饰的元素,包含一个value值)
    value可选:
    • ElementType.ANNOTATION_TYPE: 指定该Annotation只能修饰Annotation。
    • ElementType.CONSTRUCTOR: 指定只能修饰构造器。
    • ElementType.FIELD: 指定只能成员变量。
    • ElementType.LOCAL_VARIABLE: 指定只能修饰局部变量。
    • ElementType.METHOD: 指定只能修饰方法。
    • ElementType.PACKAGE: 指定只能修饰包定义。
    • ElementType.PARAMETER: 指定只能修饰参数。
    • ElementType.TYPE: 指定可以修饰类,接口,枚举定义。
  7. @Document(可被javadoc提取成文档)
  8. @Inherited 被他修饰的Annotation具有继承性

自定义注解

最简单的例子,Android中代替findviewById()对组件进行初始化。只需要在成员变量组件前加上注解就行,能有效的减少代码量,可读性更强。

首先定义注解,运行时读取,作用于成员变量
写法和接口雷同
用法是@FindView(R.id.XXX)这里传入的Id就是这个value的返回值,可以在获取到这个注解实例后调用这个value() 方法获取值
可以不止一个方法,多一个方法,就可以多传一个参数,如果有默认值,也可以不传

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface FindView{
    /**
     * id 注解,这里没有默认值,不过可以自由添加,
     * 例如:public String tableName() default "className";
     * @return
     */
    int value();
}

从上面注解的定义来,类似于接口,所以注解本身不具备解析和其他操作的功能,这里我们想要的是利用这个注解携带的value,初始化这个注解所表示的成员变量,所以我们需要自己写一个工具类,通过传入的activity获取注解,并利用反射初始化成员变量。以下是详细代码:

public class ViewUtil {
    public static void viewInject(Activity activity){
        viewInjects(activity);
    }

    private static void viewInjects(Activity activity){
        Class<? extends Activity> clazz = activity.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for(Field field:fields){
            FindView viewFind = field.getAnnotation(FindView.class);
            if(viewFind!=null){
                int viewId = viewFind.value();
                View view = activity.findViewById(viewId);
                try {
                    field.setAccessible(true);
                    field.set(activity, view);

                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

最后是具体的使用方法

public class MainActivity extends AppCompatActivity {
       @FindView(R.id.btn)
    Button btn;
    @FindView(R.id.btn1)
    Button btn1;
    @FindView(R.id.btn2)
    Button btn2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewUtil.viewInject(this);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                btn.setText("Test2");
            }
        });
    }
}

以上是@Retention(RetentionPolicy.RUNTIME)运行时解读的具体写法。
优点是可读性强,写起来简单。
缺点便是常驻内存,并利用反射进行初始化,耗费内存

如何解决这个问题呢?其实我们可以利用apt,使用编译时解读或者类加载时解读,后面的文章我会重点讲到,这里就不多啰嗦了。

So easy!

上一篇下一篇

猜你喜欢

热点阅读