Java 初步了解反射机制

反射:框架设计的灵魂

  • 框架:

    半成品软件。可以在框架的基础上进行软件开发,简化编码

  • 反射:

    将类的各个组成部分封装为其他对象,这就是反射机制

    • 好处:

      可以在程序运行过程中,操作这些对象。

      可以解耦,提高程序的可扩展性。

Java在计算机中经历的三个阶段:

img

获取字节码Class对象的三种方式

定义一个Person.java,用于下面的讲解。

package view.study.demo44;

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

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    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;
    }
}

Class.forName("类的全称,包括所在包")

Class.forName("全类名"):将字节码文件加载进内存,返回Class对象。

常用之处:多用于配置文件,将类名定义在配置文件中。读取文件,加载类。

package view.study.demo44;

public class DemoForName {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> aClass = Class.forName("view.study.demo44.Person");
        System.out.println(aClass);
    }

}

运行程序,控制台输出:

class view.study.demo44.Person

[类名].class

类名.class:通过类名的属性class获取。

常用之处:多用于参数的传递。

package view.study.demo44.test;

import view.study.demo44.Person;

public class DemoClass {

    public static void main(String[] args) {
        Class<Person> personClass = Person.class;
        System.out.println(personClass);
    }

}

运行程序,控制台输出:

class view.study.demo44.Person

[对象].getClass()

对象.getClass():getClass()方法在Object类中定义着。

常用之处:多用于对象的获取字节码的方式。

package view.study.demo44;

public class DemoGetClass {

    public static void main(String[] args) {
        Person person = new Person();
        Class<? extends Person> personClass = person.getClass();
        System.out.println(personClass);
    }

}

运行程序,控制台输出:

class view.study.demo44.Person

结论

上面的三种方式,在一个程序中输出,其实是一样的。

package view.study.demo44.test;

import view.study.demo44.Person;

public class Demo01Class {

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> class1 = Class.forName("view.study.demo44.Person");

        Class<Person> class2 = Person.class;

        Person person = new Person();
        Class<? extends Person> class3 = person.getClass();

        System.out.println(class1 == class2);
        System.out.println(class1 == class3);
    }

}

运行程序,控制台输出:

true
true

同一个字节码文件(如Person.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

Class对象获取功能:获取成员变量

Field[] getFields()
// 获取所有public修饰的成员变量

Field getField(String name)
// 获取指定名称的 public修饰的成员变量

Field[] getDeclaredFields()
// 获取所有的成员变量,不考虑修饰符

Field getDeclaredField(String name) 
// 获取指定名称的成员变量,不考虑修饰符

定义一个Person.java,用于下面方法的使用:

package view.study.demo45;

public class Person {
    private String name;
    private int age;
    public int a;
    public int b;
    public int c;

    public Person() {
    }

    public Person(String name, int age, int a, int b, int c) {
        this.name = name;
        this.age = age;
        this.a = a;
        this.b = b;
        this.c = c;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a=" + a +
                ", b=" + b +
                ", c=" + c +
                '}';
    }

    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 int getA() {
        return a;
    }

    public void setA(int a) {
        this.a = a;
    }

    public int getB() {
        return b;
    }

    public void setB(int b) {
        this.b = b;
    }

    public int getC() {
        return c;
    }

    public void setC(int c) {
        this.c = c;
    }
}

getFields()方法

public class Demo01Reflection {

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

        // 获取Person的Class对象
        Class<?> personClass = Person.class;

        // 获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

    }

}

运行程序,控制台输出:

public int view.study.demo45.Person.a
public int view.study.demo45.Person.b
public int view.study.demo45.Person.c

假如想要获取:成员变量的初始值(如上例中)

Object o = field.get(new Person());

类似的,也可以使用java.lang.Class中的set()方法,为成员变量设置值。

* Field:成员变量
* 操作:
1. 设置值
    * void set(Object obj, Object value)  
2. 获取值
    * get(Object obj) 

3. 忽略访问权限修饰符的安全检查
    * setAccessible(true):暴力反射

getField(String name)方法

public class Demo01Reflection {

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

        // 获取Person的Class对象
        Class<?> personClass = Person.class;

        // 获取指定名称的 public修饰的成员变量
        Field a = personClass.getField("a");
        System.out.println(a);

    }

}

运行程序,控制台输出:

public int view.study.demo45.Person.a

getDeclaredFields()方法

public class Demo01Reflection {

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

        // 获取Person的Class对象
        Class<?> personClass = Person.class;

        // 获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }

    }

}

运行程序,控制台输出:

private java.lang.String view.study.demo45.Person.name
private int view.study.demo45.Person.age
public int view.study.demo45.Person.a
public int view.study.demo45.Person.b
public int view.study.demo45.Person.c

getDeclaredField(String name)方法

public class Demo01Reflection {

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

        // 获取Person的Class对象
        Class<?> personClass = Person.class;

        // 获取指定名称的成员变量,不考虑修饰符
        Field name = personClass.getDeclaredField("name");
        Field b = personClass.getDeclaredField("b");
        System.out.println(name);
        System.out.println(b);

    }

}

运行程序,控制台输出:

private java.lang.String view.study.demo45.Person.name
public int view.study.demo45.Person.b

Class对象获取功能:获取构造方法

Constructor<?>[] getConstructors()
// 返回一个构造函数对象数组,该构造函数对象数组每个元素,都反映Class对象表示的类所对应公共构造函数。

Constructor<T> getConstructor(类<?>... parameterTypes)
// 返回一个构造函数对象,该构造函数对象反映此Class对象表示的类所指定公共构造函数。

Constructor<?>[] getDeclaredConstructors()
// 返回一个构造函数对象数组,该构造函数对象数组每个元素,都反映Class对象(或interface对象)表示的类所对应公共构造函数。

Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
// 返回一个构造函数对象,该构造函数对象反映此Class对象(或interface对象)表示的类所指定公共构造函数。

下面就介绍:getConstructors()方法和getConstructors()方法。剩下两种方法的使用如介绍中方法一致

getConstructor()方法

这里例子中,所使用的Person.java,使用的是“获取成员变量”例子中定义的。

import java.lang.reflect.Constructor;

public class Demo02Reflection {
    public static void main(String[] args) throws NoSuchMethodException {

        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        // 获取一个Person.class构造函数对象
        Constructor<?> constructor = personClass.getConstructor();
        System.out.println(constructor);
        
    }
}

运行程序,控制台输出:

public view.study.demo45.Person()

getConstructors()方法

这里例子中,所使用的Person.java,使用的是“获取成员变量”例子中定义的。

import java.lang.reflect.Constructor;

public class Demo02Reflection {
    public static void main(String[] args) throws NoSuchMethodException {

        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        // 获取一个Person.class构造函数数组对象
        Constructor<?>[] constructors = personClass.getConstructors();
        for (Constructor<?> constructor1 : constructors) {
            System.out.println(constructor1);
        }
        
    }
}

运行程序,控制台输出:

public view.study.demo45.Person()
public view.study.demo45.Person(java.lang.String,int,int,int,int)

Class对象获取功能:获取成员方法

Method[] getMethods()
// 返回一个包含{@code Method}对象的数组,这些对象反映此{@code Class}对象表示的类或接口的所有公共方法,包括由类或接
// 口声明的方法以及从超类和超接口继承的方法。

Method getMethod(String name, 类<?>... parameterTypes)
// 返回一个指定的{@code Method}对象,该对象反映此{@code Class}对象表示的类或接口的指定公共成员方法。

Method[] getDeclaredMethods()
// 返回一个包含{@code Method}对象的数组,这些对象反映了此{@code Class}对象表示的类或接口的所有已声明方法,包括公共,
// 受保护,默认(程序包)访问和私有方法,但不包括继承的方法。

Method getDeclaredMethod(String name, 类<?>... parameterTypes)
// 返回一个指定的{@code Method}对象,该对象反映此{@code Class}对象表示的类或接口的指定声明方法。

getMethods()方法

package view.study.demo45;

import java.lang.reflect.Method;

public class Demo03Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        // 获取一个包含{@code Method}对象的数组
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
    }
}

运行程序,控制台输出:

toString
getName
setName
getAge
setAge
getB
setB
getC
setC
getA
setA
wait
wait
wait
equals
hashCode
getClass
notify
notifyAll

其中红色部分是其父类或其继承的接口的方法。

getMethod(String name, 类<?>... parameterTypes)方法

package view.study.demo45;

import java.lang.reflect.Method;

public class Demo03Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        // 返回一个指定的{@code Method}对象
        Method getAge = personClass.getMethod("getAge");
        System.out.println(getAge);
    }
}

运行程序,控制台输出:

public int view.study.demo45.Person.getAge()

getDeclaredMethods()方法

package view.study.demo45;

import java.lang.reflect.Method;

public class Demo03Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        Method[] declaredMethods = personClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
    }
}

运行程序,控制台输出:

public java.lang.String view.study.demo45.Person.toString()
public java.lang.String view.study.demo45.Person.getName()
public void view.study.demo45.Person.setName(java.lang.String)
public int view.study.demo45.Person.getAge()
public void view.study.demo45.Person.setAge(int)
public int view.study.demo45.Person.getB()
public void view.study.demo45.Person.setB(int)
public int view.study.demo45.Person.getC()
public void view.study.demo45.Person.setC(int)
public int view.study.demo45.Person.getA()
public void view.study.demo45.Person.setA(int)

getDeclaredMethod(String name, 类<?>... parameterTypes)方法

package view.study.demo45;

import java.lang.reflect.Method;

public class Demo03Reflection {
    public static void main(String[] args) throws NoSuchMethodException {
        // 创建Person.class对象
        Person person = new Person("LeeHua", 22, 6, 6, 6);
        // 获取Person的Class对象
        Class<?> personClass = person.getClass();

        Method getName = personClass.getDeclaredMethod("getName");
        System.out.println(getName);
    }
}

运行程序,控制台输出:

public java.lang.String view.study.demo45.Person.getName()

案例

需求

写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

实现之前,定义Person.java、Student.java:

package view.study.demo46;

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

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
    public void personMethod() {
        System.out.println("我是Person中的方法!!!");
    }

    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;
    }
}
package view.study.demo46;

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public void studentMethod() {
        System.out.println("我是Student中的方法!!!");
    }

    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;
    }
}

实现步骤

  1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
  2. 在程序中加载读取配置文件
  3. 使用反射技术来加载类文件进内存
  4. 创建对象
  5. 执行方法

实现

第一步:创建配置文件——pro.properties,并将需要创建的对象的全类名和需要执行的方法定义在配置文件中

配置文件内容如下:

className=view.study.demo46.Person
methodName=personMethod

第二步:在程序中加载获取配置文件*
*

// 1. 在程序中加载读取配置文件
// 1.1 创建Properties对象
Properties pro = new Properties();
// 1.2 加载配置文件,转换为一个集合
// 1.2.1 获取类的类加载器
ClassLoader classLoader = DemoReflection.class.getClassLoader();
// 1.2.2 读取配置文件
// InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
InputStream inputStream = new FileInputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo46/pro.properties");
// 1.2.3 加载配置文件
pro.load(inputStream);

第三步:获取配置文件中的定义数据

// 2. 获取配置文件中定义的数据
// 2.1 获取类名称
String className = pro.getProperty("className");
// 2.2 获取方法名称
String methodName = pro.getProperty("methodName");

第四步:加载该类进内存

// 3. 加载该类进内存
Class<?> aClass = Class.forName(className);

第五步:创建对象

// 4. 创建对象
Object object = aClass.newInstance();

第六步:获取方法对象

// 5. 获取方法对象
Method method = aClass.getMethod(methodName);

第七步:执行方法

// 6. 执行方法
method.invoke(object);

代码总和:

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class DemoReflection {
    public static void main(String[] args) throws IOException, ClassNotFoundException,
            IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        // 1. 在程序中加载读取配置文件
        // 1.1 创建Properties对象
        Properties pro = new Properties();
        // 1.2 加载配置文件,转换为一个集合
        // 1.2.1 获取类的类加载器
        ClassLoader classLoader = DemoReflection.class.getClassLoader();
        // 1.2.2 读取配置文件
        // InputStream inputStream = classLoader.getResourceAsStream("pro.properties");
        InputStream inputStream = new FileInputStream("/Users/liyihua/IdeaProjects/Study/src/view/study/demo46/pro.properties");
        // 1.2.3 加载配置文件
        pro.load(inputStream);

        // 2. 获取配置文件中定义的数据
        // 2.1 获取类名称
        String className = pro.getProperty("className");
        // 2.2 获取方法名称
        String methodName = pro.getProperty("methodName");

        // 3. 加载该类进内存
        Class<?> aClass = Class.forName(className);

        // 4. 创建对象
        Object object = aClass.newInstance();

        // 5. 获取方法对象
        Method method = aClass.getMethod(methodName);

        // 6. 执行方法
        method.invoke(object);
    }
}

运行程序,控制台输出:

我是Person中的方法!!!

修改配置文件中的内容:

className=view.study.demo46.Student
methodName=studentMethod

运行程序,控制台输出:

我是Student中的方法!!!

如此,修改配置文件,不用修改代码。实现了创建任意类的对象,并且执行其中任意方法。

posted @ 2020-02-13 04:26  LeeHua  阅读(336)  评论(0编辑  收藏  举报