Java注解、反射,安卓IOC(一)

Java 注解 (Annotation)

Java 注解,指的是代码里边的特殊标记,可以在编译、运行时被读取,并执行相应的处理。Annotation 可用于修饰包、类、构造器、方法、变量等。

Annotation 类型

此处来一张网上的图 (可在新标签页中放大查看)

注解大全

转自深入理解Java:注解(Annotation)

基本 Annotation

Java中5个基本的注解分别为:

  • @Override ———— 用来限定子类重写父类的方法。
  • @Deprecated ———— 标记已经过时的方法。
  • @SuppressWarnings ———— 抑制编译器的警告。
  • @SafeVarargs ———— Java7 抑制“堆污染”警告,可变参数与泛型类一起使用会出现类型安全警告,若开发人员确信不会出现问题,可用此注解进行声明。
  • @FunctionalInterface ———— Java8 函数式接口,检测指定某个接口中只有一个抽象方法。

元 Annotation

元Annotation是用来修饰其他注解定义,即注解其他注解。
Java中有6种元注解,其中@Native注解一般用于给IDE工具做提示用。下边具体介绍其他几种注解。

1、@Retention:指定本修饰的注解可以保留多长时间。包含了一个RetentionPolicy类的value值,所以需指定该value的值。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
    RetentionPolicy value();
}
  • RetentionPolicy.CLASS ———— 默认值,编译器将注解记录在字节码文件中,程序运行时,JVM不保留注解信息。
  • RetentionPolicy.RUNTIME ———— 编译器将注解记录在字节码文件中,程序运行时,JVM可以获得注解信息,可通过反射获取该注解的信息。
  • RetentionPolicy.SOURCE ———— 注解只保留在源代码中,编译器直接丢弃。

2、@Target:指定被修饰的注解能用于哪些程序元素。

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {
    ElementType[] value();
}
  • ElementType.ANNOTATION_TYPE: 修饰Annotation。
  • ElementType.TYPE: 修饰类、接口(包括注解类型)、枚举。
  • ElementType.FIELD: 修饰成员变量。
  • ElementType.METHOD: 修饰方法定义。
  • ElementType.PARAMETER: 修饰参数定义。
  • ElementType.CONSTRUCTOR: 修饰构造方法。
  • ElementType.LOCAL_VARIABLE: 修饰局部变量。
  • ElementType.PACKAGE: 修饰包定义。

在Java8中新增了两个ElementType参数,用来限定哪些类型可以标注

  • ElementType.TYPE_PARAMETER: 类型变量
  • ElementType.TYPE_USE: 使用类型的任何语句

TYPE_PARAMETER举例,若要对泛型进行标注,则定义注解时需设定Target为TYPE_PARAMETER:

@Target(ElementType.TYPE_PARAMETER)
public @interface Animal{}

public class Zoo<@Animal T>{
    ...
}

TYPE_USE可用于标注各种使用到类型的地方,举例如下(上述例子可以将TYPE_PARAMETER改为TYPE_USE):


定义:
@Target(ElementType.TYPE_USE)
public interface UseTest{}

使用:
@UseTest String content; 修饰类型,
此种写法相当于java.lang.@UseTest String content; 
若@UseTest java.lang.String content; 此为定义局部变量,写法不合法,UseTest需指定Target为LOCAL_VARIABLE。

String content = (@UseTest String) obj; //类型转换
List<@UseTest String> infos = new ArrayList<>();  //泛型
implements @UseTest XXXX;  //实现接口
throws @UseTest NullPointException;  //声明抛出异常

3、@Documented:被该注解修饰的注解会被javadoc工具提取成文档。如果一个注解由@Documented修饰,则使用该注解的程序api文档中会包含该注解的说明。

4、@Inherited: 此注解修饰的注解具有继承性。若@XXX被@Inherited修饰,则使用@XXX注解的类具有继承性,其子类自动被@XXX修饰。

5、@Repeatable:重复注解,Java8的新特性。

在Java8之前,重复注解的解决方案代码如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Student{
    String name();
}

定义一个容器注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Students{
    Student[] value();
}

使用:
@Students({@Student(name = "Jack"), @Student(name = "Will")})
public class StudentTest{
    ......
}

在Java8中的方案则如下:

//定义如上的容器注解Students,添加Repeatable注解,如下所示
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Repeatable(Students.class)
public @interface Student{
    String name();
}

使用:
@Student(name = "Jack")
@Student(name = "Will")
public class StudentTest{
    ......
}

Java 反射简介

通过Java反射可以获取对象的属性、方法等。

1、获取类


//第一种方式
Class stuClazz1 = Class.forName("com.lauzy.freedom.ReflectDemo.Student");
    
//第二种方式
Class stuClazz2 = Student.class;
    
//第三种方式
Student stu3 = new Student();
Class stuClazz3 = stu3.getClass();

2、创建对象


Class stuClazz2 = Student.class;
Object stu = stuClazz2.newInstance();
    

3、获取属性例子


Object stu = stuClazz2.newInstance();   //获取实例
Field age = stuClazz2.getDeclaredField("age");  //获取特定属性
age.setAccessible(true);    //打破封装性
age.set(stu, 25);   //设置属性

4、方法总结

  • getDeclaredFields(): 获取所有属性。
  • getDeclaredField("***"): 获取特定的属性。
  • getModifiers(): 获取属性或方法的修饰符。
  • getType(): 获取属性或方法的类型名。
  • getDeclaredMethods():获取所有方法。
  • getReturnType():获取方法的返回类型。
  • getParameterTypes():获取方法的参数类型。
  • getDeclaredMethod("***",参数类型.class,……): 获取特定的方法。
  • getDeclaredConstructors(): 获取所有的构造方法。
  • getDeclaredConstructor(参数类型.class,……): 获取特定的构造方法。
  • getSuperclass():获取继承的父类。
  • getInterfaces():获取实现的所有接口。
  • field.set(Object object, Object value);//设置object对象的value属性
  • method.invoke(Object object, Object... values); //调用方法,values为方法的参数

5、代码实例


try {
    Class stuClazz1 = Class.forName("com.lauzy.freedom.ReflectDemo.Student");
    Class stuClazz2 = Student.class;
    Student stu3 = new Student();
    Class stuClazz3 = stu3.getClass();
    for (Field field : stuClazz1.getDeclaredFields()) {
        System.out.println(Modifier.toString(field.getModifiers())  //获取属性修饰符
                + "-" + field.getType().getSimpleName()     //获取属性类型名
                + "-" + field.getName());  //获取属性名
    }
    System.out.println("--------");
    for (Method method : stuClazz2.getDeclaredMethods()) {
        System.out.println(Modifier.toString(method.getModifiers())  //获取方法修饰符
                + "-" + method.getReturnType().toString()   //方法返回类型名
                + "-" + method.getName());  //方法名
    }
    System.out.println("--------");
    System.out.println(stuClazz2.getDeclaredConstructor(String.class, String.class, int.class).toString());
    System.out.println("--------");
    System.out.println(stuClazz2.getSuperclass().getName().toString());
    System.out.println("--------");
    for (Class aClass : stuClazz2.getInterfaces()) {
        System.out.println(aClass.getName());
    }
    System.out.println("--------");
    Object stu = stuClazz2.newInstance();   //获取实例
    Field name = stuClazz2.getDeclaredField("name");  //获取特定属性
    name.setAccessible(true);    //打破封装性
    name.set(stu, "Jack");   //设置属性
    System.out.println(name.get(stu));

    Method profile = stuClazz2.getDeclaredMethod("getProfile", String.class, int.class);//特定方法
    profile.setAccessible(true);
    profile.invoke(stu, "male", 30);//调用方法
} catch (Exception e) {
    e.printStackTrace();
}

输出结果:


private-String-name
public-String-gender
private-int-age
--------
public-class java.lang.String-getName
public-void-setName
private-void-getProfile
public-int-getAge
public-void-setAge
--------
public com.lauzy.freedom.ReflectDemo.Student(java.lang.String,java.lang.String,int)
--------
com.lauzy.freedom.AnnotationDemo.Person
--------
java.io.Serializable
--------
Jack
Name : Jack ; Gender : male ; Age : 30

自定义注解、反射获取属性

分别定义Name、Gender和SaveMoney注解:


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Name {
    String value() default "Will";
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Gender {
    String value() default "";
}


@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SaveMoney {

    int money() default 10000;

    int term() default 1;

    String platform() default "XXX";
}

注解的使用:


public class Person {
    @Name(value = "Jack")
    @Gender(value = "man")
    public String name;

    @SaveMoney(money = 20000, term = 5, platform = "ChinaBank")
    public void saveMoney(int money) {
        System.out.println("and then he spent " + money  + " on clothes.");
    }
}

利用反射获取注解的属性和方法:


public class AnnUtils {
    public static void test(Class<?> clazz) {

        for (Field field : clazz.getFields()) {
            if (field.isAnnotationPresent(Name.class) && field.isAnnotationPresent(Gender.class)) {
                Name name = field.getAnnotation(Name.class);
                Gender gender = field.getAnnotation(Gender.class);
                System.out.print("A " + gender.value() + " called " + name.value());
            }
        }

        try {
            Class<Person> personClass = Person.class;
            Method[] methods = personClass.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(SaveMoney.class)) {
                    SaveMoney saveMoney = method.getAnnotation(SaveMoney.class);
                    System.out.print(" deposited " + saveMoney.money() + "RMB to " +
                            saveMoney.platform() + " for " + saveMoney.term() + " months, ");

                    method.invoke(personClass.newInstance(), 1000);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行

AnnUtils.test(Person.class);

此时的输出结果为:

A man named Jack deposited 20000RMB to ChinaBank for 5 months, and then he spent 1000 on clothes.

此篇博客为基础用法及实例,下一篇Java注解、反射,安卓IOC(二)会介绍注解和反射在Android中的使用,运行时注解及butterknife的简单实现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容