【javaWeb 反射】 实例介绍 反射机制 和 框架类设计

反射与框架设计

本文是根据Java课程图文做的笔记

00. 背景

第一阶段:源代码和字节码文件在硬盘上
第二阶段:万物皆对象,内存中有一个对象来描述字节码文件,即Java里面的Class类,描述字节码文件共同的属性和行为.它描述字节码文件中所有的成员变量\构造方法和成员方法.
第三阶段:对象在内存上
在这里插入图片描述

01. 基础概念

  • 框架:半成品软件。在框架的基础上进行软件开发,简化编码.比如一个软件有1000行代码,900行都可以是框架提供的,你往里面填东西就好了.

  • 反射机制:将类的各个组成部分封装为其他对象

    • 具体含义:
      在这里插入图片描述
    • 具体表现:
      String类 定义一个字符串对象,就把字符串的字节码文件加载进内存中.内存中有class类对象,它把所有的方法抽取出来封装成method对象,把所有方法放到method数组里面.需要的时候调用名称等一些信息.在这里插入图片描述 * 好处: 1. 可以在程序运行过程中,操作这些对象。
      1. 可以解耦,提高程序的可扩展性。
  • 使用框架的时候并不需要会反射

02. 用法

首先定义基础的Student类和Person

public class Person {
    
    
    private String name;
    private int age;

    public String a;
    protected String b;
    String c;
    private String d;


    public Person() {
    
    
    }

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

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", a='" + a + '\'' +
                ", b='" + b + '\'' +
                ", c='" + c + '\'' +
                ", d='" + d + '\'' +
                '}';
    }


    public void eat(){
    
    
        System.out.println("eat...");
    }

    public void eat(String food){
    
    
        System.out.println("eat..."+food);
    }
}

Student类

package main;
public class Student {
    
    
    public void sleep(){
    
    
        System.out.println("sleep...");
    }
}

获取Class对象的方式:

  1. Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象 多用于配置文件
  2. 类名.class:通过类名的属性class获取 多用于参数传递
  3. 对象.getClass():getClass()方法在Object类中定义。 用于获取对象的字节码文件

同一个字节码文件,在程序运行过程中只会被加载一次,不管使用哪种方式获得的类对象都是同一个

import main.Person;
import main.Student;

public class ReflectDemo1 {
    
    
    /**
        获取Class对象的方式:
            1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象
            2. 类名.class:通过类名的属性class获取
            3. 对象.getClass():getClass()方法在Object类中定义着。

     */
    public static void main(String[] args) throws Exception {
    
      //抛出大异常

        //1.Class.forName("全类名")
        Class cls1 = Class.forName("main.Person");
        System.out.println(cls1);  //class main.Person
        //2.类名.class  
        Class cls2 = Person.class;  
        System.out.println(cls2);  // class main.Person
        //3.对象.getClass()
        Person p = new Person();
        Class cls3 = p.getClass();
        System.out.println(cls3);   // class main.Person

        //== 比较三个对象 验证是否是堆内存中同一个对象
        System.out.println(cls1 == cls2);//true 比较内存地址
        System.out.println(cls1 == cls3);//true


        Class c = Student.class;
        System.out.println(c == cls1);
        
    }
}

03. Class对象功能

获取功能:

1. 获取成员变量们

  • Field[] getFields():获取所有public修饰的成员变量

  • Field getField(String name) 获取指定名称的 public修饰的成员变量

  • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
    在反射面前没有什么私有共有

  • Field getDeclaredField(String name) 获取指定名称的成员变量

Field:成员变量
操作:
1. 设置值
* void set(Object obj, Object value)
2. 获取值
* get(Object obj)
3. 忽略访问权限修饰符的安全检查
* setAccessible(true):暴力反射

获取成员变量示例:

package reflect;

import main.Person;
import java.lang.reflect.Field;

public class ReflectDemo2 {
    
    
    public static void main(String[] args) throws Exception {
    
    

        // 0.获取Person的Class对象
        Class personClass = Person.class;

        // 1.Field[] getFields()获取所有public修饰的成员变量
        Field[] fields = personClass.getFields();
        for (Field field : fields) {
    
    
            System.out.println(field);   // public java.lang.String main.Person.a
        }
        
        System.out.println("------------");
        
        // 2.Field getField(String name) 获取指定名称的 public修饰的成员变量
        Field a = personClass.getField("a");
        // 获取成员变量a 的值
        Person p = new Person();
        Object value = a.get(p);
        System.out.println(value);
        // 设置a的值
        a.set(p,"张三");
        System.out.println(p);

        System.out.println("===================");

        // 3.Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
        Field[] declaredFields = personClass.getDeclaredFields();
        //小技巧:idea输入iter
        for (Field declaredField : declaredFields) {
    
    
            System.out.println(declaredField);
        }
        
        // 4.Field getDeclaredField(String name)
        Field d = personClass.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查,不然会报错:非法访问异常
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);
        System.out.println(value2);

    }
}

在这里插入图片描述

2. 获取构造方法们

  • Constructor<?>[] getConstructors()
  • Constructor<T> getConstructor(类<?>... parameterTypes)
  • Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
  • Constructor<?>[] getDeclaredConstructors()

Constructor:构造方法
* 创建对象:
* T newInstance(Object... initargs)
* 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

package reflect;

import main.Person;
import java.lang.reflect.Constructor;

public class ReflectDemo3 {
    
    
    public static void main(String[] args) throws Exception {
    
    

        //0.获取Person的Class对象
        Class personClass = Person.class;
        /*
            获取构造方法们
                 * Constructor<?>[] getConstructors()
                 * Constructor<T> getConstructor(类<?>... parameterTypes)

                 * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
                 * Constructor<?>[] getDeclaredConstructors()
         */

        //Constructor<T> getConstructor(类<?>... parameterTypes)
        Constructor constructor = personClass.getConstructor(String.class, int.class);
        System.out.println(constructor);
        //创建对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);

        System.out.println("----------");

        Constructor constructor1 = personClass.getConstructor();
        System.out.println(constructor1);
        //创建对象
        Object person1 = constructor1.newInstance();
        System.out.println(person1);

        Object o = personClass.newInstance();
        System.out.println(o);


        //constructor1.setAccessible(true);
    }
}

在这里插入图片描述

3. 获取成员方法们:

  • Method[] getMethods()

  • Method getMethod(String name, 类<?>... parameterTypes)

  • Method[] getDeclaredMethods()

  • Method getDeclaredMethod(String name, 类<?>... parameterTypes)

Method:方法对象
执行方法:
* Object invoke(Object obj, Object… args)
获取方法名称:
* String getName:获取方法名

4. 获取全类名

  • String getName()
 package reflect;

import main.Person;
import java.lang.reflect.Method;

public class ReflectDemo4 {
    
    
    public static void main(String[] args) throws Exception {
    
    

        //0.获取Person的Class对象
        Class personClass = Person.class;

        //获取指定名称的方法
        Method eat_method = personClass.getMethod("eat");
        Person p = new Person();
        //执行方法
        eat_method.invoke(p);


        Method eat_method2 = personClass.getMethod("eat", String.class);
        //执行方法
        eat_method2.invoke(p,"饭");

        System.out.println("-----------------");

        //获取所有public修饰的方法
        Method[] methods = personClass.getMethods();
        for (Method method : methods) {
    
    
            System.out.println(method);
            String name = method.getName();
            System.out.println(name);
            //method.setAccessible(true);
        }

        //获取类名
        String className = personClass.getName();
        System.out.println(className);//cn.itcast.domain.Person

    }
}

在这里插入图片描述

03. 反射案例

  • 需求:写一个"框架",可以帮我们创建任意类的对象,可以执行任意方法
  • 原始方法存在的弊端:框架应该是提前写好的,不允许修改的。原始new方法肯定达不到
  • 前提:不能改变框架类的任何代码。可以创建任意类的对象,可以执行任意方法
  • 技术条件配置文件 和 反射机制. 只需要改配置文件,不需要改代码

步骤:
1. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
2. 在程序中加载读取配置文件
3. 使用反射技术来加载类文件进内存
4. 创建对象
5. 执行方法

创建配置文件pro.properties

//需要执行的方法名和类名
className=main.Student
methodName=sleep

创建框架类:

package reflect;

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

/**
 * 框架类
 */
public class ReflectTest {
    
    
    public static void main(String[] args) throws Exception {
    
    

      /*  Person p = new Person();
        p.eat();*/
	  /*
        Student stu = new Student();
        stu.sleep();*/

        //1.加载配置文件
        //1.1创建Properties对象
        Properties pro = new Properties();
        
        //1.2加载配置文件,转换为一个集合,双列的Map集合  Properties的直接父类是Hashtable,实现Map接口 
        //1.2.1获取class目录下的配置文件,即配置文件的路径
        ClassLoader classLoader = ReflectTest.class.getClassLoader();  //字节码文件的类加载器,类加载器将字节码文件加载进内存
        //classLoader能找到类目录下的一些文件,包括配置文件
        //getResourceAsStream返回资源对应的字节流
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is); //将字节或者字符流加载进内存

        //2.获取配置文件中定义的数据,,需要加载的全类名和需要执行的方法
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");


        //3.加载该类进内存
        Class cls = Class.forName(className);
        
        //4.创建对象
        Object obj = cls.newInstance();
        
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        
        //6.执行方法
        method.invoke(obj);
    }
}

在这里插入图片描述
修改配置文件,不修改框架类:
在这里插入图片描述
执行框架类:
在这里插入图片描述

Guess you like

Origin blog.csdn.net/weixin_44121966/article/details/119276605