反射 -hsp

反射引入

根据现有的条件,创建一个类对象并使用该对象的方法(类给了文件路径)
以往的方法是new对象

实战需求:在不修改程序本身源码的情况,通过外部文件配置,控制程序,符合设计模式OCP原则(不修改源码扩展功能)

反射快速入门

//从配置文件中读取需要的对象以及方法
        Properties properties = new Properties();
        properties.load(new FileInputStream("src\\re.properties"));
        String classFullPath = properties.get("Class").toString();
        String methodName = properties.get("Method").toString();
  
  
        Class aClass = Class.forName(classFullPath);
        //得到一个指定类的Class对象
        Object o = aClass.newInstance();
        //通过这个Class对象得到指定类的实例对象
        Method method = aClass.getMethod(methodName);
        //通过这个指定类的Class对象得到配置文件中指定的方法
        method.invoke(o);
        //调用这个方法,基于传入的o类

Java Reflection

允许程序在执行期借助ReflectionAPI取得任何类的内部信息

  • 当加载完类之后,在堆中就产生一个Class类型的对象(一个类对应一个对象),对象包含了类的完整结构信息,通过这个对象得到类的结构,就像一个蓝图,通过这个Class类对象看到类的结构即反射
    在这里插入图片描述
    个人理解:
    类加载阶段,将 java类 编译后的 Class文件 加载到 方法区,此时是Class类的实例对象 (一种数据结构,存放Cat类的 成员变量,构造器等…)
    运行阶段,实例化一个Cat类,是从方法区中的 存放Cat类的Class对象中,得到一个Cat类的对象,存放在堆区,其对象引用存放在对应线程的栈区

反射各种类和方法

在这里插入图片描述

        Class aClass = Class.forName(classFullPath);
        Object o = aClass.newInstance();
        Method method = aClass.getMethod(methodName);
        method.invoke(o);

        //得到成员对象
       	//getField不能得到私有属性
        Field age = aClass.getField("age");
        System.out.println(age.get(o));

        //得到构造器
        Constructor constructor = aClass.getConstructor();
        System.out.println(constructor);

        //有参构造器需要写参数的class类的字节码文件
        Constructor constructor1 = aClass.getConstructor(String.class);
        System.out.println(constructor1);

反射的优缺点

优点:动态创建和使用对象(框架核心),使用灵活
缺点:使用反射后是解释执行,影响执行速度

想要优化速度,则需要设置setAccessible,方法/成员变量/构造函数 都有这个方法
true表关闭检查,优化就一点点…

类加载器

  • Class类不是用户创建,是由系统创建
    当第一次使用new去一个类时,首先调用类加载器ClassLoader类的构造器
    使用反射实例化一个类,第一次同样会进行类加载
    同一个类对象只有一次!
        Class aClass = Class.forName(classFullPath);
        Class aClass1 = Class.forName(classFullPath);
        System.out.println(aClass1.equals(aClass));
        //同一个Class,只是引用不同,实际上都一样
  • 通过Class 类对象,可以通过各种api方法得到类的完整结构
    在这里插入图片描述

几种方式获取Class对象

        //得到 指定类 的 Class类对象
        //1. 已知全类名,并在该类路径,通过CLass类静态方法
        Class<?> aClass = Class.forName("com.lgj.Cat");
        System.out.println(aClass.getName());

        //2. 类名.class,多用于参数传参
        System.out.println(Cat.class.getName());

        //3.通过已知实例对象反向拿到其Class对象
        Cat cat = new Cat();
        Class<? extends Cat> aClass1 = cat.getClass();
        System.out.println(aClass1.getName());

        //4.通过 四种 类加载器获取(在加载阶段)ClassLoader
        ClassLoader classLoader = cat.getClass().getClassLoader();
        Class<?> aClass2 = classLoader.loadClass("com.lgj.Cat");
        System.out.println(aClass2.getName());

        //基本数据类型直接通过.class得到
        Class<Integer> integerClass = int.class;
        System.out.println(integerClass.getName());

        //基本数据类型的 包装类! 通过.TYPE得到
        Class<Boolean> type = Boolean.TYPE;
        System.out.println(type.getName());

在这里插入图片描述

类的静态/动态加载

类加载被激活的时间
1.当创建对象
2.子类对象被加载
3.调用类中静态成员

4.反射(动态)

举例:在有些让用户选项的的地方,部分的类是不需要被加载的,比如开始游戏时选择游戏类,有些时候用户只会选择一个类,其他类用不上,若是写死了new对象,则编译时就必须要编译通过
动态加载(反射),只有在真正用到的时候才需要加载类
在这里插入图片描述

类加载

类加载分为五个阶段
在这里插入图片描述

下面是对各阶段步骤的讲解

首先一部就是加载阶段,很好理解,将磁盘中的字节码文件 载入 内存中

在这里插入图片描述
连接阶段 验证 会加载一个SecurityManager管理,对引用文件的验证
在这里插入图片描述

连接阶段 - 准备 仅仅只给 静态 遍历赋值,进行第一次初始化 基本数据类型将会变成其最原始初始值(常量则直接赋真实设定值)
实例属性不会分配空间
在这里插入图片描述

连接阶段 - 解析
常量池落地,此时引用全变为直接引用(从符号引用 落地 内存地址)
在这里插入图片描述

Initialization初始化
此时还在类加载,所以实例属性仍然不操作
此时clinit会收集类中的所有静态属性/方法,并运行(按照代码写的顺序)

并且jvm对clinit方法设定了同步机制,正因如此,多线程操作类加载也不会出现问题,也只有一个Class
在这里插入图片描述

通过反射获取类结构信息

第一组:Class类

在这里插入图片描述

第二组 属性

在这里插入图片描述

第三组 Method类

在这里插入图片描述
补充:

  1. 返回修饰符的等级的组合关系是相加
  2. 返回的数据类型,是方法返回值的对应的Class类对象
第四组 构造器

在这里插入图片描述

反射创建对象

在这里插入图片描述

    public static void main(String[] args) throws Exception {
        Class<?> catClass = Class.forName("com.lgj.Cat");

        //1 通过Class类对象的方法,直接使用无参构造器创建实例
        Object cat1 = catClass.newInstance();
        System.out.println(cat1);

        //2. 通过public有参构造器创建实例对象
        Constructor<?> constructor = catClass.getConstructor(int.class);
        Object cat2 = constructor.newInstance(520);
        System.out.println(cat2);

        //3.通过得到 所有的构造器,传参筛选出需要的那一个构造器
        //得到构造器后需要设置setAccessible(true)爆破使用

        Constructor<?> declaredConstructor = catClass.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        Object cat3 = declaredConstructor.newInstance("暴力破解猫", 9999);
        System.out.println(cat3);
    }

在这里插入图片描述
在这里插入图片描述

    public static void main(String[] args) throws Exception {
        Class<?> catClass = Class.forName("com.lgj.Cat");
        //通过反射获得 实例变量
        Cat cat1 = (Cat) catClass.newInstance();

        //通过反射获得属性
        Field field = catClass.getField("age");
        field.set(cat1,999);

        System.out.println(field.get(cat1));

        //4.反射得到 静态私有属性
        //操作 私有 成员,创建对象时需要使用getDeclared,并且设置setAccessible
        Field field1 = catClass.getDeclaredField("secrete");
        field1.setAccessible(true);
        System.out.println(field1.get(null));//因为是静态属性才使用这个

        //反射拿私有方法并调用
        Method hi = catClass.getDeclaredMethod("Hi", String.class);
        hi.setAccessible(true);
        hi.invoke(cat1,"黄明俊");
    }
  • 在反射中,方法有返回值,统一是Object

反射的两道作业

在这里插入图片描述

        Class<PrivateTest> privateTestClass = PrivateTest.class;
        PrivateTest privateTest = privateTestClass.newInstance();
        Field name = privateTestClass.getDeclaredField("name");
        name.setAccessible(true);
        name.set(privateTest,"黄明俊");

        System.out.println(privateTest.getName());

在这里插入图片描述

        Class<?> fileClass = Class.forName("java.io.File");
        Constructor<?>[] declaredConstructors = fileClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        Constructor<?> constructor = fileClass.getConstructor(String.class);
        Object o = constructor.newInstance("d:\\test.txt");
        //千万别强转...失去反射的意义了
        Method method = fileClass.getMethod("createNewFile", null);
        method.invoke(o,null);
        System.out.println("文件创建成功");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值