JavaWeb笔记-07- 类的加载、反射获取、反射案例

1、类的加载


加载 :
    将class文件读入内存,并创建一个Class对象
    任何类被使用时都会建立Class对象

连接 : 
    验证 是否有正确是内部结构,并和其他类协调一致
    准备 负责为类的静态成员分配内存,并设置默认初始化值
    解析 将类的二进制数据的符号引用替换为初识化值

初始化: 开启堆栈空间 , 默认初始化,显示初始化等。

2、类加载器


负责将 .class文件 加载到内存中,并生成对应的Class对象

组成: 
Bootstrap ClassLoader 根类加载器(引导类加载器)
    负责Java核心类的加载 eg:System,String等  
        在JDK中JRE的lib目录下rt.jar文件中

Extension ClassLoader 扩展类加载器
    负责JRE的扩展目录中jar包的加载。
        在JDK中JRE的lib目录下ext目录

Sysetm ClassLoader 系统类加载器
    负责在JVM启动时加载来自java命令的class文件
    classpath环境变量所指定的jar包和类路径

3、反射


反射理解: 
    通过Class文件对象,去使用该文件的 成员变量 构造方法 成员方法。

反射机制: 
    1)把java文件保存到本地硬盘  .java
    2)编译.java 成 .class文件
    3)使用jvm , 把class文件通过类加载 加载到到内存中
    4)class文件在内存中用 class类 来表示
        属性类 : Field
        构造方法类 : Constructor
        方法类 : Method

获取class字节码文件对象的方式

A: Object类的getClass()方法。
    Person p = new Person();
    Class c = p.getClass();   对象名.getClass();  c拿到p的class文件

B:数据类型的静态属性class。
    数据类型.class();

C:Class类中的静态方法 
    Class.forName();  传参为类的全路径(带报名的路径)

使用哪个方式呢:
    开发中: 第三种  
        原因:第三种得到的是一个字符串,而不是具体的类名。
            可以把字符串配置到配置文件中。
    自己用:第二种方便。

通过反射获取构造方法并使用。

方法:
获得所有公共(public)构造方法: 
    //Constructor[] getConstructors();

获得所有构造方法:   
    //Constructor[] getDeclaredCsonstructors(); 

获得单个构造方法: 
    Constructor<T> getConstructor(Class<?>... parameterType)    
    参数表示的是: 你要获得的构造方法的参数,
            及数据类型的class字节码文件对象 

    //通过反射获取无参构造方法并使用。

        //获得该路径下的class字节码文件对象
        Class c = Class.forName("person.Person"); 

        //返回的是构造器(方法)对象(通过字节码文件对象)
        Constructor con = c.getConstructor();  

        //通过构造器对象来创建一个对象
        Object obj = con.newInstance();  
            //这里创建的对象就是上面字节码路径的person对象  理解为自下而上创建      

        注:上面三条语句相当于  Person p = new Person();  
    // 通过反射获取带参构造方法并使用。
            --public Person(String name, int age, String address)

        // 获得该路径下的class字节码文件对象
            Class c = Class.forName("person.Person");

        // 返回的是构造器对象(通过字节码文件对象)
            Constructor con = c.getConstructor(String.class, int.class, String.class);

        // 通过带参构造器对象来创建一个对象
            Object obj = con.newInstance("李",11,"陕西")

        注:上面三条语句相当于  Person p = new Person("李",11,"陕西");  
// 通过反射获取私有构造方法并使用。  
            --private Person(String name)

        // 获得该路径下的class字节码文件对象
        Class c = Class.forName("person.Person");

        // 获得私有构造器对象(通过字节码文件对象)
        Constructor con = c.getDeclaredConstructor(String.class);

        // 正常情况下不能通过私有方法访问
        con.setAccessible(true); // 值为true 指示反射的对象取消Java语言访问检查

        // 通过私有构造器对象来创建一个对象
        Object obj = con.newInstance("李");

        注:上面三条语句相当于 Person p = new Person("李"); 

通过反射获取成员变量并使用

方法:  set(Object obj, object value)
    将指定对象(obj)上此 Field对象表示的字段设置为指定的新值
    //获取所有的的成员变量

        // 获得该路径下的class字节码文件对象
        Class c = Class.forName("person.Person");

        //获取所有公共(public)的成员变量
        //Field[] fields =  c.getFields();

        //获取所有的成员变量
        //Field[] fields =  c.getDeclaredFields();

        for (Field field : fields) {
            System.out.println(field);
    //获取单个的成员变量

        // 获得该路径下的class字节码文件对象
        Class c = Class.forName("person.Person");

         //通过无参构造方法创建一个对象 
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();

        //获取 address字段的属性类对象。
        Field addressfields =  c.getField("address");

        //通过该属性类对象 调用方法 为 obj对象的该属性 赋值
        addressfields.set(obj, "西安");  
        //深刻理解 : 通过set方法 对   obj对象的addressfields字段  设值为 "西安"


    注:如果是私有对象,则使用 .getDeclaredField();
        并 暴力访问   -- .setAccessible(true);

通过反射获取成员方法并使用

获取所有方法: 
    getMethods
    getDeclaredMethods

获取单个方法:
    getMethod
    getDeclaredMethod

暴力访问:
    method.setAccessible(true);

方法: invoke(Object obj, Object...args)
    参数1,表示对象是谁
    参数2,调用该方法的实际传参
    返回值是object
    //通过反射获取无参无返回值 方法并使用

        // 获得该路径下的class字节码文件对象
        Class c = Class.forName("person.Person");

        // 通过无参构造方法创建一个对象
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();

        // 获取show字段的方法类对象。
        Method m1 = c.getMethod("show");

        //通过方法类对象,调用方法
        m1.invoke(obj);

    //通过反射获取带参无返回值 方法并使用

        // 获得该路径下的class字节码文件对象
        Class c = Class.forName("person.Person");

        // 通过无参构造方法创建一个对象
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();

        // 获取show字段的方法类对象。
        Method m1 = c.getMethod("show2", String.class);

        // 通过方法类对象,调用方法
        m1.invoke(obj, "带参");

    //通过反射获取带参带返回值 方法并使用

        // 获得该路径下的class字节码文件对象
        Class c = Class.forName("person.Person");

        // 通过无参构造方法创建一个对象
        Constructor con = c.getConstructor();
        Object obj = con.newInstance();

        // 获取getString字段的方法类对象
        Method m1 = c.getMethod("getString", String.class,int.class);

        //有返回值,用object接收 
        Object objString = m1.invoke(obj, "hello",100);

        //输出返回值
        System.out.println(objString);

4、反射案例


A:通过反射运行配置文件的内容

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

        //加载键值对 数据
        Properties prop = new Properties();
        FileReader fr = new FileReader("class.txt");
        prop.load(fr);
        fr.close();

        //获取数据 --类名和方法
        String className = prop.getProperty("className");
        String methodName = prop.getProperty("methodName");

        //反射 
        Class c = Class.forName(className);

        Constructor con = c.getConstructor();
        Object obj = con.newInstance();

        //调用方法
        Method m = c.getMethod(methodName);
        m.invoke(obj);
        }
    }

    class.txt 文件内容
    className=???    填写文件带包名的路径
    methodName=???     方法名

B:通过反射越过泛型检查

    //原理:用class字节码操作    利用泛型只在编译期出现的现象

    //创建<Integer> 型集合
    ArrayList<Integer> array = new ArrayList<Integer>();

    //得到array的class文件对象
    Class c = array.getClass();  
    Method m = c.getMethod("add", Object.class);

    //调用array的add方法 传进字符串。
    m.invoke(array, "hello");   

    System.out.println(array);

C:通过反射给任意的一个对象的任意的属性赋值为指定的值

    //工具类
    public class Case3_Tool {   
        public void setProperty(Object obj, String propertyName, Object value) throws Exception {

        //获取对象字节码文件
        Class c = obj.getClass();

        //获取指定属性类对象
        Field field = c.getDeclaredField(propertyName);

        field.setAccessible(true);
        //通过指定属性类对象 对 指定对象的该属性 赋值
        field.set(obj,value);   
        }
    }


    //测试类
    public class TestCase3 {
        public static void main(String[] args) throws Exception {

        Person p = new Person();  //创建操作对象
        Case3_Tool t = new Case3_Tool();  //创建工具对象

        t.setProperty(p, "name", "李");  //调用工具对对象赋值
        t.setProperty(p, "age", 10);

        System.out.println(p);
        }
    }

    class Person{
        private String name;
        public int age;
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }   
    }

猜你喜欢

转载自blog.csdn.net/qq_41307491/article/details/81229193
今日推荐