反射 Reflection

2024-03-20 09:52
文章标签 反射 reflection

本文主要是介绍反射 Reflection,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

反射

反射的概念

  1. 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
  2. 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射

image.png

反射机制

Java反射机制可以完成

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时得到任意一个类所具有的成员变量和方法
  4. 在运行时调用任意一个对象的成员变量和方法
  5. 生成动态代理

反射相关的主要类:

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

一般使用:对象.方法 反射使用:方法.对象
一般使用:对象.变量 反射使用:变量.对象

反射优点和缺点

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

反射调用优化-关闭访问检查

Method和Field、Constructor对象都有setAccessible()方法
setAccessible作用是启动和禁用访问安全检查的开关。(默认为false)

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

Class类

  1. Class也是类,因此也继承Object类

image.png

  1. Class类对象不是new出来的,而是系统创建的
  2. 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次(以hashcode区别)(使用synchronized同步)
  3. 每个类的实例都会记得自己是由哪个 Class 实例所生成
  4. 通过Class对象可以完整地得到一个类的完整结构,通过一系列API
  5. Class对象是存放在堆的
  6. 类的字节码二进制数据,是放在方法区的, 有的地方称为类的元数据(包括 方法代码变量名,方法名,访问权限等等)https://www.zhihu.com/question/38496907

Class类的常用方法:

java.lang.Class

  1. getName:获取全类名
  2. getSimpleName:获取简单类名
  3. getFields:获取所有public修饰的属性,包含本类以及父类的
  4. getDeclaredFields:获取本类中所有属性
  5. getMethods:获取所有public修饰的方法,包含本类以及父类的
  6. getDeclaredMethods:获取本类中所有方法
  7. getConstructors: 获取本类所有public修饰的构造器
  8. getDeclaredConstructors:获取本类中所有构造器
  9. getPackage:以Package形式返回 包信息
  10. getsuperClass:以Class形式返回父类信息
  11. getlnterfaces:以Class[]形式返回接口信息
  12. getAnnotations:以Annotation[] 形式返回注解信息

获取Class对象的几种方式

  1. 代码阶段:Class.forName()

前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,
例:Class clazz = Class.forName( "java.lang.Cat”);
应用场景:多用于配置文件,读取类全路径,加载类

  1. Class类阶段:类.class

前提:若已知具体的类,通过类的class 获取,该方式最为安全可靠,程序性能最高
应用场景:多用于参数传递,比如通过反射得到对应构造器对象

  1. Runtime阶段:对象.getClass()

前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,
例:Class clazz = object.qetClass();
应用场景:通过创建好的对象,获取Class对象

  1. 其他方式(类加载器):

ClassLoader classLoader = object.getClass().getClassLoader();
Class clazz = classLoader.loadClass("类的全类名");

  1. 基本数据(int, char,boolean,float,double,byte,long,short) 按如下方式得到Class类对象

Class clazz = 基本数据类型.class;

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

Class clazz = 包装类.TYPE;

以下类型有Class对象

  1. 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
  2. interface:接口
  3. 数组
  4. enum:枚举
  5. annotation:注解
  6. 最本基本数据类型
  7. void

类加载

基本说明

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。

  1. 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
  2. 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性

类加载时机

  1. 创建类的实例:属于静态加载。当使用 new 关键字创建一个类的实例时,该类会被加载、连接和初始化,这是在编译期就确定需要加载的类。
  2. 访问类的静态变量:属于静态加载。当访问一个类的静态变量(static field)时,如果该类还没有加载,则会触发类的加载、连接和初始化,这也是在编译期就确定需要加载的类。
  3. 调用类的静态方法:属于静态加载。当调用一个类的静态方法时,如果该类还没有加载,则会触发类的加载、连接和初始化,同样是在编译期确定需要加载的类。
  4. 访问类的静态字段的值:属于静态加载。当访问一个类的静态字段的值(static final field)时,不会触发类的初始化,因为这些字段在编译期就会被赋值。
  5. 使用反射:属于动态加载。通过反射机制来创建类的实例、访问类的静态变量或调用类的静态方法时,是在程序运行时根据需要动态加载类。

类加载过程

image.png
类加载各阶段完成任务
image.png
前两个阶段在JVM中

加载阶段(Loading)

加载阶段是指查找并加载类的字节码文件(.class 文件),一般是通过类加载器(ClassLoader)来完成的。类加载器根据类的全限定名在类路径中查找对应的字节码文件,并将其加载到内存中。

链接阶段(Linking)-验证

验证确保被加载的类符合 Java 虚拟机规范,比如检查字节码的格式、语义等。
目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
包括:文件格式验证(是否以魔数 oxçafebabe开头)、元数据验证、字节码验证和符号引用验证。
可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间

链接阶段(Linking)-准备

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

链接阶段(Linking)-解析

虚拟机将常量池内的符号引用转换为直接引用,比如将类、方法、字段等的符号引用解析为实际内存地址

初始化(Initialization)

到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行()方法的过程。
()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。类初始化是按需进行的,只有在首次主动使用类时才会触发。
虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕

通过反射获取类的结构

通过反射创建对象

  1. 方式一:调用类中的public修饰的无参构造器
  2. 方式二:调用类中的指定构造器
  3. Class类相关方法
    1. newlnstance:调用类中的无参构造器,获取对应类的对象
    2. getConstructor(Class…clazz):根据参数列表,获取对应的构造器对象
    3. getDecalaredConstructor(Class…clazz):根据参数列表,获取对应的构造器对象
  4. Constructor类相关方法
    1. setAccessible():参数使用true:暴破,使用反射可以访问私有(private)构造器/属性/方法
    2. newlnstance(Object…obj):调用构造器
package com.gg
class abc{puiblic String name ;abc(String name){this.name = name ;}
}
Class<?> clazz = Class.forName("com.gg.abc");
Object o = clazz.newInstance(); // 调用无参构造 o的运行对象类型为abc类Constructor<?>  c = clazz.getDecalaredConstructor(String.class);//调用有参构造
Object o1 = c.newInstance("gggg"); // o1的运行对象类型为abc类

通过反射访问类中的成员

  1. 根据方法名和参数列表获取Method方法对象:Method m =clazz.getDeclaredMethod(方法名,XX.class);
  2. 获取对象:Object o = clazz.newInstance();
  3. 暴破:m.setAccessible(true);
  4. 访问:Object returnValue = m.invoke(o,实参列表); //o表示对象
  5. 注意:如果是静态方法,则invoke的参数o,可以写成null
  6. 如果方法有返回值,统一返回Object,但其运行类型依旧是方法中定义的返回类型
package com.gg
class abc{puiblic String name ;abc(String name){this.name = name ;}public void aabc(String name){System.out.println(name + " " + this.name);}
}
// 获取Class对象
Class<?> clazz = Class.forName("com.gg.abc");
//调用有参构造
Constructor<?>  c = clazz.getDecalaredConstructor(String.class);
// o1的运行对象类型为abc类
Object o1 = c.newInstance("gggg"); 
// 获取aabc方法对象
Method m = clazz.getDeclaredMethod("aabc",String.class);
// 反射调用aabc方法
Object res = m.invoke(o1,"aaaa");
// 输出 "aaaa gggg"

通过反射访问类中的属性

  1. 根据属性名获取Field对象Field f = clazz.getDeclaredField(属性名);
  2. 暴破:
    1. f.setAccessible(true); // f 是Field
  3. 访问
    1. f.set(o,值);// o表示对象
    2. f.get(o); //o表示对象
  4. 注意:如果是静态属性,则set和get中的参数o,可以写成null
package com.gg
class abc{puiblic String name ;abc(String name){this.name = name ;}public void aabc(String name){System.out.println(name + " " + this.name);}
}
// 获取Class对象
Class<?> clazz = Class.forName("com.gg.abc");
//调用有参构造
Constructor<?>  c = clazz.getDecalaredConstructor(String.class);
// o1的运行对象类型为abc类
Object o1 = c.newInstance("gggg"); //此时name="gggg"
// 获取name属性对象
Field f = clazz.getDeclaredField("name");
// 设置name属性的值
f.set(o1,"123");// 此时name="123"
// 获取并输出name属性值
System.out.println(f.get(o1));
// 输出 "123"

Field类常用方法

java.lang.reflect.Field

  1. getModifiers:以int形式返回修饰符、
    1. [说明:默认修饰符 是0, public 是1,private 是2,protected 是 4,static是8,final是16]
    2. public static = public(1) + static(8) = 1 + 8 = 9
  2. getType:以Class形式返回类型
  3. getName:返回属性名

Method类常用方法

java.lang.reflect.Method

  1. getModifiers:以int形式返回修饰符
    1. [说明:默认修饰符 是0, public 是1,private 是2,protected 是 4,static是8,final是16]
  2. getReturnType:以Class形式获取 返回类型
  3. getName:返回方法名
  4. getParameterTypes:以Class[ ]返回参数类型数组

Constructor类常用方法

java.lang.reflect.Constructor

  1. getModifiers:以int形式返回修饰符
  2. getName:返回构造器名(全类名)
  3. getParameterTypes:以Class[ ]返回参数类型数组

这篇关于反射 Reflection的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/829082

相关文章

【反射知识点详解】

Java中的反射(Reflection)是一个非常强大的机制,它允许程序在运行时检查或修改类的行为。这种能力主要通过java.lang.reflect包中的类和接口来实现。 通过反射,Java程序可以动态地创建对象、调用方法、访问字段,以及获取类的各种信息(如构造器、方法、字段等)。 反射的用途 反射主要用于以下几种情况: 动态创建对象:通过类的Class对象动态地创建其实例。访问类的字段

Go 在orm中使用反射

作为静态语言,golang 稍显笨拙,还好 go 的标准包reflect(反射)包弥补了这点不足,它提供了一系列强大的 API,能够根据执行过程中对象的类型来改变程序控制流。本文将通过设计并实现一个简易的 mysql orm 来学习它,要求读者了解mysql基本知识,并且跟我一样至少已经接触 golang 两到三个月。 orm 这个概念相信同学们都非常熟悉,尤其是写过rails的同学,对acti

类型信息:反射-Class

在说反射前提一个概念:RTTI(在运行时,识别一个对象的类型) public class Shapes {public static void main(String[] args) {List<Shape> shapes = Arrays.asList(new Circle(), new Square(), new Triangle());for (Shape shape : shapes

单例模式以及反射对单例模式的破坏及防御

单例模式(Singleton Pattern)是一种确保类在应用程序生命周期内只存在一个实例的设计模式。它不仅提供了全局访问点,还能节省内存、控制实例的生命周期。但常见的单例模式实现方式如饿汉式、懒汉式、双重校验锁、静态内部类等,虽然设计良好,但都容易被 Java 的反射机制所破坏。本文将介绍这些单例实现方式的优缺点、反射如何破坏它们的唯一性,以及如何防御这种破坏。 1. 单例模式的常见实现

servlet用反射代替if..else

String methodName = request.getParameter("method"); Method method = this.getClass().getDeclaredMethod(methodName,HttpServletRequest.class, HttpServletResponse.class); method.invoke(this, request, re

Redis缓存 自定义注解+aspect+反射技术实现

最近再给云随笔后台增加redis模块,突然发现spring-boot-starter-data-redis模块很不人性化,实现不了通用的方式,(当然,你也可以自己写个通用的CacheUtil来实现通用的方式),但由于本人非常的爱装逼,就在这里不讲解那种傻瓜式操作了,这里只讲干货,干到你不可置信的干货). 例如:这里我使用了它其中的RedisTemplate ,发现存到redis中后,数据

JAVA反射使用父类的非public方法(getMethods()和getDeclaredMethods()区别)

getMethods()和getDeclaredMethods()区别 虽然是老生常谈了,但是还是要先说一下两者的区别。 getMethods():能够获取类的所有public方法,包括自身定义的以及从父类继承的。 getDeclaredMethods():能够获取类本身的所有方法,包括private方法,实现的接口方法,但是不能获取从父类继承的非public方法。 因此getDeclaredM

Java反射机制讲解

Java反射机制详解 Java反射机制是Java语言的一个强大特性,它允许运行中的Java程序对自身进行检查并且可以直接操作程序的内部属性。这意味着可以动态地创建对象、调用方法、访问字段等。 1. 反射的基本概念 反射主要是指运行时能够“反观”自己,并且可以直接操作对象的结构(类、字段、方法等)。Java反射的主要功能包括: 获取Class对象创建对象实例获取和设置字段值调用方法获取构造器

反射: 获取变量类型

更高级的编程语言,提供反射、解释机制,获取对象类型非常方便,因为运行时保存有对象的全部信息,也包括类型,而对于编译型语言而言,变量类型要靠编译期或构造/依赖类型某个存储类型的结构。 不同语言的反射 C++ typeid编译器编译时可以确定某些无多态的变量对象类型,无需额外结构辅助。对于有多态行为的类对象,编译器利用vtable内指向对应类型的type_info, 运行期可通过vtable获

mybatis特殊符号处理,mybatis一级二级缓存,java反射机制

mybatis特殊符号处理 在 mybatis 中的 xml 文件中,存在一些特殊的符号,比如:<、>、"、&、<>等,正常书写 mybatis 会报错,需要对这些符号进行转义。具体转义如下所示: 特殊字符 转义字符 <  &lt; >  &gt; "  &quot; ’  &apos; &  &amp; 除此之外,还可以使用印射文件特殊处理,因为sql印射文件时xml类型文件,在转义上面有些