反射机制的使用与实践|青训营笔记

73 阅读4分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记

反射

image-20210410151953461

1.获取Class对象的三种方法

1.Class.forName("全类名");(调用Class类的静态方法) 2.类名.class(调用类的class属性) 3.对象.getClass() (getClass方法在Object中定义)

Class<?> aClass = Class.forName("domain.Person");
Class<Person> personClass = Person.class;
Class<? extends Person> aClass1 = new Person().getClass();
System.out.println(aClass==personClass);//true
System.out.println(personClass==aClass1);//true

同一个类获取的Class的对象都是同一个

即:每个类编译成的.class文件在入内存被ClassLoader(类加载器)处理只会生成一个Class对象

(插入泛型复习)

类型通配符一般是使用?代替具体的类型参数。例如 List<?> 在逻辑上是List,List 等所有List<具体类型实参>的父类。(?可以表示所有类)

  • 表示该通配符所代表的类型是T类型的子类。
  • 表示该通配符所代表的类型是T类型的父类。

2.class对象的功能

1.获取功能

1.获取成员变量

类型方法名作用
FieldgetField(String name)获取指定public修饰的成员变量
Field[]getFields()获取全部public修饰的成员变量

Field表示的是成员变量类型的对象,它有两个方法:

  1. a.get(Object o) 获取o对象的成员变量a的值
  2. a.set(Object o,Object value) 设置o对象的成员变量a的值为value
  3. a.setAccessible(true)忽略访问权限修饰符的安全检查
类型方法名作用
FieldgetDeclaredField(String name)获取指定的成员变量
Field[]getDeclaredFields()获取所有的成员变量

在使用私有的Field对象时,应该写一条语句:field.setAccessible(true)(暴力发射,屏蔽修饰符);才能使用field的方法

不仅成员变量有这个方法,构造方法和成员方法都有暴力反射的方法,屏蔽修饰符。因此,在反射面前,没有什么是公有的私有的

2.获取构造方法

类型方法名 传入对应构造函数的参数的类字节码对象
Constructor<T>getConstructor(Class<?>... parameterTypes)
Constructor<?>[]getConstructors()
类型方法名
Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)
Constructor<?>[]getDeclaredConstructors()

构造方法对象的作用是创建对象,constructor.newInstance(传入对应实际参数)

public Person() {
}
​
public Person(String name, Integer age) {(构造器传入的参数类型都必须是类,不然getConstructor找不到)
        this.name = name;
        this.age = age;
}
​
​
Class<Person> personClass = Person.class;
​
Constructor<Person> constructor = personClass.getConstructor(String.class, Integer.class);
//利用构造方法对象创建Person对象   构造方法对象.newInstance
Object cqt = constructor.newInstance("cqt", 19);
System.out.println(cqt.toString());
​
​
Constructor<Person> constructor1 = personClass.getConstructor();
Person person = constructor1.newInstance();
System.out.println(person.toString());
//空参的构造函数也可以直接使用类字节码对象的newInstance方法  类字节码对象.newInstance
Person person1 = personClass.newInstance();
System.out.println(person1.toString());

3.获取成员方法

类型方法名 方法名 参数字节码对象
MethodgetMethod(String name, Class<?>... parameterTypes)
Method[]getMethods()
类型方法名
MethodgetDeclaredMethod(String name, Class<?>... parameterTypes)
Method[]getDeclaredMethods()

成员方法的作用,使用方法method.invoke(参数);还有getName()获取方法名

public void eat(){
    System.out.println("eat.....");
}
public void eat(String food){
    System.out.println("eat....."+food);
}
​
​
​
Class<Person> personClass = Person.class;
​
//获取无参方法,直接传入方法名即可
Method eat = personClass.getMethod("eat");
//运行方法,和成员变量一样,首先得传入对应对象
Person person = new Person();
eat.invoke(person);
​
//获取有参方法,与构造方法类似
Method eat1 = personClass.getMethod("eat", String.class);
eat1.invoke(person,"rice");
​
Method[] methods = personClass.getMethods();
for (Method method : methods) {
    System.out.println(method);
    String name = method.getName();
    System.out.println(name);
}

4.获取类名

类型方法名
StringgetName()
String name = personClass.getName();
System.out.println(name);//domain.Person

3.框架设计

设计一款可以调用任意对象的任意无参方法的框架

1.一个pro.properties文件,里面保存需要运行类的方法的全类名和方法名

className=Person
methodName=eat

2.一个ReflectTest类,里面完成框架的实现

1.读取properties的文件里面的全类名和方法

2.利用Class.forName("全类名")获得类的字节码对象

3.字节码对象getMethod获得方法对象

4.字节码对象new Instance获得一个类对象

5.利用method.invoke(Object O)使用方法

public class ReflectTest {
    public static void main(String[] args) throws Exception {
        String className;
        String methodName;
​
​
       //获取properties文件的方法  一
        Properties properties = new Properties();
        //获取类加载器->获取文件字节流
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        //调用类加载器的getResourceAsStream方法可以得到文件的字节流->获取文件字节流
        InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
        //Properties对象不能直接加载文件,需要加载文件的字节流->加载Properties
        properties.load(resourceAsStream);
        className= properties.getProperty("className");
        methodName=properties.getProperty("methodName");
​
        /*
        //获取properties文件的方法二
        ResourceBundle bundle = ResourceBundle.getBundle("pro.properties");
        className = bundle.getString("className");
        methodName=bundle.getString("methodName");
        */
​
        Class<?> aClass = Class.forName(className);
​
        Method method = aClass.getMethod(methodName);
        Object o = aClass.newInstance();
        method.invoke(o);
    }
}

\