反射

反射

1.类加载器

1.1 类加载

当程序要使用某个类时,如果该类还未加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或类初始化

类的加载

  • 就是将class文件读入内存,并为之创建一个java.lang.Class对象
  • 任何类被使用时,系统都会为之建立一个java.lang.Class对象

类的连接

  • 验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
  • 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
  • 解析阶段:将类的二进制数据中的符号引用替换为直接引用

类的初始化

  • 在该阶段,主要就是对类变量进行初始化

类的初始化步骤

  • 假如类还未被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还未被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句

注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3

类的初始化时机

  • 创建类的实例
  • 调用类的类方法
  • 访问类或接口的类变量,或者为该类变量赋值
  • 使用反射方式来强制创建某个类或者接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类

1.2 类加载器

类加载器的作用

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

JVM的类加载机制

  • 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类负责载入,除非显示的使用另外一个类加载器来载入
  • 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试·从自己的类路径中加载该类
  • 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换为Class对象,存储到缓存区

image-20220425152805993

package com.itheima.ClassLoader;

public class ClassLoderDemo {
    public static void main(String[] args) {
        //返回用于委派的系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        System.out.println("===========================");
        //返回用于委派的系统类加载器的父类加载器
        ClassLoader parentClassLoader = systemClassLoader.getParent();
        System.out.println(parentClassLoader);
        System.out.println("===========================");
        //返回用于委派的系统类加载器的父类加载器的父类加载器
        ClassLoader parentParentClassLoader = parentClassLoader.getParent();
        System.out.println(parentParentClassLoader);
    }
}

2.反射

2.1 反射概述

image-20220425153606291

java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译器就完成确定,在运行期仍然可以扩展。

2.2 获取Class类的对象

我们要想通过反射去使用一个类首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象

这里我们提供三种方式获取Class类型的对象

  • 使用类的Class属性来获取该类对应的Class对象。举例:Student.class将会返回Student类对象的Class对象

  • 调用对象的getClass()方法,返回该对象所属类对应的Class对象

    该方法是Object类中的方法,所有的Java对象都可以调用该方法

  • 使用Class类中的静态方法forName(String Name),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径

package com.itheima.Reflect;

public class ReflectDemo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //使用类的class属性来获取该类对应的Class对象
        Class<Student> c1= Student.class;
        System.out.println(c1);
        Class<Student> c2=Student.class;
        System.out.println(c1==c2);
        System.out.println("===========================");
        //使用类的getClass()方法来获取该类对应的Class对象
        Student s=new Student();
        Class<?extends Student> c3=s.getClass();
        System.out.println(c1==c3);
        System.out.println("===========================");
        //使用Class类中的静态方法forName()来获取该类对应的Class对象
        Class<?> c4=Class.forName("com.itheima.Reflect.Student");
        System.out.println(c1==c4);

    }
}

2.3 反射获取构造方法并使用

Class类中用于获取构造方法的方法

  • Constructor<?>[]getConstructors():返回所有公共构造方法对象的数组
  • Constructor<?>[]getDeclaredConstructors():返回所有构造方法对象的数组
  • ConstructorgetConstructor(Class<?>...parameterTypes):返回单个公共构造方法对象
  • ConstructorgetDeclaredConstructor(Class<?>.parameterTypes):返回单个构造方法对象

Constructor类中用于创建对象的方法

  • T newInstance(Object...initargs):根据指定的构造方法创建对象
package com.itheima.Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c=Class.forName("com.itheima.Reflect.Student");
        //获取类的构造方法
        //1.获取构造方法的数组,只能获取public的构造方法
        //Constructor<?>[] cons=c.getConstructors();
        //1.获取构造方法的数组,可以获取所有的构造方法
        Constructor<?>[] cons=c.getDeclaredConstructors();
        //2.遍历数组,获取构造方法
        for (Constructor con : cons) {
            System.out.println(con);
        }
        System.out.println("===========================");
        //获取指定的public构造方法,参数:你要获取的构造方法的参数的个数和数据类型对应的字节码文件对象
        //Constructor con1=c.getConstructor();
        Constructor<?> con1=c.getConstructor(String.class,int.class,String.class);
        //Constructor提供了一个类的单个构造函数的信息和访问权限
        //T newInstance(Object...initargs):使用由此Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
        Object obj= con1.newInstance("张三",20,"监狱");
        System.out.println(obj);

        //Student s=new Student();
        //System.out.println(s);
        //获取指定的构造方法


    }
}

2.4 反射获取构造方法并使用练习

练习1:通过反射实现如下操作

  • Student s =new Student("林青霞",30,"西安");
  • System.out.println(s);
  • 基本数据类型也可以通过.class得到对应的Class类型
package com.itheima.Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo03 {
    public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c =Class.forName("com.itheima.Reflect.Student");
        //获取单个公共构造方法
        Constructor<?> constructor = c.getConstructor(String.class,int.class,String.class);
        //基本数据类型也可以通过.class得到对应的Class类型
        Object obj=constructor.newInstance("林青霞",30,"西安");
        System.out.println(obj);
    }
}

练习2:通过反射实现如下操作

  • Student s=new Student("林青霞");
  • System.out.println(s);
  • setAccessible(true):设置可访问私有属性,值为true,取消访问检查
package com.itheima.Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c = Class.forName("com.itheima.Reflect.Student");
        //获取单个构造方法
        Constructor<?> constructor = c.getDeclaredConstructor(String.class);
        //暴力反射
        //setAccessible(true):设置可访问私有属性,值为true,取消访问检查
        constructor.setAccessible(true);
        //调用构造方法
        Object obj = constructor.newInstance("张三");
        System.out.println(obj);
    }
}

2.5 反射获取成员变量并使用

image-20220426091641481

2.6 反射获取成员变量并使用练习

练习:通过反射实现如下操作

  • Student s=new Student();
  • s.name="林青霞";
  • s.age=30;
  • s.address="西安";
  • System.out.println(s);
package com.itheima.Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class ReflectDemo06 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException, InstantiationException, IllegalAccessException {
        // 获取Class对象
        Class c = Class.forName("com.itheima.Reflect.Student");
        // 获取类中的无参构造方法
        Constructor<?> con=c.getConstructor();
        //获取类中的成员变量
        Field nameFiled = c.getDeclaredField("name");
        Field ageFiled = c.getDeclaredField("age");
        Field addressFiled = c.getDeclaredField("address");
        //调用构造方法创建对象
        Object obj = con.newInstance();
        //给成员变量赋值
        nameFiled.setAccessible(true);
        nameFiled.set(obj,"林青霞");
        ageFiled.set(obj,30);
        addressFiled.set(obj,"西安");
        //输出对象
        System.out.println(obj);
    }
}

2.7 反射获取成员方法并使用

image-20220426095214541

package com.itheima.Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectDemo07 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //获取Class对象
        Class<?> c =Class.forName("com.itheima.Reflect.Student");
        //Method[] getMethods()
        //返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象,包括那些由类或接口和那些从超类和超接口继承的声明。
        //Method[] getDeclaredMethods()
        //返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("===============================");
        Method[] declaredMethods = c.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
        System.out.println("===============================");
        //Method getMethod(String name,Class<?>... parameterTypes)
        //返回一个方法对象,该方法对象反射的类或接口的指定公共方法,如果没有找到,则返回null。
        //Method getDeclaredMethod(String name,Class<?>... parameterTypes)
        //返回一个方法对象,该方法对象反射的类或接口的指定声明的方法,如果没有找到,则返回null。
        Method method1 = c.getMethod("method1");
        Constructor<?> constructor = c.getConstructor();
        Object o = constructor.newInstance();
        //o.method1();
        // 方法提供有关类和接口上单一方法的信息和访问权限。
        //Object invoke(Object obj, Object... args)
        //在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。
        //obj:调用方法的对象
        //args:调用方法的参数
        method1.invoke(o);
//        Student s = new Student();
//        s.method1();
    }
}

2.8 反射获取成员方法并使用练习

练习:通过反射实现如下操作

  • Student s = new Student();
  • s.method1();
  • s.method2("林青霞");
  • String ss = s.method3("林青霞",30);
  • System.out.println(ss);
  • s.function();
package com.itheima.Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectDemo08 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        //创建Class对象
        Class<?> c= Class.forName("com.itheima.Reflect.Student");
        //调用无参构造方法
        Constructor<?> con=c.getDeclaredConstructor();
        Object obj=con.newInstance();
        //获取method1方法
        Method method1 = c.getDeclaredMethod("method1");
        //获取method2方法
        Method method2 = c.getDeclaredMethod("method2",String.class);
        //获取method3方法
        Method method3 = c.getDeclaredMethod("method3",String.class,int.class);
        //获取function方法
        Method function = c.getDeclaredMethod("function");
        //调用method1方法
        method1.invoke(obj);
        //调用method2方法
        method2.invoke(obj,"林青霞");
        //调用method3方法
        Object o = method3.invoke(obj, "林青霞", 30);
        System.out.println(o);
        //调用function方法
        function.setAccessible(true);
        function.invoke(obj);
    }
}

2.9 反射练习

练习1:我有一个ArrayList集合,现在我想在这个集合中添加一个字符串数据,如何实现?

package com.itheima.Reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class ReflectDemo09 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //创建集合
        ArrayList<Integer> list = new ArrayList<>();
        //添加元素
//        list.add(10);
//        list.add(20);
//        list.add(30);
        Class<? extends ArrayList> c = list.getClass();
        Method m = c.getMethod("add", Object.class);
        m.invoke(list, "hello");
        m.invoke(list, "world");
        m.invoke(list, "java");
        
        System.out.println(list);
    }
}

练习2:通过配置文件运行类中的方法

package com.itheima.Reflect;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class ReflectDemo10 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
       // Person p = new Person();
        //p.eat();

        //Animal a = new Animal();
        //a.play();

        /*
            class.txt
            className=xxx
            methodName=xxx
         */

        //加载数据
        Properties prop=new Properties();
        FileReader fr=new FileReader("E:\\idea\\thefirst\\src\\com\\itheima\\Reflect\\class.txt");
        prop.load(fr);
        fr.close();
        /*
           className=com.itheima.Reflect.Person
           methodName=eat
         */
        //获取数据
        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);
    }
}
posted @ 2023-05-19 15:11  _xiaolin  阅读(28)  评论(0)    收藏  举报