韩顺平Java | C23 反射Reflection

news/2024/5/19 3:41:10 标签: java, 反射

需求:通过外部文件配置,在不修改源码情况下控制程序(符合设计模式ocp开闭原则:不修改源码的情况下扩容功能)

反射机制

反射机制允许程序在执行期间借助于ReflectioAPI取得任何类的内部信息(如成员变量,构造器,成员方法),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。
加载完类后,在堆中产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息,通过这个对象得到类的结构,故名曰反射
在这里插入图片描述

java">//反射相关的主要类
import java.lang.reflect

java.lang.Class // 代表一个类,Class对象代表某个类加载后在堆中的对象
java.lang.reflect.Method //代表类的方法,Method对象代表某个类的方法
java.lang.reflect.Field //代表类的成员变量,Field对象表示某个类的成员变量
java.lang.reflect.Constructor //代表类的构造方法,Constructor对象表示构造器

优点:动态创建和使用对象(框架底层核心)
缺点:反射基本是解释执行,对执行速度有影响
优化:关闭访问检查。Method、Field和Constructor都有setAccessible()方法,用于启动和禁用访问安全检查的开关,参数值为true表示放射的对象在使用时取消访问检查,提高反射效率,反之false执行。

java">//相关API:使用Properties类读取配置文件
Properties properties = new Properties();
properties.load(new InputStream("src\\cfg.properties"));
String classfullName = properties.get("classfullName");

反射机制基础代码

java">Class<?> cls = Class.forName("classfulldName"); //1 获取Class类
Object o = cls.getInstance(); //2 通过Class类获取类对象
Method method = cls.getMethod("methodName");//3 通过Class类获取方法对象
method.invoke(o); //4 通过方法对象反向调用类对象

※Class类

Class对象不是new出来的,而是系统创建的
对于某个类的Class对象,在内存中只有一份,因为类只加载一次

Class类的常用方法

java">//获得Class类
Class<?> cls = Class.forname(String name); //返回指定类名name的Class对象
//显示
System.out.println(cls); //显示cls对象是哪个类的Class对象 如com.hspedu.Car
System.out.println(cls.getClass()); //显示cls的运行类型 java.lang.Class
System.out.println(cls.getPackage().getName()); //包名 com.hspedu
cls.getName() //类的全路径,全类名 com.hspedu.Car,返回Class对象所表示的实体名称(类、接口、数组、基本类型等)
//获得实例
Object newInstance() //返回Class对象的一个实例
Car car = (Car)cls.newInstance(); 
System.out.println(car); //实际调用toString()
//通过反射得到属性(不能获取私有属性)
Field field = cls.getField("brand");
System.out.println(fieid.get(car));
brand.set(car, "奔驰");//通过反射给属性赋值
//遍历得到所有的属性(字段)
Field[] fields = cls.getFields();
for(Field f : fields){
    System.out.print(f.getName()); //名称
}

在这里插入图片描述

获取类的Class对象的方式

java">Class.forName(String classfullname) //代码/编译阶段,应用:多用于配置文件,读取类全路径,加载类.class //加载阶段 应用:多用于参数传递,如通过反射获取构造器对象

对象.getClass() //运行阶段 应用:通过创建好的对象获取Class对象

//通过类加载器(4种)获取类的Class对象
ClassLoader classLoader = car.getClass().getClassLoader();//先得到类加载器car
Class<?> cls = classloader.loadClass(classAllPath)//通过类加载器得到Class对象

基本数据类型.class //基本数据类型(int, char, boolean, float, double, byte, long, short),得int

包装类.TYPE // 基本数据类型对应的包装类 Integer, Character, BOOLEAN, DOUBLE, LONG, BYTE... 得int,和上同一个Class类,hashcode()相等

哪些类型有Class对象:

java">//外部类,成员内部类,静态内部类,局部内部类,匿名内部类
Class<String> aclass = String.class; //外部类class java.lang.String
//interface:接口
Class<Serializable> serializableClass = Serializable.class; //interface java.io.Serializable
//数组
Class<Integer[]> aClass = Integer[].class; //class [Ljava.lang.Inte
Class<float[][]> aClass = float[][].class; //class [[F
//enum:枚举
Class<Thread.State> aClass = Thread.State.class; //class java.lang.Thread$State
//annotation:注解
Class<Derecated> aClass = Derecated.class; //interface java.lang.Deprecated
//基本数据类型
Class<Long> aClass = long.class; //long
//void
Class<Void> voidClass = void.class; //void
Class<Class> classClass = Class.class; //class java.lang.Class

※类加载

静态加载:编译时加载相关类,如果没有则报错,依赖性太强。如new、子类被加载时,父类也加载(继承)、调用类中的静态成员
动态加载:运行时加载需要得类,如果运行时不用该类,即使该类不存在,也不报错,降低了依赖性。如反射
在这里插入图片描述

类加载的五个阶段

在这里插入图片描述

  • 加载阶段:JVM将字节码从不同的数据源(class文件、jar包、网络等)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象
  • 连接阶段-验证:确保Class文件的字节流中包含的信息符合当前虚拟机要求(文件格式验证-是否以魔数oxcafebabe开头-打开编译后class文件可看、元数据验证、字节码验证、负好引用验证),不会危害虚拟机自身安全。可以使用-Xverify:none参数关闭大部分的类验证措施,缩短虚拟机类加载时间
  • 连接阶段-准备:JVM对静态变量,分配内存并默认初始化(对应数据类型默认初始值,如0-int/short、0L-long、null-引用类型、false-boolean等)。内存均在方法区中分配
java">//类加载的连接阶段-准备,属性/成员变量/字段是如何处理的
class A {
    public int n1 = 10; //n1是实例属性,不是静态变量,连接准备阶段不分配内存
    public static n2 = 20; //n2是静态变量,连接准备阶段分配内存,默认初始化0,不是20
    public static final int n3 = 30; //n3是static final常量,和类/静态变量,一旦赋值就不变,n3=30
}
  • 连接阶段-解析:虚拟机JVM将常量池的符号引用替换为直接引用的过程
  • 初始化initialiazation:程序员执行类中定义的Java程序代码,是执行<clinit>() 方法的过程。
    该方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
    虚拟机保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,知道活动线程执行<clinit>()方法完毕。(该机制保证某个类在内存中只有一个Class对象)
java">//直接使用类的静态属性也会导致类加载
public class ClassLoad03 {
    public static void main(String[] args) throws ClassNotFoundException {
        //分析
        //1. 加载B类,并生成 B的class对象
        //2. 连接 num = 0
        //3. 初始化阶段
        //    依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并合并
        /*
                clinit() {
                    System.out.println("B 静态代码块被执行");
                    //num = 300;
                    num = 100;
                }
                合并: num = 100
         */
		//	  执行clinit()
		
        new B();//类加载
        System.out.println(B.num);//100, 如果直接使用类的静态属性,也会导致类的加载
        /*
             B 静态代码块被执
             B() 构造器被执行
             100 
        */
    }
}

class B {
    static {
        System.out.println("B 静态代码块被执行");
        num = 300;
    }
    static int num = 100;
    public B() {//构造器
        System.out.println("B() 构造器被执行");
    }
}

反射获取类的结构信息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
构造函数:默认修饰符 是0,public是1。
在这里插入图片描述

访问属性
访问方法

反射相关类

在这里插入图片描述

java">//通过反射创建对象
//1 获取User类的Class对象
Class<?> userClass = Class.forName("com.hspedu.reflection.User");
//2.1 通过public的无参构造器创建实例
Object o = userClass.newInstance();
//2.2 通过public的有参构造器创建实例(先得到对应构造器,并传入实参)
Constructor<?> constructor = userClass.getConstructor(String.class);
Object o2 = constructor.newInstance("o2")
//2.3 通过菲public的有参构造器创建实例(先得到私有构造器,再传入实参)
Constructor<?> constructor3 = userClass.getDeclaredConstructor(int.class, String.class);
constructor3.setAccessible(true);//爆破:使用反射可以访问private构造器/方法/属性
Object user3 = constructor3.newInstance(100, "张三");

反射调用性能优化

Class类常用方法


http://www.niftyadmin.cn/n/5465127.html

相关文章

人工智能+的广泛应用,已渗透到生活的方方面面

引言 随着科技的不断进步和人工智能技术的快速发展&#xff0c;我们正处于一个人工智能时代。人工智能不仅仅是一种技术&#xff0c;更是一种革命性的变革力量&#xff0c;它正在以前所未有的方式改变着我们的生活和工作方式。 人工智能&#xff08;AI&#xff09;指的是人工…

Verasity开发更新

我们正在幕后努力构建全新的Verasity生态系统。关于代币经济学&#xff0c;正如我们在上次开发更新中提到的&#xff0c;我们正在构建新的基础设施并测试新过程&#xff0c;因此我们将更改和淘汰旧信息&#xff0c;并部署新的代币信息及其他支持文件&#xff0c;如代币免责声明…

YOLOv9改进策略 :主干优化 | 无需TokenMixer也能达成SOTA性能的极简ViT架构 | CVPR2023 RIFormer

💡💡💡本文改进内容: token mixer被验证能够大幅度提升性能,但典型的token mixer为自注意力机制,推理耗时长,计算代价大,而RIFormers是无需TokenMixer也能达成SOTA性能的极简ViT架构 ,在保证性能的同时足够轻量化。 💡💡💡RIFormerBlock引入到YOLOv9,多个数…

AI大模型释放巨大潜能,引领特定领域创新

近年来,AI大模型凭借其强大的学习能力和泛化性在多个领域展现出卓越的表现,成为推动相关领域创新发展的重要力量。以下我们将探讨AI大模型在自然语言处理、计算机视觉等领域的应用实践。 自然语言处理:大模型释放语义理解能力 在自然语言处理领域,大型语言模型如GPT-3、BERT等…

关于OcenaBase v4.2中,分区转移和负载均衡的技术解读

OceanBase​​​​​​​​​​​​​​作为一款原生分布式数据库&#xff0c;其核心的技术特性之一是高可扩展性&#xff0c;其具体表现在两个方面&#xff1a; 首先&#xff0c;是灵活的扩缩容能力&#xff0c;包括垂直扩缩容和水平扩缩容&#xff1a; 垂直扩缩容&#xff…

中科驭数超低时延网络解决方案入选2023年度金融信创优秀解决方案

近日&#xff0c;由中国人民银行领导、中国金融电子化集团有限公司牵头组建的金融信创生态实验室发布「2023年度第三期金融信创优秀解决方案」&#xff0c;中科驭数超低时延网络解决方案从众多方案中脱颖而出&#xff0c;成功入选&#xff0c;代表了该方案的技术创新和金融实践…

【授时防火墙】GPS北斗卫星授时信号安全防护装置系统

【授时防火墙】GPS北斗卫星授时信号安全防护装置系统 【授时防火墙】GPS北斗卫星授时信号安全防护装置系统 1、装置概述 卫星信号安全防护装置&#xff08;以下简称“防护装置”&#xff09;是一款专门针对卫星导航授时安全的设备。该设备能接收 BD 系统和 GPS 系统卫星信号&am…

Android 9.0 Launcher3定制化之修改添加的默认文件夹为9宫格样式

1.前言 在9.0的系统产品rom定制化开发中,对于Launcher3的定制功能也是不少的,比如在Launcher3中添加默认文件夹,把默认的app添加的文件夹里面,其他的app 然后按顺序排序。在文件夹布局就是默认的9宫格布局,接下来分析下相关源码来实现功能 2.Launcher3定制化之修改添加的…