Java基础 | 关于反射的一些了解

反射

什么是反射

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

反射能做什么

我们知道反射机制允许程序在运行时(指的是编译之后的.class文件执行的时候)取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!

那又是怎么在运行时取得已知名称的class的内部信息?

Java中的每一个类都有一个对应的Class对象,每个类的Class对象是在类被加载到JVM时创建的,应该是内存。

Class对象(Class对象代表了一个类的元数据,包括了该类的结构信息。),元数据(Metadata)是描述数据的数据,它提供了有关数据的信息,帮助人们理解、管理和使用数据。在计算机科学和信息技术领域,元数据通常用于描述各种类型的数据和资源,以及它们的属性、结构和关系。

这个Class对象比较重要,可以说是反射的入口

获取 Class 类通常有以下三种方式
  1. 对象.getClass()

    通过对象的getClass()方法获取Class类,说明对象已经创建好了(new对象的同时进行类加载,使用在这里就会创建Class对象),其实已经有Class类了。

  2. 类.class

    这种方式获取Class类,需要提前知道类的名称,也就是项目中已经导入了相应的包,依赖性强。

    使用 .class 语法获取的 Class 对象是在编译时期已经加入到 JVM 中的类。在编译 Java 程序时,如果使用 .class 语法获取某个类的 Class 对象,编译器会将其转换为对应的 Class 引用,并将该引用添加到生成的字节码中。
    这意味着在编译后的字节码中,Class 对象的引用被硬编码进去了,因此在运行时加载该类时,可以直接通过这个引用获取到 Class 对象。

  3. Class.forName()

    只需要传入一个类的完全限定名即可。

    当使用 Class.forName() 方法获取对应类的Class对象时,它会首先尝试查找是否已经加载了这个类。如果该类已经加载,它会返回内存中已存在的Class对象。但如果该类还没有被加载,Class.forName() 方法会触发类加载器去查找、加载并初始化这个类,然后返回对应的Class对象。

推荐使用 Class.forName() 的方式获取Class类。是反射常用到的API

类的加载过程包括以下步骤:
  1. 加载(Loading):通过指定的类加载器从文件系统、网络等位置读取类的字节码,并创建对应的Class对象。

  2. 链接(Linking):将类的二进制数据合并到JVM的运行时环境中,包括验证类的格式、解析符号引用等过程。

  3. 初始化(Initialization):为类的静态变量赋予初始值,并执行其他初始化代码,包括静态代码块和静态方法。

Class 的常见方法

getName():获得类的完整名字。
  getFields():获得类的public类型的属性。
  getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
  getMethods():获得类的public类型的方法。
  getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
  getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
  getConstructors():获得类的public类型的构造方法。
  getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
  newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

//获得类完整的名字
String className = c2.getName();
System.out.println(className);//输出com.ys.reflex.Person

//获得类的public类型的属性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());//age
}

//获得类的所有属性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
    System.out.println(field.getName());//name    age
}

//获得类的public类型的方法。这里包括 Object 类的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
    System.out.println(method.getName());//work waid equls toString hashCode等
}

//获得类的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
    System.out.println(method.getName());//work say
}

//获得指定的属性
Field f1 = c2.getField("age");
System.out.println(f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("name");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println(f2);

//创建这个类的一个对象
Object p2 =  c2.newInstance();
//将 p2 对象的  f2 属性赋值为 Bob,f2 属性即为 私有属性 name
f2.set(p2,"Bob");
//使用反射机制可以打破封装性,导致了java对象的属性不安全。
System.out.println(f2.get(p2)); //Bob

//获取构造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
    System.out.println(constructor.toString());//public com.ys.reflex.Person()
}
反射在 Spring 中的应用举例

反射在众多框架中都有普遍的应用。比如 Spring IOC 容器帮我们实例化众多的bean,下面我们简单模拟一下 反射 在其中起到的作用。

Spring 配置文件:
<bean id="pony" class="com.xblzer.dp.proxy.springaop.Pony"></bean>
使用的时候直接这样就能拿到定义的类了:
ApplicationContext ctx = new ClassPathXmlApplicationContext("app_aop.xml");
Pony pony = (Pony) ctx.getBean("pony");
那么是怎么做到的呢?就是通过 反射 。Spring 通过配置文件实例化对象,并将其放到容器的过程大概就是(模拟):
//伪代码
//1.解析<bean .../>元素的id属性得到该字符串值为“pony”  
String idStr = "pony";  
//解析<bean .../>元素的class属性得到该字符串值为“com.xblzer.dp.proxy.springaop.Pony”  
String classStr = "com.xblzer.dp.proxy.springaop.Pony";  
//利用反射机制,通过classStr获取Class类对象  
Class<?> cls = Class.forName(classStr);  
//实例化对象  
Object obj = cls.newInstance();  
//放到Spring容器  
Map<String, Object> container = new HashMap<>();
container.put(idStr, obj); 
反射总结

灵活使用反射能让我们代码更加灵活,这里比如JDBC原生代码注册驱动,hibernate 的实体类,Spring 的 AOP等等都有反射的实现。但是凡事都有两面性,反射也会消耗系统的性能,增加复杂性等,合理使用才是真!

多看一下 Class 类的API,诸多框架都用到了反射机制,而反射离不开调用这些基本的API。
JavaSE 8 API官网:docs.oracle.com/javase/8/do…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值