什么是反射

反射是:指程序可以访问、检测和修改它本身状态或行为的一种能力 反射是一种能力,所以给的定义就是说明了它能干嘛。 我们平时用反射主要做: - 获取类型的相关信息 - 动态调用方法 - 动态构造对象 - 从程序集中获得类型。

通过反射机制实例化对象

基础实体

package com.haojishu.entity;

public class User {
  public Integer age;
  private String Username;
  private String addr;

  public User() {
    System.out.println("构造参数");
  }

  public Integer getAge() {
    return age;
  }

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

  public String getUsername() {
    return Username;
  }

  public void setUsername(String username) {
    Username = username;
  }

  public String getAddr(String 河北石家庄) {
    return addr;
  }

  public void setAddr(String addr) {
    this.addr = addr;
  }

  @Override
  public String toString() {
    return "User{"
        + "age="
        + age
        + ", Username='"
        + Username
        + '\''
        + ", addr='"
        + addr
        + '\''
        + '}';
  }
}

反射获取对象

      // 获取类字节码文件
      Class<?> aClass = Class.forName("com.haojishu.entity.User");
      // 实例化类
      Object newInstance = aClass.newInstance();
      // 类转换实体
      User user = (User) newInstance;

使用反射机制创建对象有什么作用呢?

  • 反射机制让程序更加灵活。
  • java代码写一遍,在不改变java源代码的基础上,可以做到不同对象的实例化。(符合OCP原则:对外开放,修改关闭。)
  • 高级框架:底层实现原理:采用了反射机制。
  • 举个栗子:(使用IO流,properties集合,通过属性文件来实例化对象)(重点)
代码优化

如何获取类路径下文件的绝对路径?

  • 为什么聊这个?因为上述代码可能换一个位置,当前路径名就失效了,(即移植性差)所以我们的聊聊这个。
  • 使用以下通用方式,这个文件必须保存在类路径下,(即src目录下)。
  • String path = Thread.currentThread().getContextClassLoader().getResource(” “).getPath();(重点)使用这个方法可以获取文件的绝对路径。(适用于各种操作系统)
  • 哈哈,看不懂?别急解释一下上述程序:
  • Thread.currentThread()当前线程对象
  • getContextClassLoader()使对象的方法,可以获取当前线程的类加载器对象。
  • getResource()这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。

修改Pom

        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                </includes>
            </resource>
        </resources>

编写配置文件pack.properties

User=com.haojishu.entity.User

最后优化代码:

Thread thread = Thread.currentThread();
ClassLoader contextClassLoader = thread.getContextClassLoader();
try {
      InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties");
      Properties properties = new Properties();
      properties.load(resourceAsStream);
      resourceAsStream.close();
      String userString = properties.getProperty("User");
      // 获取类字节码文件
      Class<?> aClass = Class.forName(userString);
      // 实例化类
      Object newInstance = aClass.newInstance();
      // 类转换实体
      User user = (User) newInstance;
      System.out.println("优化后的代码实例化" + user);
    } catch (Exception e) {
      e.printStackTrace();
    }

这样我们在修改包路径的时候只需要修改配置文件即可

反射Field(属性)获取字节码

  • 反射属性之前,应该获取整个类。(Class.forName(" ")方法获取。)
  • 获取整个类中的所有的Field。

    • getFields()方法;用之前获取的整个类调用;其获取所有public修饰的属性,返回到Field[]数组中。
    • getDeclaredFields()方法;获取所有属性。不受修饰符现在,返回到Field[]数组中。
    • getName()方法:可以获取属性名字。对于类也有名字,也用这个方法,对于反射获得的类还有simpleName()方法,来获取简单名字。
    • getType()方法:获取属性的类型,返回值为类,然后我们再通过这个得到的类用getName方法获取属性类型的名字。
    • getModifiers()方法:获取属性的修饰符列表,返回的是int类型,每个修饰符的数字是,修饰符的代号。我们可以用toString方法(Modifier.toString),Modefier为reflect的子类,返回一个字符串类型。
    • 举个栗子:

      Thread thread = Thread.currentThread();
      ClassLoader contextClassLoader = thread.getContextClassLoader();
      
      try {
      InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties");
      Properties properties = new Properties();
      properties.load(resourceAsStream);
      resourceAsStream.close();
      String userString = properties.getProperty("User");
      // 获取类字节码文件
      Class<?> aClass = Class.forName(userString);
      String aClassName = aClass.getName();
      newInstance = aClass.newInstance();
      String name = newInstance.getClass().getName();
      System.out.println("获取类名称: " + aClassName);
      System.out.println("获取类名称: " + name);
      Field[] declaredFields = aClass.getDeclaredFields();
      System.out.println("获取反射类字段数量: " + declaredFields.length);
      for (Field decl : declaredFields) {
      System.out.println("获取字段名称: " + decl.getName());
      System.out.println("获取字段类型: " + decl.getType().getName());
      int modifiers = decl.getModifiers();
      System.out.println("获取字段属性标识符: " + Modifier.toString(modifiers));
      }
      } catch (Exception e) {
      e.printStackTrace();
      }
      

设置属性字段

  • 通过反射机制,反编译一个类的属性Field
  • 怎么通过反射机制访问对象属性?(赋值,获取属性的值)

    • 首先获取整个Class。
    • 然后通过Class实例化对象。
    • 获取对象的属性;依据名字。
    • 属性.set(对象,222)方法:以获取的属性调用,给对象的属性赋值。
    • 反射机制代码复杂,但灵活。
    • 读取属性的值:属性.get(对象)。

      Thread thread = Thread.currentThread();
      ClassLoader contextClassLoader = thread.getContextClassLoader();
      
      try {
      InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties");
      Properties properties = new Properties();
      properties.load(resourceAsStream);
      resourceAsStream.close();
      String userString = properties.getProperty("User");
      // 获取类字节码文件
      Class<?> aClass = Class.forName(userString);
      String aClassName = aClass.getName();
      newInstance = aClass.newInstance();
      String name = newInstance.getClass().getName();
      System.out.println("获取类名称: " + aClassName);
      System.out.println("获取类名称: " + name);
      // 获取属性对象
      Field age = aClass.getDeclaredField("age");
      // 设置属性
      age.set(newInstance, 12);
      System.out.println("打印属性设置后的对象值: " + newInstance);
      // 读取属性
      System.out.println("读取属性值:" + age.get(newInstance));
      
      } catch (Exception e) {
      e.printStackTrace();
      }
      

反射Mothod(方法)

  • 首先获取Class。
  • 获取所有的Method方法。
  • Method[] methods=类.getDeclaredMethod();方法:获取所有的Method.
  • 获取方法的返回值类型:方法.getReturnType().getSimpleName();
  • 获取修饰符列表:Modifer.toString(方法.getModifiers()
  • 获取方法参数的类型:方法.getParameterTypes();返回Class[]数组。再调用getSimpleName()即可。
  • 通过反射机制怎么调用对象的方法。(重点)
  • 1.获取Calss,并用CLass实例化对象。
  • 2.获取Method:使用Method logMethod = 类.getDeclaredMethod("方法名“,类型名.class,类型名.class);方法。
  • 3.调用方法:(对象,方法名,参数,返回值)

    • Object value = 方法.invoke(“对象”,“参数值”,“参数值”);invoke方法返回一个Object类型。

      Thread thread = Thread.currentThread();
      ClassLoader contextClassLoader = thread.getContextClassLoader();
      
      try {
      InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties");
      Properties properties = new Properties();
      properties.load(resourceAsStream);
      resourceAsStream.close();
      String userString = properties.getProperty("User");
      // 获取类字节码文件
      Class<?> aClass = Class.forName(userString);
      String aClassName = aClass.getName();
      newInstance = aClass.newInstance();
      Method[] methods = aClass.getMethods();
      System.out.println(methods);
      for (Method method : methods) {
      String name = method.getName();
      System.out.println("方法名称: " + name);
      int modifiers = method.getModifiers();
      String toString = Modifier.toString(modifiers);
      System.out.println("方法修饰符: " + toString);
      Class<?>[] parameterTypes = method.getParameterTypes();
      for (Class parme : parameterTypes) {
        System.out.println("支持参数: " + parme.getSimpleName());
      }
      }
      
      // 调用方法名
      Method setAge = aClass.getDeclaredMethod("setAge", Integer.class);
      setAge.invoke(newInstance, 12);
      System.out.println("反射应用结果: " + newInstance);
      } catch (Exception e) {
      e.printStackTrace();
      }
      

反射Constructor(构造方法)

  • 反射机制怎么调用构造方法?
  • 使用反射机制创建对象?

    • 1.调用无参构造方法。(newInstance方法)
    • 2.调用有参构造方法(也可以用这种方法调用无参构造方法)。
    • 第一步:先获取这个有参构造方法;Constructor con = 类.getDeclaredConstructor(int.class,String.classs) 第二步:调用构造方法new对象。Object aa =con.newInstance(参数列表)

      Thread thread = Thread.currentThread();
      ClassLoader contextClassLoader = thread.getContextClassLoader();
      
      try {
      InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties");
      Properties properties = new Properties();
      properties.load(resourceAsStream);
      resourceAsStream.close();
      String userString = properties.getProperty("User");
      // 获取类字节码文件
      Class<?> aClass = Class.forName(userString);
      String aClassName = aClass.getName();
      newInstance = aClass.newInstance();
      
      Constructor<?> declaredConstructor =
        aClass.getDeclaredConstructor(Integer.class, String.class, String.class);
      declaredConstructor.newInstance(12, "张三", "李四");
      
      } catch (Exception e) {
      e.printStackTrace();
      }
      

通过反射获取父类和父接口

  • 1.获取类的Class
  • 2.获取Class父类:Class superClass = 子类.getSuperclass();
  • 获取子类实现的所有接口:Class[] interfaces = 子类.getInterfaces();方法。

    Thread thread = Thread.currentThread();
    ClassLoader contextClassLoader = thread.getContextClassLoader();
    try {
      InputStream resourceAsStream = contextClassLoader.getResourceAsStream("pack.properties");
      Properties properties = new Properties();
      properties.load(resourceAsStream);
      resourceAsStream.close();
      String userString = properties.getProperty("User");
      // 获取类字节码文件
      Class<?> aClass = Class.forName(userString);
      // 实例化类
      Object newInstance = aClass.newInstance();
      // 获取父类
      Class<?> superclass = aClass.getSuperclass();
      System.out.println("获取父类名称: " + superclass.getSimpleName());
      Class<?>[] interfaces = aClass.getInterfaces();
      for (Class inter : interfaces) {
        // 不包含报名的接口简称
        System.out.println(inter.getSimpleName());
      }
    } catch (Exception e) {
      e.printStackTrace();
    }