JavaWeb 反射再学习

反射

反射:框架设计的灵魂,需重点掌握

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

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

    • 优点
      1. 可以在程序的运行过程中,操作这些对象
      2. 可以解耦,提高程序的可扩展性
  • 获取Class对象的方式(字节码文件对象)

    • 源代码阶段:Class.forName(“包名\类名”);将字节码文件加载进内存,返回Class对象
    • Class类对象阶段:通过 类名.class,来获取Class对象
    • 运行时阶段:对象.getClass();返回一个Class对象,getClass()方法在Object类中定义
  • 使用场景

    • Class.forName(全类名),多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
    • 类名.class,多用于参数的传递
    • 对象.getClass(),多用于对象的获取字节码的方式
  • Class对象的功能

    • 获取包含成员变量
      • getFields():返回Class类所有public修饰的成员变量数组
      • getField(String name):返回指定名称的public修饰的成员变量
      • getDeclaredFields():返回Class类中所有的成员变量数组数组,包含私有和默认成员变量
      • getDeclaredField(String name):返回指定名称的成员变量,包含私有和默认成员变量
    package com.reflect;
    import com.domain.Person;
    import java.lang.reflect.Field;
    
    /**
     * @author: 小码农
     * @create: 2021-08-10 15:51
     **/
    public class ReflectDemo2 {
        public static void main(String[] args) throws Exception {
            //获取Person的Class对象
            Class<Person> pc1 = Person.class;
            //获取成员变量
            //getFields()返回一个成员变量数组
            Field[] fields = pc1.getFields();
            for(Field field:fields){
                System.out.println(field);
            }
            //getField()返回指定名称的public修饰的成员变量
            Field a = pc1.getField("a");
            //获取成员变量a的值
            Person p = new Person();
            Object value = a.get(p);
            System.out.println(value);
            a.set(p,"12345");
            System.out.println(p);
    
            System.out.println("---------------");
            Field[] declaredFields = pc1.getDeclaredFields();
            for (Field declaredField:declaredFields){
                System.out.println(declaredField);
            }
            Field d = pc1.getDeclaredField("d");
            //使用private修饰的内容前,需要设置忽略全是修饰符的安全检查
            d.setAccessible(true);//暴力反射
            Object obj = d.get(p);
            System.out.println(obj);
        }
    }
    
    • 获取包含构造方法
      • getConstructor():返回指定传入参数类型的公共构造函数方法
      • getConstructors():返回Class类的所有公共构造函数数组
      • getDeclaredConstructor():返回指定传入参数类型的构造函数,包含私有和默认构造函数
      • getDeclaredConstructors():返回Class类的所有构造函数数组,包含私有构造函数
    package com.reflect;
    
    import com.domain.Person;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    /**
     * @author: 小码农
     * @create: 2021-08-10 15:51
     **/
    public class ReflectDemo3 {
        public static void main(String[] args) throws Exception {
            //获取Person的Class对象
            Class<Person> pc1 = Person.class;
    
            //getConstructor(String name)
            Constructor<Person> con1 = pc1.getConstructor(String.class,int.class);
            System.out.println(con1);
            //使用newInstance()方法创建对象
            Person person = con1.newInstance("xm",11);
            System.out.println(person);
            Constructor<Person> con2 = pc1.getConstructor();
            Person person2 = pc1.newInstance();
            System.out.println(person2);
        }
    
    }
    
    • 获取包含成员方法
      • getMethod(String name,参数类型):返回指定名称、传参类型的公共成员方法
      • getMethods():返回Class类的所有公共成员方法数组
      • getDeclaredMethod(String name,参数类型):返回指定名称、传参类型的成员方法,包含默认私有
      • getDeclaredMethods(String name,参数类型):返回Class类的所有成员方法数组,包含默认和私有
    package com.reflect;
    
    import com.domain.Person;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    
    /**
     * @author: 小码农
     * @create: 2021-08-10 15:51
     **/
    public class ReflectDemo4 {
        public static void main(String[] args) throws Exception {
            //获取Person的Class对象
            Class<Person> pc1 = Person.class;
    
            //获取所有public修饰的方法getMethods()
            Method[] methods = pc1.getMethods();
            for (Method method:methods){
                System.out.println(method);
            }
            Method toString = pc1.getMethod("toString");
            System.out.println(toString);
            //获取私有方法对象
            Method eat = pc1.getDeclaredMethod("eat");
            System.out.println(eat);
            //创建对象
            Person p = new Person();
            eat.setAccessible(true);//使用私有方法,需暴力反射
            eat.invoke(p);
    
            //获取私有含参方法
            Method eat1 = pc1.getDeclaredMethod("eat", String.class);
            eat1.setAccessible(true);//暴力反射
            eat1.invoke(p,"小明");
    
            //获取包含private修饰的方法
            Method[] declaredMethods = pc1.getDeclaredMethods();
            for (Method m:declaredMethods){
                System.out.println(m);
                System.out.println(m.getName());
            }
        }
    }
    
    • 获取类名
      • String getName():获取类名
    public class ReflectDemo5 {
        public static void main(String[] args) throws Exception {
            //获取Person的Class对象
            Class<Person> pc1 = Person.class;
            //Class类对象.getName()返回字符串形式的全类名
            String name = pc1.getName();
            System.out.println(name);
        }
    }
    

暴力反射:使用private修饰的内容前,需要设置忽略全是修饰符的安全检查

  • 对象.setAccessible(true);

  • Field:成员变量

    • 操作:
      • 设置值:set(Object obj,Object value)
      • 获取值:get(Object obj)
  • Constructor:构造方法

    • 操作:
      • 创建对象:具体对象.newInstance(参数1,参数2…);
      • 创建空参对象可以直接使用Class类的newInstance()方法,不用在找空参构造方法,然后再构造空参对象,Class类的newInstance()方法一步到位
      • 同样如果使用私有构造方法创建对象,也需要提前进行暴力反射
  • Method:成员方法

    • 操作:
      • 使用方法:invoke(对象,方法需要传入的参数);当成员方法是无参方法时,传入参数为空
      • 获取方法名称:方法对象.getName();返回字符串类型的方法对象名
      • 对于私有成员方法,使用的前提是对先该方法进行暴力反射 方法对象.setAccessiable(true);
      • 需要注意的是,使用使用Methods()方法返回public修饰的方法数组时,一些Class类继承的Object对象的方法也会存入返回的成员方法数组中
  • Class:获取类名

    • 操作
      • Class对象.getName():返回字符串形式的全类名

结论

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

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

  1. 源代码阶段:一个包含成员变量、构造方法、成员方法的java文件通过编译命令javac形成一个class字节码文件,字节码文件中将成员变量、构造方法、成员方法分区
  2. Class类对象阶段:字节码文件通过类加载器ClassLoader将成员变量、构造方法、成员方法封装为包含成员变量的数组对象Field[],包含构造方法的数组对象Constructor[],包含成员方法的数组对象Method[]
  3. Runtime运行时阶段:在这个阶段,我们可以使用new 对象();从构造器对象数组中找到构造器创建新的对象,从成员变量数组中获取成员变量,从成员方法数组中使用成员方法。

案例:

  • 需求:写一个"框架",在不改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法
  • 实现
    1. 配置文件
    2. 反射技术
  • 步骤:
    1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
    2. 在程序中加载读取配置文件
    3. 使用反射技术来加载类文件进内存
    4. 创建对象
    5. 执行方法
package com.reflect;

import com.domain.Person;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * @author: 小码农
 * @create: 2021-08-10 15:51
 **/
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //要求实现:可以创建任意类的对象,可以执行任意方法
        //1 加载配置文件
        //1.1 创建Properties对象
        Properties pro = new Properties();
        //1.2 加载配置文件,转换为一个集合
        //1.2.1 获取class目录下的配置文件方式
        ClassLoader classLoader = ReflectTest.class.getClassLoader();//获取字节码文件的类加载器
        InputStream is = classLoader.getResourceAsStream("Pro.Properties");
        pro.load(is);

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

        //3 创建Class对象
        Class<?> cls = Class.forName(className);
        //3.1 创建具体类对象
        Object obj = cls.newInstance();
        //3.2 创建方法对象
        Method mn = cls.getDeclaredMethod(methodName);
        mn.setAccessible(true); //暴力反射

        //4 调用方法
        mn.invoke(obj);
    }
}

配置文件 Pro.Properties

className=com.domain.Student
methodName=sleep

Student类

package com.domain;

/**
 * @author: 小码农
 * @create: 2021-08-10 17:35
 **/
public class Student {
    public void sleep(){
        System.out.println("sleep...");
    }
}

Person类

package com.domain;

/**
 * @author: 小码农
 * @create: 2021-08-10 15:52
 **/
public class Person {
    private String name;
    private int age;
    

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

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

    private void eat(){
        System.out.println("eat...");
    }
    private void eat(String s){
        System.out.println(s+"eat...");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值