Android架构师之路--Java进阶基础--注解与反射

Java进阶基础--注解与反射

一、注解

Java注解(Annotation)又称Java标注,是JDK5.0引入的一种注释机制。注解是元数据的一种形式,提供有关于程序但不属于程序本身的数据。注解对它们注解的代码的操作没有直接影响。

1、注解声明

Java中所有的注解,默认实现Annotation接口:

package java.lang.annotation;

public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();

}

于声明一个“Class”不同的是,注解的声明使用@interface关键字。

public @interface Lance{ }

1.1、元注解

在定义注解时,注解类也能够使用其他的注解声明。对注解类型进行注解的注解类,我们称之为meta-annotation(元注解)。一般的,我们在定义自定义注解时,需要指定的元注解有两个:

@Target

注解标记另一个注解,以限制可以应用注解的Java元素类型。目标注解指定以下元素类型之一作为其值:

①ElementType.ANNOTATION_TYPE  可以应用于注解类型。

②ElementType.CONSTRUCTOR 可以应用于构造函数。

③ElementType.FIELD 可以应用于字段或属性。

④ElementType.LOCAL_VARIABLE 可以应用于局部变量。

⑤ElementType.METHPD 可以应用于方法级注解。

⑥ElementType.PACKAGE 可以应用于包声明。

⑦ElementType.PARAMETER 可以应用于方法的参数。

⑧ElementType.TYPE 可以应用于类的任何元素。

@Retention

注解指定标记注解的存储方式:

①RetentionPolicy.SOURCE 标记的注解仅保留在源码级别中,并被编译器忽略。

②RetentionPolicy.CLASS 标记的注解在编译时由编译器保留,但Java虚拟机(JVM)会忽略。

③RetentionPolicy.RUNTIME 标记的注解由JVM保留,因此运行时环境可以使用它。

另外还有@Documented 与 @Inherited 元注解,前者用于被javadoc工具提取成文档,后者表示允许子类继承父类中定义的注解。

@Retention 三个值中 SOURCE < CLASS < RUNTIME,即CLASS包含了SOURCE,RUNTIME包含SOURCE、CLASS。

例子:

@Target({ElementType.TYPE,ElementType.FIELD}) // 允许在类与类属性上标记该注解

@Retention(RetentionPolicy.SOURCE) //注解保留在源码中

public @interface Lance {}

1.2、注解类型元素

在声明注解中,允许在使用注解时传递参数。

@Target({ElementType.TYPE,ElementType.FIELD})

@Retention(RetentionPolicy.SOURCE)

public @interface Lance {

    String value(); //无默认值

    int age() default 1; //有默认值

}

注意:在使用注解时,如果定义的注解中的类型元素无默认值,则必须进行传值。

1.3、注解应用场景

按照@Retention元注解定义的注解存储方式,注解可以被在三种场景中使用:

1.4、Android注解语法检查

在Android中我们需要设计接口以供使用者调用时,如出现需要对入参进行类型限定,如限定为资源ID,布局ID等类型参数,将参数类型直接给定int即可。然而,我们可以利用Android为我们提供的语法检查注解,来辅助进行更为直接的参数类型检查与提示。

参数限制为:图片资源ID

public Drawable getDrawable(@Drawable int id) throw是 NotFoundException

同时,我们也可以使用@intDef来定义自己的入参类型检查。

二、反射

一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的,并且能都获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类进行操作。

反射则是一开始并不知道我们初始化的类对象是什么,自然也无法使用new关键字来创建对象。这时候,我们使用JDK提供的反射API进行反射调用。

反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。是Java被视为动态语言的关键。

Java反射机制主要提供了以下功能:

①在运行时构造任意一个类的对象

②在运行时获取或者修改任意一个类所具有的成员变量和方法

③在运行时调用任意一个对象的方法和属性

1、Class

反射始于Class,Class是一个类,封装了当前对象所对应的类的信息。

1.1、获得Class对象

①通过类名获取   类名.class

②通过对象获取    对象名.getClass()

③通过全类名获取     Class.forName(全类名)    classLoader.loadClass(全类名)

2、判断是否为某个类的实例

一般地,我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法: public native boolean isInstance(Object obj); 

判断是否为某个类的类型:  public boolean isAssignableFrom(Class<?> cls)

3、创建实例

①使用Class对象的newInstance()方法来创建Class对象对应类的实例

Class<?> c = String.class;         Object str = c.newInstance();

②先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。

//获取String所对应的Class对象             Class<?> c = String.class;

//获取String类带一个String参数的构造器        Constructor constructor = c.getConstructor(String.class);

//根据构造器创建实例          Object obj = constructor.newInstance("23333");

4、获取构造器信息

获取构造器的方法:

①Constructor getConstructor(Class[] params)  获得使用特殊的参数类型的public构造函数(包括父类)

②Constructor[] getConstructors()   获得类的所有公共构造函数

③Constructor getDeclaredConstructor(Class[] params)   获得使用特定参数类型的构造函数(包括私有)

④Constructor[] getDeclaredConstructors()   获得类的所有构造函数(与接入级别无关)

5、获取类的成员变量(字段)信息

获取字段信息的方法:

①Field  getField(String name)    获得命名的公共字段(public修饰)

②Field[]  getFields()   获得类的所有公共字段(public修饰)

③Field  getDeclaredField(String name)   获得类声明的命名的字段(包括private修饰)

④Field[]  getDeclaredFields   获得类声明的所有字段(包括private修饰)

当我们从类中获取了一个方法后,就可以用 invoke() 方法来调用这个方法。

public Object invoke(Object obj, Object... args);

6、利用反射创建数组

数组在Java里是比较特殊的一种类型,它可以赋值一个Object Reference其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象  

public static Object newInstance(Class<?> componentType, int length);

7、反射获取泛型真实类型

当我们对一个泛型类进行反射时,需要得到泛型中的真实数据类型,来完成如JSON反序列化的操作。此时需要通过Type体系来完成。Type接口包含了一个实现类(Class)和四个实现接口:

①TypeVariable                 泛型类型变量。可以获得泛型上下限等信息

②ParameterizedType       具体的泛型类型。可以获得元数据中泛型签名类型(泛型真实类型)

③GenericArrayType         当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现

④WildcardType                通配符泛型。可以获得上下限信息

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