0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Java反射机制到底是什么?有什么作用

Wildesbeast 来源:今日头条 作者:程序猿的内心独白 2020-02-15 14:07 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

什么是Java反射机制?

Java反射机制是 Java 语言的一个重要特性,它在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法。此外,在 ORM 中间件的实现中,运用 Java 反射机制可以读取任意一个 JavaBean 的所有属性,或者给这些属性赋值

通过反射机制,可以在运行的时候访问到对象的属性、方法、构造方法等等

哪些地方用到反射机制?

其实我们都用过反射机制,只是并不知道它是反射机制而已。比如我们使用JDBC连接数据库的时候(Class.forName() 这个相信大家都用过),只是当时并没有在意那么多(心里想能用就行,管它呢),使用到反射机制的框架有(Spring、SpringMVC、Mybatis、Hibernate、Structs)

反射机制提供了哪些功能?

在运行时判定任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时判定任意一个类所具有的成员变量和方法

在运行时调用任意一个对象的方法

生成动态代理

如何实现反射机制?

先来个实体类 Student

/**

* @author Woo_home

* @create by 2019/12/10

*/

public class Student {

private int id;

private String name;

public Student(){}

public int getId() {

return id;

}

public void setId(int id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return "Student{" +

"id=" + id +

", name='" + name + ''' +

'}';

}

}

反射的三种实现方式

一、通过对象的getClass()方法

getClass()方法是Object类的方法,因为所有类都继承自Object类,所以可以直接使用getClass()方法

public class ReflectDemo {

public static void main(String[] args) {

Student student = new Student();

Class studentClass = student.getClass();

System.out.println(studentClass);

}

}

二、通过类的.class属性

直接获取一个类的class

public class ReflectDemo {

public static void main(String[] args) {

Class studentClass = Student.class;

System.out.println(studentClass);

}

}

三、通过Class类的forName()方法(常用)

Class.forName() 是一个静态方法

public class ReflectDemo {

public static void main(String[] args) {

// 使用 try、catch 处理异常

try {

Class studentClass = Class.forName("com.example.app.demo.Student");

System.out.println(studentClass);

}catch (ClassNotFoundException e){

e.printStackTrace();

}

}

}

public class ReflectDemo {

// 使用 throws 处理异常

public static void main(String[] args) throws ClassNotFoundException{

Class studentClass = Class.forName("com.example.app.demo.Student");

System.out.println(studentClass);

}

}

三种方法打印的结果都是一样的,如下图所示

注意: 这里的Class.forName()方法需要使用try、catch语句括住或者在方法或类中抛出ClassNotFoundException 异常,不然会报错

有些人就问了,为什么需要使用try、catch语句括住呢,来看下forName方法源码:

可以看到forName需要抛出一个 ClassNotFoundException 异常,自然而然地你使用forName()方法也自然要抛出 / 处理日常了

@CallerSensitive

public static Class forName(String className)

throws ClassNotFoundException {

Class caller = Reflection.getCallerClass();

return forName0(className, true, ClassLoader.getClassLoader(caller), caller);

}

判断一个类是否为某个类的实例

1、instanceof

public class ReflectDemo {

public static void main(String[] args) {

HashMap map = new HashMap<>();

if (map instanceof Map){

System.out.println("HashMap is Map instance");

}

}

}

输出:HashMap is Map instance

2、isInstance

public class ReflectDemo {

public static void main(String[] args) {

HashMap map = new HashMap<>();

if (Map.class.isInstance(map)){

System.out.println("HashMap is Map Instance");

}

}

}

输出:HashMap is Map Instance

利用反射创建对象实例

1、通过Class对象的 newInstance() 方法

public class ReflectDemo {

public static void main(String[] args) throws IllegalAccessException, InstantiationException {

// 这里的Student是使用上面一开始的Student类

Class studentClass = Student.class;

// 使用newInstance创建实例

Student student = (Student)studentClass.newInstance();

student.setId(1);

student.setName("John");

System.out.println(student);

}

}

输出:John

2、通过Constructor对象的 getConstructor() 方法

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

Class classes = List.class;

Constructor constructor = classes.getConstructor(List.class);

List result = (List) constructor.newInstance("John");

System.out.println(result);

}

}

输出:John

Field

根据字段名获取字段(只能获取public的)

public class ReflectDemo {

public static void main(String[] args) throws NoSuchFieldException {

Class classes = Student.class;

Field id = classes.getField("id");

Field name = classes.getField("name");

System.out.println(id);

System.out.println(name);

}

}

class Student{

public int id;

public String name;

}

输出:

以下为输出结果

public int com.example.app.demo.Student.id

public java.lang.String com.example.app.demo.Student.name

如果获取的字段是被private修饰的,那么将会抛出 NoSuchFieldException 异常

以下为输出结果

Exception in thread “main” java.lang.NoSuchFieldException: id

at java.lang.Class.getField(Class.java:1703)

at com.example.app.demo.ReflectDemo.main(ReflectDemo.java:12)

为什么会这样呢?我们朝着这个问题看下源码是怎么实现的

getField(String name)

根据名称获取公有的(public)类成员

@CallerSensitive

public Field getField(String name)

throws NoSuchFieldException, SecurityException {

checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);

// 可以看到在getField传入的name实际是调用getField0来实现的

Field field = getField0(name);

if (field == null) {

throw new NoSuchFieldException(name);

}

return field;

}

getField0(String name)

而getField0主要判断依据是searchFields,searchFields根据privateGetDeclaredFields判断

private Field getField0(String name) throws NoSuchFieldException {

Field res;

// Search declared public fields

if ((res = searchFields(privateGetDeclaredFields(true), name)) != null) {

return res;

}

// 省略部分代码...

return null;

}

privateGetDeclaredFields(boolean publicOnly)

可以看到当privateGetDeclaredFields传入的值是true的时候表示使用的是 declaredPublicFields ,也就是说是public声明的字段,当传入的值为false的时候使用的是 declaredFields(所有声明字段)

private Field[] privateGetDeclaredFields(boolean publicOnly) {

checkInitted();

Field[] res;

ReflectionData rd = reflectionData();

if (rd != null) {

// 判断输入的布尔值是true还是false

res = publicOnly ? rd.declaredPublicFields : rd.declaredFields;

if (res != null) return res;

}

// 省略部分代码...

return res;

}

getFields(String name)

获取所有 public 修饰的字段

public class ReflectDemo {

public static void main(String[] args) throws NoSuchFieldException {

Class classes = Student.class;

Field[] allField = classes.getFields();

for (Field field : allField) {

System.out.println(field);

}

}

}

class Student{

private int id;

public String name;

}

输出:

以下为输出结果

public java.lang.String com.example.app.demo.Student.name

可以看到我们的Student类中声明了一个private修饰的id和一个public修饰的name,根据输出结果可以知道getFields()方法只能获取public修饰的字段

看下实现源码:

在getFields中返回了一个copyFields方法,该方法中调用了 privateGetPublicFields 方法

@CallerSensitive

public Field[] getFields() throws SecurityException {

checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);

return copyFields(privateGetPublicFields(null));

}

来看下privateGetPublicFields方法可以知道在该方法中调用了 privateGetDeclaredFields(true) 这个方法,关于这个方法上面已经讲述过

private Field[] privateGetPublicFields(Set> traversedInterfaces) {

// Local fields

Field[] tmp = privateGetDeclaredFields(true);

addAll(fields, tmp);

// 省略部分代码...

return res;

}

getDeclaredField(String name)

根据名称获取已声明的类成员。但不能得到其父类的类成员

public class ReflectDemo {

public static void main(String[] args) throws NoSuchFieldException {

Class classes = Student.class;

Field allField = classes.getDeclaredField("name");

System.out.println(allField);

}

}

class Student{

private int id;

private String name;

}

Class Student{

public int id;

public String name;

}

输出:可以看到getDeclaredField()方法即可以获取public类型的字段也能获取private类型的字段

以下为输出结果:

private java.lang.String com.example.app.demo.Student.name

public java.lang.String com.example.app.demo.Student.name

来看下源码的原理

@CallerSensitive

public Field getDeclaredField(String name)

throws NoSuchFieldException, SecurityException {

checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);

Field field = searchFields(privateGetDeclaredFields(false), name);

if (field == null) {

throw new NoSuchFieldException(name);

}

return field;

}

可以看到 getDeclaredField() 方法调用的也是 privateGetDeclaredFields 方法,上面已经讲述过

getDeclaredFields()

获取所有已声明的类成员变量

public class ReflectDemo {

public static void main(String[] args) {

Class classes = Student.class;

Field[] allField = classes.getDeclaredFields();

for (Field field : allField) {

System.out.println(field);

}

}

}

class Student{

public int id;

public String name;

}

输出:

以下为输出结果:

public int com.example.app.demo.Student.id

public java.lang.String com.example.app.demo.Student.name

而且 getDeclaredFields 还可以获取private修饰的字段

public class ReflectDemo {

public static void main(String[] args) {

Class classes = Student.class;

Field[] allField = classes.getDeclaredFields();

for (Field field : allField) {

System.out.println(field);

}

}

}

class Student{

private int id;

private String name;

}

输出:

以下为输出结果:

private int com.example.app.demo.Student.id

private java.lang.String com.example.app.demo.Student.name

来看下源码 getDeclaredFields() 方法的源码

可以看到 getDeclaredFields() 方法中也是调用 privateGetDeclaredFields 方法的,并且这里的 privateGetDeclaredFields传入的值是 false ,也就是获取所有已声明的字段

@CallerSensitive

public Field[] getDeclaredFields() throws SecurityException {

checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);

return copyFields(privateGetDeclaredFields(false));

}

Method

getMethod(String name)

返回类或接口的特定方法。其中第一个参数为方法名称,后面的参数为方法参数对应 Class 的对象的方法名

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException {

// 传入CaseMethod的方法名

Method methods = CaseMethod.class.getMethod("caseOfMethod");

System.out.println(methods);

}

}

class CaseMethod{

public void caseOfMethod(){

System.out.println("case");

}

}

输出:

以下为输出结果:

public void com.example.app.demo.CaseMethod.caseOfMethod()

getMethods()

获取类或接口的所有 public 方法,包括其父类的 public 方法

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException {

// 获取所有方法,使用Method数组接收

Method[] methods = CaseMethod.class.getMethods();

for (Method method : methods) {

System.out.println(method);

}

}

}

class CaseMethod{

public void caseOfMethod(){

System.out.println("case");

}

}

输出:

可以看到输出结果中第一个就是我们自定义的CaseMethod中的方法,其余的都是Object类的方法

以下为输出结果:

public void com.example.app.demo.CaseMethod.caseOfMethod()

public final void java.lang.Object.wait() throws java.lang.InterruptedException

public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException

public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException

public boolean java.lang.Object.equals(java.lang.Object)

public java.lang.String java.lang.Object.toString()

public native int java.lang.Object.hashCode()

public final native java.lang.Class java.lang.Object.getClass()

public final native void java.lang.Object.notify()

public final native void java.lang.Object.notifyAll()

getDeclaredMethod(String name, Class… parameterTypes)

获取类或接口的特定声明方法。其中第一个参数为方法名称,后面的参数为方法参数对应 Class 的对象

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException {

Method methods = CaseMethod.class.getDeclaredMethod("caseOfMethod",null);

System.out.println(methods);

}

}

class CaseMethod{

public void caseOfMethod(){

System.out.println("case");

}

private void caseOf(){

System.out.println("case1");

}

}

getDeclaredMethods()

获取类或接口声明的所有方法,包括 public、protected、默认(包)访问和 private 方法,但不包括继承的方法

public class ReflectDemo {

public static void main(String[] args) {

Method[] methods = CaseMethod.class.getDeclaredMethods();

for (Method method : methods) {

System.out.println(method);

}

}

}

class CaseMethod{

public void caseOfMethod(){

System.out.println("case");

}

private void caseOf(){

System.out.println("case1");

}

}

输出:

以下为输出结果:

public void com.example.app.demo.CaseMethod.caseOfMethod()

private void com.example.app.demo.CaseMethod.caseOf()

使用invoke方法

当获取一个对象之后就可以使用invoke方法,invoke方法示例如下:

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

Method methods = CaseMethod.class.getMethod("caseOfMethod");

CaseMethod caseMethod = new CaseMethod();

methods.invoke(caseMethod);

}

}

class CaseMethod{

public void caseOfMethod(){

System.out.println("case");

}

private void caseOf(){

System.out.println("case1");

}

}

输出:case

那么invoke是如何实现的呢?来看下源码

boolean override;

@CallerSensitive

public Object invoke(Object obj, Object... args)

throws IllegalAccessException, IllegalArgumentException,

InvocationTargetException

{

// 判断override是否为true

if (!override) {

// 判断modifiers(方法的修饰符)是否为public

if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {

// 通过方法的修饰符(protected、private、)或声明类(如子类可以访问父类的protected方法)与caller之间的关系

Class caller = Reflection.getCallerClass();

// 判断caller是否有权限方法该方法

checkAccess(caller, clazz, obj, modifiers);

}

}

MethodAccessor ma = methodAccessor; // read volatile

if (ma == null) {

ma = acquireMethodAccessor();

}

return ma.invoke(obj, args);

}

注意: invoke方法如果提供了错误的参数,会抛出一个异常,所以要提供一个异常处理器。建议在有必要的时候才使用invoke方法,有如下原因:

1、invoke方法的参数和返回值必须是Object类型,意味着必须进行多次类型转换

2、通过反射调用方法比直接调用方法要明显慢一些

Constructor

getConstructor(Class… parameterTypes)

获取类的特定 public 构造方法。参数为方法参数对应 Class 的对象

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {

Constructor constructor = CaseMethod.class.getConstructor(int.class, String.class);

System.out.println(constructor);

}

}

class CaseMethod{

private int id;

private String name;

private int cap;

public CaseMethod(int id,String name){

this.id = id;

this.name = name;

}

public CaseMethod(int cap){

}

}

输出:

以下为输出结果:

public com.example.app.demo.CaseMethod(int,java.lang.String)

getConstructors()

获取类的所有 public 构造方法

public class ReflectDemo {

public static void main(String[] args) {

Constructor[] constructors = CaseMethod.class.getConstructors();

for (Constructor constructor : constructors) {

System.out.println(constructor);

}

}

}

class CaseMethod{

private int id;

private String name;

private int cap;

public CaseMethod(int id,String name){

this.id = id;

this.name = name;

}

public CaseMethod(int cap){

}

}

输出:

以下为输出结果:

public com.example.app.demo.CaseMethod(int,java.lang.String)

public com.example.app.demo.CaseMethod(int)

getDeclaredConstructor(Class… parameterTypes)

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException {

// 注意:由于private修饰的CaseMethod构造函数没有参数,所以getDeclaredConstructor()可以为空

// 默认的getDeclaredConstructor(Class... parameterTypes)方法是要传参数类型的

Constructor constructor = CaseMethod.class.getDeclaredConstructor();

Constructor declaredConstructor = CaseMethod.class.getDeclaredConstructor(int.class, String.class);

System.out.println(constructor);

System.out.println(declaredConstructor);

}

}

class CaseMethod{

private int id;

private String name;

private int cap;

private CaseMethod(){

}

public CaseMethod(int id,String name){

this.id = id;

this.name = name;

}

}

输出:

以下为输出结果:

private com.example.app.demo.CaseMethod()

public com.example.app.demo.CaseMethod(int,java.lang.String)

getDeclaredConstructors()

获取类的所有构造方法

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException {

Constructor[] constructors = CaseMethod.class.getDeclaredConstructors();

for (Constructor constructor : constructors) {

System.out.println(constructor);

}

}

}

class CaseMethod{

private int id;

private String name;

private int cap;

private CaseMethod(){

}

public CaseMethod(int id,String name){

this.id = id;

this.name = name;

}

}

输出:

以下为输出结果:

private com.example.app.demo.CaseMethod()

public com.example.app.demo.CaseMethod(int,java.lang.String)

使用newInstance创建实例

public class ReflectDemo {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

Constructor constructor = CaseMethod.class.getConstructor(String.class);

CaseMethod caseMethod = constructor.newInstance("Lisa");

System.out.println(caseMethod);

}

}

class CaseMethod{

private String name;

public CaseMethod(String name){

this.name = name;

}

@Override

public String toString() {

return "CaseMethod{" +

"name='" + name + ''' +

'}';

}

}

输出:

CaseMethod{name=‘Lisa’}

总结

Java获得Class对象的引用的方法中,Class.forName() 方法会自动初始化Class对象,而 .class 方法不会,.class 的初始化被延迟到静态方法或非常数静态域的首次引用

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 服务器
    +关注

    关注

    14

    文章

    10364

    浏览量

    91760
  • JAVA
    +关注

    关注

    20

    文章

    3006

    浏览量

    116835
  • 数据库
    +关注

    关注

    7

    文章

    4083

    浏览量

    68547
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    Java并发编程的“基石”——多线程概念初识

    Java 的内存可见性机制(如 happens-before 原则)能够确保在极端并发下,任何一个调度节点看到的集群状态都是一致且准确的,从根本上杜绝了“脑裂”和资源超卖。 其次是 精妙的多线程协同
    发表于 04-16 18:50

    到底有多小?

    想知道硅到底有多小;CPU 并不比 Wii 上的百老汇芯片大,但功能却无限强大,哈哈。
    发表于 03-25 06:51

    浅谈锡膏在手机制造上的作用

    锡膏在手机制造中扮演着“隐形桥梁”与“工艺基石”的双重角色,其作用贯穿电路板焊接、元件可靠性保障、生产效率提升及质量管控等核心环节,是确保手机性能稳定、寿命持久的关键材料。以下从功能实现、工艺价值及行业趋势三个维度展开分析:
    的头像 发表于 02-25 17:16 622次阅读

    MOS管到底是什么?和三极管、继电器什么本质区别?

    MOS管到底是什么?和三极管、继电器什么本质区别?在电子设备的“心脏”部位,藏着许多默默工作的“开关选手”。它们操控着电流的通断,决定着设备的效率与稳定性。其中,MOS管作为近年来高频
    的头像 发表于 01-07 13:46 1012次阅读
    MOS管<b class='flag-5'>到底是</b>什么?和三极管、继电器<b class='flag-5'>有</b>什么本质区别?

    自动驾驶中毫米波雷达到底有作用

    毫米波雷达、超声波雷达等感知硬件,更像是一个配角,成为自动驾驶技术实现的辅助硬件。那在自动驾驶中毫米波雷达到底有作用
    的头像 发表于 12-10 17:07 2064次阅读
    自动驾驶中毫米波雷达<b class='flag-5'>到底有</b>何<b class='flag-5'>作用</b>?

    IGBT到底是什么?-从名称入手来带您了解

    对于工作需要用到IGBT、但从未专业学习过IGBT的人来说, IGBT到底是什么、它为什么叫IGBT、它的核心关键词是什么、要怎么理解它 等一系列问题并无法一次性在某个地方获取到,都需要查阅大量的资料,学习大量的基础才能有个初步的了解。 为了让更多的人在更少的时间内掌握IGBT,我将在
    的头像 发表于 11-25 17:38 2755次阅读
    IGBT<b class='flag-5'>到底是</b>什么?-从名称入手来带您了解

    请问Keil中的map文件到底是什么意思?

    Keil中的map文件到底是什么意思?里面是如何进行相关执行操作的
    发表于 11-25 06:59

    单片机和嵌入式,到底是什么关系?

    的关系:什么是单片机?什么是嵌入式?它们到底是不是一回事?先说说大家最先听说的那个词——单片机(MCU)。单片机,全称是MicrocontrollerUnit,顾名思
    的头像 发表于 11-14 10:28 2080次阅读
    单片机和嵌入式,<b class='flag-5'>到底是</b>什么关系?

    单片机的差分信号到底是什么?

    差分信号到底是什么?通俗来讲,就是驱动端发送两个等值、反相的信号,接收端通过比较这两个电压的差值来判断逻辑状态“0”还是“1”。 差分信号的产生是由输入源发出信号后经过缓冲器和倒相器后,所产生
    发表于 11-12 06:44

    UWB技术到底是什么?什么优点?

    “超宽带”的核心在于“宽”。但与Wi-Fi或5G通过提高频率来获取更快速度不同,UWB选择了一条独特的路径:在极宽的频谱上传输极其短暂的脉冲信号。
    的头像 发表于 09-19 17:30 1067次阅读
    UWB技术<b class='flag-5'>到底是</b>什么?<b class='flag-5'>有</b>什么优点?

    UWB技术到底是什么?什么优点?

    “超宽带”的核心在于“宽”。但与Wi-Fi或5G通过提高频率来获取更快速度不同,UWB选择了一条独特的路径:在极宽的频谱上传输极其短暂的脉冲信号。
    的头像 发表于 09-19 17:27 1338次阅读

    IEC 到底是什么?为什么它能影响全球?

    IEC 到底是什么?为什么它能影响全球?
    的头像 发表于 09-04 17:07 3908次阅读

    浮思特 | 红外热像仪什么用?一文带你看懂它的“隐藏能力”

    问题来了,红外热像仪到底是干嘛的?它到底有什么用?别急,今天就用这篇文章,跟你聊聊红外热像仪的真正用途!红外热像仪到底是什么?一句话解释:它是一种可以“看到”温度差异的
    的头像 发表于 08-08 10:43 971次阅读
    浮思特 | 红外热像仪<b class='flag-5'>有</b>什么用?一文带你看懂它的“隐藏能力”

    晶振的 “负载电容” 到底是什么

    负载电容,到底是什么? 负载电容,简单来说,是指晶振的两条引线连接IC块内部及外部所有有效电容之和,我们可以将其看作晶振片在电路中串接的电容。从更专业的角度讲,它是为了使晶振能够在其标称频率下稳定
    的头像 发表于 07-25 16:26 1210次阅读

    请问编译纯rtos到底是选择Linux+rtos的sdk编译only rtos还是直接使用rtos sdk?

    编译纯rtos到底是选择Linux+rtos的sdk编译only rtos还是直接使用rtos sdk?
    发表于 07-11 07:22