Java 反射 类加载阶段 类加载时机 Class类 Field类 Method类 Constructor类

JAVA反射机制是在运行状态中,对任意一个类,都能知道这个类的所有属性和方法;对任意一个对象,都能够调用它的任意方法和属性,这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

反射(Reflect)是在运行时动态访问类与对象的技术,是JDK1.2版本后的高级特性,隶属于java.lang.reflect,大多数Java框架都基于反射实现参数配置、动态注入等特性。

加载完类之后,在队中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了累的完整结构信息。

  • 可以在程序运行过程中,操作这些对象。
  • 可以解耦,提高程序的可扩展性。

image-20230920180953215

反射机制的相关类

  • Class类:代表类的实体,在运行的Java应用程序中表示类和接口,Class对象表示某个类加载后在堆中的对象
  • Field类:代表类的成员变量(成员变量也称为类的属性)
  • Method类:代表类的方法
  • Constructor类:代表类的构造方法

Field类、Method类和Constructor类下面三个类对象都有setAccessible() 方法,作用是启动和禁用访问安全检查的开关。

  • true表示反射的对象在使用时取消访问检查,提高反射效率。
  • false表示反射的对象执行访问检查。

反射的优点和缺点

  • 优点:可以动态的创建和使用对象,使用灵活,没有反射机制,框架技术就失去底层支撑。
  • 缺点:使用反射基本是解释执行,对执行速度有影响。

反射机制是Java实现动态语言的关键。

  • 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强。
  • 动态加载:运行是加载需要的类,如果运行时不用该类,即使不存在也不报错,降低了依赖性。

类被加载的时机

  1. 静态加载:创建对象(new)
  2. 静态加载:当子类被加载时,父类也会加载
  3. 静态加载:调用类中的静态成员
  4. 动态加载:反射

静态加载

public static viod main(String[] args) {Cat cat = new Cat();     // 此时,如果没有Cat类,则会在编译的时候就报错cat.eat();
}

动态加载

public static void main(String[] args) {Class cls = Class.forName("Dog");    // 此时,如果没有Dog类,编译时不会报错,代码走到这一行的时候会报错Object obj = cls.newInstance();Method m = cls.getMethod("hi");m.invoke(o);
}

类的加载

image-20230920181055286

加载阶段

JVM在该阶段主要是将字节码从不同的数据源(可能是class文件,也可能是jar包、网络等)转化为二进制字节流加载到内存中,并生成一个该表该类的Class对象。

连接阶段

验证

目的是为了确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。包括但不限于:文件格式验证(是否以魔数0xcafebabe开头)、元数据验证、字节码验证和符号引用验证。

可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。

准备

JVM会在该阶段对静态变量分配内存并初始化(对应数据类型的初始值,如 0、0L、null、false)。这些变量所使用的内存都会在方法区中进行分配。

如果是final 修饰的静态常量,会直接赋最终的值。

解析

虚拟机将常量池内的符号引用替换为直接引用的过程。

比如:一个常量的引用是A类的一个对象,在这一步之前这个常量引用的是一个符号来指向A类,但不是对象;而到这一步之后,会将该常量引用具体的内存地址值。(符号是相对的,引用是绝对的)

初始化

到初始化阶段,才真正开始执行类中的Java代码,此阶段是执行<clinit>() 方法的过程。该方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中所有的静态变量的赋值动作和静态代码块中的语句,并进行合并。

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

Class类

Class也是类,继承与Object类,Class类对象不是new出来的,是在加载类的时候系统创建的,因此一个类的Class对象在内存中只有一份,因为类只加载一次。

通过Class对象可以获得该类的结构信息和一些其他信息。

Class类对象是放在堆中的,类的字节码二进制数据是放在方法区的。

在类第一次被加载时,会进入ClassLoader类中的loadClass方法创建Class对象。

有Class类对象的类型有:

  • 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
  • 接口
  • 数组
  • 枚举
  • 注解
  • 基本数据类型
  • void

获取成员变量

  • getFields():获取所有public修饰的成员变量,包括父类的。

  • getField(String name):获取指定名称的public修饰的成员变量,包括父类的。

  • getDeclaredFields():获取所有的成员变量,不考虑修饰符,包括父类的。

  • getDeclaredField(String name):获取指定名称的成员变量,包括父类的。

获取构造方法

  • getConstructor(Class...<?> parameterTypes):获得该类中与参数类型匹配的公有构造方法。

  • getConstructors():获得该类的所有公有构造方法。

  • getDeclaredConstructor(Class...<?> parameterTypes):获得该类中与参数类型匹配的构造方法。

  • getDeclaredConstructors():获得该类所有构造方法。

获取成员方法

  • getMethod(String name, Class...<?> parameterTypes):获得该类某个公有的方法,包括父类的。

  • getMethods():获得该类所有公有的方法,包括父类的。

  • getDeclaredMethod(String name, Class...<?> parameterTypes):获得该类某个方法,包括父类的。

  • getDeclaredMethods():获得该类所有方法,包括父类的。

获取类、类对象

  • 类名.class:通过类名的属性class获取,多用于参数的传递。同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。

  • 对象.getClass():此方法在Object类中定义。多用于对象的获取字节码的方式。

  • forName(String className):根据类名返回类的对象,多用于配置文件,将类名定义在配置文件中。读取文件,加载类。

  • getName():获得类的完整路径名字。

  • getClassLoader():获得类的加载器。

  • newInstance():创建类的实例。调用的是类的无参构造。

  • getPackage():获得类的包。

  • getSimpleName():获得类的名字。

  • getSuperclass():获得当前类继承的父类的名字。

  • getInterfaces():获得当前类实现的类或是接口。

  • getClasses():返回一个数组,数组中包含该类中所有公共类和接口类的对象。

  • getDeclaredClasses():返回一个数组,数组中包含该类中所有类和接口类的对象。

  • Cast:把对象转换成代表类或是接口的对象。

  • asSubclass(Class<U> clazz):把传递的类的对象转换成代表其子类的对象。

获取类中注解

  • getAnnotation(Class<A> annotationClass):返回该类中与参数类型匹配的公有注解对象。

  • getAnnotations():返回该类所有的公有注解对象。

  • getDeclaredAnnotation(Class<A> annotationClass):返回该类中与参数类型匹配的所有注解对象。

  • getDeclaredAnnotations():返回该类所有的注解对象。

获取Class对象的方式

第一种:已知一个类的全类名,且该类在类路径下,可以通过Class类的静态方法forName() 方法获取,如果获取不到,会抛出ClassNotFoundException 异常。

Class catCls = Class.forName("run.arbor.Cat");

一般用于配置文件,读取类的全路径,加载类。

第二种:若已知具体的类,可以通过累的class获取,该方式最安全可靠,程序性能最高。

Class dogCls = Dog.class;

一般用于参数传递,比如通过反射得到对应构造器对象。

第三种:已知一个类的实例,调用该实例的getClass() 方法来获取Class对象。

Cat cat = new Cat();
Class catCls2 = cat.getClass(); // 获取到的是运行类型

通过创建好的对象,获取Class对象

第四种:通过ClassLoader的方式。

ClassLoader cl = cat.getClass().getClassLoader();
Class catCls3 = cl.loadClass();

第五种:获取基本数据类型的Class类对象,会自动转为包装类。

Class cls = int.class;

第六种:基本数据类型对应的包装类,可以通过.type 得到Class类对象

Class cls = Integer.TYPE;

Constructor类 构造方法

  • T newInstance(Object... initargs) :使用由此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
  • void setAccessible(true):暴力反射,将此反射对象的 accessible标志设置为指示的布尔值。

获取相关信息

  • getModifiers() :以int形式返回修饰符,0 默认修饰符、1 public、2 private、4 protected、8 static、16 final。
  • getName():获取方法名。
  • getParameterTypes():以Class[]返回参数类型数组。
public class ReflectTest {public static void main(String[] args) throws Exception {// 1. 获取User的Class对象Class<?> userClass = Class.forName("run.arbor.reflect.User");// 2. 通过public的无参构造器创建实例Object user = userClass.newInstance();System.out.println("public的无参构造:" + user);// 3. 通过public的有参构造创建实例Constructor<?> constructor = userClass.getConstructor(String.class);// 此时的constructor对象就代表了public User(String name)构造器Object arbor = constructor.newInstance("arbor");System.out.println("public的有参构造:" + arbor);// 4. 通过非public的有参构造创建实例Constructor<?> declaredConstructor = userClass.getDeclaredConstructor(String.class, int.class);// 4.1 因为是私有的,无法使用该对象创建对象,抛出了java.lang.IllegalAccessException// 4.2 所以需要使用暴力反射来调用非public的方法或者属性,true为暴力反射,默认为falsedeclaredConstructor.setAccessible(true);Object arbor1 = declaredConstructor.newInstance("arbor", 25);System.out.println("非public的有参构造:" + arbor1);}
}class User {private String name = "乔木先生";private int age = 23;public User() {}public User(String name) {this.name = name;}private User(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "User{" + "name='" + name + '\'' + ", age=" + age + '}';}
}

Method类 方法

执行方法

  • Object invoke(Object obj, Object... args) :在具有指定参数的指定对象上调用此 方法对象表示的基础方法。

获取方法名称

  • String getName():获取方法名,返回由该方法对象表示的方法的名称,作为 String 。
  • void setAccessible(true):暴力反射,将此反射对象的 accessible标志设置为指示的布尔值。
  • boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断该方法是否被添加指定的注解

获取相关信息

  • getModifiers() :以int形式返回修饰符,0 默认修饰符、1 public、2 private、4 protected、8 static、16 final。
  • getReturnType():以Class形式获取返回类型。
  • getName():获取方法名。
  • getParameterTypes():以Class[]返回参数类型数组。
public class ReflectTest {public static void main(String[] args) throws Exception {// 1. 获取Boss的Class对象Class<?> bossClass = Class.forName("run.arbor.reflect.Boss");// 2. 通过public的无参构造器创建实例Object boss = bossClass.newInstance();System.out.println("无参构造创建的对象:" + boss);// 3. 使用反射调用hi方法Method hi = bossClass.getMethod("hi", String.class);hi.invoke(boss, "arbor");// 4. 使用反射调用say方法Method say = bossClass.getDeclaredMethod("say", int.class, String.class, char.class);// 4.1 因为say方法是私有的,如果想访问,需要暴力反射say.setAccessible(true);// 4.2 因为say方法是static的,如果想设置属性,可以不传入对象,传入一个null即可// Object invoke = say.invoke(boss,  20, "arbor", '大');Object invoke = say.invoke(null, 20, "arbor", '大');System.out.println(invoke);}
}class Boss {public Boss() {}private static String say(int n, String s, char c) {return n + " " + s + " " + c;}public void hi(String s) {System.out.println("hi, " + s);}
}

Field类 成员变量

设置值

  • void set(Object obj, Object value):将指定的对象参数中由此 Field对象表示的字段设置为指定的新值。

获取值

  • void get(Object obj):返回由该 Field表示的字段在指定对象上的值。

忽略访问权限修饰符的安全检查:

  • void setAccessible(true):暴力反射,将此反射对象的 accessible标志设置为指示的布尔值。

获取相关信息

  • getModifiers() :以int形式返回修饰符,0 默认修饰符、1 public、2 private、4 protected、8 static、16 final。

如果是 public static 则返回相加得到的数字,就是 1 + 8 = 9。得到的结果不会重复,所以能算出有哪些修饰符。

  • getType():以Class形式返回类型。
  • getName():返回属性名。
public class ReflectTest {public static void main(String[] args) throws Exception {// 1. 获取Student的Class对象Class<?> studentClass = Class.forName("run.arbor.reflect.Student");// 2. 通过public的无参构造器创建实例Object student = studentClass.newInstance();System.out.println("无参构造创建的对象:" + student);// 3. 使用反射操作age属性Field age = studentClass.getField("age");System.out.println("获取对象的age属性值:" + age.get(student));age.set(student, 25);System.out.println("修改age属性后的对象:" + student);// 4. 使用反射操作name属性Field name = studentClass.getDeclaredField("name");// 4.1 因为name属性是私有的,如果想访问,需要暴力反射name.setAccessible(true);// 4.2 因为name属性是static的,如果想设置属性,可以不传入对象,传入一个null即可// name.set(student, "arbor");name.set(null, "arbor");System.out.println("修改name属性后的对象:" + student);}
}class Student {private static String name = "乔木先生";public int age = 23;public Student() {}@Overridepublic String toString() {return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/479428.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3dmax渲染有噪点的六大原因及解决方案

3dmax效果图渲染可以试试云渲染——渲染100&#xff08;支持批量渲染&#xff0c;不占用本地电脑资源&#xff0c;最高480线程大大提高渲染效率&#xff09; 问题一&#xff1a;图像尺寸过低导致渲染效果不佳 原因&#xff1a;3dmax中的图像尺寸过低&#xff0c;导致渲染后的效…

频段划分学习射频知识的意义

一、射频电路设计与低频电路设计的不同点 随着频率提高&#xff0c;相应电磁波的波长与变得可与分立电路元件的尺寸相比拟时&#xff0c;电阻、电容和电感这些元件的电响应&#xff0c;将偏离他们的理想频率特性。以 WIFI 2.4G 频段为例&#xff0c;当频率为 2437MHz&#xff0…

java中容易被忽视的toString()方法

之前一直认为toString就是将数据转换成字符类型&#xff0c;直到最近写出了一个bug才对toString有了新的认识 不同数据类型&#xff0c;toString() 有不同的操作 定义一个student类&#xff0c;包含姓名 String类型、性别 String类型、年龄 int 类型、分数列表 String类型的li…

微波炉维修笔记

微波主要是靠2.45GHz 左右的微波(12.2cm 波长)加热水分子实现食物加热 所有不要使用金属器皿进行加热&#xff0c;要么因为电磁屏蔽&#xff0c;起不到加热效果&#xff0c;要么火光四射 微波炉基本组成 借鉴姜师傅的视频 碰到不加热其它都正常的问题 1.检查高压电容 使用万…

Cadence Allegro学习笔记【原理图篇】

点击下图中的按钮可以打开3D预览模式 按住shift和按鼠标中间可以旋转&#xff0c;单独按鼠标中间可以平移动 cadence元器件属性摆放横竖模式切换: 双击元器件后进入属性对话框&#xff0c;系统默认是横排显示&#xff0c;鼠标选中左侧框上面右击&#xff0c;出现如下对话框选…

地下管线管网三维建模工具MagicPipe3D V3.4.2发布

经纬管网建模系统MagicPipe3D&#xff0c;本地离线参数化构建地下管网三维模型&#xff08;包括管道、接头、附属设施等&#xff09;&#xff0c;输出标准3DTiles服务、Obj模型等格式&#xff0c;支持Cesium、Unreal、Unity、Osg等引擎加载进行三维可视化、语义查询、专题分析&…

vscode配置,通过VS开发泰山派

可以使用windows下的vscode编写ros代码 ssh服务端配置 给大家的镜像默认已经安装并开启ssh服务,这个操作可以省略 安装ssh服务端 sudo apt install openssh-server 开始ssh服务 sudo service sshd start 查看ssh服务状态 sudo systemctl status sshd.service windows端vscod…

网络原理 - HTTP/HTTPS(3)

HTTP请求 认识请求"报头" header的整体的格式也是"键值对"的结构. 每个键值对占一行,键和值之间使用分号进行分割. 报头的种类有很多,此处仅介绍几个常见的. Host 表示服务器主机的地址和端口.(Host和URL中的ip地址端口啥的,绝大部分情况下都是一样的,少…

【Java多线程】线程安全问题与解决方案

目录 1、线程安全问题 1.2、线程安全原因 2、线程加锁 2.1、synchronized 关键字 2.2、完善代码 2.3、对同一个线程的加锁操作 3、内容补充 3.1、内存可见性问题 3.2、指令重排序问题 3.3、解决方法 3.4、总结 volatile 关键字 1、线程安全问题 某个代码&#xff…

数据库应用:kylin 部署 达梦数据库DM8

目录 一、实验 1.环境 2.部署前规划 3.部署达梦数据库DM8 4.创建数据库及数据库事例管理 5.达梦数据库的基本操作 二、问题 1.xhost命令报错 2.执行安装程序DMInstall.bin 报错 3.解压安装程序报错 4.安装程序找不到文件 5.图像化界面打不开 6.安装内存太小 7.打开…

【CCEdit】通过扩散模型进行创意且可控的视频编辑

文章目录 CCEdit1. 核心特性1.1 三叉戟网络结构1.2 精细的外观控制1.3 高度的自适应性 2. 三叉戟结构2.1 结构分支&#xff08;ControlNet架构&#xff09;2.2 外观分支2.3 主分支 3. 数据集——BalanceCC benchmark dataset4. 训练5. 长视频编辑6. 使用场景7. 评估指标 CCEdit…

证券公司VIP快速交易通道是什么?可以免费申请吗?

交易速度是王道&#xff01;&#xff01;&#xff01; “快速交易通道&#xff0c;早已不是什么新鲜事了。”深圳某上市券商经纪业务部负责人称&#xff0c;尽管各家券商提供的VIP通道形式不同&#xff0c;功能各异&#xff0c;而且无论是深市还是沪市都能申请VIP交易通道&…