Loading

注解与反射的学习

注解和反射

注解

作用:不是程序本身,可以对程序作出解释;可以被其他程序(比如:编译器)读取

元注解 meta-annotation

  • 作用:负责注解其他注解,java 定义了 4 个标准的 meta-annotation,用来提供对其他注解类型做说明

    //定义一个注解
    
    //@Target 表示我们的注解可以用在哪些地方
    @Target(value = {ElementType.METHOD,ElementType.TYPE})
    
    //@Retention 表示我们的注解什么地方还有效,描述注解的生命周期
    //runtime>class>sources (整个过程,编译,源代码)
    @Retention(value = RetentionPolicy.RUNTIME)
    
    //@Documented表示是否向我们的注解生产到 Javadoc中
    @Documented
    
    //@Inherited 子类可以继承父类的注解
    @Inherited
    @interface MyAnnotation{
    }
    

自定义注解

  • 使用@interface 自定义注解时,自动继承 java.lang.annotation,Annotation 接口
  • @interface 用来声明一个注解,格式 public @interface 注解名
  • 每一个方法实际上是声明一个配置参数
  • 方法的名称就是参数的名称
  • 返回值类型就是参数的类型(返回值类型只能是基本数据类型,Class,String,enum)
  • 可以通过 default 来声明参数的默认值
  • 如果只有一个参数成员,参数名称可以为 value,注解内可以省略 value 直接写值
  • 注解元素必须要有值,我们定义的时候,经常使用空字符串,0 作为默认值
//自定义注解
public class Demo2 {

    //注解没有顺序
    //注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
    @MyAnnotation2(name = "JAVA")
    public void test() {
    }

    //默认 设置value,可以省略写 value
    @MyAnnotation3("C")
    public void test1() {

    }
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2 {
    //注解的参数:参数类型+参数名();
    String name() default "";

    int age() default 0;

    int id() default -1;//如果默认值为-1,代表不存在

    String[] school() default {"清华大学"};
}


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

反射 reflection

java 不是动态语言,但有一定的动态性,我们可以利用反射机制获得类似动态语言的特性,这样让 编程更加灵活。

  • reflection 是 java 被视为动态语言的关键,反射机制允许程序在执行期间借助 Reflection API 取得任何类的内部信息,并直接操作任意对象的内部属性及方法Class c =Class.forName(“java.lang.String”)

  • 加载完类之后,在堆内存的方法区中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),该对象就包含完整的类的结构的信息。

  • 正常获取对象的方式:引入需要的“包类”名称——通过 new 实例化——取得实例化对象

  • 反射获取反射:实例化对象——getClass()方法——得到完整的“包类”名称

反射优缺点

  • 优点:实现动态创建对象和编译,增加灵活性
  • 缺点:对性能有影响,使用反射是基于一种解释操作,我们告诉 JVM要做什么,并满足要求,这类操作鳗鱼直接执行相同的操作

Class 类

通过反射我们可以获得很多信息,某个类的属性、方法和构造器、某个类到底是实现哪些接口。对每个类而言 JRE都为期保留一个不变的 Class 类型对象。

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

Class 类常用方法

获取 Class 类的实例

  • 若已知具体的类,通过类的 class 属性获取,方法最安全可靠,程序性能高

    //方式一:通过类名.class 获得
            Class c3 = Student.class;
    
  • 若已知某个类的实例,调用实例的 getClass()方法获取 Class 对象

    //方式二:通过对象获得
    				Class c1 = student.getClass();
    
  • 若已知一个类全类名,且该类路径下,可通过 Class 类的静态方法 forName()获取,可能抛出ClassNotFoundException

    //方式三:通过forName 获得
            Class c2 = Class.forName("com.study.reflection.Student");
    

Class 对象能做什么

 Class c1 = Class.forName("com.study.reflection.User");
        //获得类的名字
        System.out.println(c1.getName());//获得包名+类名
        System.out.println(c1.getSimpleName());//获得类名

        //获得类的属性
        Field[] fields = c1.getFields();//只能找到 public 属性

        fields = c1.getDeclaredFields();//找到全部的属性
        for (Field field : fields) {
            System.out.println(field);
        }

        //获得指定私有属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        System.out.println("========");

        //获得类的方法
        Method[] methods = c1.getMethods();//获得本类机器父类的全部 public 方法
        for (Method method : methods) {
            System.out.println("正常的" + method);
        }
        Method[] declaredMethods = c1.getDeclaredMethods();//获得本类所有方法。包含 private
        for (Method declaredMethod : declaredMethods) {
            System.out.println("declared" + declaredMethod);
        }

        //获得指定方法
        Method getName = c1.getMethod("getName", null);//第二个参数是指方法参数类型.class
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        System.out.println("+++++++++++++++");
        //获得构造器
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        Constructor[] declaredConstructors = c1.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
            System.out.println("#" + declaredConstructor);
        }

        //获得指定构造器
        Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
        System.out.println("指定" + constructor);

    }

动态的创建对象,通过反射

 //获取 Class 对象
        Class c1 = Class.forName("com.study.reflection.User");

        //构造一个User对象 1.类必须有一个无参的构造器,2.类的构造器访问权限需要足够
        User user = (User) c1.newInstance();//本质上调用类的无参构造器
        System.out.println(user);

        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User user2 = (User) constructor.newInstance("李四", 001, 18);
        System.out.println(user2);

        //通过反射调用普通方法
        User user3 = (User) c1.newInstance();
        //通过对象获取方法
        Method setName = c1.getMethod("setName", String.class);

        //invoke()执行方法  参数(给那个对象,"传递方法的值")
        setName.invoke(user3, "张三");
        System.out.println(user3.getName());

        //反射操作属性
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");

        //不能直接操作私有属性,需要关闭安全检测,属性或方法的setAccessible(true)
        name.setAccessible(true);//设置为 true 关闭,取消检测
        name.set(user4, "王五");
        System.out.println(user4.getName());

    }

setAccessible

  • 作用是启动和禁用访问安全检查的开关,这样能够提高反射的效率;使得原本无法访问的私有的成员也可以访问
  • 参数默认值为 false 则说反射的对象应该实施 java 语言安全检查
//分析性能问题
public class TestSetAccessible {
    //普通方式调用
    public static void test01() {
        User user = new User();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通方式执行时间:" + (endTime - startTime) + "ms");
    }

    //反射方式调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getMethod("getName", null);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行时间:" + (endTime - startTime) + "ms");
    }

    //反射方式调用 关闭检测setAccessible(true);
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getMethod("getName", null);
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user, null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("关闭检测反射方式执行时间:" + (endTime - startTime) + "ms");
    }

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        test01();
        test02();
        test03();
    }
}

反射操作泛型

Java 中的泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换的麻烦,但是一旦编译完成,所有与泛型有关的类型全部擦除。 使用泛型直接读取泛型,是读取不到的,因为反射是操作加载以后的类的。

为了通过反射操作这些类型以迎合实际开发的需要 ,java 新增了如下几种类型代表不能归到 Class 类的类型但是又和原始数据齐名的类型

  • ParameterizedType : 表 示 一 种 参 数 化 的 类 型 , 比 如 Collection,可以获取 String 信息

  • GenericArrayType:泛型数组类型

  • TypeVariable:各种类型变量的公共父接口

  • WildcardType:代表一种通配符类型表达式, 比如? extends Number,? super Integer (Wildcard 是一个单词,就是通配符)

//通过反射获取泛型
public class Test11 {

  //参数是泛型
    public void test01(Map<String, User> map, List<User> list) {
        System.out.println("test01");
    }

  //返回值是泛型
    public Map<String, User> test02() {
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = Test11.class.getMethod("test01", Map.class, List.class);
        //获得泛型的参数类型
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("获得泛型参数类型01" + genericParameterType);
            if (genericParameterType instanceof ParameterizedType) {//判断是否属于参数化类型
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();//获得真实类型
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("获得泛型参数类型的具体参数类型01" + actualTypeArgument);
                }
            }
        }

        method = Test11.class.getMethod("test02", null);
        //获取返回值类型
        Type genericReturnType = method.getGenericReturnType();
        System.out.println("泛型的返回值类型02:" + genericReturnType);
        if (genericReturnType instanceof ParameterizedType) {
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("获得泛型参数类型的具体参数类型02" + actualTypeArgument);
            }
        }
    }

}

注解和反射的使用

//练习利用注解和反射完成类和表结构的映射关系,通过注解来传递表的表头
public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.study.Student2");
        //通过反射获得所有注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //获得注解的 value 的值-表名
        Table annotation = (Table) c1.getAnnotation(Table.class);
        String value = annotation.value();
        System.out.println(value);
        
        //获得类指定的属性注解
        Field name = c1.getDeclaredField("id");//获取属性
        //获取属性注解
        FieldTo f = name.getAnnotation(FieldTo.class);
        //获取属性注解对象内的值
        System.out.println(f.columnName());
        System.out.println(f.type());
        System.out.println(f.length());

    }
}

@Table("db_student")
class Student2 {
    @FieldTo(columnName = "db_id", type = "int", length = 10)
    private int id;

    @FieldTo(columnName = "db_age", type = "int", length = 10)
    private int age;

    @FieldTo(columnName = "db_name", type = "varchar", length = 3)
    private String name;

    
    public Student2() {
    }

    public Student2(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 "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//创建类名注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
//作用在类上
@interface Table {
    String value();
}

//属性的注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface FieldTo {
    String columnName();

    String type();

    int length();
}
posted @ 2021-03-01 11:07  笑忘舒  阅读(91)  评论(0编辑  收藏  举报