Java反射及自定义读取注解

参考https://blog.csdn.net/xu__cg/article/details/52877573

RTTI,即 Run-Time Type Identification 运行时类型识别。

一、反射机制:

1.反射机制概念
在Java中的反射机制是指在运行状态中,对于任意一个类都能够知道这个类所有的属性和方法;并且对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息以及动态调用对象方法的功能成为Java语言的反射机制。

2.反射的应用场合
在Java程序中许多对象在运行是都会出现两种类型:编译时类型和运行时类型。
编译时的类型由声明对象时实用的类型来决定,运行时的类型由实际赋值给对象的类型决定
如:

Person p=new Student();

其中编译时类型为Person,运行时类型为Student。
除此之外,程序在运行时还可能接收到外部传入的对象,该对象的编译时类型为Object,但是程序有需要调用该对象的运行时类型的方法。为了解决这些问题,程序需要在运行时发现对象和类的真实信息。然而,如果编译时根本无法预知该对象和类属于哪些类,程序只能依靠运行时信息来发现该对象和类的真实信息,此时就必须使用到反射了。

二、Java反射API

反射API用来生成JVM中的类、接口或则对象的信息。

  • Class类:反射的核心类,可以获取类的属性,方法等信息。
  • Field类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
  • Method类: Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
  • Constructor类: Java.lang.reflec包中的类,表示类的构造方法。

三、使用反射的步骤

1.步骤

  • 获取想要操作的类的Class对象
  • 调用Class类中的方法
  • 使用反射API来操作这些信息

2.如何获取一个Class对象?

  • 调用某个对象的getClass()方法

Person p=new Person();
返回的是一个泛型类
Class clazz=p.getClass();

//---------------Shape.java--------------//

package product.test;

public class Shape {
    void showMsg(){
        System.out.println("this is Shape class");
    }
}

//---------------Circle.java--------------//

package product.test;

public class Circle extends Shape{
    @Override
    void showMsg() {
        System.out.println("this is Circle class");
    }
}

//---------------getClassName.java--------------//

package product.test;

public class getClassName {

    public static void showName(Shape shape){

        Class clazz = shape.getClass(); //获取shape实际所属的类
        System.out.println(clazz.getName());//getName()获取类名
        if (clazz.getName().contains("Shape"))
            System.out.println("this is a shape object");
        else if (clazz.getName().contains("Circle"))
            System.out.println("this is a circle object");
        }

    public static void main(String[] args){
        //调用showName方法,分别传入Circle对象和Shape对象
        showName(new Circle());
        showName(new Shape());
    }

}

//---------------运行结果--------------//
product.test.Circle
this is a circle object
product.test.Shape
this is a shape object

  • 调用某个类的class属性来获取该类对应的Class对象

Class clazz1 = Person.class;
Class clazz2 = int.class;
Class clazz3 = Double[].class;

//---------------getClassName.java--------------//

package product.test;


public class getClassName {

    public static void showName(Shape shape){

    Class clazz = shape.getClass();//获取shape类对象

    if (clazz == Shape.class) //使用==号对类类型进行判断
        System.out.println("this is a shape object");
    else if (clazz == Circle.class)
        System.out.println("this is a circle object");
    }

    public static void main(String[] args){
    //调用showName方法,分别传入Circle对象和Shape对象
    showName(new Circle());
    showName(new Shape());
    }
}

//---------------运行结果--------------//

this is a circle object
this is a shape object

  • 使用Class类中的forName()静态方法; (最安全/性能最好)

返回的是一个Class类型
Class clazz=Class.forName(“类的全路径”); (最常用)

//加载数据库驱动

Class.forName(“com.mysql.jdbc.Driver”);

Class candy = Class.forName(“product.test.Candy”);

//---------------Candy.java--------------//

    package product.test;
    
    public class Candy {
        static {
            System.out.println("Loading Candy in static block");
        }
    
        public static void main(String[] args){
            System.out.println("Loading Candy in main block");
        }

}

//---------------loadClass.java--------------//

package product.test;

public class loadClass {
    public static void main(String[] args){
    System.out.println("Before loading Candy");

    try {
        Class.forName("product.test.Candy");//Class.forName加载Candy对象
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
  }
}

//---------------运行结果--------------//
Before loading Candy
Loading Candy in static block

3.Class类的常用方法

1、getName()
一个Class对象描述了一个特定类的属性,Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。

2、newInstance()
Class还有一个有用的方法可以为类创建一个实例,这个方法叫做newInstance()。例如:
x.getClass.newInstance(),创建了一个同x一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。

3、getClassLoader()
返回该类的类加载器。

4、getComponentType()
返回表示数组组件类型的 Class。

5、getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。

6、isArray()
判定此 Class 对象是否表示一个数组类。

7、instanceof()
判断一个对象真正所属类
objectName instanceof className

//---------------getClassName.java--------------//
注:本例中 showName(new Circle()); 传入的class对象通过 shape instanceof Circle 和 shape instanceof Shape 两个表达式均为true

package product.test;

public class getClassName {

    public static void showName(Shape shape){

    if (shape instanceof Circle) //使用instanceof判断所属类
        System.out.println("this is a circle object");
    else if (shape instanceof Shape)
        System.out.println("this is a shape object");
    }

    public static void main(String[] args){
    //调用showName方法,分别传入Circle对象和Shape对象
    showName(new Circle());
    showName(new Shape());
    }
}

//---------------运行结果--------------//

this is a circle object
this is a shape object

7、isInstance()
判断一个对象所属类
clazz.isInstance(Object object)

//---------------getClassName.java--------------//

package product.test;


public class getClassName {

public static void showName(Shape shape){

    Class clazz = shape.getClass();

    if (clazz.isInstance(new Shape())) //使用isInstance判断所属类
        System.out.println("this is a shape object");
    else if (clazz.isInstance(new Circle()))
        System.out.println("this is a circle object");

    }

    public static void main(String[] args){
    //调用showName方法,分别传入Circle对象和Shape对象
    showName(new Circle());
    showName(new Shape());
    }
}

//---------------运行结果--------------//

this is a circle object
this is a shape object

8、 getDeclaredMethods() //获取类成员方法
getParameterTypes() //获取参数类型
getDeclaredAnnotations() //获取注解
getAnnotations() //获取注解
getExceptionTypes() //获取异常
getDeclaredFields() //获取成员属性
getModifiers() //获取权限修饰符
getDeclaredConstructors() //获取构造方法

实体类
//---------------Person.java--------------//

package product.test;


public class Person {
    private String name;
    private String gender;
    private int age;

    //无参构造
    public Person() {
    }

    //有参构造
    public Person(String name, String gender, int age) {
        this.name = name;
        this.gender = gender;
        this.age = age;
    }

    //^getName()
    @Target(name = "zhangsan")
    public String getName() {
        return name;
    }
    //^setName()
    public void setName(String name) {
        this.name = name;
    }
    //^getGender()
    @Target(gender = "male")
    public String getGender() {
        return gender;
    }
    //^setGender()
    public void setGender(String gender) {
        this.gender = gender;
    }
    //^getAge()
    public int getAge() {
        return age;
    }
    //^setAge()
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    //^toString()
    public String toString() throws NullPointerException{
    return "人类{" +
    "姓名='" + name + '\'' +
    ", 性别='" + gender + '\'' +
    ", 年龄=" + age +
    '}';
    }
}

自定义注解类
附:
注解的保留策略:Java定义了3种策略:SOURCE、CLASS、RUNTIME,它们封装在java.lang.annotation.RetentionPolicy枚举对象中。
SOURCE策略的注解只在源文件中保留,在编译期间删除。
CLASS策略的注解,在编译期间存储在.class文件中,在运行时不能通过JVM来获得。
RUNTIME策略的注解,在编译期间存储在.class文件中,在运行时可以通过JVM来获得。
因此RUNTIME保留策略提供了最长的注解持续期。
保留策略是通过使用一个Java内置注解@Retention来指定注解的,语法如下:@Retention(保留策略)
eg : @Retention(RetentionPolicy.RUNTIME)

//---------------Target.java--------------//

package product.test;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


@Retention(RetentionPolicy.RUNTIME)
public @interface Target {

    String name() default "";

    String gender() default "male";

}

测试反射类
//---------------testPerson.java--------------//

package product.test;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;

/**
* 通过用户输入类的全路径,来获取该类的成员方法和属性
* Declared获取全部不管是私有和公有
* 1.获取访问类的Class对象
* 2.调用Class对象的方法返回访问类的方法和属性信息
*/
public class testPerson {

    public static void main(String[] args) {
    try {
    //获取Person类的Class对象
    Class clazz = Class.forName("product.test.Person");

    System.out.println("----------------------------成员方法信息----------------------------");
    //获取Person类的所有方法信息
    Method mt = null;
    Class prmt[] = null;
    Class exception[] = null;
    Annotation annotations[] = null;
    Annotation declaredAnnotations[] = null;
    Method[] method = clazz.getDeclaredMethods();//获取成员方法

    for (int i = 0; i < method.length; i++) 
    {
        mt = method[i];
        System.out.println("********第"+(i+1)+"个方法********");
        //打印成员方法
        System.out.println(mt.toString());
        prmt = mt.getParameterTypes();//获取参数类型
        declaredAnnotations = mt.getDeclaredAnnotations();//获取注解
        annotations = mt.getAnnotations();//获取注解
        exception = mt.getExceptionTypes();//获取异常
        for (int j = 0; j < prmt.length; j++) 
        {
            //打印成员方法所有的参数类型
            System.out.println("param #" + j + " " + prmt[j]);
        }

        for (int k = 0; k < exception.length; k++) 
        {
            //打印成员方法所有的异常
            System.out.println("exception #" + k + " " + exception[k]);
        }

        for (Annotation d : declaredAnnotations)
        {
            //打印成员方法所有的注解
            System.out.println("declaredAnnotations #" + d);
        }

        for (Annotation a : annotations)
        {
            //打印成员方法所有的注解
            System.out.println("annotations #" + a);
        }

    }
    System.out.println("----------------------------成员方法信息----------------------------");

    System.out.println("----------------------------成员属性信息----------------------------");
    //获取Person类的所有成员属性信息
    Field[] field = clazz.getDeclaredFields();
    for(Field f:field){
        System.out.println(f.toString());
        //获取成员属性的权限修饰符
        int modifiers = f.getModifiers();
        //打印成员属性的权限修饰符
        System.out.println("modifiers # " + Modifier.toString(modifiers));
    }
    System.out.println("----------------------------成员属性信息----------------------------");

    System.out.println("----------------------------构造方法信息----------------------------");
    //获取Person类的所有构造方法信息
    Constructor[] constructor = clazz.getDeclaredConstructors();
    for(Constructor c:constructor){
        System.out.println(c.toString());
    }
    System.out.println("----------------------------构造方法信息----------------------------");
    } catch (Exception e) {
        e.printStackTrace();
    }
 }
}

//---------------运行结果--------------//
----------------------------成员方法信息----------------------------
第1个方法
public java.lang.String product.test.Person.toString() throws java.lang.NullPointerException
exception #0 class java.lang.NullPointerException
第2个方法
public java.lang.String product.test.Person.getName()
declaredAnnotations #@product.test.Target(name=zhangsan, gender=male)
annotations #@product.test.Target(name=zhangsan, gender=male)
第3个方法
public void product.test.Person.setName(java.lang.String)
param #0 class java.lang.String
第4个方法
public void product.test.Person.setAge(int)
param #0 int
第5个方法
public int product.test.Person.getAge()
第6个方法
public java.lang.String product.test.Person.getGender()
declaredAnnotations #@product.test.Target(name=, gender=male)
annotations #@product.test.Target(name=, gender=male)
第7个方法
public void product.test.Person.setGender(java.lang.String)
param #0 class java.lang.String
----------------------------成员方法信息----------------------------
----------------------------成员属性信息----------------------------
private java.lang.String product.test.Person.name
modifiers # private
private java.lang.String product.test.Person.gender
modifiers # private
private int product.test.Person.age
modifiers # private
----------------------------成员属性信息----------------------------
----------------------------构造方法信息----------------------------
public product.test.Person()
public product.test.Person(java.lang.String,java.lang.String,int)
----------------------------构造方法信息----------------------------

4.创建Class对象
当我们获取到所需类的Class对象后,可以用它来创建对象,创建对象的方法有两种:

使用Class对象的newInstance()方法来创建该Class对象对应类的实例,但是这种方法要求该Class对象对应的类有默认的空构造器。
先使用Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建 Class对象对应类的实例,通过这种方法可以选定构造方法创建实例。
使用method.invoke()调用成员方法赋值

测试反射类
//---------------testPerson.java--------------//

package product.test;


import java.lang.reflect.*;



public class createPerson {

    public static void main(String[] args){
    try {
        //获取Person类的Class对象
        Class clazz = Class.forName("product.test.Person");

        /**
        * 第一种方法创建对象
        * newInstance方法
        */
        //创建对象
        Person p = (Person) clazz.newInstance();
        //设置属性
        p.setName("张三");
        p.setAge(16);
        p.setGender("男");
        System.out.println(p.toString());



        /**
        * 第二种方法创建
        * 构造方法
        */
        //获取构造方法
        Constructor c = clazz.getDeclaredConstructor(String.class,String.class,int.class);
        //创建对象并设置属性
        Person p1 = (Person) c.newInstance("李四","男",20);
        System.out.println(p1.toString());



        /**
        * 第三种方法创建
        */
        Method setMethod = null;
        Method getMethod = null;
        Object obj = clazz.newInstance(); //调用无参构造
        Object personValue = null;
        Class fieldClass = null;
        Object sendRequestBody = null;

        Field[] field = clazz.getDeclaredFields();

        String person = "卜学习_男_10";
        String[] personString = person.split("_");

        for (int i = 0; i < field.length; i++) {
            // field[i].setAccessible(true);
            String fieldName = toUppCaseForFirstChar(field[i].getName());
            Class fieldType = field[i].getType();

            sendRequestBody = personString[i];

            if(fieldType == String.class)

                fieldClass = String.class;

            else if (fieldType == int.class){
                fieldClass = int.class;
                sendRequestBody = Integer.valueOf(personString[i]).intValue();
            }
            else
                fieldClass = String.class;

            setMethod = clazz.getDeclaredMethod("set" + fieldName,fieldClass);
            // getMethod = clazz.getDeclaredMethod("get"+ fieldName);
            getMethod = clazz.getDeclaredMethod("toString");

            setMethod.invoke(obj,sendRequestBody);
            personValue = getMethod.invoke(obj);
        }

        System.out.println("invokePerson:"+ personValue);

    } catch (Exception e) {
        e.printStackTrace();
    }

  }

    /**
    *把String的第一个字符变为大写
    * @param string
    */
    public static String toUppCaseForFirstChar(String string)
    {
        char c[] = string.toCharArray();
        c[0] -= 32;
        System.out.println(c);
        return String.valueOf(c);
    }

    /**
    *把String的第一个字符变为小写
    * @param string
    */
    public static String toLowerCaseForFirstChar(String string)
    {
        char c[] = string.toCharArray();
        c[0] += 32;
        System.out.println(c);
        return String.valueOf(c);
    }

}

//---------------运行结果--------------//
人类{姓名=‘张三’, 性别=‘男’, 年龄=16}
人类{姓名=‘李四’, 性别=‘男’, 年龄=20}
Name
Gender
Age
invokePerson:人类{姓名=‘卜学习’, 性别=‘男’, 年龄=10}

你可能感兴趣的:(Java)