注解和反射

214 阅读7分钟

注解和反射

注解

什么是注解

这里你可以描述知识空间建设的目标和意义,有利于团队成员理解目标并朝着目标努力。

如何自定义一个注解

这里你可以介绍知识空间的主要内容和面向的成员群体。

什么是元注解

对注解类进行注解的注解类

自定义注解时需要用到的两个元注解

@Target 限制可以应用该注解的元素类型

@Rentention 限制注解的存储方式

使用注解时传递参数

注解的应用场景

按照@Rentention限制的三种存储方式,有三种应用场景

RetentionPolicy.SOURCE

作用于源码级别的注解,可以用于语法检查、APT注解处理器等场景。

RetentionPolicy.CLASS

作用于AspectJ、热修复Roubust等场景

RetentionPolicy.RUNTIME

结合反射使用

反射

反射定义

反射是在运行时而非编译时,才知道要操作的类是什么,动态的获取类型的信息,比如接口信息、成员信息、方法信息、构造方法等,根据这些信息去创建对象、访问方法、访问或者修改成员变量。

Class类

每个已加载的类在内部都有一份类信息,每个对象都有指向他所属的类信息的引用。类信息对应的类就是java.lang.Class。Class是一个泛型类。

可通过三种途径获取Class对象。

  1. 通过类名获取

Class<Date> cls = Date.getClass;
  1. 通过类名全路径直接加载获取

Class<?> cls = Class.forName("java.util.HashMap");
  1. 通过对象获取,getClass并不知道具体的类型, 所以返回的是Class<?>

Date date = new Date();

Class<?> clz = date.getClass();

使用Class对象操作字段信息

  1. 获取所有的public字段,包括父类的 getFields()

  2. 获取本类中所有的字段,包括非public的,但不包括父类的 getDeclaredFields()

  3. 获取本类或者父类中指定名称的public字段 getField("字段名称")

  4. 获取本类中声明的指定名臣的字段,包括非public的 getDeclaredField("字段名称")

  5. 获取字段的名称  field.getName()

  6. 获取字段的值  field.get(对象)

  7. 修改public字段的值  field.set(对象名称,新的值)

  8. 修改非public字段的值 需要设置field.setAccessible(true)

public class Student {

    private String name;

    public String location;

    public int age;



    public Student(String name,String location,int age){

        this.name = name;

        this.location = location;

        this.age = age;

    }



    private void run(){

        System.out.println("this ist the private run method");

    }



    public String getName() {

        System.out.println("this ist the public getName method");

        return name;

    }



    public void setName(String name) {

        System.out.println("this ist the public getName method");

        this.name = name;

    }



    public String getLocation() {

        System.out.println("this ist the public getLocation method");

        return location;

    }



    public void setLocation(String location) {

        System.out.println("this ist the public setLocation method. location = " + location);

        this.location = location;

    }



    public int getAge() {

        System.out.println("this ist the public getAge method");

        return age;

    }



    public void setAge(int age) {

        System.out.println("this ist the public setAge method");

        this.age = age;

    }

}







    private static void getField() throws IllegalAccessException, InstantiationException {

        Class<Student> clz = Student.class;

        Student student = (Student) clz.newInstance();

        Field[] fields = clz.getFields();

        System.out.println("获取所有的public的字段,包括父类的");

        for(Field field : fields){

            System.out.println(field.getName());

        }

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

        System.out.println("获取本类中所有的的字段,包括非public的,但不包括父类的");

        fields = clz.getDeclaredFields();

        for(Field field : fields){

            System.out.println(field.getName());

        }

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

        System.out.println("获取本类或者父类中指定名称的public字段,");

        Field field = null;

        try {

            field = clz.getField("age");

            System.out.println("字段名称 : " + field.getName());

            System.out.println("获取字段的值");

            System.out.println(field.get(student));

            System.out.println("修改字段的值");

            field.set(student,43565);

            System.out.println(field.get(student));

        } catch (NoSuchFieldException | IllegalAccessException e) {

            e.printStackTrace();

        }

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

        System.out.println("获取本类中声明的指定名称的字段");

        try {

            field = clz.getDeclaredField("name");

            field.setAccessible(true);

            System.out.println("字段名称 : " + field.getName());

            System.out.println("修改私有字段的值");

            field.set(student,"修改成功");

            System.out.println(field.get(student));

        } catch (NoSuchFieldException e) {

            e.printStackTrace();

        }

    }

输出为:

获取所有的public的字段,包括父类的

location

age

*********************************************

获取本类中所有的的字段,包括非public的,但不包括父类的

name

location

age

*********************************************

获取本类或者父类中指定名称的public字段,

字段名称 : age

获取字段的值

0

修改字段的值

43565

*********************************************

获取本类中声明的指定名称的字段

字段名称 : name

修改私有字段的值

修改成功

使用Class对象操作方法

  1. 获取本类和父类中所有的public方法  getMethods()

  2. 获取本类中声明的所有方法,包括非public的,但不包括父类的  getDeclaredMethods()

  3. 获取本类和父类中指定名称的public方法  getMethod("setLocation",String.class) String.class表示入参为String类型

  4. 获取本类中已声明的指定名称的方法,包括非public的,但是不包括父类的 getDeclaredMethod("run")

  5. public方法的调用 method1.invoke(对象,这是入参)

  6. 非public方法的调用 需要执行method1.setAccessible(true)



Class<Student> clz = Student.class;

Student student = null;

try {

    student = (Student) clz.newInstance();

} catch (InstantiationException e) {

    e.printStackTrace();

} catch (IllegalAccessException e) {

    e.printStackTrace();

}

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

System.out.println("返回所有的public方法,包括父类的");

Method[] methods = clz.getMethods();

for(Method method:methods){

    System.out.println(method.getName());

}

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

System.out.println("返回本类中声明的所有方法,包括非public的,但不包括父类的");

methods = clz.getDeclaredMethods();

for(Method method:methods){

    System.out.println(method.getName());

}

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

System.out.println("返回本类和父类中指定名称和参数类型的public方法");

try {

    Method method1 = clz.getMethod("setLocation",String.class);

    System.out.println(method1.getName());

    method1.invoke(student,"这是地址");

} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {

    e.printStackTrace();

}

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

System.out.println("返回本类中声明的指定名称和参数类型的方法,包括非public方法");

try {

    Method method1 = clz.getDeclaredMethod("run");

    System.out.println(method1.getName());

    method1.setAccessible(true);

    method1.invoke(student);

} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {

    e.printStackTrace();

}

运行结果为:

************************

返回所有的public方法,包括父类的

getName

getLocation

setName

setLocation

getAge

setAge

wait

wait

wait

equals

toString

hashCode

getClass

notify

notifyAll

************************

返回本类中声明的所有方法,包括非public的,但不包括父类的

getName

run

getLocation

setName

setLocation

getAge

setAge

************************

返回本类和父类中指定名称和参数类型的public方法

setLocation

this ist the public setLocation method. location = 这是地址

************************

返回本类中声明的指定名称和参数类型的方法,包括非public方法

run

this ist the private run method

创建对象和构造方法

  1. 获取所有的public构造方法

  2. 获取所有的构造方法,包括非public的

  3. 获取指定参数类型的public构造方法

  4. 获取指定参数类型的构造方法,包括非public 的

  5. 通过public构造方法创建对象

  6. 通过非public方法创建对象

Class<Student> clz = Student.class;

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

System.out.println("获取所有的public构造方法");

Constructor<Student>[] constructors = (Constructor<Student>[]) clz.getConstructors();

for (Constructor constructor : constructors) {

    System.out.println(constructor);

}

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

System.out.println("获取所有的构造方法,包括非public的");

constructors = (Constructor<Student>[]) clz.getDeclaredConstructors();

for (Constructor constructor : constructors) {

    System.out.println(constructor);

}

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

System.out.println("获取指定参数类型的public构造方法");

Constructor constructor = null;

try {

    constructor = clz.getConstructor(String.class, String.class, int.class);

    System.out.println(constructor);

    Student s = (Student) constructor.newInstance("this is name", "this is location", 18);

    System.out.println(s);

} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {

    e.printStackTrace();

}

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

System.out.println("获取指定参数类型的构造方法,包括非public的");

constructor = null;

try {

    constructor = clz.getDeclaredConstructor(String.class);

    System.out.println(constructor);

    constructor.setAccessible(true);

    Student s = (Student) constructor.newInstance("this is name");

    System.out.println(s);

} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {

    e.printStackTrace();

}

运行结果为:

*********************************************

获取所有的public构造方法

public reflect.Student(java.lang.String,java.lang.String,int)

*********************************************

获取所有的构造方法,包括非public的

private reflect.Student(java.lang.String)

public reflect.Student(java.lang.String,java.lang.String,int)

*********************************************

获取指定参数类型的public构造方法

public reflect.Student(java.lang.String,java.lang.String,int)

Student{name='this is name', location='this is location', age=18}

*********************************************

获取指定参数类型的构造方法,包括非public的

private reflect.Student(java.lang.String)

Student{name='this is name', location='null', age=0}

  • 通过入口类Class可以访问类的各种信息,比如字段、方法、构造方法、父类、接口、泛型信息等,也可以创建和操作对象、调用方法等。
  • 借助反射,可以边写通用的,灵活的程序,但是一般不建议使用,一是因为反射更容易出现运行时错误,二是反射的性能相对低一些,在访问字段、调用方法前,反射先要查找对应的Field/Method,所以要慢一些。

反射获取真实类型

TypeVariable 泛型类型变量,可以得到泛型上下限等信息



public class TestType<K extends Comparable & Serializable, V> {

    K key;

    V value;



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

        // 获取字段类型

        Field fieldK = TestType.class.getDeclaredField("key");

        Field fieldV = TestType.class.getDeclaredField("value");



        TypeVariable typeVariableK = (TypeVariable) fieldK.getGenericType();

        TypeVariable typeVariableV = (TypeVariable) fieldV.getGenericType();



        System.out.println(typeVariableK.getName()); // K

        System.out.println(typeVariableV.getName()); // V



        System.out.println(typeVariableK.getGenericDeclaration()); // class TestType

        System.out.println(typeVariableV.getGenericDeclaration()); // class TestType



        for (Type type : typeVariableK.getBounds()) {

            System.out.println(type);  

            // interface java.lang.Comparable

            // interface java.io.Serializable

        }



        for (Type type : typeVariableV.getBounds()) {

            System.out.println(type);  // class java.lang.Object

        }

    }

}

ParameterizedType

具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)



public class TestType {

    Map<String,Integer> map;



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

        Field field = TestType.class.getDeclaredField("map");

        System.out.println(field.getGenericType()); //java.util.Map<java.lang.String, java.lang.Integer>



        ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();

        System.out.println(parameterizedType.getRawType()); // interface java.util.Map



        for (Type type : parameterizedType.getActualTypeArguments()){

            System.out.println(type); 

            //class java.lang.String

            //class java.lang.Integer

        }

    }

}

GenericArrayType

当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。



public class TestType {

    List<String> [] lists;



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

        Field f = TestType.class.getDeclaredField("lists");

        GenericArrayType genericType = (GenericArrayType) f.getGenericType();

        System.out.println(genericType.getGenericComponentType()); 

        //java.util.List<java.lang.String>

    }

}

WildcardType

通配符泛型,获得上下限信息;

public class TestType {

    private List<? extends Integer> a; // 上限

    private List<? super String> b; //下限



    public static void main(String[] args) throws Exception {

        Field fieldA = TestType.class.getDeclaredField("a");

        Field fieldB = TestType.class.getDeclaredField("b");

        ParameterizedType pTypeA = (ParameterizedType) fieldA.getGenericType();

        ParameterizedType pTypeB = (ParameterizedType) fieldB.getGenericType();

        WildcardType wTypeA = (WildcardType) pTypeA.getActualTypeArguments()[0];

        WildcardType wTypeB = (WildcardType) pTypeB.getActualTypeArguments()[0];

        System.out.println(wTypeA.getUpperBounds()[0]); // class java.lang.Integer

        System.out.println(wTypeB.getLowerBounds()[0]); // class java.lang.String

        System.out.println(wTypeA); // ? extends java.lang.Integer

    }

}