59.反射-框架设计的灵魂

反射-设计框架的灵魂

被反射的类必须显示创建无参构造函数才能被反射出该构造器;基本数据类型与包装类型不一样!

  • 框架:半成平软件,在框架的基础上进行软件开发,简化代码
  • 反射:将类的各个组成部分封装成其他对象,这就是反射机制
    • 反射的好处:
      1. 可以在程序员运行过程中,提高程序的可扩展性
      2. 可以解耦,提高程序的可扩展性
59.反射-框架设计的灵魂_第1张图片
反射.png

类加载器,一个加载类的描述方式,将被加载的类当做对象,那么加载类的描述方式就是对应的类。和python中的元类是一个概念。
使用反射,就是站在JVM的角度去操控类!

一、获取Class对象的方式

  1. Class.forName("全类名<包名+类名>"):【.class源代码阶段】 将字节码文件加载进内存,返回Class对象
    • 多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
  2. 类名.class:【方法区阶段】例如Student.class,通过类名的属性class获取
    • 多用于参数的传递
  3. 对象.getClass(): 【堆内存阶段】getCalss()方法在Object类中定义。
    • 多用于对象的获取字节码的方式。
package reflexLearn;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        // .class : Class.forName("全类名")
        Class s1 = Class.forName("reflexLearn.Student");

        // 方法区 : 类名.class
        Class s2 = Student.class;

        // 堆内存 : 对象名.getClass()
        Student student = new Student();
        Class s3 = student.getClass();

        // 类加载器【类加载机制】只允许加一次相同的.class文件
        boolean con = s1 == s2 && s1 == s3;
        // true
    }
}

/*学生类*/
class Student{
    private String name;
    private int age;
    public Student(){}
    private Student(String name, int age) { this.name = name;this.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; }
    public String getS(String s){ return s; }
    private int getI(int n){ return n; }

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

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

二、Class对象功能。

以上面的代码为例进行操作

1.获取成员变量们【Field】成员

  • 获取功能:
    1. Field[] getFields() :获取所有public修饰的成员变量.
    2. Field getField(String name): 获取指定名称的public修饰的成员变量
    3. Field[] getDeclaredFields(): 获取所有的成员变量,不考虑修饰符。
    4. Field getDeclaredFields(String name) 指定名称的成员变量,不考虑修饰符
  • 获取Field成员变量的操作:
    1. void set(Object obj, Object value):设置对应对象的值
    2. get(Object obj): 获得对应对象的值
    3. setAccessible(true):暴力反射,忽略访问权限修饰符的安全检查
/**@获取 Student的class对象*/
Class studentClass = Student.class;
//-----------------------//

// 1. 获取所有public修饰的成员变量
Field[] fields = studentClass.getFields();

// 2. 获取指定的被public修饰的成员变量
Field name = studentClass.getField("name");

// 3.获取成员变量 name 的值
Student stu1 = new Student("小红",22);
System.out.println(name.get(stu1));

// 4.设置成员变量的值
name.set(stu1,"唐小红");
System.out.println(stu1);

//----------------------//

// 5. 获取所有的成员变量,不考虑修饰符
Field[] declaredFields = studentClass.getDeclaredFields();

// 6. 获取指定成员变量,不考虑修饰符
Field age = studentClass.getDeclaredField("age");

// 忽略访问权限修饰符的安全检查,暴力反射,否则可能会抛错
age.setAccessible(true);

// 设置成员
age.set(stu1, 18);

// 获取成员
System.out.println(age.get(stu1));

2.获取构造方法们【Constructor】

  • 获取功能
    1. Constructor[] getConstructors(): 获取public修饰的所有构造方法对象数组。
    2. Constructor getConstructor(类...parameterTypes)获取public修饰的指定方法签名的构造方法对象
    3. Constructor[] getDeclaredConstructors(): 获取所有构造方法,忽略权限修饰符
    4. Constructor getDeclaredConstructor(类...parameterTypes): 获取指定方法签名的构造方法对象,忽略权限修饰符
  • Constructor构造方法对象的创建对象
    1. T newInstance(Object...initargs) 创建此对象所表示一个类的新的实例。
    2. 如果使用空参构造方法创建对象,可以简化操作:Class对象的newInstance方法
    3. setAccessible(true):暴力反射
/**获取Student的Class对象*/
Class studentClass = Student.class;

// 1. 获取**public**修饰的所有构造方法对象数组
Constructor[] con1 = studentClass.getConstructors();

// 2. **获取**public**修饰的指定方法签名的构造方法对象
Constructor con2 = studentClass.getConstructor(String.class, int.class);

// 3. 获取所有构造方法,忽略权限修饰符
Constructor[] con3 = studentClass.getDeclaredConstructors();

// 4. 获取指定方法签名的构造方法对象,忽略权限修饰符
Constructor con4 = studentClass.getDeclaredConstructor();

// 5. 创建构造对象所表示一个类的新的实例
Object obj1 = con2.newInstance("小红", 22);
System.out.println(obj1);

// 6. 暴力反射,然后创建对象
con4.setAccessible(true);
Object obj2 = con4.newInstance();
System.out.println(obj2);

3.获取成员方法们

  • 获取功能
    1. Method[] getMethods(): 获取public修饰的某些Method对象的数组,(该类或接口声明的以及从 超类和超接口继承的那些的类或接口)的公共member方法。
    2. Method getMethod(String name, Class...parameterTypes):返回一个publicMethod对象,它反映此Class对象所表示的类或接口的指定公共成员方法。
    3. Method[] getDeclaredMethods():返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法(公共、保护、默认(包)访问和私有方法),但不包括继承的方法
    4. Method getDeclaredMethod(String name, Class...parameterTypes):返回一个 Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法
  • Method方法对象的使用<需要对应的实例>:
    1. Object invoke(Object obj, Object...args):指定实例obj,传参并执行方法。
    2. String getName(): 获取方法名。
    3. setAccessible(true):暴力反射
/**获取类对象*/
Class studentClass = Student.class;

// 1. 获取public修饰的所有方法对象列表【public 包含继承的】
Method[] meLis1 = studentClass.getMethods();

// 2. 获取public修饰的指定方法签名的方法对象
Method me1 = studentClass.getMethod("getS", String.class);
Student stu1 = (Student) studentClass.getConstructor(String.class, int.class)
        .newInstance("杰克", 18);
System.out.println(me1.invoke(stu1, "这是一个公共方法!"));

// 3. 获取该对象表示的类的所有类型的方法对象,但是不包括继承的!
Method[] meList2 = studentClass.getDeclaredMethods();

// 4. 获取指定方法签名的方法对象
Method me2 = studentClass.getDeclaredMethod("getI",int.class);

// 5.暴力反射
me2.setAccessible(true);

// 6.执行方法
me2.invoke(stu1, 666);

// 7.获得方法名
me2.getName();

4.获取类名

  • String getName():获取全类名
studentClass.getName()

三、综合案例

需求:写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法【简单的,无参的】
实现:配置文件、反射

配置文件

className=reflexLearn.Student
methodName=getName

被执行类为上方的Student类

package reflexLearn;

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

public class ReflexTest {

    public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        // 1. 加载配置文件
        Properties pro = new Properties();

        // 2. 获取class目录下的配置文件
        // [下面这种方法的保证pro.properties在当前模块的src下(与包根同级)]
        ClassLoader classLoader = ReflexTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);

        // 3. 获取配置问价中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        // 4. 加载该内进入内存
        Class cls = Class.forName(className);

        // 5. 创建对象
        Constructor con = cls.getDeclaredConstructor();
        con.setAccessible(true);
        Object obj = con.newInstance();

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

        // 7. 执行方法
        method.invoke(obj);
    }
}

你可能感兴趣的:(59.反射-框架设计的灵魂)