23 反射
lix_uan 613 1

类加载

类在内存中的声明周期

  • 加载 -> 使用 -> 卸载

类加载的三个阶段

23 反射

类初始化

会导致类初始化的操作

  • 运行主方法所在的类
  • 第一次new对象的时候
  • 调用某个类的静态成员
  • 子类初始化时,如果父类还没有初始化,那么先初始化父类
  • 通过反射操作某个类时,如果这个类没有初始化,也会导致该类的初始化

不会导致类初始化的操作

  • 使用某个类的静态常量 static final
  • 通过子类调用父类的静态变量、静态方法,只会导致父类初始化,不会导致子类初始化
  • 用某个类型声明数组并创建数组对象时,不会导致这个类初始化

类加载器

类加载器的分类

  • 引导类加载器
  • 扩展类加载器
  • 应用程序类加载器
  • 自定义加载器

双亲委托模式

  • 下一级的类加载器,如果接到任务时,会先搜索是否加载过,如果没有,会先把任务往上传,如果都没有加载过,一直到根加载器,如果根加载器在它负责的路径下没有找到,会往回传,如果一路回传到最后一级都没有找到,那么会报ClassNotFoundException或NoClassDefError,如果在某一级找到了,就直接返回Class对象

获取Class对象的四种方式

哪些类型可以获取Class对象

  • 所有类型

方式1

  • 类型名.class

方式2

  • 对象.getClass()

方式3

  • Class.forName(类型全名称)

方式4

  • ClassLoader的类加载器对象.loadClass(类型全名称)

    public class TestClass {
        @Test
        public void test05() throws ClassNotFoundException{
            Class c = TestClass.class;
            ClassLoader loader = c.getClassLoader();
    
            Class c2 = loader.loadClass("com.atguigu.test05.Employee");
            Class c3 = Employee.class;
            System.out.println(c2 == c3);
        }
    
        @Test
        public void test03() throws ClassNotFoundException{
            Class c2 = String.class;
            Class c1 = "".getClass();
            Class c3 = Class.forName("java.lang.String");
    
            System.out.println(c1 == c2);
            System.out.println(c1 == c3);
        }
    }

反射的应用

获取类型的详细信息

创建任意引用类型的对象

@Test
public void test1() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
    //        AtGuigu obj = new AtGuigu();//编译期间无法创建

    Class<?> clazz = Class.forName("com.atguigu.test.AtGuigu");
    //clazz代表com.atguigu.test.AtGuigu类型
    //clazz.newInstance()创建的就是AtGuigu的对象
    Object obj = clazz.newInstance();
    System.out.println(obj);
}

操作任意类型的属性

public class TestField {
    public static void main(String[] args)throws Exception {
        //1、获取Student的Class对象
        Class clazz = Class.forName("com.atguigu.test.Student");

        //2、获取属性对象,例如:id属性
        Field idField = clazz.getDeclaredField("id");

        //3、如果id是私有的等在当前类中不可访问access的,我们需要做如下操作
        idField.setAccessible(true);

        //4、创建实例对象,即,创建Student对象
        Object stu = clazz.newInstance();

        //5、获取属性值
        /*
         * 以前:int 变量= 学生对象.getId()
         * 现在:Object id属性对象.get(学生对象)
         */
        Object value = idField.get(stu);
        System.out.println("id = "+ value);

        //6、设置属性值
        /*
         * 以前:学生对象.setId(值)
         * 现在:id属性对象.set(学生对象,值)
         */
        idField.set(stu, 2);

        value = idField.get(stu);
        System.out.println("id = "+ value);
    }
}

调用任意类型的方法

public class TestMethod {
    @Test
    public void test()throws Exception {
        // 1、获取Student的Class对象
        Class<?> clazz = Class.forName("com.atguigu.test.Student");

        //2、获取方法对象
        /*
         * 在一个类中,唯一定位到一个方法,需要:(1)方法名(2)形参列表,因为方法可能重载
         * 
         * 例如:void setName(String name)
         */
        Method method = clazz.getDeclaredMethod("setName", String.class);

        //3、创建实例对象
        Object stu = clazz.newInstance();

        //4、调用方法
        /*
         * 以前:学生对象.setName(值)
         * 现在:方法对象.invoke(学生对象,值)
         */
        method.invoke(stu, "张三");

        System.out.println(stu);
    }
}
评论区

索引目录