java中的反射

news/2025/1/11 14:03:18/

反射

dgerrt5fgvu763fehrtetgh

类加载的过程

	当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化。
加载
加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象(字节码对象),也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象(一个类只可能有一个字节码对象)。
现在 : 对象.getClass() == 类名.class --> 前面的对象是否属于后面的类型以前的写法: 对象 instanceof

类的加载由类加载器(ClassLoader)完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类(类加载器的基类)来创建自己的类加载器[自定义类加载器]。

通过使用不同的类加载器,可以从不同来源加载类的二进制数据(字节码文件),通常有如下几种来源。

  1. 从本地文件系统加载class文件,这是前面绝大部分示例程序的类加载方式。

  2. 从JAR包加载class文件,这种方式也是很常见的,JDBC编程时用到的数据库驱动类就放在JAR文件中,JVM可以从JAR文件中直接加载该class文件。

  3. 通过网络加载class文件。
    把一个Java源文件动态编译,并执行加载。

    类加载器通常无须等到“首次使用”该类时才加载该类,Java虚拟机规范允许系统预先加载某些类。

链接
当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据(字节码对象)合并到JRE中。类连接又可分为如下3个阶段。

1)验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。Java是相对C++语言是安全的语言,例如Java有C++不具有的数组越界的检查。这本身就是对自身安全的一种保护。验证阶段是Java非常重要的一个阶段,它会直接的保证应用是否会被恶意入侵的一道重要的防线,越是严谨的验证机制越安全。验证的目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,不会危害虚拟机自身安全。其主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

四种验证做进一步说明:

文件格式验证:主要验证字节文件是否符合Class文件格式规范,并且能被当前的虚拟机加载处理。例如:主,次版本号是否在当前虚拟机处理的范围之内。常量池中是否有不被支持的常量类型。指向常量的中的索引值是否存在不存在的常量或不符合类型的常量。

元数据验证:对字节码描述的信息进行语义的分析,分析是否符合java的语言语法的规范。

字节码验证:最重要的验证环节,分析数据流和控制,确定语义是合法的,符合逻辑的。主要的针对元数据验证后对方法体的验证。保证类方法在运行时不会有危害出现。

符号引用验证:主要是针对符号引用转换为直接引用的时候,是会延伸到第三解析阶段,主要去确定访问类型等涉及到引用的情况,主要是要保证引用一定会被访问到,不会出现类等无法访问的问题。

2)准备:类准备阶段负责为类的静态变量分配内存,并设置默认初始值(系统默认值)。

**3)解析:**将类的二进制数据[字节码对象]中的符号引用替换成直接引用。说明一下:符号引用:符号引用是以一组符号来描述所引用的目标,符号可以是任何的字面形式的字面量,只要不会出现冲突能够定位到就行。布局和内存无关。直接引用:是指向目标的指针,偏移量或者能够直接定位的句柄。该引用是和内存中的布局有关的,并且一定加载进来的。

初始化
初始化是为类的静态变量赋予正确的初始值(程序员给静态成员变量赋的值),准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的,如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段,给a分配内存,因为变量a是static的,所以此时a等于int类型的默认初始值0,即a=0,然后到解析,到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。
总结:加载: 生成类的字节码对象链接:验证: 校验字节码对象中的成员是否符合Java类规范准备: 加载类中静态成员并赋予系统默认值解析: 把类中的符号引用替换成直接引用初始化: 把类中静态成员的默认值替换成真实值;

fdw3hjyt6sd3edhn

类何时加载

  1. 创建类的实例,也就是new一个对象
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(Class.forName(“全类名”))
  5. 初始化一个类的子类(会首先初始化子类的父类)
  6. JVM启动时标明的启动类,即文件名和类名相同的那个类

类何时被加载: 类第一次被使用的时候加载

类加载器

类加载器负责加载所有的类,其为所有被载入内存中的类生成一个java.lang.Class实例对象。一旦一个类被加载如JVM中,同一个类就不会被再次载入了。
  1. 根类加载器(bootstrap classloader):它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类)。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
  2. 扩展类加载器(extensions classloader):它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。
  3. 系统类加载器(system class loader):被称为系统(也称为应用)类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项、java.class.path系统属性,或者CLASSPATH换将变量所指定的JAR包和类路径。程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。
  4. 自定义类加载器(custom class loader): 我要指定一些类由某个类加载器加载 那么就可以自定义类记载器

类加载器加载Class的8个步骤[了解]

1. 检测此Class是否载入过,即在缓冲区中是否有此Class,如果有直接进入第8步,否则进入第2步。
2. 如果没有父类加载器,则要么Parent是根类加载器,要么本身就是根类加载器,则跳到第4步,如果父类加载器存在,则进入第3步。
3. 请求使用父类加载器去载入目标类,如果载入成功则跳至第8步,否则接着执行第5步。
4. 请求使用根类加载器去载入目标类,如果载入成功则跳至第8步,否则跳至第7步。
5. 当前类加载器尝试寻找Class文件,如果找到则执行第6步,如果找不到则执行第7步。
6. 从文件中载入Class,成功后跳至第8步。
7. 抛出ClassNotFountException异常。
8. 返回对应的java.lang.Class对象。

类加载机制-双亲委派模型

fhjy4rfrhyjugtredfrgty

类加载器使用场景(重要)

类加载器的功能: 快速获取当前类所在模块src文件夹根目录下的文件 [开辟一个相对路径的起点]//IO知识的相对路径起点 : 项目的根//类加载器的相对路径起点 : 当前代码所在模块的src根目录ClassLoader: 类加载器的类(类加载器的基类)   static ClassLoader getSystemClassLoader() : 获取到系统类加载器对象InputStream getResourceAsStream(String fileName): 快速的获取传入文件路径指向的文件String fileName: 文件路径 --> 相对路径写: 相对路径的起点是src文件夹static InputStream getSystemResourceAsStream(String name):直接获取输入流对象 -> 推荐方法  
//示例1public static void main(String[] args) {//目前没有自定义类加载器 -> 最底层的类加载器是 : 系统类加载器ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println("systemClassLoader = " + systemClassLoader);//系统类加载器 找其"父类"加载器 [逻辑继承] -> 拓展类加载器ClassLoader extraClassLoader = systemClassLoader.getParent();System.out.println("extraClassLoader = " + extraClassLoader);//拓展类加载器 找其"父类"加载器 [逻辑继承] -> 根类加载器ClassLoader rootClassLoader = extraClassLoader.getParent();System.out.println("rootClassLoader = " + rootClassLoader);}
//示例2login.propertie :username=adminpassword=123456public static void main(String[] args) throws IOException {//IO : 相对路径起点 项目根目录//BufferedReader br = new BufferedReader(// new FileReader("Test\\src\\com\qh\classloader\login.propertie"));//类加载器 : 相对路径的起点 : 当前模块的src根目录
//        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
//        InputStream is = systemClassLoader
//                .getResourceAsStream("com\qh\classloader\login.propertie");InputStream is = ClassLoader.getSystemResourceAsStream("com\\qh\\classloader\\login.properties");Properties properties = new Properties();properties.load(is);String username = properties.getProperty("username");String password = properties.getProperty("password");Scanner scanner = new Scanner(System.in);System.out.println("请你输入您的账号 : ");String uName = scanner.nextLine();System.out.println("请您输入您的密码 : ");String pwd = scanner.nextLine();if (username.equals(uName) && password.equals(pwd)){System.out.println("登录成功!");}else{System.out.println("登录失败!");}}

反射

反射 : 一种知识体系 [此知识体系在获取类中的成员并使用[构造方法,成员变量/常量,成员方法,注解...]]反射是未来框架的底层源码使用的知识体系;  //框架 : 帮助程序员快捷开发的代码思想层面 : 
以前你是普通工程师 : 直面需求 
未来你是框架工程师 : 直面解决需求的人 代码层面 : //编写代码的手法"正射" : 学生对象调用学习方法(几个小时) -> student.study(8);反射 : 学习方法对象启动传入需要学习的学生对象和学习的时间 -> study方法对象.invoke(student,8);学习反射的目的 : 是为了未来能看懂 框架 的底层源码;

反射使用的步骤

1. 获取类的字节码对象 [因为类的字节码对象中封装着类的成员]
2. 通过字节码对象获取类中的成员 [构造方法(Constructor),成员变量(Field),成员方法(Method)]   
3. 类中成员对象自启动 [构造方法-> 创建对象,成员变量 -> 获取值/赋值.成员方法 -> 启动方法]    

获取类的字节码对象[反射起手式]

1. 对象.getClass() [getClass()来自于Object]
2. 类名.class [class属性 : 每个类型都有的属性(包含数组,包含基本数据类型)] //int[].class int数组的字节码对象, int.class 整型的字节码对象....
3. Class.forName("类的全类名") :   类的全类名-> 包名.类名  
//示例public static void main(String[] args) throws ClassNotFoundException {//获取Student类的字节码对象//1. 对象.getClass()Student1 student1 = new Student1();Class aClass1 = student1.getClass();//2. 类名.classClass aClass2 = Student1.class;//3. Class.forName("全类名")Class aClass3 = Class.forName("com.qh.reflect.Student1");System.out.println("aClass1 = " + aClass1);System.out.println("aClass2 = " + aClass2);System.out.println("aClass3 = " + aClass3);//一个类的字节码对象只可能有一个System.out.println(aClass1 == aClass1);System.out.println(aClass2 == aClass2);System.out.println(aClass3 == aClass3);}

获取类中成员

获取类中的构造方法对象
获取字节码对象中封装的构造方法对象 : //只能获取public修饰的构造方法对象 -> 可以看到父类public修饰的构造方法Constructor<?>[] getConstructors() : 获取类中所有的构造方法对象Constructor<T> getConstructor(Class<?>... parameterTypes) : 获取类中指定的构造方法对象  //任何权限修饰符的构造方法对象都能获取 -> 弊端 : 不能获取父类的构造方法Constructor<?>[] getDeclaredConstructors()  Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Class<?>... parameterTypes : 方法形参类型的字节码对象
获取类中的成员变量对象
获取字节码对象中封装的成员变量对象 :Field[] getFields()  Field getField(String name)  //推荐Field[] getDeclaredFields()  //String name : 要获取的成员变量的变量名    Field getDeclaredField(String name)      
获取类中的成员变量对象
获取字节码对象中封装的成员变量对象 :Field[] getFields()  Field getField(String name)  //推荐Field[] getDeclaredFields()  //String name : 要获取的成员变量的变量名    Field getDeclaredField(String name)      

获取类中的成员方法对象

获取字节码对象中封装的成员方法对象 :Method[] getMethods()  Method getMethod(String name)  //推荐Method[] getDeclaredFields()   //String name : 要获取的成员变量的变量名    Method getDeclaredMethod(String name,<?>... parameterTypes)

使用类中成员

如果该成员是没有访问权限的,虽然Declared系列方法能够获取此成员对象,但是真正使用时需要开启暴力反射开关!

void setAccessible(boolean flag) : 开启暴力反射开关 //只需要在使用没有权限的成员之前,开启暴力反射开关即可

使用类中构造方法对象[创建对象]
 T newInstance(Object... initargs)  : 使用构造方法对象创建对象返回值T : 得到的对象Object... initargs :  此构造方法对象启动需要的实际参数 [形参和实参匹配]   //在Class类中有一个快捷创建此类对象的方法 [启动的是类中的无参构造(使用前提是: 类中必须有无参构造)]//Class 类中的方法 : T newInstance()Student student = clazz.newInstance();System.out.println("student = " + student);         
使用类中成员变量对象[设置值和获取值]
获取值的方法 :Object get(Object obj)  返回值 Object : 拿到的成员变量的值形参 Object obj : 你要获取的是哪个对象的该属性值    设置值的方法 :void set(Object obj, Object value) 形参 Object obj : 你要设置的是哪个对象的该属性值  形参 Object value : 你要设置的值    
//示例1:构造方法public class Student1 {private String name;int age;public String address;protected String sex;public Student1() {System.out.println("无参构造启动了");}private Student1(String name) {System.out.println("private-"+name);}Student1(String name, int age){System.out.println("默认-"+name+"-"+age);}public Student1(String name, int age, String address) {System.out.println("public-"+name+"-"+age+"-"+address);}protected Student1(int age, String sex){System.out.println("protected-"+age+"-"+sex);}}public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {Class<Student1> student1Class = Student1.class;// 返回一个包含 Constructor对象的数组,反映由该Class对象表示的类的所有 [公共] 构造函数Constructor<?>[] constructors = student1Class.getConstructors();for (Constructor constructor : constructors) {System.out.println("constructor = " + constructor);}System.out.println("-------------");// 返回由该 Class对象表示的类声明的 [所有] 构造函数的 Constructor对象的数组Constructor[] declaredConstructors = student1Class.getDeclaredConstructors();for (Constructor declaredConstructor : declaredConstructors) {System.out.println("declaredConstructor = " + declaredConstructor);}System.out.println("-------------");// 返回一个 Constructor对象,该对象反映由该Class对象表示的类的指定 [公共] 构造函数// 参数:要获取的构造方法的参数的个数及数据类型对应的字节码文件对象Constructor<Student1> declaredConstructor1 = student1Class.getDeclaredConstructor();System.out.println("declaredConstructor1 = " + declaredConstructor1);Constructor<Student1> declaredConstructor2 = student1Class.getDeclaredConstructor(String.class);System.out.println("declaredConstructor2 = " + declaredConstructor2);Constructor<Student1> declaredConstructor3 = student1Class.getDeclaredConstructor(String.class,int.class);System.out.println("declaredConstructor3 = " + declaredConstructor3);Constructor<Student1> declaredConstructor4 = student1Class.getDeclaredConstructor(String.class,int.class,String.class);System.out.println("declaredConstructor4 = " + declaredConstructor4);Constructor<Student1> declaredConstructor5 = student1Class.getDeclaredConstructor(int.class,String.class);System.out.println("declaredConstructor5 = " + declaredConstructor5);System.out.println("-------------");//在Class类中有一个快捷创建此类对象的方法 [启动的是类中的无参构造(使用前提是: 类中必须有无参构造)]//Class 类中的方法 : T newInstance()Student1 student10 = student1Class.newInstance();System.out.println("student0 = " + student10);//根据构造方法对象获取此类的对象Student1 student0 = declaredConstructor1.newInstance();System.out.println("student0 = " + student0);declaredConstructor2.setAccessible(true);//开启暴力反射开关Student1 student2 = declaredConstructor2.newInstance("张三");//没有访问权限,不能直接访问privateSystem.out.println("student2 = " + student2);Student1 student3 = declaredConstructor3.newInstance("李四", 23);System.out.println("student3 = " + student3);Student1 student4 = declaredConstructor4.newInstance("王五", 24, "上海");System.out.println("student4 = " + student4);Student1 student5 = declaredConstructor5.newInstance( 24, "男");System.out.println("student5 = " + student5);System.out.println("-------------");}
//示例2:成员变量public class Student2 {private String name;int age;public String address;protected String sex;}public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {Student2 Student2 = new Student2();Class student2Class = Student2.getClass();返回一个包含Field对象的数组,Field对象反映由该Class对象表示的类或接口的所有可访问的 [公共] 的成员变量Field[] fields = student2Class.getFields();for (Field field : fields) {System.out.println("field = " + field);}System.out.println("----------------");//返回一个包含Field对象的数组,Field对象反映由该Class对象表示的类或接口的 [所有] 成员变量Field[] declaredFields = student2Class.getDeclaredFields();for (Field declaredField : declaredFields) {System.out.println("declaredField = " + declaredField);}System.out.println("----------------");//获取单个类中成员变量对象Field name = student2Class.getDeclaredField("name");name.setAccessible(true);Field age = student2Class.getDeclaredField("age");Field address = student2Class.getDeclaredField("address");Field sex = student2Class.getDeclaredField("sex");System.out.println("name = " + name);System.out.println("age = " + age);System.out.println("address = " + address);System.out.println("sex = " + sex);System.out.println("----------------");//赋值name.setAccessible(true);//开启暴力反射开关name.set(Student2,"张三");age.set(Student2,23);address.set(Student2,"上海");sex.set(Student2,"男");//获取值Object nameValue = name.get(Student2);Object ageValue = age.get(Student2);Object addressValue = address.get(Student2);Object sexValue = sex.get(Student2);System.out.println("nameValue = " + nameValue);System.out.println("ageValue = " + ageValue);System.out.println("addressValue = " + addressValue);System.out.println("sexValue = " + sexValue);}
//示例3:成员方法public class Student3 {public void S1(){System.out.println("public");}protected void S2(String name){System.out.println("protected-"+name);}String S3(){System.out.println("默认");return "默认";}private void S4(int age){System.out.println("private-"+age);}}public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Student3 student3 = new Student3();Class<? extends Student3> student3Class = student3.getClass();//返回一个包含方法对象的数组,方法对象反映由该Class对象表示的类或接口的所有 公共方法//包括由类或接口声明的对象以及从超类和超级接口继承的类Method[] methods = student3Class.getMethods();for (Method method : methods) {System.out.println("method = " + method);}System.out.println("-----------------------");//返回一个包含方法对象的数组,方法对象反映由该Class对象表示的类或接口的所有声明方法//包括:public\protected\default(package)访问和私有方法,但不包括继承方法Method[] declaredMethods = student3Class.getDeclaredMethods();for (Method declaredMethod : declaredMethods) {System.out.println("declaredMethod = " + declaredMethod);}System.out.println("-----------------------");//单独获取成员方法对象Method s1 = student3Class.getDeclaredMethod("S1");Method s2 = student3Class.getDeclaredMethod("S2",String.class);Method s3 = student3Class.getDeclaredMethod("S3");Method s4 = student3Class.getDeclaredMethod("S4",int.class);System.out.println("s1 = " + s1);System.out.println("s2 = " + s2);System.out.println("s3 = " + s3);System.out.println("s4 = " + s4);System.out.println("-----------------------");System.out.println("s1.invoke(student3) = " + s1.invoke(student3));System.out.println("s2.invoke(student3,\"张三\") = " + s2.invoke(student3, "张三"));System.out.println("s3.invoke(student3) = " + s3.invoke(student3));s4.setAccessible(true);//开启暴力反射开关System.out.println("s4.invoke(student3,23) = " + s4.invoke(student3, 23));}
//示例4:info. :className=com.qh.reflect.TestmethodName=showpublic class Test {public void show(){System.out.println("show()方法执行了");}public void method(){System.out.println("method()方法执行了");}}public class TestDemo {public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {//获取配置文件中的信息InputStream systemResourceAsStream = ClassLoader.getSystemResourceAsStream("com\\qh\\reflect\\info.properties");//一键加载到属性集里Properties properties = new Properties();properties.load(systemResourceAsStream);//获取信息String className = properties.getProperty("className");String methodName = properties.getProperty("methodName");systemResourceAsStream.close();//反射代码//获取类的字节码对象Class classForName = Class.forName(className);//创建此类对象Object name = classForName.newInstance();//获取类中成员方法对象Method declaredMethod = classForName.getDeclaredMethod(methodName);//启动方法declaredMethod.invoke(name);}
}

http://www.ppmy.cn/news/6216.html

相关文章

从 2022 年优秀 Linux 发行版中挑选你喜欢的版本

导读如果你想从 2022 年最佳 Linux 发行版列表中挑选一个最喜欢的版本&#xff0c;那么今天你需要考虑以下几个选项。 2022 年是充满惊喜的一年&#xff0c;Linux 发行版的表现也不例外。从充满功能的新版本到各种桌面选项&#xff0c;总有一些值得期待的东西。 如果你想从 20…

Promise对象的使用

一、什么是Promise Promise 是异步编程的一种解决方案&#xff0c;比传统的解决方案&#xff08;回调函数和事件&#xff09;更合理和更强大。从语法上说&#xff0c;Promise 是一个对象&#xff0c;从它可以获取异步操作的消息。Promise 提供统一的 API&#xff0c;各种异步操…

让人恶心的多线程代码,性能怎么优化?

Java 中最烦人的&#xff0c;就是多线程&#xff0c;一不小心&#xff0c;代码写的比单线程还慢&#xff0c;这就让人非常尴尬。 通常情况下&#xff0c;我们会使用 ThreadLocal 实现线程封闭&#xff0c;比如避免 SimpleDateFormat 在并发环境下所引起的一些不一致情况。其实…

小程序02/小程序 页面生命周期钩子函数、组件生命周期钩子函数

一. 页面生命周期钩子函数 onLoad 监听页面加载 onShow 监听页面显示 onReady 监听页面初次渲染完成 onHide 监听页面隐藏 onUnload 监听页面清除消失 onPullDownRefresh 监听用户…

Redis Lua拓展及使用示例

本文主要介绍 Lua 脚本的安全性问题、以及解决这些问题的方法进行说明&#xff0c; 及对执行 Lua 脚本EVAL的实现原理进行介绍&#xff0c;最后还有Lua的相关示例。 Lua 脚本功能是 Reids 2.6 版本的最大亮点&#xff0c; 通过内嵌对 Lua 环境的支持&#xff0c; Redis 解决了…

Java 8 Stream 从入门到进阶——像SQL一样玩转集合

0.阅读完本文你将会 了解Stream的定义和它的特征了解Stream的基础和高阶用法 1. 前言 在我们日常使用Java的过程中&#xff0c;免不了要和集合打交道。对于集合的各种操作有点类似于SQL——增删改查以及聚合操作&#xff0c;但是其方便性却不如SQL。 所以有没有这样一种方式…

高可用架构和系统设计经验

导语 | 本文从研发规范层面、应用服务层面、存储层面、产品层面、运维部署层面、异常应急层面这六大层面去剖析一个高可用架构和系统需要有哪些关键的设计和考虑。 一、高可用系统架构设计思想 1-1、可用性和高可用概念 可用性是一个可以量化的指标&#xff0c;计算的公式在维…

学习记录-mybatis+vue+elementUi实现分页查询(前端部分)

前端这一块最方便的莫过于是element已经提供好了 接口&#xff0c;三个最关键的接口这里首先解决第一个&#xff0c;总数。 //总记录数totalCount:100,我直接在data中将其先初始化为100&#xff0c;之后直接在响应中设置&#xff0c;这是从后端查询到的值&#xff0c;不需要任何…