浅析Java反射机制及其应用

引用:

作者:老玩童周伯通 链接:https://www.zhihu.com/question/24304289/answer/147529485

作者:sczyh30 链接:https://www.sczyh30.com/posts/Java/java-reflection-1/

作者:陈树义 链接:https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html

0. 为什么出现了反射?

Background:Java通过JVM对类进行编译,加载,然后被类加载器加载进JVM的内存中,类Object加载到方法区中,创建了Object类的Class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。JVM创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。

上述过程的程序对象是自己new的,程序相当于已经制定好了交给JVM运行,但假如服务器上某个请求要用到某个类,但没加载进JVM,此时就需要引入反射机制,使得程序在运行时动态的加载一些类。这些类可能之前用不到所以不用加载到JVM,而是在运行时根据需要才加载。

应用举例

  • 有多个数据库需要连接的情况下:项目底层有时是用Mysql,有时用Oracle,需要动态地根据实际情况加载驱动类,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化,通过Class tc = Class.forName(“com.java.dbtest.TestConnection”);通过类的全类名让JVM在服务器中找到并加载这个类,而如果是Oracle则传入的参数就变成另一个了。
  • Spring中Bean管理:Spring中配置各种各样的bean时,是以配置文件的形式配置的,需要用到哪些bean就配哪些,Spring容器就会根据你的需求去动态加载,使得程序能健壮地运行。增加了程序的灵活性。例如: 实例化一个 person()对象, 不使用反射,需要new person(); 如果想变成实例化其他类,那么必须修改源代码,并重新编译。使用反射: class.forName(“person”).newInstance(); 而且这个类描述可以写到配置文件中,如 .xml, 这样如果想实例化其他类,只要修改配置文件的"类描述"就可以了,不需要重新修改代码并编译。

核心作用:在运行时动态加载类。

1. 反射是什么?

1.1 定义

反射 (Reflection) 是 Java 的特征之一,它允许运行时的 Java 程序获得程序中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

1.2 特性

Java 反射主要提供以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
  • 在运行时调用任意一个对象的方法

重点:运行时

2. 反射怎么用?

2.1应用场景

反射最重要的用途就是开发各种通用框架。很多框架(比如 Spring)都是配置化的(比如通过 XML 文件配置 Bean),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要加载的对象。

举一个例子,在运用 Struts 2 框架的开发中我们一般会在 struts.xml 里去配置 Action,比如:

<action name="login"
               class="org.ScZyhSoft.test.action.SimpleLoginAction"
               method="execute">
           <result>/shop/shop-index.jsp</result>
           <result name="error">login.jsp</result>
  </action>

配置文件与 Action 建立了一种映射关系,当 View 层发出请求时,请求会被 StrutsPrepareAndExecuteFilter 拦截,然后 StrutsPrepareAndExecuteFilter 会去动态地创建 Action 实例。比如我们请求 login.action,那么 StrutsPrepareAndExecuteFilter就会去解析struts.xml文件,检索action中name为login的Action,并根据class属性创建SimpleLoginAction实例,并用invoke方法来调用execute方法,这个过程离不开反射。

对与框架开发人员来说,反射虽小但作用非常大,它是各种容器实现的核心。

2.2 反射的基本应用

2.2.1 获取反射中的Class对象

在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象。

在 Java API 中,获取 Class 类对象有三种方法:

第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

Class clz = Class.forName("java.lang.String");

第二种,使用 .class 方法。

这种方法只适合在编译前就知道操作的 Class。

Class clz = String.class;

第三种,使用类对象的 getClass() 方法。

String str = new String("Hello");
Class clz = str.getClass();
2.2.2 通过反射创建类对象

通过反射创建类对象主要有两种方式:通过 Class 对象的 newInstance() 方法、通过 Constructor 对象的 newInstance() 方法。

第一种:通过 Class 对象的 newInstance() 方法。

Class clz = Apple.class;
Apple apple = (Apple)clz.newInstance();

第二种:通过 Constructor 对象的 newInstance() 方法

Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();

通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。

Class clz = Apple.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
Apple apple = (Apple)constructor.newInstance("红富士", 15);
2.2.3 通过反射获取类属性、方法、构造器

我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。

Class clz = Apple.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

输出结果是:

price

而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

输出结果是:

name
price

与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。

3. 反射总结

  • 什么是反射?
  1. 反射机制使得程序在运行时期能够获取自身信息,只要给定类的名字,就可以通过反射机制获取类的所有信息。
  2. 动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
  • 反射有什么特点?
  1. 在运行中判断任意一个对象所属的类;
  2. 在运行时构造任意一个类的对象;
  3. 在运行中判断任意一个类所具有的成员变量和方法;
  4. 在运行时调用任意一个对象的方法;
  • 反射机制的应用?
  1. JDBC中,利用反射动态加载了数据库驱动程序。
  2. J2EE框架都用到反射机制,注入属性,调用方法,如Spring。
  3. Web服务器中利用反射调用了Sevlet的服务方法。
  4. Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
  • 反射的优缺点?
    优点:反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
    缺点:1)性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。因此反射机制主要应用在对灵活性和扩展性要求很高的系统框架上,普通程序不建议使用;
       2)使用反射会模糊程序内内部逻辑:程序员希望在源代码中看到程序的逻辑,反射等绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值