注解
一般情况下,注解是用来给程序员、编译器提示的,就比如 @NoNull
就是用来提示不为空,在null
时,编译器会根据注解报出相关的异常。
但是注释还有一个很重要的作用就是和反射一起使用,我们可以通过反射拿到被注释的字段、方法、类,当然也可以拿到注释中的信息。
还是先说用来注解注解的注解(原生注解),哈哈哈!
@Target
表示该注解可以用于什么地方,可能的ElementType参数有:
- CONSTRUCTOR:构造器的声明
- FIELD:域声明(包括enum实例)
- LOCAL_VARIABLE:局部变量声明
- METHOD:方法声明
- PACKAGE:包声明
- PARAMETER:参数声明
- TYPE:类、接口(包括注解类型)或enum声明
@Retention
表示需要在什么级别保存该注解信息。可选的RetentionPolicy
参数包括:
- SOURCE:注解将被编译器丢弃
- CLASS:注解在class文件中可用,但会被VM丢弃
- RUNTIME:VM将在运行期间保留注解,因此可以通过反射机制读取注解的信息。
@Document
将注解包含在Javadoc中。
@Inherited
允许子类继承父类中的注解。
看完了若是一脸懵,请看注解的构造方式:
//这里就实现了MyAnnotationTest这个注解的构建
//这里代表该注解是用来注解方法、并且可以被反射获取信息
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotationTest {
String name() default " ";
int id()default 0;
}
注解的使用:
//该注解的使用:
//赋值
@MyAnnotationTest(name = "小明" ,id = 1)
public void annnotationMethod(){
System.out.println("这是一个被注解的方法");
}
是不是感觉这里看不出来什么特殊含义。但是,假如说,对我自己,这个注解代表被注解的方法是用来干某些耗时操作的,那么是不是就有意义了?
当然这样使用还是太浅层了,我们来结合反射试试!
反射
我们还是先不谈注解和反射搭配好伐?先单独谈谈反射:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
首先定义了一个Student类,这个类是我们想象出来的(可以啥子都有),专门用来接下来实现反射用的。一般情况下,我们是在对这个类一点都不了解的情况下才会想着去获取它的信息,好奇心按捺不住了啊!!!
在另外的类里面获取Student的信息是这样实现的:
//先获取类的对象
private static Class<?> c;
static {
try {
c = Class.forName("com.example.hp.javaclasstest.reflection.Student");
//另外一种方法是传递当前类的对象过去,使用object.getClass()获取到类对象。
} catch (ClassNotFoundException e) {
c = null;
}
}
//这里就是获取到所有的构造函数,是可以直接将它打印出来的
Constructor<?>[] constraints = c.getConstructors();
//这里是获取到某一个方法
try {
Method method = c.getMethod("showMyData",null);
//这个null是指该方法的返回对象,如果是String类型,则是String.class
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
其它的字段、方法、类的获取方式待自己慢慢探索吧。
但是万一有的小伙伴手痒,又想使用它拿到的方法该怎么办呢?
没关系,上热腾腾的代码:
//获取到这个类的单例
Object object = null;
try {
object = c.getConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//实现某个方法的使用
//object:单例,null:该方法传入参数,无则null
method.invoke(object,null);
是不是感受出来什么了?通过反射获取一个类的信息就是这么简单!
其实在字段或者方法上面加上注解也就是比普通的特殊了那么一丢丢,我们稍加判断同样是能够获取到被注解了的对象。
笔者手懒,就通通使用上面已经建好了的对象。
上代码!!!
//获取到Student的所有公开方法
Method[] methods = c.getMethods();
//注意,请仔细看这里!!!
for(Method m:methods){
//判断方法是否被MyAnnotationTest这个注解类注解
if(m.isAnnotationPresent(MyAnnotationTest.class)){
//这个m就是被 MyAnnotationTest 注释了的方法
//我们直接将它拿出来吧,return出来。
return m;
}
}
先缓口气,我们迈出了很大一步,我们已经获取到了被注释了的方法。接下来就是按照我们自己的需求来写了。
假如,我只是要实现这个方法,那么就可以采用invoke()
方法去实现啦。但是我要想知道注释里面的信息呢(例如上面的"小明",1
)。
这也是超级简单的:
//获取到注释类对象
MyAnnotationTest annotation = m.getAnnotation(MyAnnotationTest.class);
//获取到我的"小明 "
String name = annotation.name;
轻而易举实现啦。
总结
Java的注解和反射搭配使用是有很大作用的,这两者搭配是可以实现超大程度的解耦。我在这个类里面实现了注解,我可以在另外一个毫不相关的类里面去获得这个类的内容,并且实现里面的方法。
很多好的三方库的都是实现的注解和反射,例如Dagger2、Retrofit、ButterKnife...但是这些第三库不是这么简单去实现的,它们会生成工具类来快速实现信息获取。
但是在Java中实现反射可是一件“费劲”的事儿,是比较消耗CPU资源的。所以,请勿滥用反射!
网友评论