Java基础扩展提升总结:反射机制

反射机制

1、简介

Java反射机制指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定的一个对象,都能够调用它的任意一个属性和方法。这种动态获取类的内容以及动态调用对象的方法称为反射机制。

Java的反射机制允许编程人员在对类未知的情况下,获取类相关信息的方式变得更加多样灵活,调用类中相应方法,是Java增加其灵活性与动态性的一种机制。

反射就是把Java类中的各种成分映射成一个个的Java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。

2、反射常用类

 使用反射机制时,有如下几个常用的类:

  • Class:类
  • Field:字段
  • Method:方法
  • Constructor:构造器

(1)Class类

Class类是Java的反射中心,用于表示一个类编译之后产生的“.class”文件,反射的原理在于Class类的对象。类加载的时候,Class类对象的由来是将 ".class" 文件读入内存,并为之创建一个Class对象。

获取Class类对象的三种方式:

  1. Object中的getClass()方法;
  2. 任何数据类型(包括基本数据类型)都有一个静态属性class;
  3. 通过Class类的静态方法forName(String className)获取;

Class中常用的方法:

获取类的相关信息的方法:

方法描述
getName()返回String形式的该类的名称。
newInstance()根据某个Class对象产生其对应类的实例,它调用的是此类的默认构造方法(没有默认无参构造器会报错)
getClassLoader()返回该Class对象对应的类的类加载器。
getSuperClass()返回某子类所对应的直接父类所对应的Class对象。
isArray()判定此Class对象所对应的是否是一个数组对象。
getComponentType()如果当前类表示一个数组,则返回表示该数组组件的 Class 对象,否则返回 null。 
getInterfaces()返回当前对象表示的类或接口实现的接口。 
isInstance(Object)此方法是 Java 语言 instanceof 操作的动态等价方法。
isInterface()判定指定的 Class 对象是否表示一个接口类型。
isPrimitive()判定指定的 Class 对象是否表示一个 Java 的基类型。

获取Field的方法:

getDeclaredField(String)返回当前 Class 对象表示的类或接口的指定已说明的一个域对象。
getDeclaredFields()返回当前 Class 对象表示的类或接口的所有已说明的域对象数组。 
getField(String)返回当前 Class 对象表示的类或接口的指定的公有字段对象。
getFields()返回当前 Class 对象表示的类或接口的所有可访问的公有字段对象数组。 

获取Method的方法:

getMethod(String, Class[])返回当前 Class 对象表示的类或接口的指定的公有成员方法对象。 
getMethods()返回当前 Class 对象表示的类或接口的所有公有成员方法对象数组,包括已声明的和从父类继承的方法。 
getDeclaredMethod(String,Class[])返回当前 Class 对象表示的类或接口的指定已说明的一个方法对象。 
getDeclaredMethods()返回 Class 对象表示的类或接口的所有已说明的方法数组。

获取Constructor的方法:

getConstructor(Class[])返回当前 Class 对象表示的类的指定的公有构造子对象。
getConstructors()返回当前 Class 对象表示的类的所有公有构造子对象数组。
getDeclaredConstructor(Class[])返回当前 Class 对象表示的类的指定已说明的一个构造子对象。 
getDeclaredConstructors()返回当前 Class 对象表示的类的所有已说明的构造子对象数组。 

(2)Field类

Field类位于 java.lang.reflect 包中,主要用于在程序运行状态中,动态地获取类的字段信息。Field类是在反射中对类的字段(或者说是成员变量)的封装,为我们提供了获取当前对象的成员变量的类型,和重新设值的方法。可以使用Class类中的提供的获取Field的方法来获取Field对象。

Field类中的常用类:

方法描述
public void setInt(Object obj, int i)将指定对象obj中类型为int的成员变量的值设置为i;
public float getFloat(Object obj)获得指定对象obj中类型为float的成员变量的值。
public void setFloat(Object obj, float f)将指定对象obj中类型为float的成员变量的值设置为f;
public boolean getBoolean(Object obj)获得指定对象obj中类型为boolean的成员变量的值。
public void setBoolean(Object obj, boolean z)将指定对象obj中类型为boolean的成员变量的值设置为z;
public void setAccessible(boolean flag) throws SecurityException此方法可以设置是否忽略权限限制直接访问private等私有权限的成员变量。

(3)Method

Method类位于 java.lang.reflect 包中,主要用于在程序运行状态中,动态地获取方法信息。Method类是对一个类中成员方法信息的封装,Method类和它从Member类继承而来的方法使得我们可以获得方法声明的完整信息。我们可以使用Class类中的提供的获取Method的方法来获取Method对象。

Method中的方法:

非常齐全的Method中的方法的详细介绍:https://blog.csdn.net/Goodbye_Youth/article/details/84036809

方法描述
getAnnotatedReturnType()

返回一个AnnotatedType对象,该对象表示使用一个类型来指定由该可执行文件表示的方法/构造函数的返回类型

getAnnotatedExceptionTypes()

返回一个AnnotatedType对象数组,这些对象表示使用类型来指定由该可执行文件表示的方法/构造函数声明的异常

getAnnotatedReceiverType()

返回一个AnnotatedType对象,该对象表示使用一个类型来指定该可执行对象表示的方法/构造函数的接收者类型

getAnnotation(Class<T> annotationClass)

如果该方法对象存在指定类型的注解,则返回该注解,否则返回null

getDeclaredAnnotation(Class<T> annotationClass)

如果该方法对象存在指定类型的注解,则返回该注解,否则返回null

getAnnotationsByType(Class<T> annotationClass)

如果该方法对象存在指定类型的注解,则返回该注解数组,否则返回null

getDeclaredAnnotationsByType(Class<T> annotationClass)

如果该方法对象存在指定类型的注解,则返回该注解数组,否则返回null

getAnnotations()

返回该方法对象上的所有注解,如果没有注解,则返回空数组

getDeclaredAnnotations()

返回该方法对象上的所有注解,如果没有注解,则返回空数组

getModifiers()

返回修饰该方法对象修饰符的整数形式,使用 Modifier 类对其进行解码

getName()

返回方法对象名称

isAnnotationPresent(Class<? extends Annotation> annotationClass)

如果该方法对象上有指定类型的注解,则返回true,否则为false

isVarArgs()

如果该方法对象的参数中存在 可变参,则返回true,否则为false

getDeclaringClass ()

返回该方法对象表示的方法所在类的Class对象

getAnnotatedParameterTypes()

返回一个AnnotatedType对象数组,这些对象表示使用类型来指定由该可执行文件表示的方法/构造函数的形式参数类型

getParameterAnnotations()

返回一组注解数组,这些注解以声明顺序修饰该方法对象的参数

getParameterCount()返回该方法对象的参数个数
getParameters()

返回一个参数对象数组,该数组表示该方法对象的所有参数

getDefaultValue()

返会该注解方法对象表示的成员默认值

getParameterTypes()

返回一个Class对象数组,该数组以声明顺序表示该方法对象的参数对象,会擦除泛型

getReturnType()

返回一个Class对象,该Class对象表示该方法对象的返回对象,会擦除泛型

getGenericReturnType()

返回一个Type对象,该Type对象表示该方法对象的返回类型,会保留泛型

getExceptionTypes()

返回一个Class对象数组,该数组表示由该方法对象抛出的异常对象,会擦除泛型

getGenericExceptionTypes()

返回一个Type对象数组,该数组表示由该方法对象抛出的异常类型,会保留泛型

getTypeParameters()

返回一个TypeVariable对象数组,该数组表示该方法对象声明列表上的类型变量数组

toString()

返回该方法对象的字符串表示形式,会擦除泛型

toGenericString()

返回该方法对象的字符串表示形式,会保留泛型

isAccessible()

获取该方法对象的可访问标志

setAccessible(boolean flag)

设置该方法对象的可访问标志

isDefault()

判断该方法对象是否为默认方法,如果是则返回true,否则为false

isSynthetic()

判断该方法对象是否为合成方法,如果是则返回true,否则为false

isBridge()

判断该方法对象是否桥接方法,如果是则返回true,否则为false

(4)Constructor

java.lang.reflect 包中的Contructor类为我们提供了,在程序运行状态中,动态地获取构造器信息和调用的方法。我们可以使用Class类中的提供的获取Contructor的方法来获取Contructor对象。

主要方法:

方法描述
getName()获取构造器方法名
toGenericString()、toString()获取构造器的字符串描述
getModifiers()获取构造器的修饰符
getParameterCount()获取参数个数
getParameterTypes()获取参数类数组(Class)
getGenericParameterTypes()获取参数类型数组(Type)
getParameters()获取构造器的参数对象(Parameter[])数组
getTypeParameters()获取参数化类型(泛型)
getExceptionTypes()获取异常类(Class)
getGenericExceptionTypes()获取异常类(Type)
equals()比较两个构造器是否相同
isSynthetic()判断构造器是否是合成方法
isVarArgs()判断构造器是否有可变参数
isAccessible()判断构造器是否可以访问
getAnnotatedParameterTypes()获取注解的参数类型(组合类型)
getAnnotation(Annotation.class)、getDeclaredAnnotation(Annotation.class)获取指定的一个注解Class
getAnnotationsByType(annotation.cass)、getDeclaredAnnotationsByType(annotation.cass)获取指定的一组注解Class
getAnnotations()和getDeclaredAnnotations()获取全部注解类
getParameterAnnotations获取参数和注解的二维矩阵
newInstance()以默认构造器进行对象实例化
newInstance(args…)以指定构造器进行对象实例化

3、示例

首先我们需要有一个类:

package reflection;

public class Student {

    //字段
    public String name;
    protected int age;
    char sex;
    private String phoneNum;

    //main方法
    public static void main(String[] args) {
        System.out.println("main方法执行了");
    }
    //构造方法

    //(默认的有参构造方法)
    Student(String str){
        System.out.println("(默认)的有参构造方法 s = " + str);
    }
    //无参构造方法
    public Student(){
        System.out.println("调用了公有、无参构造方法执行了。。。");
    }
    //有一个参数的构造方法
    public Student(char name){
        System.out.println("姓名:" + name);
    }
    //有多个参数的构造方法
    public Student(String name ,int age){
        System.out.println("姓名:"+name+"年龄:"+ age);
    }
    //受保护的构造方法
    protected Student(boolean n){
        System.out.println("受保护的构造方法 n = " + n);
    }
    //私有构造方法
    private Student(int age){
        System.out.println("私有的构造方法 年龄:"+ age);
    }


    //成员方法
    public void show(){
        System.out.println("is show...");
    }

    public void show1(String s){
        System.out.println("调用了:公有的,String参数的show1(): s = " + s);
    }
    protected void show2(){
        System.out.println("调用了:受保护的,无参的show2()");
    }
    void show3(){
        System.out.println("调用了:默认的,无参的show3()");
    }
    private String show4(int age){
        System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age);
        return "abcd";
    }

    public static void show5(String s){
        System.out.println("调用了:静态的,String参数的show5(): s = " + s);
    }

    //getter、setter,反射中称之为属性

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public String getPhoneNum() {
        return phoneNum;
    }

    public void setPhoneNum(String phoneNum) {
        this.phoneNum = phoneNum;
    }

    //tostring()
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", phoneNum='" + phoneNum + '\'' +
                '}';
    }
}

(1)获取Class对象的三种方式

package reflection;

public class TestGetClass {
    public static void main(String[] args) {
        //第一种方式获取Class对象
        Student stu1 = new Student();
        Class stuClass = stu1.getClass();
        //获取类的名称
        System.out.println(stuClass.getName());
        //第二种方式获取Class对象
        Class stuClass2 = Student.class;
        System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个

        //第三种方式获取Class对象
        try {
            Class stuClass3 = Class.forName("reflection.Student");//带包名的类路径
            System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

测试结果: 

调用了公有、无参构造方法执行了。。。
reflection.Student
true
true

 (2)获取Class类对象的构造器以及如何使用构造器创建对象

package reflection;

import java.lang.reflect.Constructor;

public class TestGetConstructors {
    public static void main(String[] args) throws Exception {
        Class clazz = Class.forName("reflection.Student");
        Constructor[] conArray = null;
        //获取构造器
        System.out.println("**********************所有公有构造方法*********************************");
        conArray = clazz.getConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }
        System.out.println("************所有的构造方法(包括:私有、受保护、默认、公有)***************");
        conArray = clazz.getDeclaredConstructors();
        for(Constructor c : conArray){
            System.out.println(c);
        }
        System.out.println("*****************获取公有、无参的构造方法*******************************");
        Constructor con_1 = clazz.getConstructor(null);
        System.out.println("con = " + con_1);

        System.out.println("******************获取私有int类型参数的构造方法*******************************");
        Constructor con_2 = clazz.getDeclaredConstructor(int.class);
        System.out.println(con_2);

        System.out.println("************************使用公有构造器创建对象*************************");
        Object obj = con_1.newInstance();
        System.out.println((Student)obj);

        System.out.println("************************使用私有构造器创建对象*************************");

        con_2.setAccessible(true);//暴力访问(忽略掉访问修饰符)
        obj = con_2.newInstance(21);
        System.out.println((Student)obj);

    }
}

测试结果:

**********************所有公有构造方法*********************************
public reflection.Student(char)
public reflection.Student()
public reflection.Student(java.lang.String,int)
************所有的构造方法(包括:私有、受保护、默认、公有)***************
protected reflection.Student(boolean)
private reflection.Student(int)
public reflection.Student(char)
public reflection.Student()
reflection.Student(java.lang.String)
public reflection.Student(java.lang.String,int)
*****************获取公有、无参的构造方法*******************************
con = public reflection.Student()
******************获取私有int类型参数的构造方法*******************************
private reflection.Student(int)
************************使用公有构造器创建对象*************************
调用了公有、无参构造方法执行了。。。
Student{name='null', age=0, sex= , phoneNum='null'}
************************使用私有构造器创建对象*************************
私有的构造方法 年龄:21
Student{name='null', age=0, sex= , phoneNum='null'}

(3)获取Class类对象的成员方法(Method)以及调用方法

package reflection;

import java.lang.reflect.Method;

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

        Class stuClass = Class.forName("reflection.Student");
        Method[] methodArray;

        System.out.println("***************获取所有的包括继承的公有方法,*******************");
        methodArray = stuClass.getMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }

        System.out.println("***************获取所有的自己的方法,包括私有的但不包括继承的*******************");
        methodArray = stuClass.getDeclaredMethods();
        for(Method m : methodArray){
            System.out.println(m);
        }

        System.out.println("***************获取公有的String类型参数的show1()方法并调用*******************");
        Method m = stuClass.getMethod("show1", String.class);
        System.out.println(m);

        //调用该方法,需要一个实例对象,并传入实参
        Object obj = stuClass.getConstructor().newInstance();
        m.invoke(obj, "刘德华");

        System.out.println("***************获取私有的int类型参数的show4()方法,有返回值******************");
        m = stuClass.getDeclaredMethod("show4", int.class);
        System.out.println(m);

        //调用该方法,传入实参,并使用Object接收返回值
        m.setAccessible(true);//解除私有限定
        Object result = m.invoke(obj, 20);
        System.out.println("返回值:" + result);

        System.out.println("***************获取静态方法,并调用******************");
        m = stuClass.getMethod("show5", String.class);

        //静态方法可以传入 类 参数进行调用,而不用传入该类的对象
        m.invoke(stuClass,"suxing");
    }
}

测试结果:

***************获取所有的包括继承的公有方法,*******************
public static void reflection.Student.main(java.lang.String[])
public java.lang.String reflection.Student.toString()
public java.lang.String reflection.Student.getName()
public void reflection.Student.setName(java.lang.String)
public void reflection.Student.show1(java.lang.String)
public static void reflection.Student.show5(java.lang.String)
public void reflection.Student.setAge(int)
public void reflection.Student.show()
public char reflection.Student.getSex()
public java.lang.String reflection.Student.getPhoneNum()
public void reflection.Student.setSex(char)
public int reflection.Student.getAge()
public void reflection.Student.setPhoneNum(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
***************获取所有的自己的方法,包括私有的但不包括继承的*******************
public static void reflection.Student.main(java.lang.String[])
public java.lang.String reflection.Student.toString()
public java.lang.String reflection.Student.getName()
public void reflection.Student.setName(java.lang.String)
public void reflection.Student.show1(java.lang.String)
private java.lang.String reflection.Student.show4(int)
public static void reflection.Student.show5(java.lang.String)
public void reflection.Student.setAge(int)
public void reflection.Student.show()
protected void reflection.Student.show2()
public char reflection.Student.getSex()
public java.lang.String reflection.Student.getPhoneNum()
public void reflection.Student.setSex(char)
void reflection.Student.show3()
public int reflection.Student.getAge()
public void reflection.Student.setPhoneNum(java.lang.String)
***************获取公有的String类型参数的show1()方法并调用*******************
public void reflection.Student.show1(java.lang.String)
调用了公有、无参构造方法执行了。。。
调用了:公有的,String参数的show1(): s = 刘德华
***************获取私有的int类型参数的show4()方法,有返回值******************
private java.lang.String reflection.Student.show4(int)
调用了,私有的,并且有返回值的,int参数的show4(): age = 20
返回值:abcd
***************获取静态方法,并调用******************
调用了:静态的,String参数的show5(): s = suxing

(3)获取Class类对象的字段(Field)及使用

package reflection;

import java.lang.reflect.Field;

public class TestGetFields {

    public static void main(String[] args) throws Exception {
        Class stuClass = Class.forName("reflection.Student");

        System.out.println("************获取所有公有的字段********************");
        Field[] fieldArray = stuClass.getFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }

        System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
        fieldArray = stuClass.getDeclaredFields();
        for(Field f : fieldArray){
            System.out.println(f);
        }

        System.out.println("*************获取指定的公有字段并调用***********************************");
        Field f = stuClass.getField("name");
        System.out.println(f);
        //获取一个对象
        Student stu = (Student) stuClass.getConstructor().newInstance();
        //为字段设置值
        f.set(stu, "刘德华");
        //验证
        System.out.println(stu);
        System.out.println("**************获取私有字段并调用********************************");
        f = stuClass.getDeclaredField("phoneNum");
        System.out.println(f);
        f.setAccessible(true);//暴力访问,解除私有限定
        f.set(stu, "18888889999");
        System.out.println(stu);


        System.out.println("**************获取受保护字段并调用********************************");
        f = stuClass.getDeclaredField("age");
        System.out.println(f);
        //f.setAccessible(true);//暴力访问,解除私有限定
        f.set(stu, 21);
        System.out.println(stu);

        System.out.println("**************获取默认字段并调用********************************");
        f = stuClass.getDeclaredField("sex");
        System.out.println(f);
        //f.setAccessible(true);//暴力访问,解除私有限定
        f.set(stu, '男');
        System.out.println(stu);

        System.out.println("**************获取字段值********************************");
        System.out.println("phoneNum = "+f.get(stu));

    }
}

测试结果:

************获取所有公有的字段********************
public java.lang.String reflection.Student.name
************获取所有的字段(包括私有、受保护、默认的)********************
public java.lang.String reflection.Student.name
protected int reflection.Student.age
char reflection.Student.sex
private java.lang.String reflection.Student.phoneNum
*************获取指定的公有字段并调用***********************************
public java.lang.String reflection.Student.name
调用了公有、无参构造方法执行了。。。
Student{name='刘德华', age=0, sex= , phoneNum='null'}
**************获取私有字段并调用********************************
private java.lang.String reflection.Student.phoneNum
Student{name='刘德华', age=0, sex= , phoneNum='18888889999'}
**************获取受保护字段并调用********************************
protected int reflection.Student.age
Student{name='刘德华', age=21, sex= , phoneNum='18888889999'}
**************获取默认字段并调用********************************
char reflection.Student.sex
Student{name='刘德华', age=21, sex=男, phoneNum='18888889999'}
**************获取字段值********************************
phoneNum = 男

4、开发中反射的简单应用

使用配置文件加载指定的类。 我们利用反射和配置文件,可以使应用程序更新时,对源码无需进行任何修改。我们只需要将新类发送给客户端,并修改配置文件即可。

配置文件:txt和properties都可以

我们依旧需要使用上面的Student类,配置文件在项目根目录下,也可以根据需求变动。

className = reflection.Student
methodName = show
package reflection;


import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;

public class TestGetProTxt {
    public static void main(String[] args) throws Exception {
        //通过反射获取Class对象
        Class stuClass = Class.forName(getValue("className"));//"reflection.Student"
        //获取show()方法
        Method m = stuClass.getMethod(getValue("methodName"));//show
        //调用show()方法
        m.invoke(stuClass.getConstructor().newInstance());
    }

    //此方法接收一个key,在配置文件中获取相应的value
    public static String getValue(String key) throws IOException{
        Properties pro = new Properties();//获取配置文件的对象
        //绝对路径
        //FileReader in = new FileReader("G:\\idea_work\\JavaBasis\\testRefl.txt");//获取输入流
        //相对路径
        //FileReader in = new FileReader("testRefl.txt");//获取输入流
        FileReader in = new FileReader("testRefl.properties");//获取输入流
        pro.load(in);//将流加载到配置文件对象中
        in.close();
        return pro.getProperty(key);//返回根据key获取的value值
    }
}

测试结果:

调用了公有、无参构造方法执行了。。。
is show...

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值