看了好多关于Java反射机制的文章,大多都太过官方,消化起来比较稍显费劲,本篇,我会依据自己的理解去阐述什么是Java的反射机制,反射用在什么地方,以及怎么来使用?

开篇前,我们还是要了解一下,什么是Java的反射机制:

“程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言”。从这个观点看,Perl、Python(看过我写的Python3学习系列的博文,不止一次突出Python动态语言的特点)、Ruby是动态语言,C++、Java、C#不是动态语言。但是JAVA有着一个非常突出的动态相关机制——Reflection(反射),用在Java身上指的是可以于运行时加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,获悉其完整构造(但不包括methods定义),并生成其对象实体(newInstance)或对其fields设值,或唤起(invoke)其methods方法。

注意 方法的声明和定义不是一回事,
声明:public string Method(string parm1,int param2,…)
定义:public string Method(string parm1,int param2,…)
          {
                // do something
          }

反射用在什么地方?

由于,我们还不清楚反射究竟是什么玩意,怎么用,是不是我们平时写代码的时候会用得上? 这些,都不知道的话,我们也没法定论,这个Java反射机制,用在什么地方比较和合适(注意,一项技术的诞生,一定是为了方便另一项技术的使用,否则会失去本身存在的意义!)

因此,我们先来说一下,反射怎么用?

一、反射的应用

我们可能听过,Java编写的程序,一次编译,到处运行。这也是Java程序为什么是无关平台的所在,原因在于,java的源代码会被编译成.class文件字节码,只要装有Java虚拟机JVM的地方(Java提供了各种不同平台上的虚拟机制,第一步由Java IDE进行源代码编译,得到相应类的字节码.class文件,第二步,Java字节码由JVM执行解释给目标计算机,第三步,目标计算机将结果呈现给我们计算机用户;因此,Java并不是编译机制,而是解释机制),.class文件畅通无阻。

Java的反射机制,操作的就是这个.class文件,首先加载相应类的字节码(运行eclipse的时候,.class文件的字节码会加载到内存中),随后解剖(反射 reflect)出字节码中的构造函数、方法以及变量(字段),或者说是取出,我们先来定义一个类Animal,里面定义一些构造函数,方法,以及变量:

Animal.java:

package com.appleyk.reflect;

public class Animal {

public String name ="Dog";
private int   age  =30 ;//默认无参构造函数
public Animal(){System.out.println("Animal");
}//带参数的构造函数
public Animal(String name , int age){System.out.println(name+","+age);
}//公开 方法  返回类型和参数均有
public String sayName(String name){return "Hello,"+name;
}

}

我们再定义一个测试类:

ReflectTest.java

package com.appleyk.test;

public class ReflectTest {

public static void main(String args[]) throws Exception{//do something
}

}

我们运行一下我们的项目,会发现如下:

对应内存中就是:

我们借助javap命令查看一下,这个Animal.class里面的内容是什么:

F:\Java\ReflectClass\bin\com\appleyk\reflect>javap -c Animal.class
Compiled from “Animal.java”
public class com.appleyk.reflect.Animal {
public java.lang.String name;

public com.appleyk.reflect.Animal();
Code:
0: aload_0
1: invokespecial #12 // Method java/lang/Object."":
()V
4: aload_0
5: ldc #14 // String Dog
7: putfield #16 // Field name:Ljava/lang/String;
10: aload_0
11: bipush 30
13: putfield #18 // Field age:I
16: getstatic #20 // Field java/lang/System.out:Ljava/
io/PrintStream;
19: ldc #26 // String Animal
21: invokevirtual #28 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
24: return

public com.appleyk.reflect.Animal(java.lang.String, int);
Code:
0: aload_0
1: invokespecial #12 // Method java/lang/Object."":
()V
4: aload_0
5: ldc #14 // String Dog
7: putfield #16 // Field name:Ljava/lang/String;
10: aload_0
11: bipush 30
13: putfield #18 // Field age:I
16: getstatic #20 // Field java/lang/System.out:Ljava/
io/PrintStream;
19: new #39 // class java/lang/StringBuilder
22: dup
23: aload_1
24: invokestatic #41 // Method java/lang/String.valueOf:(
Ljava/lang/Object;)Ljava/lang/String;
27: invokespecial #47 // Method java/lang/StringBuilder."<
init>":(Ljava/lang/String;)V
30: ldc #49 // String ,
32: invokevirtual #51 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
35: iload_2
36: invokevirtual #55 // Method java/lang/StringBuilder.ap
pend:(I)Ljava/lang/StringBuilder;
39: invokevirtual #58 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
42: invokevirtual #28 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
45: return

public java.lang.String sayName(java.lang.String);
Code:
0: new #39 // class java/lang/StringBuilder
3: dup
4: ldc #64 // String Hello,
6: invokespecial #47 // Method java/lang/StringBuilder."<
init>":(Ljava/lang/String;)V
9: aload_1
10: invokevirtual #51 // Method java/lang/StringBuilder.ap
pend:(Ljava/lang/String;)Ljava/lang/StringBuilder;
13: invokevirtual #58 // Method java/lang/StringBuilder.to
String:()Ljava/lang/String;
16: areturn
}

我们发现,字节码里面包含了类Animal的构造函数、变量以及方法,但注意,全都是public类型的,我们的定义的类的私有变量 private int   age  =30 哪去了?当然,既然是类的私有部分,肯定不会暴露在外面的,但是不阻碍我们通过反射获得字节码中的私有成员(本篇只举例说明私有变量(字段field),其他私有类成员同理)。

我们的类Animal在Anima.java中定义,但在Animal.class文件中,我们的Animal类阐述如下:

下面,我们来写一段demo,来演示一下,如何使用反射机制,将.class文件中的类加载出来,并解剖出字节码中对应类的相关内容(构造函数、属性、方法):

看代码前,我们学两个小技巧:

(1)获得类的完全限定名:

copy以后,直接paste

(2)自动生成返回值对象

ReflectTest.java:

package com.appleyk.test;

import java.lang.reflect.Constructor;

import com.appleyk.reflect.Animal;

public class ReflectTest {

public static void main(String args[]) throws Exception{//do something //1、加载类 ,指定类的完全限定名:包名+类名Class c1 = Class.forName("com.appleyk.reflect.Animal");System.out.println(c1);//打印c1,发现值和字节码中的类的名称一样//2、解刨(反射)类c1的公开构造函数,且参数为null Constructor ctor1= c1.getConstructor();//3、构造函数的用途,就是创建类的对象(实例)的//除了私有构造函数外(单列模式,禁止通过构造函数创建类的实例,保证一个类只有一个实例)//ctor1.newInstance()默认生成一个Object对象,我们需要转化成我们要的Animal类对象// Object a1 = ctor1.newInstance();Animal a1 = (Animal)ctor1.newInstance(); //4、证明一下a1确实是Animal的实例,我们通过访问类中的变量来证明System.out.println(a1.name);
}

}

我们看下,上述demo 的执行结果:

我们接着走,获得类中的变量(字段)和方法,两种方式,一个是getXXX,一个是getDeclaredXXX,二者是有区别的,下面demo注释的很详细,并且,我们使用反射出的字段和方法,去获取相应实例的字段值和唤起方法(相当于执行某实例的方法),我们看下完整版demo:
 
加强版的 ReflectTest.java

package com.appleyk.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import com.appleyk.reflect.Animal;

public class ReflectTest {

public static void main(String args[]) throws Exception {// do somethingSystem.out.println("A(无参构造函数)--加载类、反射类的构造函数、利用构造函数new一个Animal实例instance--");// 1、加载类 ,指定类的完全限定名:包名+类名Class c1 = Class.forName("com.appleyk.reflect.Animal");System.out.println(c1);// 打印c1,发现值和字节码中的类的名称一样// 2.a、解刨(反射)类c1的公开构造函数,且参数为nullConstructor ctor1 = c1.getConstructor();// 3、构造函数的用途,就是创建类的对象(实例)的// 除了私有构造函数外(单列模式,禁止通过构造函数创建类的实例,保证一个类只有一个实例)// ctor1.newInstance()默认生成一个Object对象,我们需要转化成我们要的Animal类对象// Object a1 = ctor1.newInstance();Animal a1 = (Animal) ctor1.newInstance();// 4、证明一下a1确实是Animal的实例,我们通过访问类中的变量来证明System.out.println(a1.name);System.out.println("A(有参构造函数)--加载类、反射类的构造函数、利用构造函数new一个Animal实例instance--");// 2.b、 解刨(反射)类c1的公开构造函数,参数为string和intConstructor ctor2 = c1.getConstructor(String.class, int.class);Animal a2 = (Animal) ctor2.newInstance("Cat", 20);System.out.println("B--获得本类中的所有的字段----------------------------");// 5、获得类中的所有的字段 包括public、private和protected,不包括父类中申明的字段Field[] fields = c1.getDeclaredFields();for (Field field : fields) {System.out.println(field);}System.out.println("C--获得本类中的所有公有的字段,并获得指定对象的字段值-----");// 6、获得类中的所有的公有字段fields = c1.getFields();for (Field field : fields) {System.out.println(field + ", 字段值 = " + field.get(a1));// 注意:私有变量值,无法通过field.get(a1)进行获取值// 通过反射类中的字段name,修改name的值(注意,原值在类中name="Dog")// 如果,字段名称等于"name",且字段类型为String,我们就修改字段的值,也就是类中变量name的值if (field.getName() == "name" && field.getType().equals(String.class)) {String name_new = (String) field.get(a1);// 记得转换一下类型name_new = "哈士奇";// 重新给name赋值field.set(a1, name_new);// 设置当前实例a1的name值,使修改后的值生效}}System.out.println("利用反射出的字段,修改字段值,修改后的name = " + a1.name);System.out.println("D--获取本类中的所有的方法--------------------");// 7、获取本类中所有的方法 包括public、private和protected,不包括父类中申明的方法Method[] methods = c1.getDeclaredMethods();for (Method m : methods) {System.out.println(m);// 我们在类Animal中只定义了一个public方法,sayName}System.out.println("E--获取本类中的所有的公有方法,包括父类中和实现接口中的所有public方法-----------");// 8、获取类中所有公有方法,包括父类中的和实现接口中的所有public 方法methods = c1.getMethods();for (Method m : methods) {System.out.println(m);// 我们在类Animal中只定义了一个public方法,sayName}System.out.println("F--根据方法名称和参数类型获取指定方法,并唤起方法:指定所属对象a1,并给对应参数赋值-----------");// 9、唤起Method方法(执行) getMethod:第一个参数是方法名,后面跟方法参数的类Method sayName = c1.getMethod("sayName", String.class);System.out.println(sayName.invoke(a1, "Tom"));}

}

我们看下对应的执行结果:

如果,你对上述执行的结果,一次性接收不了的话,建议将上述测试demo自己亲自敲一遍,先别急着一次性敲完,一点点来,按照序号来,你会发现,反射的机制,无非就是先加载对应字节码中的类,然后,根据加载类的信息,一点点的去解剖其中的内容,不管你是public的还是private的,亦或是本类的还是来自原继承关系或者实现接口中的方法,我们java的反射技术 reflect,均可以将其从字节码中拉回到现实,不仅可以得到字段的名字,我们还可以获得字段的值和修改字段的值,不仅可以得到方法的申明我们还可以拿到方法的定义和唤起方法(执行方法),当然,你会有一个这样的疑惑?
 
为什么new一个对象那么简单,非要用反射技术中的newInstance?
为什么,我可以直接对象a1. 变量访问变量,却非要用反射那么费劲的获得name字段呢?
为什么,我几行代码就能搞定的事情,非要用反射呢?

回到最开始我们讲的地方:

ok,解密答案之前,我们先来思考一个问题?
 
假设我们定义了很多类,有Animal、Person、Car… ,如果我想要一个Animal实例,那我就new Animal(),如果另一个人想要一个Person实例,那么他需要new Person(),当然,另一个说,我只要一个Car实例,于是它要new Car()…这样一来就导致,每个用户new的对象需求不相同,因此他们只能修改源代码,并重新编译才能生效。这种将new的对象写死在代码里的方法非常不灵活,因此,为了避免这种情况的方法,Java提供了反射机制,典型的应用如下:

我们知道Spring的IOC吧,即“控制反转”(通过第三方配置文件实现对 对象的控制)。简单说是将我们设计好的对象交给容器控制,而不是直接交给程序内部进行对象的控制。
 
比如,在Spring中,我们经常看到:

针对上述的配置,我们Spring是怎么帮助我们实例化对象,并放到容器中去了呢? 没错,就是通过反射!!!!
 
我们看下,下面的伪代码实现过程:

//解析<bean …/>元素的id属性得到该字符串值为"sqlSessionFactory"
String idStr = “sqlSessionFactory”;
//解析<bean …/>元素的class属性得到该字符串值为"org.mybatis.spring.SqlSessionFactoryBean"
String classStr = “org.mybatis.spring.SqlSessionFactoryBean”;
//利用反射知识,通过classStr获取Class类对象
Class cls = Class.forName(classStr);
//实例化对象
Object obj = cls.newInstance();
//container表示Spring容器
container.put(idStr, obj);

    //当一个类里面需要用另一类的对象时,我们继续下面的操作//解析<property .../>元素的name属性得到该字符串值为“dataSource”  String nameStr = "dataSource";  //解析<property .../>元素的ref属性得到该字符串值为“dataSource”  String refStr = "dataSource";  //生成将要调用setter方法名  String setterName = "set" + nameStr.substring(0, 1).toUpperCase()  + nameStr.substring(1);  //获取spring容器中名为refStr的Bean,该Bean将会作为传入参数  Object paramBean = container.get(refStr);  //获取setter方法的Method类,此处的cls是刚才反射代码得到的Class对象  Method setter = cls.getMethod(setterName, paramBean.getClass());  //调用invoke()方法,此处的obj是刚才反射代码得到的Object对象  setter.invoke(obj, paramBean);

是不是很熟悉,虽然是伪代码,但是和我们本篇讲的反射机制的使用是相同的,现在知道我们的反射机制用在哪了吧,没错就是我们经常提到的Java web框架中,里面就用到了反射机制,只要在代码或配置文件中看到类的完全限定名(包名+类名),其底层原理基本上使用的就是Java的反射机制。
 
 
因此,如果你不做框架的话,基本上是用不到反射机制的,我们大多时候是使用框架的一方,而反射机制都已经在底层实现过了,因此,我们不必担心,我们会写那么复杂的代码。但是,我们必须要理解这种机制的存在!
————————————————
版权声明:本文为CSDN博主「appleyk」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Appleyk/article/details/77879073

最容易理解的反射机制的底层原理相关推荐

  1. 秒懂系列,深入理解Java反射机制

    文章目录 深入理解Java反射机制 一.Java反射概述 二.Java创建对象思想传递 三.类对象 3.1 类对象和类的对象 3.2 三种获取类对象的方法 四.Class对象的常用方法 4.1 获取成 ...

  2. 理解Java反射机制

    理解Java反射机制 1. 概述 2. 反射原理 3. 反射的优缺点 4. 反射的用途 5. 反射相关的类 5.1 Constructor 5.2 Field 5.3 Method 5.4 Class ...

  3. Apache ZooKeeper - Watch 机制的底层原理

    文章目录 Watch 机制 API 使用 Watch 机制的底层原理 客户端 Watch 注册实现过程 ZKWatchManager 服务端 Watch 注册实现过程 WatchManager 服务端 ...

  4. Java反射机制实现与原理

    本文介绍Android反射机制实现与原理,在介绍之前,要和Java进行比较,所以先看下Java中的反射相关知识: 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或 ...

  5. java反射的原理_java反射机制的实现原理

    java反射机制的实现原理 反射机制: 所谓的反射机制就是java语言在运行时拥有一项自观的能力. 通过这种能力可以彻底的了解自身的情况为下一步的动作做准备. 下面具体介绍一下java的反射机制.这里 ...

  6. 深入探索Java反射机制:解析原理与应用

    深入探索Java反射机制:解析原理与应用

  7. 深入理解java反射机制

    一,java的核心机制 java有两种核心机制:java虚拟机(JavaVirtual Machine)与垃圾收集机制(Garbage collection): Java虚拟机:是运行所有Java程序 ...

  8. 深入理解Java中的反射机制和使用原理!详细解析invoke方法的执行和使用

    反射的概念 反射:Refelection,反射是Java的特征之一,允许运行中的Java程序获取自身信息,并可以操作类或者对象的内部属性 通过反射,可以在运行时获得程序或者程序中的每一个类型的成员活成 ...

  9. 【重铸Java根基】理解Java反射机制

    最近带应届新员工,教然后知不足,发现自己把很多基础知识已经还给了大学老师,因此开贴,温故而知新! 从最基础的Java知识开始由浅入深,在某个知识点中遇到有疑惑的点会额外多写几句或者单独开帖子展开. 本 ...

最新文章

  1. php java memcached_php和java的memcached使用的兼容性问题解决过程
  2. Centos6.3下利用rsyslog+loganalyzer+mysql部署日志服务器
  3. mysql innodb引擎--范围查询优化
  4. excel大作业素材_Excel | 同一单元格内多个姓名,如何统计人数
  5. 指甲之指甲长期没有甲半月弧(二)
  6. 2.7.3-YARN-获取debug命令:resourceManager+nodeManager
  7. Xcode 9中去除Block警告
  8. Python+OpenGL绘制任意形状的三次贝塞尔曲线
  9. oracle 11g 通过在线重定义方式修改表结构
  10. 捕获asp.net下的未处理异常
  11. poj 3074(DLX)
  12. 阿里云高级技术专家彦林:云原生架构下的微服务演进
  13. 关于Webstorm汉化后无法打开设置,谈谈心里的想法
  14. PHP 发送谷歌邮箱
  15. Xmanager 6 激活
  16. java_232_GOF23设计模式_建造者模式详解_练习
  17. Android设置沉浸式
  18. 关于安卓开发的一些你必须要掌握的网络知识(一):网络基础与网络框架OkHttp
  19. 通过GCN来实现对Cora数据集节点的分类
  20. 使用ARouter进行Android模块化开发,详解系列文章

热门文章

  1. Mysql还原数据库
  2. 中行:年内可能继续加息 存款准备金率或上调
  3. 图片的懒加载和预加载?
  4. 傅里叶变换的学习(讲的很好)
  5. 从进化视角看GameFi和元宇宙
  6. Android Preference 卡片圆角风格定制
  7. mysql 设置远程访问_mysql远程连接的设置
  8. 如何做好售前技术支持工作 (引用)
  9. scrapy——是否遵循爬虫规则
  10. mysql pdo查询语句,PHP PDO准备语句 – mysql LIKE查询