0
点赞
收藏
分享

微信扫一扫

反射

类的加载时机


  • 当程序要使用某个类时,如果该类还未被加载到内存中
  • 系统会通过加载,连接,初始化三步来实现对这个类进行初始化
  • 加载:就是指将​​class​​​ 文件读入内存,并为之创建一个​​Class​​​ 对象。任何类被使用时系统都会建立一个​​Class​​ 对象
  • 连接:验证是否有正确的内部结构,并和其他类协调一致,准备负责为类的静态成员分配内存,并设置默认的初始化值
  • 初始化:初始化成员变量等等


加载时机


  • 创建类的实例
  • 访问类的静态变量,或者为静态变量赋值
  • 调用类的静态方法
  • 初始化某个类的子类
  • 使用反射方式来强制创建某个类或接口对应的​​java.lang.Class​​ 对象


类加载器


  • ????什么是类加载器​​classLoader​


负责将 ​​.class​​​ 文件加载到内存中,并为之生成对应的 ​​Class​​ 对象,虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行

类加载器分类

根类加载器


  • 也被称为引导类加载器,负责 Java 核心类的加载
  • 比如​​System​​​,​​String​​​ 等。在 JDK 中​​JRE​​​ 的​​lib​​​ 目录下​​rt.jar​​ 文件中


扩展类加载器


  • 负责​​JRE​​​ 的扩展目录中​​jar​​ 包的加载
  • 在 JDK 中 JRE 的​​lib​​​ 目录下​​ext​​ 目录


系统类加载器


  • 负责在​​JVM​​​ 启动时加载来自​​java​​​ 命令的​​class​​ 文件
  • 以及​​classpath​​​ 环境变量所指定的​​jar​​ 包和类路径


什么是反射

????创建一个对象的三个阶段


  1. 源文件阶段​​.java​​ 的文件
  2. 字节码阶段​​.class​
  3. 创建对象阶段​​new​​ 对象名称


反射


  • JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法
  • 对于任意一个对象,都能够调用它的任意一个方法和属性
  • 这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制
  • 想要使用反射,就必须得要获取字节码文件


获取字节码文件


  • Object 类的 getClass() 方法:判断两个对象是否是同一个字节码文件
  • 静态属性 class:当作静态方法的锁对象
  • Class 类中静态方法 forName():读取配置文件


如下是一个获取字节码的小 Demo 创建 Person.java

/**
 * @author BNTang
 **/
public class Person {
}

创建 Client.java 测试类

/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> c = Class.forName("top.it6666.demo1.Person");

        Class<Person> personClass = Person.class;

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

        System.out.println(c == personClass);
        System.out.println(personClass == ps);
    }
}

通过字节码创建对象

修改之前创建的 Person.java

/**
 * @author BNTang
 **/
public class Person {
    public String name;
    public Integer age;
    private String gender;

    public Person() {
        super();
    }

    public Person(String name, Integer age, String gender) {
        super();
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    public void show() {
        System.out.println("我是" + this.name + "年龄" + this.age + "性别" + this.gender);
    }

    private void eat(String food) {
        System.out.println("我吃了" + food);
    }
}

通过无参构造创建对象


  • 获取字节码
  • 调用字节码的​​newInstance()​​ 方法


/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        // 1.获取字节码
        Class<?> c = Class.forName("top.it6666.demo1.Person");

        // 2.通过字节码创建对象
        // 从JDK9开始过时了
        // Person p = (Person)clazz1.newInstance();
        Person p = (Person)c.getDeclaredConstructor().newInstance();
        p.name = "zs";
        p.age = 23;
        
        p.show();
    }
}

通过有参构造创建对象


  • 获取字节码的构造器,​​clazz.getConstructor(type.class)​​ 因为在反射阶段操作的都是字节码,不知道具体的类型,只有在创建对象的时候才去给实际参数
  • 通过构造器创建对象,调用构造器的​​newInstance​​ 方法并传入参数


/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        // 1.获取字节码
        Class<?> c = Class.forName("top.it6666.demo1.Person");

        // 2.获取字节码的构造器
        Constructor<?> cs = c.getConstructor(String.class, Integer.class, String.class);

        // 3.通过构造器来创建对象
        Person person = (Person)cs.newInstance("", 23, "");
        person.show();
    }
}

获取字段

获取公共的字段

/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        // 1.获取字节码
        Class<?> c = Class.forName("top.it6666.demo1.Person");

        // 2.获取字节码的构造器
        Constructor<?> cs = c.getConstructor(String.class, Integer.class, String.class);

        // 3.通过构造器来创建对象
        Person person = (Person)cs.newInstance("", 23, "");

        // 获取公共字段
        Field name = c.getField("name");
        name.set(person, "诸葛亮");

        person.show();
    }
}

获取私有的字段

/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        // 1.获取字节码
        Class<?> c = Class.forName("top.it6666.demo1.Person");

        // 2.获取字节码的构造器
        Constructor<?> cs = c.getConstructor(String.class, Integer.class, String.class);

        // 3.通过构造器来创建对象
        Person person = (Person)cs.newInstance("", 23, "");

        // 获取私有字段
        Field gender = c.getDeclaredField("gender");

        // 去除私有的权限
        gender.setAccessible(true);
        gender.set(person, "未知");

        person.show();
    }
}

获取方法

获取公共方法

/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        // 1.获取字节码
        Class<?> c = Class.forName("top.it6666.demo1.Person");

        // 2.获取字节码的构造器
        Constructor<?> cs = c.getConstructor(String.class, Integer.class, String.class);

        // 3.通过构造器来创建对象
        Person person = (Person)cs.newInstance("", 23, "");

        // 获取公共方法
        Method show = c.getMethod("show");
        System.out.println(show.getName());
        show.invoke(person);
    }
}

获取私有方法

/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        // 1.获取字节码
        Class<?> c = Class.forName("top.it6666.demo1.Person");

        // 2.获取字节码的构造器
        Constructor<?> cs = c.getConstructor(String.class, Integer.class, String.class);

        // 3.通过构造器来创建对象
        Person person = (Person)cs.newInstance("", 23, "");

        // 获取私有方法
        Method eat = c.getDeclaredMethod("eat", String.class);

        // 去除私有权限
        eat.setAccessible(true);
        eat.invoke(person, "面条");
    }
}

越过数组泛型检测


  • 数组如果定义好了泛型就不能添加泛型以外的类型
  • 可以通过反射来去实现添加以外的类型
  • 在一个​​Integer​​ 泛型的数组当中添加字符串类型


/**
 * @author BNTang
 **/
public class Client {
    public static void main(String[] args) throws Exception {
        List<Integer> list = new ArrayList<>();
        list.add(23);

        // 越过之前
        System.out.println(list);

        Class<?> clazz = Class.forName("java.util.ArrayList");
        Method method = clazz.getMethod("add", Object.class);
        method.invoke(list, "BNTang");

        // 越过之后
        System.out.println(list);
    }
}

Servlet 创建过程

需求


  • 通过反射根据​​xml​​ 配置文件,创建对象


xml 文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0"
         metadata-complete="true">
    <absolute-ordering/>
    <display-name>web</display-name>
    <servlet>
        <!-- 自己起一个名称  -->
        <servlet-name>bntang</servlet-name>
        <servlet-class>top.it6666.servlet.FirstServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <!-- 必须和上面名称一致 -->
        <servlet-name>bntang</servlet-name>
        <url-pattern>/firstServlet</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

创建 Servlet 类

创建 IServlet.java 接口

/**
 * @author BNTang
 **/
public&nbsp;interface&nbsp;IServlet&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;/**
     * 初始化
     */
&nbsp;&nbsp;&nbsp;&nbsp;void&nbsp;init();
}

/**
 * @author BNTang
 **/
public&nbsp;class&nbsp;FirstServlet&nbsp;implements&nbsp;IServlet&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;@Override
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;init()&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&quot;init&nbsp;????&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;}
}

通过 xml 读取配置创建对象

使用 ​​dom4j​​​ 解析,下载地址:​​https://wwe.lanzoui.com/iOLWirydtwf​​

反射_反射与注解

/**
 * @author BNTang
 **/
public&nbsp;class&nbsp;Client&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;void&nbsp;main(String[]&nbsp;args)&nbsp;throws&nbsp;Exception&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SAXReader&nbsp;reader&nbsp;=&nbsp;new&nbsp;SAXReader();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Document&nbsp;doc&nbsp;=&nbsp;reader.read(&quot;src/web.xml&quot;);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 获取根元素
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element&nbsp;rootElement&nbsp;=&nbsp;doc.getRootElement();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(rootElement);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;List&lt;Element&gt;&nbsp;list&nbsp;=&nbsp;rootElement.elements(&quot;servlet&quot;);

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;(Element&nbsp;servEle&nbsp;:&nbsp;list)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Element&nbsp;classEle&nbsp;=&nbsp;servEle.element(&quot;servlet-class&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(classEle.getText());

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// 根据类名可以创建字节码
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class&lt;?&gt;&nbsp;c&nbsp;=&nbsp;Class.forName(classEle.getText());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Object&nbsp;obj&nbsp;=&nbsp;c.getDeclaredConstructor().newInstance();
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Method&nbsp;m&nbsp;=&nbsp;c.getMethod(&quot;init&quot;);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m.invoke(obj);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}
}

最终的文件结构如下图所示,然后运行测试类即可

反射_反射与注解_02

反射_反射与注解_03

举报

相关推荐

0 条评论