Java深渊处的秘密(第二遍总结:通俗解释反射原理)

Java深渊处的秘密(第一遍总结:通俗解释反射原理)中我们已经从搜集的零碎资料大致知道了为何会使用反射,反射的原理,反射的简单使用。但是仍然没有建立一个系统的理解框架,在第二遍总结里,将从java反射的前世今生透彻的了解。就会有种豁然开朗的感觉。

学习经验介绍:知识需要反复琢磨,反射的知识不多也不难,但是我前前后后花了一周左右的时间才了解完,对同一个知识多达两三次的理解,都有不同的收获。如果第一遍不理解本文,建议收藏,过几天再看,会有意外的收获!

1 什么是反射

建议先看 第九部分直接看收一下

语言的阐述总是枯燥折磨人的, Java深渊处的秘密(第一遍总结:通俗解释反射原理)已经通俗解释过了(建议跳转过去看看),这里我建议先从代码中直接感受一波,再理解言语解释更舒服~

2 体验反射的使用

读取配置文件调用Cat类的Sound方法
在这里插入图片描述
在这里插入图片描述

Class.forName(classfullpath)换成 Class.forName(“com.hspedu.Cat”)也可以
目的是获得:Cat类的完成类型名
准确来说 getclass()—返回运行类型

在这里插入图片描述

3 反射原理

在这里插入图片描述

  1. 为什么要把 Cat.class (字节码文件) 转化为 Class 类对象?

    因为字节码文件中的信息系统是无法识别使用的,因此把这些信息处理之后,就变成了以数据结构的形式存储,比如
  • 成员变量 以数组的形式存放在Fiied中------->Filed[] fileds
  • 构造器 以数组的形式存放在Constructor中------->Constructor[] cons
  • 成员方法 以数组的形式存放在Method中-------> Method[] ms


这些信息存放在堆中某个位置,为了方便管理,把这些与Cat类相关的信息定义为 Class类对象,当然每一个类有都有一个Class类对象,通称为Class类对象

  1. Cat.class (字节码文件)是通过什么方式变成可以被系统调用的Class类对象?

    在java虚拟机中,提供了一个叫类加载器的东西,类加载器中(ClassLoader)封装了很多静态方法。有一个方法是loadclass(),这个方法可以把Cat的字节码文件映射处理一下,放到堆中(反射)
  1. 运行阶段是什么意思?创建对象是怎么回事? 既然堆中的Class类对象已经是可以调用使用了(以数据结构方式存储),我们就编写代码使用它。这个时候我们有两个方案
  • Cat cat = new Cat() New完一个对象,我们点击运行按钮,系统就会从编写的Cat类读取信息,到字节码文件,再到堆中的Class类对象,再根据New这个指令在堆中开辟空间,把Cat类的从成员方法,成员变量等信息存进去(new
    Cat开辟出的堆 因为更底层的设计 知道自己具体属于哪个 Class类对象
  • 通过编写可以利用反射方式的代码,系统就可以自动创建一个cat对象,但是我们是无法直接使用cat的,任何操作都是需要利用Class对象实现的(自动是指不利用new,不是啥代码都不写)
    比如想使用cat的hi() 方法,利用Class对象创建一个Cat的对象cat,创建完了并不可以直接使用cat,然后继续利用Class对象调用cat的方法hi()---------实际代码中不出现Cat,hi,背后原理出现了Cat,hi

4 反射相关的主要类

在java.lang.reflection中

  • java.lang.Class 代表一个类,类加载后在堆中的对象(字节码映射的那个)
  • java.lang.reflect.Method 代表 某个类的方法
  • java.lang.reflect.Field 代表 某个类的方成员变量
  • java.lang.reflect.Constructor 代表 某个类的构造方法

在这里插入图片描述

5 反射调用优化

代码演示
在这里插入图片描述
在这里插入图片描述

setAccessible()是什么?

  • Method
  • Field
  • Constructor 这三个的对象都有SetAccessible()方法,作用是启动和禁用访问安全检查的开关,参数値为true表示取消访问检查,提高反射效率
    安全检查:检查

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

6 Class类

  1. Class也是类,继承Object类
    (定位Class后快捷键ctrl+Alt+P-------调出父类)
    在这里插入图片描述
  2. Class类对象不是New出来的,而是系统创建的
    在这里插入图片描述
    经过断点和追踪源码,发现Class.forName()可以追踪到类加载器的LoadClass方法(),参数name显示com.hspedu.Cat。
    在这里插入图片描述
    经过断点和追踪源码,发现new Cat可以追踪到类加载器的LoadClass方法(),参数name显示com.hspedu.Cat。

对比发现,无论是使用反射还是New,都可以成功跑到类加载器的部分,不就说明Class类对象是系统自己创建的吗

  1. 对于某个类的Class类对象,在内存中只存在一份

在这里插入图片描述
对于Cat类,无论创建几次Cat类的Class类对象,实际内存中只有一份,所以cls1==cls2
因为Cat类和Dog类不同,所以内存中cls2 != cls3
虽然New和反射都可以创建Class类对象,但是底层运作的原理还是有区别的,所以cls4 != cls1

探究原理:为什么内存中可以做到只保存一份?
假想一下,如果有十个线程调用同一个类,那么是不是在内存中保留十分?验证的结果说明,只保留一份。这说明这十个线程是同步的,一个线程干了,其他的线程就不是去干了。

具体来说是因为虚拟机会保证一个类的< clinit >()方法在多线程环境中被正确地加锁,同步。如果多个线程同时初始化一个类,那么只有一个线程去执行这个类的< clint >()方法,其他线程需要阻塞等待。直到活动线程执行< clint >() 方法完毕。

  1. 每个类的实例(cat)都会记得自己是由哪一个Class类对象产生的

  2. 通过Class类对象可以完整的得到一个类的完整的结构
    在这里插入图片描述

  3. 类的字节码二进制数据(又称为类的元数据:方法代码,变量名,方法名,访问权限…)存放在方法区
    在这里插入图片描述

为什么要存放在方法区?

为了提高效率,试想一下,如果有上百个类,就有上百了类对象,那么程序运行起来岂不是非常消耗时间,因此提出了一个优化方案:把类的具体数据单独存放起来,放在方法区(相当于武器库里的存放所有士兵的武器装备)。在堆里面存放类对象(士兵名单)。

上百个士兵中,我只要找到士兵A的武器:先看一遍写有上百个士兵的名单,这很快,找到了士兵A,然后取武器库找到存放士兵A武器的区域。这比直接从武器库找属于士兵A的武器效率高太多了。

7 Class类对象常用方法

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

8 获取Class对象的六种方式

之所有会有这么多的方式 是为了在不同的情况下方便使用
在这里插入图片描述

在这里插入图片描述

方法2: 参数传递的意思是:比如Cat类的有参构造函数 public Cat(String name)
目的:调用有参构造函数
需要这样传递参数: Constructor constructor2 = cls.getConstructor(String.class);

在这里插入图片描述

9 静态加载和动态加载

  1. 静态加载
    如果代码中调用了一个不存在的类,静态加载是无法通过编译的。
    在这里插入图片描述
    在这里插入图片描述
  2. 动态加载
    如果利用动态加载(反射),可以通过编译。
    我们在控制台输入2,并不调用不存在的Fish类,照样可以运行
    但如果控制台输入1,此时才会编译处理Fish类,返现不存在,才报错(使用的时候才处理
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

10 类加载过程

在这里插入图片描述
当你写完代码后,点击运行,你得代码源文件就按照上图的流程一步一步执行,下面对部分名词进行解释,方便理解图表达的每个信息。

案例一:借助这段代码

public static void main(String[]  args){
		static int a=10;
		static int b=20;
		static String s = "zjh"
}

类加载第二个阶段 :连接阶段的准备--------对静态变量默认初始化:

  • a = 0
  • b = 0
  • s = null

类加载的第三个阶段:初始化:

  • int a=10;
  • static int b=20;
  • static String s = “zjh”

案例二:

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

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

11 通过类对象-获取类结构信息

在这里插入图片描述

在这里插入图片描述

12 Filed、Method、Constructor细节补充

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

13 反射爆破创建实例

为何使用爆破?
因为我们想要使用 非public 的属性,构造器,方法,但因为没有权限 就强行 给一个权限去使用

什么是爆破?
setAccessible(true):说明可以访问一切

怎么使用?
下面代码将从Filed,constructor,method三个方面介绍

实体类准备

在这里插入图片描述

构造器

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

Filed

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

Method

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

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值