当程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言。我们认为java并不是动态语言,但是java有一个非常突出的动态相关机制,俗称:反射。
IT行业里这么说,没有反射也就没有框架,现有的框架都是以反射为基础。在实际项目开发中,用的最多的是框架,填的最多的是类,反射这一概念就是将框架和类揉在一起的调和剂。
什么是类(Class)?
更多的可以回顾下C++ 结构体和类的区别,这里温习下
面向对象(Object Oriented,OO)
起初,“面向对象”是指在程序设计中采用封装、继承、多态等设计方法。现在,面向对象的思想已经涉及到软件开发的各个方面。如,面向对象的分析(OOA,ObjectOriented Analysis),面向对象的设计(OOD,Object Oriented Design)、以及面向对象的编程实现(OOP,Object Oriented Programming)。
对象和类解释:
-
对象:对象是人们要进行研究的任何事物,它不仅能表示具体的事物,还能表示抽象的规则、计划或事件。对象具有状态,一个对象用数据值来描述它的状态。对象还有操作,用于改变对象的状态,对象及其操作就是对象的行为。对象实现了数据和操作的结合,使数据和操作封装于对象的统一体中。
-
类:具有相同特性(数据元素)和行为(功能)的对象的抽象就是类。因此,对象的抽象是类,类的具体化就是对象,也可以说类的实例是对象,类实际上就是一种数据类型。类具有属性,它是对象的状态的抽象,用数据结构来描述类的属性。类具有操作,它是对象的行为的抽象,用操作名和实现该操作的方法来描述。
对象和类的关系:
类与对象的关系就如模具和铸件的关系,类的实例化化的结果就是对象,而对对象的抽象就是类,类描述了一组有相同特性(属性)和相同行为的对象。
在java语言中,static修饰的东西不是对象,但是它属于类。普通的数据类型不是对象,例如:int a = 5;它不是面向对象,但是它有其包装类 Integer 或者分装类来弥补了它。除了以上两种不是面向对象,其余的包括类也有它的面向对象,类是java.lang.Class的实例化对象(注意Class是大写)。也就是说:
Class A{}
当我创建了A类,那么类A本身就是一个对象,谁的对象?java.lang.Class的实例对象。
什么是反射(Reflection)?
在学习 Java 反射机制前,大家应该先分清楚两个概念: 编译期和运行期。
编译期和运行期
-
编译期:是指把源码交给编译器编译成计算机可以执行的文件的过程。在 Java 中也就是把 Java 代码编成 class 文件的过程。编译期只是做了一些翻译功能,并没有把代码放在内存中运行起来,而只是把代码当成文本进行操作,比如检查错误。
-
运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就把在磁盘中的代码放到内存中执行起来。
反射机制
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的反射机制。概况就是:
反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。
Apple apple = new Apple(); //直接初始化,「正射」 apple.setPrice(4);
上面这样子进行类对象的初始化,我们可以理解为「正」。
而反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。
这时候,我们使用 JDK 提供的反射 API 进行反射调用:
Class clz = Class.forName("com.chenshuyi.reflect.Apple"); Method method = clz.getMethod("setPrice", int.class); Constructor constructor = clz.getConstructor(); Object object = constructor.newInstance(); method.invoke(object, 4);
上面两段代码的执行结果,其实是完全一样的。但是其思路完全不一样,第一段代码在未运行时就已经确定了要运行的类(Apple),而第二段代码则是在运行时通过字符串值才得知要运行的类(com.chenshuyi.reflect.Apple)。
在 Java 中,只要给定类的名字,就可以通过反射机制来获得类的所有信息,而这个类在编译过程中甚至是还未存在的。在运行的时候我们可以通过配置文件获取某个类的类名,然后使用反射机制构造这个类的对象,调用这个对象的方法,修改这个对象的成员变量。
反射机制很重要的一点就是“运行时”,其使得我们可以在程序运行时加载、探索以及使用编译期间完全未知的 .class 文件。换句话说
Java 程序可以加载一个运行时才得知名称的 .class 文件,然后获悉其完整构造,并生成其对象实体、或对其 fields(变量)设值、或调用其 methods(方法)。一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
使用 Java 反射机制可以在运行时期检查 Java 类的信息,检查 Java 类的信息往往是你在使用 Java 反射机制的时候所做的第一件事情
反射机制用处:
-
在运行时判断任意一个对象所属的类;
-
在运行时构造任意一个类的对象;
-
在运行时判断任意一个类所具有的成员变量和方法;
-
在运行时调用任意一个对象的方法;
-
生成动态代理。
反射的使用场景
Java 反射机制在 web 开发框架, ORM 框架, 插件化开发等场景中得到了广泛运用。
比如说 web 开发框架 Spring 中,最重要的概念就是 IOC 控制反转。而 IOC 的实现原理就是反射。通过反射来构造 Java Bean 的对象,调用其方法。
比如说 Android 开发中常用的 ORM 框架: GreenDao, LiteOrm 等, 也是通过反射来读写 Java Bean 对象的成员变量的。
如果你只是使用这些框架,你可能感觉不到反射的存在,实际上反射却是无处不在。
反射常用API
获取反射中的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();
通过反射创建类对象
通过反射创建类对象主要有两种方式:通过 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);
下面整理下:
通过反射获取类属性、方法、构造器
-
getName() 方法返回类的全限定类名(包含包名)
-
getPackage() 获取包的相关信息,比如包名
-
getSuperclass() 访问类的父类
-
getInterfaces() 获取指定类所实现的接口集合(Class数组)
-
getConstructors() 获取 Constructor 类的实例
-
getMethods() 获取 Method 对象,Method 对象数组包含了指定类中声明为公有的(public)的所有变量集合
-
getModifiers() 访问一个类的修饰符, 即public,private,static 等等的关键字
-
java.lang.reflect.Modifier 类中的方法来检查修饰符的类型
-
getFields() 访问一个类的成员变量,但无法获取私有属性。
-
getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性
与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。
反射源码解析
们平常很多框架都使用了反射,而反射中最最终的就是 Method 类的 invoke 方法了
具体查看 java.lang.reflect.Method.invoke 源码
参考内容:
Java-反射机制介绍 qiushao.net/2020/02/15/Java/Java-反射机制介绍/
大白话说Java反射:入门、使用、原理 https://www.cnblogs.com/chanshuyi/p/head_first_of_reflection.html
Java Reflection(反射机制)详解 https://www.jianshu.com/p/2315dda64ad2
谈谈Java反射机制 https://www.jianshu.com/p/6277c1f9f48d
什么是类?什么是对象?类和对象有什么关系? https://blog.csdn.net/qq_34086047/article/details/51395730
谈谈Java反射机制 https://www.jianshu.com/p/6277c1f9f48d
JAVA反序列化 - 反射机制 https://xz.aliyun.com/t/7029
转载本站文章《java反射机制原理剖析》,
请注明出处:https://www.zhoulujun.cn/html/java/KeyConcepts/8485.html