[Java]反射和注解

注解的定义

注解本身没有任何意义,单独的注解就是一种注释,他需要结合其他的反射、插桩等才有意义。
Java注解(Annotation)又称Java标注,是JDK1.5引入的一种注释机制。是元数据的一种形式,提供有关于程序本身的数据。注解对他们注解的代码的操作没有直接影响。
先定义一个注解:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    String value() default "test";
}

注解的使用:

@MyAnnotation("1111")
public class AnnotationTest {

}

注意点:

  • 当注解里面定义的元素只有一个,并且命名为value时,可以忽略名字,多个或者是命名不为value时,需要手动注明元素的名字。
  • java用 @interface Annotation{ } 定义一个注解 @Annotation,一个注解是一个类。
  • 当注解里面的元素默认值时,使用时可以不填入值
元注解

在定义注解时,注解类也能够使用其他的注解声明。对于注解类型进行注解的类,我们称之为meta-annotation(元注解)。

@Target表示注解允许可以作用于那些节点,如果没有指明作用位置,将作用于java类上面,一起看一下ElementType的值:

public enum ElementType {
    TYPE, //接口、类、枚举、注解
    FIELD,//变量
    METHOD,//方法
    PARAMETER,//方法参数
    CONSTRUCTOR,//构造方法
    LOCAL_VARIABLE,//局部变量
    ANNOTATION_TYPE,//注解类型
    PACKAGE,//包
    TYPE_PARAMETER,//输入参数声明
    TYPE_USE;//使用一种类型

    private ElementType() {
    }
}

@Retention表示注解的保留级别,一起看一下RetentionPolicy类:

public enum RetentionPolicy {
    /**
     * 标记的注解仅保留在源码级别,并被编译器忽略
     */
    SOURCE,

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

    /**
     * 标记的注解由JVM保留,因此运行时环境可以使用它
     */
    RUNTIME
}

它们三者是包含关系:
SOURCE<CLASS<RUNTIME,即CLASS包含了SOURCE,RUNTIME包含了SOURCE和CLASS。

反射

一般情况下,我们使用某个类必定知道它是什么类,是用来做什么的,并且能够获得此类的引用。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
发射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创造对象了。这时候,我们使用JDk提供的反射API进行反射调用。反射就是在运行状态中,对于任意一个类,都能够知道这个类额所有属性和方法;对于一个对象,都能够调用它的任意方法和属性,并且能改变它的属性。是Java被视为动态语言的关键。

反射的基本使用

getClass方法

  1. 类的 class 属性
Class<Test> c1 = Test.class;
  1. Object 对象 的 getClass() 方法
 Test test = new Test();
 Class<Test> c2 = (Class<Test>) test .getClass(); 
  1. 通过 Class 类的 forName() 方法(最常用)
 try {
        Class c3 = Class.forName("com.mfw.Test");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

获取类的构造函数

Test test = new Test();
Class c4 = test.getClass();
Constructor[] constructors ;
constructors = c4.getDeclaredConstructors();

获取类的成员属性

try {
            Test test = new Test();
            Class<Test> cl = (Class<Test>) test.getClass();
            //根据字段名获取字段,能获取父类的
            Field f1 = cl.getField("test");
            //获取类public的,包括从父类继承来的字段
            Field[] fields1 = cl.getFields();
            //根据字段名获取字段,不能获取父类的
            Field f2 = cl.getDeclaredField("test");
            //可以获取本类所有的字段,包括private的,但是不能获取继承来的字段
            Field[] fields2 = cl.getDeclaredFields();
        }catch (Exception e){
            e.printStackTrace();
        }

获取类的成员方法

 //根据方法名和参数类型获得一个方法对象,只能是获取public修饰的
 Method m1 = cl.getMethod("test", String.class);
 //获取所有的public修饰的成员方法,包括父类中
 Method[] methods1 = cl.getMethods();
 //根据方法名和参数类型获得一个方法对象,包括private修饰的
 Method m2 = cl.getDeclaredMethod("test", String.class);
 //获取当前类中所有的方法,包含私有的,不包括父类中
 Method[] declaredMethods = cl.getDeclaredMethods();
实战部分

用自定义注解和反射实现页面跳转的参数注入(Kotlin版本):

自定义注解:

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

注解使用工具类:

object InjectUtils {

    public fun injectIntent(activity: Activity) {
        val javaClass = activity.javaClass
        val intent = activity.intent
        val extras = intent.extras
        val fields = javaClass.declaredFields
        fields.forEach { field ->
            if (field.isAnnotationPresent(InjectIntent::class.java)) {
                val annotation = field.getAnnotation(InjectIntent::class.java)
                val key = if (TextUtils.isEmpty(annotation.value)) field.name else annotation.value
                if (extras != null) {
                    if (extras.containsKey(key)) {
                        var value = extras[key]
                        val componentType = field.type.componentType
                        if (componentType!!.isArray && Parcelable::class.java.isAssignableFrom(
                                componentType
                            )
                        ) {
                            val objects = value as Array<Any>
                            val temp = Arrays.copyOf(objects, objects.size,field.type as Class<Array<out Any>>)
                            value = temp
                        }
                        field.isAccessible = true
                        field.set(activity, value)
                    }

                }

            }
        }

    }
}

使用:

class SecondActivity : AppCompatActivity() {
    @InjectIntent("name")
    private lateinit var name:String
    @InjectIntent("age")
    private lateinit var age:String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_second)
        InjectUtils.injectIntent(this)
        Log.i("SecondActivity","==========$name==============$age")
    }
}

运行结果非常简单,就不再贴出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值