Java入门篇——注解Annotation

来源:互联网 发布:淘宝招聘官网首页 编辑:程序博客网 时间:2024/05/01 17:36

在Java1.5以后,引入了注解,也称作元数据。作为新的特性,同时也是基础知识之一,我们应该学会使用这种用法,虽然反射会带来代码效率问题,但相比于它的优点,这种损失我们还是可以承受的。

元数据被定义为:描述数据的数据,对数据及信息资源的描述性信息。

我们可以认为注解的目的就是对数据添加的附加信息。在java源代码中添加注解,有助于减轻编写“样板”代码的负担(findViewById),更加干净易读的代码以及编译器类型检查。

注解的语法比较简单,使用一个@符号修饰代表的就是一个注解。Java 5内置的几种注解有

//当前的方法定义将覆盖超类中的方法@Override//代表被这个元数据修饰的元素已被废弃,使用已废弃的方法或对象编译器会发出警告@Deprecated//关闭不当的编译器警告信息,比如unchecked,未检查的类型等@SuppressWarnings

自定义注解类型

使用@interface来定义注解类型

public @interface Test {}

使用注解类型

//一般写法,比较优美@Testvoid test(){}//注解可以看做是一种修饰符,它的使用和修饰符几乎一模一样// 不太好看,不建议public static synchronized @Test void  test(){}

上面简单定义了一个注解,但是一般我们定义的注解,还会定义一些注解的类型,Annotation有四种元注解类型,元注解专职负责注解其他的注解,详情可以看Java API里面的Annotation

# Retention @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Retention {    RetentionPolicy value();}# Target @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Target {    ElementType[] value();}# Documented@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Documented {}# Inherited @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.ANNOTATION_TYPE)public @interface Inherited {}

Rentention
Rentention定义该注解在哪一个级别保留该注解信息,可选的RetentionType参数包含

// 存在于Java源文件,注解被编译器丢弃SOURCE// 存在于Java源文件,以及经编译器后生成的Class字节码文件,但在运行时VM不再保留注解CLASS// 存在于源文件、编译生成的Class字节码文件,以及保留在运行时VM中,因此可通过反射读取注解RUNTIME

Target
Target表示该注解可以用于什么地方,可能使用的参数ElementType包括

// 注解类型,表示这个注解只能用于注解类型// 比如Target,Rentention,Inherited,Documented这些元注解都是用于注解类型的ANNOTATION_TYPE// 构造器声明CONSTRUCTOR// 字段声明(包括enum实例)FIELD// 局部变量声明LOCAL_VARIABLE// 方法声明METHOD// 包声明PACKAGE// 参数声明PARAMETER// 类,接口(包括注解类型)或者enum声明TYPE

Documented
当前注解的元素会被javadoc工具进行文档化,那么在查看Java API文档时可查看该注解元素。

Inherited
允许子类继承父类中的注解

注解元素
在注解中还会包含一些元素表示值,当使用Class里面的方法分析处理注解的时候,程序就可以访问这些值。在注解中定义元素就像在普通接口中定义方法,但是注解可以使用default定义元素的默认值。对于没有元素的注解,我们可以把它作为标记来使用。比如被@Test标记的方法为测试方法
注解元素可用的类型包括如下几个

基本数据类型StringClassenumAnnotation以上类型的数组

对于注解里面的元素,必须有一个确定的值,不能够使用null这种未定义的值作为默认值,所以我们可以使用空对象这样的概念来解决这个问题,比如定义空字符串作为字符串为null的情况。

解析注解
在很多ORM数据库框架中都使用了注解来定义Bean类,直接使用Bean类生成数据库表。比如ORMLite。

如果不对注解进行解析的话,其实注解就没什么意义了,可以通过Annotation中提供的API来访问注解。那么先来看一下Class提供给我们的用于解析注解的结构方法

// 如果当前元素包含指定的注解类型,则返回该注解对象,如果不存在则返回null<A extends Annotation> getAnnotation(Class<A> annotationClass)// 返回这个元素上的所有注解Annotation[] getAnnotations()// 返回直接定义在这个元素上的注解Annotation[] getDeclaredAnnotations()// 如果当前这个元素包含指定的注解类型则返回trueboolean isAnnotationPresent(Class<? extends Annotation> annotationClass)

在Android中也有通过注解实现的IOC,我们在编写程序的时候,如果xml里面有很多很多的控件,这样就需要写很多遍findViewById,不但写起来很累,而且很占空间,于是人们就想到了通过注解来减轻这样的编写“样板”代码的负担。

ViewInject注解类

@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface ViewInject {    int id() default -1;}

BaseActivity基类,使用模板方法

public abstract class BaseActivity extends Activity {    private Context mContext;    private void inject() {        Class activity = getClass();        //获取Activity内所有的字段        Field[] fields = activity.getDeclaredFields();        for (Field field : fields) {            field.setAccessible(true);            // 获取指定的注解类型,如果返回null则跳过            ViewInject viewInject = field.getAnnotation(ViewInject.class);            if (viewInject != null) {                // 获取指定的属性                int value = viewInject.id();                try {                    // 反射获取findViewById方法                    Method method = activity.getMethod("findViewById", int.class);                    method.setAccessible(true);                    // 调用该方法,因为findViewById要求是在Activity对象上的方法                    Object object = method.invoke(mContext, value);                    field.set(mContext, object);                } catch (NoSuchMethodException e) {                    e.printStackTrace();                } catch (InvocationTargetException e) {                    e.printStackTrace();                } catch (IllegalAccessException e) {                    e.printStackTrace();                }            }        }    }    protected abstract int requestLayout();    protected abstract void bindView();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(requestLayout());        mContext = this;        inject();        bindView();    }}

MainActivity实现类

public class MainActivity extends BaseActivity {    @ViewInject(id=R.id.text)    private TextView textView;    @Override    protected int requestLayout() {        return R.layout.layout_annotation;    }    @Override    protected void bindView() {        textView.setText("1131");    }}

现在大致上那些retrofit,dagger,butterknife使用的注解也是基于这个原理的罢,有空去好好研究一下这几个开源框架的源码,使用注解,我们应该还需要有类加载,泛型,反射等基础知识,才能够把注解玩的飞起。

1 0
原创粉丝点击