Java基础之注解Annotation

Java基础之注解Annotation

先说说学习注解的原因吧!在一次项目中,android需要同步表数据,但是这么多表的创建不可能在本地分别写语句进行创建,不现实,同时也不利于扩展,所以就抽出写一个公共方法用于表的创建,这样我们只需要结合反射就能进行表的创建。学习的初衷很简单,就是要解决这个问题,所以在我们学习后,我们会通过这个小小的案例来实战一把。

一、简介
1、什么是注解?

注解同样是JDK1.5以后引入来的技术,Annotation(注解)就是Java提供了一种元程序中的元素关联任何信息和着任何元数据(metadata)的途径和方法。Annotion(注解)是一个接口,程序可以通过反射来获取指定程序元素的Annotion对象,然后通过Annotion对象来获取注解里面的元数据。说的在通俗一点,注解就是一种标记,加上注解就是加上一种标记,有了这种标记我们就可以通过反射进行判断。

2、注解的分类?

此处根据使用用途分为3类:

  • JDK内置系统注解
  • 元注解
  • 自定义注解

下面我们分别对这三种注解进行说明:
(1)、系统内置注解:这个我们应该不陌生,最常见的3类分别是:

  • @Override注解,它的作用就是标志子类重写父类的方法。如果我们用在一个父类没有的方法上编译器就会报错。在android开发中,@Override注解随处可见,继承Activity,我们需要重写OnCreate()方法、继承AsyncTask类,我们需要重写doInBackground方法。
  • @Deprecated注解,Deprecated单词的汉语意思就是不赞成、反对,那么这个注解的作用也是类似。我们知道随着jdk的版本不断变更,肯定有些方法不被推荐使用,但是对于那些老的资深程序,可能是习惯吧!还继续使用,编译器对此方法就会有警告提醒,eclipse里面会有条黄线,看着很不爽,如果想没有这个东西,就在函数上部添加这个注解进行声明。
  • @SuppressWarnnings, @SuppressWarnings 被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。

(2)、元注解:元注解就是对注解进行标记的注解,有四类。

  • @Target:目标的意思,表示注解用于什么地方。例如:用于类、还是方法。可选参数有:
    ElementType.CONSTRUCTOR: 构造器声明
    ElementType.FIELD: 成员变量、对象、属性(包括enum实例)
    ElementType.LOCAL_VARIABLE: 局部变量声明
    ElementType.METHOD: 方法声明
    ElementType.PACKAGE: 包声明
    ElementType.PARAMETER: 参数声明
    ElementType.TYPE: 类、接口(包括注解类型)或enum声明

  • @Retention:保留的意思,表示注解的声明周期,通俗点就是注解在什么范围内有效。可选参数有:
    RetentionPolicy.SOURCE: 停留在java源文件,编译器被丢掉
    RetentionPolicy.CLASS:停留在class文件中,但会被VM丢弃(默认)
    RetentionPolicy.RUNTIME:内存中的字节码,VM将在运行时也保留注解,因此可以通过反射机制读取注解的信息

  • @Documented:文档的意思,没有成员只是一个标记。表示可以被javadoc工具类文档化
  • @Inherited:继承的意思,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

(3)自定义注解,这个是我们使用最频繁的注解,我们可以根据自己的需要进行定义。语法是使用@interface用来声明一个注解,其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

自定义注解格式:
public @interface 注解名称
例如:public @interface TableAnnotation

注解参数的可支持数据类型:

  • 所有基本数据类型(int,float,boolean,byte,double,char,long,short)
  • String类型
  • Class类型
  • enum类型
  • Annotation类型
  • 以上所有类型的数组

Annotation类型里面的参数该怎么设定:
第一,只能用public或默认(default)这两个访问权修饰.例如,String value();这里把方法设为defaul默认类型;
第二,参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和 String,Enum,Class,annotations等数据类型,以及这一些类型的数组.例如,String value();这里的参数成员就为String; 
第三,如果只有一个参数成员,最好把参数名称设为”value”,后加小括号.例:下面的例子FruitName注解就只有一个参数成员。
实例:

@Target(ElementType.TYPE)
public @interface TableAnnotation {
String name() default "";
boolean isPrimaryKey() default false;
}

注解元素的默认值:

注解元素必须有确定的值,要么在定义注解的默认值中指定,要么在使用注解时指定,非基本类型的注解元素的值不可为null。因此, 使用空字符串或0作为默认值是一种常用的做法。这个约束使得处理器很难表现一个元素的存在或缺失的状态,因为每个注解的声明中,所有元素都存在且,并都具有相应的值,为了绕开这个约束,我们只能定义一些特殊的值,例如空字符串或者负数,一次表示某个元素不存在,在定义注解时,这已经成为一个习惯用法。

以上就是关于注解的基础知识点,下面我们就结合上篇的反射来撸一把开篇的案例。

首先我们创建表的注解AnnotationTables:

    package com.dsw.annotationdemo;

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface AnnotationTables {
        //表的名称
        String name() default "";
    }

我们创建表的注解,作用范围为ElementType.TYPE为类,RetentionPolicy.RUNTIME为Runtime时期。该表的注解用于标记我们需要创建的表。

接下来我们创建表字段的注解AnnotationColumns:

 package com.dsw.annotationdemo;

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface AnnotationColumns {
    /**
     * 注解就是标记,所以字段需要有哪些标记呢?
     * 类型、是否为空、名称、是否为主键
     */

    String name() default "";
    String type();
    boolean isNull() default false;
    boolean isPrimaryKey() default false;   
    }

所需要的注解已经创建完毕,那么就开始创建我们的实体,我们创建一个名为Person的实体。

 package com.dsw.annotationdemo;

    @AnnotationTables(name="Person")
    public class Person {
        @AnnotationColumns(name="name",isNull=false,type="String",isPrimaryKey=false)
        private String name;
        @AnnotationColumns(name="age",isNull =false,type="int",isPrimaryKey=false)
        private int age;
        @AnnotationColumns(name="id",isNull=false,type="int",isPrimaryKey=true)
        private int id;
        private String sex;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getSex() {
            return sex;
        }
        public void setSex(String sex) {
            this.sex = sex;
        }
    }

我们在创建一个Student类,不使用AnnotationTables来对比:

 package com.dsw.annotationdemo;

    public class Student {
        @AnnotationColumns(name="name",isNull=false,type="String",isPrimaryKey=false)
        private String name;
        @AnnotationColumns(name="age",isNull=false,type="String",isPrimaryKey=false)
        private String age;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getAge() {
            return age;
        }
        public void setAge(String age) {
            this.age = age;
        }
    }

好了,所需要的原材料都已准备好了,那么现在就开始利用我们上篇的反射知识来烹饪出一个Person表。在Android系统中,我们首先需要创建一个本地数据库,这就需要我们继承SQLiteOpenHelper类,创建我们的DataBaseHelper类。

    package com.dsw.annotationdemo;

    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;

    public class DataBaseHelper extends SQLiteOpenHelper {
        public DataBaseHelper(Context context, String name,int version) {
            //调用系统SQLiteOpenHelper的构造函数进行创建表
            super(context, name, null, version);
        }

        @Override
        public void onCreate(SQLiteDatabase arg0) {

        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        }
    }

创建数据库的关键类我们创建好了,我们不创建Activity来操作了,就继承Application写个子类,在它的onCreate()方法中来实现吧!注意,此时我们要在manifest中对application标签的android:name=”“赋值上我们创建的application的路径名。如:android:name=”com.dsw.annotationdemo.MyApplication”

 package com.dsw.annotationdemo;

    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;

    import android.app.Application;
    import android.database.sqlite.SQLiteDatabase;
    import android.util.Log;

    public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        List<Class<?>> tableClassList = new ArrayList<Class<?>>();
        //创建Person表
        tableClassList.add(Person.class);
        tableClassList.add(Student.class);
        //创建一个SQLiteOpenHelper的对象
        DataBaseHelper dbHelper = new DataBaseHelper(getApplicationContext(), "qxt.db", 1);
        //获取数据库SQLiteDatabase对象
        SQLiteDatabase sqlDatabase = dbHelper.getWritableDatabase();
        initTablesToDb(sqlDatabase,tableClassList);

    }

    /**
     * 创建表
     * @param sqlDatabase
     *      数据库SQLiteDatabase对象
     * @param tableClassList
     *      需要本地化表的集合
     */
    private void initTablesToDb(SQLiteDatabase sqlDatabase,List<Class<?>> tableClassList) {
        if(tableClassList != null && tableClassList.size() >0){
            for(Class<?> clazz : tableClassList){
                StringBuilder sbCreateSQL = new StringBuilder();
                sbCreateSQL.append("Create table ");
                if(clazz.isAnnotationPresent(AnnotationTables.class)){//判断是否为AnotationTable注解类型
                    //获取AnnotationTable注解
                    AnnotationTables tableAnno = clazz.getAnnotation(AnnotationTables.class);
                    //获取AnnotatbleTable注解的属性值,即表名
                    String tableName = tableAnno.name();
                    sbCreateSQL.append(tableName + "(");
                    //通过反射获取Person实体的成员变量,判断哪些是注解要被创建成Person表中的字段
                    Field[] fields = clazz.getDeclaredFields();
                    for(int i=0;i<fields.length;i++){
                        Field field = fields[i];
                        //判断字段是否为注解字段
                        if(field.isAnnotationPresent(AnnotationColumns.class)){
                            AnnotationColumns columnAnno = field.getAnnotation(AnnotationColumns.class);
                            String fieldName = columnAnno.name();
                            String fieldNull = columnAnno.isNull() ? "" : "not null";
                            String fieldKey = columnAnno.isPrimaryKey() ?  "primary key" :"" ;
                            String fieldType = columnAnno.type();
                            sbCreateSQL.append(fieldName + " " +fieldType + " "+ fieldKey + " " + fieldNull + ",");
                        }
                    }
                    sbCreateSQL.replace(sbCreateSQL.length() -1, sbCreateSQL.length(), ")");
                    Log.d("db","db:" + sbCreateSQL.toString());
                    //创建表
                    sqlDatabase.execSQL(sbCreateSQL.toString());
                }
            }
        }
    }
    }

代码注释的比较详细,就不怎么解释了,核心点就是通过反射手段获取注解,然后通过注解获取我们需要的信息,然后创建对应的表。我们直接看结果吧!

db

通过查看数据库,我们发现Person表被创建了,而且只有被AnnotationColumns注解的字段,Student表没有被创建。

至此,我们的案例已经完成,希望大家多多指教。

========================================
作者:mr_dsw 欢迎转载,与人分享是进步的源泉!

转载请保留地址:http://blog.csdn.net/mr_dsw
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值