Skip to content

Commit c9d8b38

Browse files
committed
update
1 parent 0ec9e3c commit c9d8b38

1 file changed

Lines changed: 178 additions & 22 deletions

File tree

Android加强/注解使用.md

Lines changed: 178 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,14 @@ RxJava详解
1616
- `Annotation`的声明是通过关键字`@interface`。这个关键字会去继承`Annotation`接口。
1717
- `Annotation`的方法定义是独特的、受限制的。
1818
`Annotation`类型的方法必须声明为无参数、无异常的。这些方法定义了`Annotation`的成员: 方法名代表成员变量名,而方法返回值代表了成员变量的类型。而且方法的返回值类型必须是基本数据类型、`Class`类型、枚举类型、`Annotation`类型或者由前面类型之一作为元素的一维数组。方法的后面可以使用`default`和一个默认的数值来声明成员变量的默认值,`null`不能作为成员变量的默认值,这与我们平时的使用有很大的区别。
19-
注解如果只有一个默认属性,可直接用`value()`函数。一个属性也没有则表示该`Annotation``Mark Annotation`
20-
19+
注解如果只有一个默认属性,可直接用`value()`函数。一个属性也没有则表示该`Annotation``Mark Annotation`
20+
例如:
21+
```java
22+
public @interface UnitTest {
23+
String value();
24+
}
25+
```
26+
在使用时可以直接使用`@UnitTest("GCD")`,`@UnitTest("GCD"`实际上就是是 @UnitTest(value="GCD)的简单写法。
2127

2228
例如:
2329
```java
@@ -31,7 +37,7 @@ public @interface UseCase {
3137

3238
###作用
3339

34-
`Annotation`一般作为一种辅助途径,应用在软件框架或者工具中。让这些工具类可以根据不同的`Annotation`注解信息来采取不同的处理过程或者改变相应程的行为。
40+
`Annotation`一般作为一种辅助途径,应用在软件框架或者工具中。让这些工具类可以根据不同的`Annotation`注解信息来采取不同的处理过程或者改变相应程的行为。具有“让编译器进行编译检查的作用”。
3541

3642
具体可分为如下三类:
3743

@@ -67,7 +73,27 @@ public @interface UseCase {
6773
- `@Target`
6874
用来标记可进行修饰哪些元素,例如`ElementType.TYPE``ElementType.METHOD``ElementType.CONSTRUCTOR``ElementType.FIELD``ElementType.PARAMETER`等,如果未指定则默认为可修饰所有。
6975
- `@Inherited`
70-
子类是否可以继承父类中的该注解。
76+
子类是否可以继承父类中的该注解。它所标注的`Annotation`将具有继承性。
77+
例如:
78+
```java
79+
java.lang.annotation.Inherited
80+
81+
@Inherited
82+
public @interface MyAnnotation {
83+
84+
}
85+
```
86+
```java
87+
@MyAnnotation
88+
public class MySuperClass { ... }
89+
```
90+
91+
```java
92+
public class MySubClass extends MySuperClass { ... }
93+
```
94+
95+
在这个例子中`MySubClass`类继承了`@MyAnnotation`注解,因为`MySubClass`继承了`MySuperClass`类,而`MySuperClass`类使用了`@MyAnnotation`注解。
96+
7197
- `@Documented`
7298
是否会保存到`javadoc`文档中。
7399

@@ -129,21 +155,49 @@ public class Generation3List extends Generation2List {
129155

130156
###`Annotation`解析
131157

132-
133158
当Java源代码被编译时,编译器的一个插件annotation处理器则会处理这些annotation。处理器可以产生报告信息,或者创建附加的Java源文件或资源。如果annotation本身被加上了RententionPolicy的运行时类,则Java编译器则会将annotation的元数据存储到class文件中。然后,Java虚拟机或其他的程序可以查找这些元数据并做相应的处理。
134159

135160
当然除了annotation处理器可以处理annotation外,我们也可以使用反射自己来处理annotation。Java SE 5有一个名为AnnotatedElement的接口,Java的反射对象类Class,Constructor,Field,Method以及Package都实现了这个接口。这个接口用来表示当前运行在Java虚拟机中的被加上了annotation的程序元素。通过这个接口可以使用反射读取annotation。AnnotatedElement接口可以访问被加上RUNTIME标记的annotation,相应的方法有getAnnotation,getAnnotations,isAnnotationPresent。由于Annotation类型被编译和存储在二进制文件中就像class一样,所以可以像查询普通的Java对象一样查询这些方法返回的Annotation。
136161

137162

138163
#####运行时`Annotation`解析
164+
139165
该类是指`@Retention``RUNTIME``Annotation`
140166
该类型的解析其实本质的使用反射。反射执行的效率是很低的
141167
如果不是必要,应当尽量减少反射的使用,因为它会大大拖累你应用的执行效率。
142168

143-
例如`Target``Method`的注解就可以通过`Method`中的如下方法进行解析:
169+
- 类注解
170+
171+
可以通过`Class``Method``Field`类来在运行时获取注解。下面是通过`Class`类获取注解的示例:
144172

145173
```java
174+
Class aClass = TheClass.class;
175+
Annotation[] annotations = aClass.getAnnotations();
176+
177+
for(Annotation annotation : annotations){
178+
if(annotation instanceof MyAnnotation){
179+
MyAnnotation myAnnotation = (MyAnnotation) annotation;
180+
System.out.println("name: " + myAnnotation.name());
181+
System.out.println("value: " + myAnnotation.value());
182+
}
183+
}
184+
```
185+
也可以获取一个制定注解类型:
186+
```java
187+
Class aClass = TheClass.class;
188+
Annotation annotation = aClass.getAnnotation(MyAnnotation.class);
189+
190+
if(annotation instanceof MyAnnotation){
191+
MyAnnotation myAnnotation = (MyAnnotation) annotation;
192+
System.out.println("name: " + myAnnotation.name());
193+
System.out.println("value: " + myAnnotation.value());
194+
}
195+
```
146196

197+
198+
`JDK`提供的主要方法有:
199+
200+
```java
147201
public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
148202
...
149203
}
@@ -157,41 +211,143 @@ public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
157211
}
158212
```
159213

160-
当然如果`Target``Field``Class`等那就要去使用`Filed``Class`中的方法来解析了。
214+
- 方法注解
215+
216+
下面是一个方法使用注解的例子:
161217
```java
162-
public static void main(String[] args) {
163-
try {
164-
Class cls = Class.forName("cn.trinea.java.test.annotation.App");
165-
for (Method method : cls.getMethods()) {
166-
MethodInfo methodInfo = method.getAnnotation(
167-
MethodInfo.class);
168-
if (methodInfo != null) {
169-
System.out.println("method name:" + method.getName());
170-
System.out.println("method author:" + methodInfo.author());
171-
System.out.println("method version:" + methodInfo.version());
172-
System.out.println("method date:" + methodInfo.date());
173-
}
174-
}
175-
} catch (ClassNotFoundException e) {
176-
e.printStackTrace();
218+
public class TheClass {
219+
@MyAnnotation(name="someName", value = "Hello World")
220+
public void doSomething(){}
221+
}
222+
```
223+
224+
你可以通过如下方式获取方法注解:
225+
226+
```java
227+
Method method = ... //obtain method object
228+
Annotation[] annotations = method.getDeclaredAnnotations();
229+
230+
for(Annotation annotation : annotations){
231+
if(annotation instanceof MyAnnotation){
232+
MyAnnotation myAnnotation = (MyAnnotation) annotation;
233+
System.out.println("name: " + myAnnotation.name());
234+
System.out.println("value: " + myAnnotation.value());
177235
}
178236
}
179237
```
238+
也可以获取一个指定的方法注解,如下:
239+
```java
240+
Method method = ... // obtain method object
241+
Annotation annotation = method.getAnnotation(MyAnnotation.class);
180242

243+
if(annotation instanceof MyAnnotation){
244+
MyAnnotation myAnnotation = (MyAnnotation) annotation;
245+
System.out.println("name: " + myAnnotation.name());
246+
System.out.println("value: " + myAnnotation.value());
247+
}
248+
```
249+
250+
- 参数注解
251+
252+
在方法的参数中声明注解,如下:
253+
```java
254+
public class TheClass {
255+
public static void doSomethingElse(
256+
@MyAnnotation(name="aName", value="aValue") String parameter){
257+
}
258+
}
259+
```
260+
261+
可以通过`Method`对象获取到参数的注解,如下:
262+
```java
263+
Method method = ... //obtain method object
264+
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
265+
Class[] parameterTypes = method.getParameterTypes();
266+
267+
int i=0;
268+
for(Annotation[] annotations : parameterAnnotations){
269+
Class parameterType = parameterTypes[i++];
270+
271+
for(Annotation annotation : annotations){
272+
if(annotation instanceof MyAnnotation){
273+
MyAnnotation myAnnotation = (MyAnnotation) annotation;
274+
System.out.println("param: " + parameterType.getName());
275+
System.out.println("name : " + myAnnotation.name());
276+
System.out.println("value: " + myAnnotation.value());
277+
}
278+
}
279+
}
280+
```
281+
注意,`Method.getParameterAnnotations()`方法会返回一个二维的`Annotation`数组,包含每个方法参数的一个注解数组。
282+
283+
- 变量注解
284+
285+
下面是一个变量使用注解的例子:
286+
```java
287+
public class TheClass {
288+
289+
@MyAnnotation(name="someName", value = "Hello World")
290+
public String myField = null;
291+
}
292+
```
293+
294+
你可以像下面这样获取变量的注解:
295+
```java
296+
Field field = ... //obtain field object
297+
Annotation[] annotations = field.getDeclaredAnnotations();
298+
299+
for(Annotation annotation : annotations){
300+
if(annotation instanceof MyAnnotation){
301+
MyAnnotation myAnnotation = (MyAnnotation) annotation;
302+
System.out.println("name: " + myAnnotation.name());
303+
System.out.println("value: " + myAnnotation.value());
304+
}
305+
}
306+
```
307+
308+
当然也可以获取一个指定的变量注解,如下:
309+
```java
310+
Field field = ... // obtain method object
311+
Annotation annotation = field.getAnnotation(MyAnnotation.class);
312+
313+
if(annotation instanceof MyAnnotation){
314+
MyAnnotation myAnnotation = (MyAnnotation) annotation;
315+
System.out.println("name: " + myAnnotation.name());
316+
System.out.println("value: " + myAnnotation.value());
317+
}
318+
```
181319

182320

183321

184322
#####编译时`Annotation`解析
185323

324+
在刚才介绍的运行时注解中,很多人肯定会说使用反射会影响性能,那有没有不影响性能的方式呢?当然有了,那就是编译时注解。在编译时会通过注解标示来动态生成一些类或者`xml`,而在运行时,这里注解是没有的,它会依靠动态生成的类来进行操作。所以它就和直接调用方法一样,当然不会有效率影响了。
325+
186326
该类值`@Retention``CLASS``Annotation`,由`APT(Annotaion Processing Tool)`自动进行解析。是在编译时注入,所以不会像反射一样影响效率问题。
187327

328+
根据sun官方的解释,APT(annotation processing tool)是一个命令行工具,它对源代码文件进行检测找出其中的annotation后,使用annotation processors来处理annotation。而annotation processors使用了一套反射API并具备对JSR175规范的支持。
329+
330+
annotation processors处理annotation的基本过程如下:首先,APT运行annotation processors根据提供的源文件中的annotation生成源代码文件和其它的文件(文件具体内容由annotation processors的编写者决定),接着APT将生成的源代码文件和提供的源文件进行编译生成类文件。
331+
332+
简单的和前面所讲的annotation实例BRFW相比,APT就像一个在编译时处理annotation的javac。而且从sun开发者的blog中看到,java1.6 beta版中已将APT的功能写入到了javac中,这样只要执行带有特定参数的javac就能达到APT的功能。
333+
334+
335+
188336
我们需要做的是:
189337

190338
- 自定义类继承`AbstractProcessor`
191339
- 重写`process`方法
192340

193341
例如:
194342

343+
```java
344+
public class Processor extends AbstractProcessor{
345+
}
346+
```
347+
我用`Android Studio`死活提示找不到`AbstractProcessor`类,但是它明明就在`jdk`中。我是这样解决的,新建一个`Moduel`,在选择类型时将该`Moduel`的类型选为`Java Library`。然后在该`Module`中创建就好了,完美解决。
348+
349+
350+
195351
```java
196352
@SupportedAnnotationTypes({ "cn.trinea.java.test.annotation.MethodInfo" })
197353
public class MethodInfoProcessor extends AbstractProcessor {

0 commit comments

Comments
 (0)