JAVA基础--反射(二)

获取Class实例的四种方式
//获取Class的前三种方式(需要掌握)
public void test(){
    //the way 1:通过运行时类的属性.class
    Class clazz = Person.class;
    //the way 2:通过运行时类的对象调用getClass()
    Person p1 = new Person();
    Class clazz2 = p1.getClass();
    //the way 3:通过Class的静态方法:forName(String classPath)
    Class clazz3 = Class.forName("com.amtyh.java.Person");
    //这里的clazz1、2、3全部指向的是同一个运行时类
    //并且在一个生命周期之内只会加载一次
    
    //the way 4 :使用类加载器加载(了解即可)
    ClassLoader classLoader = 当前类类名.class.getClassLoader();
    Class clazz4 = classLoader.loadClass("com.amtyh.java.Person");
}

哪些类型可以有Class对象:

  • class:外部类、成员(成员内部类、静态内部类),局部内部类,匿名内部类

  • interface:接口

  • []:数组,只要元素类型和维度一样(注意不是长度),就是同一个类

  • enum:枚举

  • annotation:注解@interface

  • primitive type:基本数据类型

  • void

    类的加载过程(了解)

    当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

在这里插入图片描述

加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问
入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要
类加载器
参与。

**链接:**将Java类的二进制代码合并到JVM的运行状态之中的过程。

  • 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
  • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
  • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

初始化:

  • 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
  • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
  • 虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。

什么时候会发生类初始化?
类的主动引用(一定会发生类的初始化)

  • 当虚拟机启动,先初始化main方法所在的类
  • new一个类的对象
  • 调用类的静态成员(除了final常量)和静态方法
  • 使用java.lang.reflect包的方法对类进行反射调用
  • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类

类的被动引用(不会发生类的初始化)

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化

  • 当通过子类引用父类的静态变量,不会导致子类初始化

  • 通过数组定义类引用,不会触发此类的初始化

  • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

    类加载的作用

    将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

  • 类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

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

通过反射的方式创建对应的运行时类的对象:

在这里插入图片描述

在JAVA bean中要求提供一个public的空参构造器:

  • 便于通过反射创建对象
  • 便于子类继承此运行时对象时,默认调用super()时,保证父类有此构造器

重点需要理解反射的动态性

调用运行时类的指定结构

相关的一些API调用方法:

获取类的成员变量信息

方法用途
getField(String name)获得某个公有的属性对象
getFields(String name)获得所有公有的属性对象
getDeclaredFiedl(String name)获得某个属性对象(访问范围较大,public和非public)
getDeclaredFields(String name)获得所有属性对象(public和非public)

获取类的方法信息

方法用途
getMethod(String name,Class…<?>parameter type)获得某个公有的方法
getMethods()获得所有公有的方法
getDeclaredMethod(String name,Class…<?>parameter type)获得某个方法(访问范围较大,public和非public)
getDeclaredMethods()获得所有方法(public和非public)

获取类的构造器信息

方法用途
getConstructor(Class…<?>parameter type)获得该类中与参数类型匹配的公有构造方法
getConstructors()获得该类的所有公有的构造方法
getDeclaredConstructor(Class…<?>parameter type)获得类中与参数类型匹配的构造方法
getDeclaredConstructors()获得所有构造方法

反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载。

原来使用new的时候,需要明确的指定类名,这个时候属于硬编码实现,而在使用反射的时候,可以只传入类名参数,就可以生成对象,降低了耦合性,使得程序更具灵活性。

举个例子我们的项目底层数据库有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.mikechen.java.myqlConnection,com.mikechen.java.oracleConnection这两个类我们要用。

这时候我们在使用 JDBC 连接数据库时使用 Class.forName()通过反射加载数据库的驱动程序,如果是mysql则传入mysql的驱动类,而如果是oracle则传入的参数就变成另一个了。

Spring 框架的 IOC(动态加载管理 Bean),Spring通过配置文件配置各种各样的bean,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,你的程序就能健壮地运行。

还有Spring AOP(动态代理)功能都和反射有关系。

除此之外还有很多框架:mybatis、dubbo、rocketmq等等都会用到反射机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值