Java注解和反射

注解和反射

一、注解(Annotation)

注解定义

  • 不是程序本身,可以对程序做出解释,这一点和注释(comment)没什么区别
  • 可以被其他程序读取
  • 可以附在 package,class,method,field 等上面,相当于给他们添加了额外的辅助信息,可以通过反射机制编程是实现对这些元数据的访问

内置注解

三个在 java.lang 包下

  • @Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误
  • @Deprecated - 标记过时方法。如果使用该方法,会报编译警告
  • @SuppressWarnings - 指示编译器去忽略注解中声明的警告

四 个作用在其他注解的注解(或者说元注解)在 java.lang.annotation 包下

  • @Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问

  • @Documented - 标记这些注解是否包含在用户文档中

  • @Target - 标记这个注解应该是哪种 Java 成员

  • @Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

  • //定义一个注解
    // Target 表示注解可以用在那些地方(在 {} 中可以指定作用在类,方法等地方)
    @Target(value = {ElementType,Type,ElementType.METHOD})
    
    // Retention 表示注解在什么地方有效(runtime > class > source)
    @Retention(RetentionPolicy.RUNTIME)
    
    // Documented 表示注解是否将注解生成在Javadoc中
    @Documented
    
    // Inherited 表示子类可以继承父类的注解
    @Inherited
    @interface MyAnnotaion{
        
    }
    

自定义注解@interface,自动继承了 java.lang.annotaion.Annotation 接口

  • @interface 用来声明一个注解,格式:public @ interface 注解名

  • 其中的每一个方法实际上是声明了一个配置参数

  • 方法的名称就是参数的名称

  • 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)

  • 可以通过 default 来声明参数的默认值

  • 如果只有一个参数成员,一般参数名为 value

  • 注解元素必须要有值,定义注解元素时,经常使用空字符串,0 作为默认值

  • //自定义注解
    public class Test{
        //注解可以显示赋值,如果没有默认值,就必须给注解赋值
        @MyAnnotaion2(name = "李明",schools = {"清华大学","北京大学"})
        public void test(){}
    }
    
    @Target(value = {ElementType,Type,ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotaion2{
        //注解的参数 : 参数类型 + 参数名();
        String name() default "";
        int age() default 0;
        int id() default -1;//如果默认值为-1,代表不存在
        String[] schools() default {"清华大学","北京大学"};
    }
    

二、反射机制

Class类

一个 Class 对象包含了特定某个结构(class / interface / enum / annotation / primitive type / void / [])的有关信息

  • Class 本身也是一个类
  • Class 对象只能由系统建立对象
  • 一个加载的类在 JVM 中只会有一个 Class 实例
  • 一个 Class 对象对应的是一个加载到 JVM 中的一个 .class 文件
  • 每个类的实例都会记得自己是由哪个 Class 实例所生成的
  • 通过 Class 可以完整地得到一个类中的所有被加载的结构
  • Class 类是由 Reflection 的根源,针对任何想动态加载、运行的类,唯有先获取相应的 Class 对象

反射(Reflection)

  • 通过java语言中的反射机制允许程序在执行期间借助与 Reflection API 取得任何类的内部信息,并直接操作任意对象的内部属性及方法,即可以操作字节码文件(可以读和修改字节码文件)

  • 相关的主要API:

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

    方式 备注
    Class.forName(“完整类名带包名”) 静态方法
    对象.getClass()
    任何类型.class
  • 加载完类之后,在堆内存的方法区就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构的信息。可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以称之为:反射

  • 区别:

    • 正常方式:引入需要的“包类”名称 ---> 通过new实例化 ---> 取得实例化对象
    • 反射方式:实例化对象 ---> getClass()方法 ---> 取得完整的“包类“名称
  • 优缺点:

    • 优点:可以实现动态创建对象和编译,体现出很大的灵活性
    • 缺点:使用反射基本上是一种解释操作,就是在告诉 JVM 希望做什么并且需要满足什么要求。这类操作总是慢于执行相同的操作
//获得类的信息
public class Test{
    public static void main(String[] args) throws ClassNotFoundException{
        //获得 User 实例的 Class 对象
        Class c1 = Class.forName("com.daxia.User");

        //获得类的名字
        System.out.printf(c1.getName());            //获得包名 + 类名
        System.out.printf(c1.getSimpleName());      //获得类名
 		
        //获得类的属性
        Field[] fields = c1.getFields();            //只能找到Public属性

        fields = c1.getDeclaredFields();            //找到全部的属性
        for(Field field : fields){
            System.out.printf(field);
        }
        //获得指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.printf(name);
        
        //获得类的方法
        Method[] methods = c1.getMethods();          //获取本类及其父类的全部Public方法
        for(Methods methods : methods){
            System.out.printf("正常的"+methods);
        }
        methods = c1.getDeclaredFields();            //获得本类的所有方法
		for(Methods methods : methods){
            System.out.printf("getDeclaredFields"+methods);
        }
        
        //获得指定方法
        //重载
        Method getName = c1.getMethod("getName",null);
        Method setName = c1.getMethod("setName",String.class);
        System.out.printf(getName);
        System.out.printf(setName);
        
        //获得指定的构造器
        Constructor[] constructors = c1.getConstructors();  //获取本类及其父类的全部Public构造器
        for(Constructor constructor : constructors){
            System.out.printf(constructor);
        }
        constructors = c1.getDeclaredConstructors();        //获取本类及其父类的全部Public构造器
        for(Constructor constructor : constructors){
            System.out.printf("#"+constructor);
        }
        //获得指定构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class,int.class);
        System.out.printf("#"+declaredConstructor);
    }
}

调用指定方法

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

  • Object 对应原方法的返回值,若原方法无返回值,此时返回 null

  • 若原方法为静态方法,此时形参 Object obj 可为 null

  • 若原方法形参列表为空,则 Object[] args 为 null

  • 若原方法声明为 private,则 需要在调用此 invoke() 方法前,显式调用方法对象的 setAccessible(true) 方法,将可以访问 private 的方法

  • Method、Field 和 Constructor 对象都有 setAccessible() 方法。setAccessible 作用就是启动和金庸访问安全检查的开关,如果代码必须用反射,并且需要频繁的被调用,设置为true,这样就使得原本无法访问的私有成员也可以被访问

  • Class c1 = class.forName("com.chen.User");
    User user = (USer)c1.newInstance();
    Method setName = c1.getDeclaredMethod("setName",String.class);
    //invoke激活
    setName.invoke(user,"李明");
    System.out.println(user,getName());
    

ORM(Object Relationship Mapping)-->对象关系映射

//反射操作注解
public class Test {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.chen.reflection.student");

        //1.通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //2.获得注解的value值
        Chen chen = (chen) c1.getAnnotation(chen.class);
        System.out.println(chen.value());

        //3.获得类指定字段的注解
        Field f = c1.getDeclaredField("id");
        Fieldhaha annotation = f.getAnnotation(Fieldchen.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.length());
        System.out.println(annotation.type());
    }
}

@Chen("db_student")
class student{
    @Fieldchen(columnName = "db_id",type = "int",length = 10)
    private int id;
    @Fieldchen(columnName = "db_age",type = "int",length = 10)
    private int age;
    @Fieldchen(columnName = "db_name",type = "varchar",length = 3)
    private String name;
  
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface chen{
    String value();
}

//定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldchen{
    String columnName();
    String type();
    int length();
}   
        
    public student() {
    }
    public student(int id, int age, String name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "student{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}
posted @ 2024-01-20 23:20  代码大虾  阅读(18)  评论(0)    收藏  举报