【java笔记】注解与反射

注解(Annotation)

Annotation是JDK5.0引入的,注解是代码里的特殊标记,可以在编译、类加载、运行时被读取,并执行响应的处理。我们在不改变代码原有逻辑的情况下,可使用注解在源文件中嵌入一些补充信息。通过反射机制编程实现对这些元数据的访问

  • 可以对程序作出解释
  • 可以被其他程序读取

内置注解

java内置的注解有4种,直接使用

@Override对覆盖超类中的方法进行标注
@Deprecated对不鼓励使用或者已经过时的方法添加注解(已不推荐使用)
@SuppressWarnings取消特定代码段中的警告
@SafeVarargs声明使用了可变长参数的方法,在与泛型类一起使用时不会出现类型安全问题

元注解

用来注解其他注解,从而创建新的注解

@Targe注解所修饰的对象范围
@Inherited表示注解可以被继承
@Documented表示这个注解应该被JavaDoc工具记录
@Retention用来声明注解的保留策略
@Repeatable允许一个注解在同一声明类型中多次使用

@Targe注解的取值是一个ElementType类型的数组

ElementType.TYPE能修饰类、接口或者枚举类型
ElementType.FIELD能修饰成员变量
ElementType.METHOD能修饰方法
ElementType.PARAMETER能修饰参数
ElementType.CONSTRUCTOR能修饰构造方法
ElementType.LOCAL_VARIABLE能修饰局部变量
ElementType.ANNOTATION_TYPE能修饰注解
ElementType.PACKAGE能修饰包
ElementType.TYPE_PARAMETER类型参数声明
ElementType.TYPE_USE使用类型

@Retention注解有3中类型,分别表示不同级别的保留策略

RetentionPolicy.SOURCE注解信息只保留在.java源码中
RetentionPolicy.CLASS编译时注解,注解信息可保留在.java源码中以及.class中
RetentionPolicy.RUNTIME运行时注解,java程序运行时,JVM可会保留注解信息

自定义注解

使用@interface关键字自定义注解,自动继承java.lang.annotation.Annotation接口

1.定义注解

@Target(ElementType.METHOD)   //能修饰方法
@Retention(RetentionPolicy.RUNTIME)   //运行时注解
@interface MyAnnotation{
    String name();
}

上面的代码定义了一个修饰方法的运行时注解,其中有一个成员变量name,注解中只有成员变量,没有方法,成员变量形式为:参数类型+参数名(),方法名定义了该成员变量的名字,返回值定义了该成员变量的类型。

2.使用该注解

@MyAnnotation(name="张三")
public void test(){}

由于该注解存在成员变量,所以在使用时必须为注解赋值

也可用default为成员变量添加默认值

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    String name();
    int age() default 18; 
}

这里的age就进行了默认赋值,使用注解时可不为其赋值,使用默认值

注解需要配合反射机制才能发挥作用,下面我们了解注解是如何配合反射使用的


反射机制(Reflection)

反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法,可在程序运行期间进行相应操作,是Java被视为动态语言的关键。

反射所提供的一些功能

  • 在运行时判断任意一个对象所属的类

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

  • 在运行时判断任意一个类所具有的成员变量和方法

  • 在运行时获取泛型信息

  • 在运行时调用任意一个对象的成员变量和方法

  • 在运行时处理注解

  • 生成动态代理


优点

  • 可以实现动态创建对象和编译,体现出很大的灵活性
  • 实现程序的解耦

缺点

  • 使用功能反射基本上是一种解释操作,需要通过JVM来执行,性能会下降

Reflection的api

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method :代表类的方法
  • java.lang.reflect.Field : 代表类的成员变量
  • java.lang.reflect.Constructor :代表类的构造器

首先我们需要获取Class类的实例

  • Class clazz = A.class; 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。
  • Class clazz= A.getClass(); 已知某个类的实例,调用该实例的getClass()方法获取Class对象
  • 常用)Class clazz=Class.forName("“demo.b”); 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取
  • 内置基本数据类型可以直接用类名.Type
  • 还可以利用ClassLoader我们之后讲解

获取了Class类的实例后,通过反射的方法来获取类的内部信息,如Field、Method、Constructor、Superclass、Interface、Annotation。


常用的反射方法

成员变量

  • public Field getField(String name):获取指定名称的成员变量(public)。
  • public Field[] getFields():获取全部成员变量(public)。
  • public Field getDeclaredField(String name)
  • public Field[] getDeclaredFields()

成员方法

  • public Method getMethod(String name,Class<?>... parameterTypes)
  • public Method[] getMethods()
  • public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
  • public Method[] getDeclaredMethods()

构造方法

  • public Constructor<T> getConstructor(Class<?>... parameterTypes)
  • public Constructor<?>[] getConstructors()
  • public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
  • public Constructor<?>[] getDeclaredConstructors()

其余获取其它信息的方法都类似

setAccessible() 启动和禁用访问安全检查的开关


操作运行时注解

通过反射操作运行时注解

(1)定义注解

这里假设定义一个数据库的注解,包括表的注解和列的注解

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableAnnotation{
    String value();
}


@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnnotation{
    String columnName();
    String type();
}

(2)使用注解

通过反射指定表名、列名和类型

@TableAnnotation("user.db")
class User{

    @FieldAnnotation(columnName = "name",type = "int")
    String id;
    @FieldAnnotation(columnName = "password",type = "varchar")
    String password;

    public User(){

    }

    public User(String id, String password) {
        this.id = id;
        this.password = password;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

(3)使用反射操作注解

通过反射让我们在运行时获取到注解所呈现的表名、列名和每列对应类型

public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {

    Class user1=Class.forName("com.wei.service.network.User");

    //通过反射获取类所有的注解
    Annotation[] annotations=user1.getAnnotations();

    for (Annotation annotation : annotations) {
        System.out.println(annotation);
    }

    //获取注解的值
    //首先获取指定的注解
    TableAnnotation tableAnnotation= (TableAnnotation) user1.getAnnotation(TableAnnotation.class);
    String value=tableAnnotation.value();
    System.out.println(value);

    //获取类中指定字段的注解
    //首先获取字段
    Field field=user1.getDeclaredField("id");
    //获取字段的注解
    FieldAnnotation fieldAnnotation=field.getAnnotation(FieldAnnotation.class);
    System.out.println(fieldAnnotation.columnName());
    System.out.println(fieldAnnotation.type());
}

运行结果:
在这里插入图片描述

编译时注解处理器
自定义编译时处理器的具体方式和运行时不同,会相对麻烦一些,我们熟知的ButterKnife、Room就是使用的APT方式。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值