牛逼!不得不服,第一次有人把Java 反射机制讲解这么透!

反射概述

什么是反射

将类的各个组成部分封装为其他对象的过程就叫做 反射,其中 组成部分 指的是我们类的 成员变量(Field)、构造方法(Constructor)、成员方法(Method)。

使用反射的优缺点

优点

在程序运行过程中可以操作类对象,增加了程序的灵活性;
解耦,从而提高程序的可扩展性,提高代码的复用率,方便外部调用;
对于任何一个类,当知道它的类名后,就能够知道这个类的所有属性和方法;而对于任何一个对象,都能够调用它的一个任意方法。
缺点

  • 性能问题:Java 反射中包含了一些动态类型,JVM 无法对这些动态代码进行优化,因此通过反射来操作的方式要比正常操作效率更低。
  • 安全问题:使用反射时要求程序必须在一个没有安全限制的环境中运行,如果程序有安全限制,就不能使用反射。
    程序健壮性:反射允许代码执行一些平常不被允许的操作,破坏了程序结构的抽象性,导致平台发生变化时抽象的逻辑结构无法被识别。

Class 对象的获取及使用

获取 Class 对象的方式
Class.forName(“全类名”)
源代码阶段,它能将字节码文件加载进内存中,然后返回 Class 对象,多用于配置文件中,将类名定义在配置文件中,通过读取配置文件来加载类。

类名.class
类对象阶段,通过类名的 class 属性来获取,多用于参数的传递。

对象.getClass()
运行时阶段,getClass() 定义在 Object 类中,表明所有类都能使用该方法,多用于对象的获取字节码的方式。

我们首先定义一个 Person 类,用于后续反射功能的测试;

package com.cunyu;/*** @author : cunyu* @version : 1.0* @className : Person* @date : 2021/4/7 22:37* @description : Person 类*/public class Person {private int age;private String name;public long id;public long grade;protected float score;protected int rank;public Person(int age, String name, long id, long grade, float score, int rank) {this.age = age;this.name = name;this.id = id;this.grade = grade;this.score = score;this.rank = rank;}public Person() {}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public long getId() {return id;}public void setId(long id) {this.id = id;}public long getGrade() {return grade;}public void setGrade(long grade) {this.grade = grade;}public float getScore() {return score;}
//加入Java开发交流君样:756584822一起吹水聊天public void setScore(float score) {this.score = score;}public int getRank() {return rank;}public void setRank(int rank) {this.rank = rank;}@Overridepublic String toString() {final StringBuffer sb = new StringBuffer("Person{");sb.append("age=").append(age);sb.append(", name='").append(name).append('\'');sb.append(", id=").append(id);sb.append(", grade=").append(grade);sb.append(", score=").append(score);sb.append(", rank=").append(rank);sb.append('}');return sb.toString();}
}

定义好 Person 类之后,我们尝试用 3 种不同的方式来获取 Class 对象,并比较它们是否相同。

package com.cunyu;/*** @author : cunyu* @version : 1.0* @className : Demo1* @date : 2021/4/7 23:29* @description : Class 对象的获取*/public class Demo1 {public static void main(String[] args) throws ClassNotFoundException {
//        第一种方式,Class.forName("全类名")Class class1 = Class.forName("com.cunyu.Person");System.out.println(class1);
//加入Java开发交流君样:756584822一起吹水聊天
//        第二种方式,类名.classClass class2 = Person.class;System.out.println(class2);//        第三种方式,对象.getName()Person person = new Person();Class class3 = person.getClass();System.out.println(class3);//        比较三个对象是否相同System.out.println(class1 == class2);System.out.println(class1 == class3);}
}

在这里插入图片描述
上述代码中,会发现最后输出的比较结果返回的是两个 true,说明通过上述三种方式获取的 Class 对象都是同一个,同一个字节码文件(*.class)在一次运行过程中只会被加载一次。

Class 对象的使用

获取成员变量

Field[] getFields()package com.cunyu;import java.lang.reflect.Field;/*** @author : cunyu* @version : 1.0* @className : Demo2* @date : 2021/4/7 23:39* @description : Class 对象的使用*/
//加入Java开发交流君样:756584822一起吹水聊天
public class Demo2 {public static void main(String[] args) throws ClassNotFoundException {Class class1 = Class.forName("com.cunyu.Person");Field[] fields = class1.getFields();for (Field field : fields) {System.out.println(field);}}
}

在这里插入图片描述
回顾下我们的 Person 类,可以发现 id、grade 成员变量都是被 public 所修饰的,说明该方法是用于获取类中所有被 public 所修饰的成员变量(包括父类)。

Field getField(String name)package com.cunyu;import java.lang.reflect.Field;/*** @author : cunyu* @version : 1.0* @className : Demo2* @date : 2021/4/7 23:39* @description : Class 对象的使用*/
//加入Java开发交流君样:756584822一起吹水聊天
public class Demo2 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class class1 = Class.forName("com.cunyu.Person");Field field1 = class1.getField("id");System.out.println(field1);Field field2 = class1.getField("age");System.out.println(field2);Field field3 = class1.getField("rank");System.out.println(field3);}
}

在这里插入图片描述
在这里插入图片描述
从上面的结果分析可知,该方法只能用于获取类中指定名称的 public 所修饰的成员变量,对于 protected、private 所修饰的成员变量,该方法是无法获取的(包括父类)。而获取或设置成员变量值时,可以通过 get/set 方法来操作,具体操作方法如下。

// 假设我们获取到的 Field 为上面的 id,获取和设置 id 的值就可以通过如下操作来进行
// 1. 获取
Field idField = personClass.getField("id");
Person person = new Person();
Object idValue = idField.get(person);
System.out.println("id:" + idValue);
// 2. 设置
idField.set(person, "1312120");
System.out.println("person:" + person);
Field[] getDeclaredFields()
//加入Java开发交流君样:756584822一起吹水聊天
package com.cunyu;import java.lang.reflect.Field;/*** @author : cunyu* @version : 1.0* @className : Demo2* @date : 2021/4/7 23:39* @description : Class 对象的使用*/public class Demo2 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class class1 = Class.forName("com.cunyu.Person");Field[] fields = class1.getDeclaredFields();for (Field field : fields) {System.out.println(field);}}
}
```![在这里插入图片描述](https://img-blog.csdnimg.cn/20210514213328192.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dqMTMxNDI1MA==,size_16,color_FFFFFF,t_70)
观察上面的结果可知,该方法可用于获取所有的成员变量,不用考虑修饰符的限制(不包括父类)。
```c
Field getDeclaredField(String name)package com.cunyu;import java.lang.reflect.Field;/*** @author : cunyu* @version : 1.0* @className : Demo2* @date : 2021/4/7 23:39* @description : Class 对象的使用*/
//加入Java开发交流君样:756584822一起吹水聊天
public class Demo2 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class class1 = Class.forName("com.cunyu.Person");Field field1 = class1.getDeclaredField("id");System.out.println(field1);Field field3 = class1.getDeclaredField("rank");System.out.println(field3);Field field2 = class1.getDeclaredField("age");System.out.println(field2);}
}

在这里插入图片描述
观察上面的结果可知,该方法可用于获取指定的成员变量,不用考虑成员变量修饰符的限制(不包括父类)。但是在利用 set、get 方法来获取和设置 private、protected 修饰的成员变量时,需要利用 setAccessible() 来忽略访问全新啊修饰符的安全检查,否则程序将会报错。

获取构造方法

package com.cunyu;import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;/*** @author : cunyu* @version : 1.0* @className : Demo3* @date : 2021/4/8 13:28* @description : 构造对象获取*/public class Demo3 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {Class personClass = Class.forName("com.cunyu.Person");//        1. 获取所有构造方法System.out.println("所有构造方法");Constructor[] constructors = personClass.getConstructors();for (Constructor constructor : constructors) {System.out.println(constructor);}
//加入Java开发交流君样:756584822一起吹水聊天
//        2. 获取指定构造方法//        空参构造方法System.out.println("空参构造方法");Constructor constructor1 = personClass.getConstructor();System.out.println(constructor1);
//`        带参构造方法System.out.println("带参构造方法");Constructor constructor2 = personClass.getConstructor(int.class, String.class, long.class, long.class, float.class, int.class);System.out.println(constructor2);
//加入Java开发交流君样:756584822一起吹水聊天
//        获取构造方法后,可以利用它来创建对象System.out.println("空参创建对象");
//        第一种方法Object person = constructor1.newInstance();System.out.println(person);
//        第二种方法Object person1 = personClass.newInstance();System.out.println(person1);System.out.println("带参创建对象");Object object = constructor2.newInstance(20, "村雨遥", 1312020, 3, 99.0F, 2);System.out.println(object);}
}

在这里插入图片描述
Constructor<?>[] getConstructors()

类似于通过 Class 实例来获取成员变量,该方法用于获取所有 public 所修饰的构造方法(包括父类);

Constructor<T> getConstructor(类<?>... parameterTypes)

该方法用于获取某一指定参数类型后的 public 所修饰的构造方法(包括父类);

Constructor<?>[] getDeclaredConstructors()
该方法用于获取所有 public 所修饰的构造方法(不包括父类);

Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
该方法用于获取某一指定参数类型后的 public 所修饰的构造方法(不包括父类);

而获取到构造方法之后,我们就可以利用 newInstance() 方法来创建类的实例。特殊的,如果我们的构造方法是无参的,此时则可以直接利用 Class.newInstance()来构造实例。

获取成员方法

package com.cunyu;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;/*** @author : cunyu* @version : 1.0* @className : Demo4* @date : 2021/4/8 13:51* @description : 成员方法获取*/public class Demo4 {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {Class personClass = Class.forName("com.cunyu.Person");//        获取所有 public 成员方法System.out.println("获取所有成员方法");Method[] methods = personClass.getMethods();for (Method method : methods) {System.out.println(method);}
//加入Java开发交流君样:756584822一起吹水聊天
//        获取指定名称的方法System.out.println("获取指定名称的方法");Method getAgeMethod = personClass.getMethod("getAge");System.out.println(getAgeMethod);//        执行方法Person person = new Person(20, "村雨遥", 1312020, 3, 99.0F, 2);int age = (int) getAgeMethod.invoke(person);System.out.println(age);}
}

在这里插入图片描述
Method[] getMethods()
用于获取当前类的所有 public 所修饰的成员方法(包括父类)。

Method getMethod(String name, 类<?>… parameterTypes)
用于获取当前类的某一个指定名称 public 所修饰的成员方法(包括父类)。

Method[] getDeclaredMethods()
用于获取当前类的所有 public 所修饰的成员方法(不包括父类)。

Method getDeclaredMethods(String name, 类<?>… parameterTypes)
用于获取当前类的某一个指定名称 public 所修饰的成员方法(不包括父类)。

而当我们获取到类的成员方法后,如果要执行某一个方法,可以使用 invoke() 方法来执行该方法。

获取类名

package com.cunyu;/*** @author : cunyu* @version : 1.0* @className : Demo5* @date : 2021/4/8 14:06* @description : 获取类名*/
//加入Java开发交流君样:756584822一起吹水聊天
public class Demo5 {public static void main(String[] args) throws ClassNotFoundException {Person person = new Person();Class personClass = person.getClass();String className = personClass.getName();System.out.println(className);}
}

在这里插入图片描述
String getName()
从上述程序的结果可知,当我们获取到 Class 对象之后,如果不知道类的全名,就可以使用 getName() 来获取该类的全名。

反射实例

假设我们有如下需求:在不改变类的代码的前提下,我们能够创建任意类的对象,并执行其中的方法。

此时,我们可以通过 配置文件 + 反射 的方式来实现这一效果,而这也就是我们现在所用框架中的基础,当我们使用反射后,只需要通过修改配置文件中的内容就能够不用去改代码就实现对应的功能。

假设我们有两个类,一个 Student,一个 Teacher,两者的定义如下;

package com.cunyu;
//加入Java开发交流君样:756584822一起吹水聊天
/*** @author : cunyu* @version : 1.0* @className : Teacher* @date : 2021/4/8 15:15* @description : 教师类*/public class Teacher {private String name;private int age;public void teach() {System.out.println("教书育人……");}
}package com.cunyu;
//加入Java开发交流君样:756584822一起吹水聊天
/*** @author : cunyu* @version : 1.0* @className : Student* @date : 2021/4/8 15:16* @description : 学生类*/public class Student {private String name;private float score;public void study() {System.out.println("好好学习,天天向上……");}
}

要实现我们的需求,通常需要如下步骤:

将要创建对象的全类名和要执行的方法都配置在配置文件中;
定义的配置文件 prop.properties ,其中主要内容包括 className 和 methodName 两个属性,分别代表类的全类名和要调用方法的名字。一个具体实例如下,分别代表名为 Student 的类和名为 study 的方法。

className=com.cunyu.Student
methodName=study

然后在主方法中加载读取配置文件;

//        创建配置文件对象
Properties properties = new Properties();
//        加载配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream("prop.properties");
properties.load(inputStream);//        获取配置文件中定义的数据
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
利用反射技术将类加载到内存中;//        加载进内存
Class name = Class.forName(className);
接着利用 newInstance() 方法创建对象;//        创建实例
Object object = name.newInstance();
最后则是利用 invoke() 方法来执行方法;
//加入Java开发交流君样:756584822一起吹水聊天
//        获取并执行方法
Method method = name.getMethod(methodName);
method.invoke(object);

将整个流程汇总起来就是:

package com.cunyu;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;/*** @author : cunyu* @version : 1.0* @className : ReflectTest* @date : 2021/4/8 15:27* @description : 测试*/public class ReflectTest {public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
//        创建配置文件对象Properties properties = new Properties();
//        加载配置文件ClassLoader classLoader = ReflectTest.class.getClassLoader();InputStream inputStream = classLoader.getResourceAsStream("prop.properties");properties.load(inputStream);
//加入Java开发交流君样:756584822一起吹水聊天
//        获取配置文件中定义的数据String className = properties.getProperty("className");String methodName = properties.getProperty("methodName");//        加载进内存Class name = Class.forName(className);//        创建实例Object object = name.newInstance();//        获取并执行方法Method method = name.getMethod(methodName);method.invoke(object);}
}

此时,我们只需要改动配置文件 prop.properties 中的配置即可输出不同结果;

`在这里插入图片描述
在这里插入图片描述
最后,祝大家早日学有所成,拿到满意offer

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/299729.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

拿破仑最欣赏的数学家,师从拉格朗日,撕逼泊松,一生痴迷热学最后却死于热学...

全世界只有3.14 % 的人关注了 青少年数学之旅 这几天&#xff0c;终于变凉了&#xff0c;超模君再也不用被热死了。 但炎热夏日的离去&#xff0c;也让超模君想起了那个和热脱不了关系的数学家——傅里叶。 “对自然界的深入研究是数学发现最丰富的源泉” ——节选自 《数学之旅…

记一次 .NET 某智慧水厂API 非托管内存泄漏分析

一&#xff1a;背景 1. 讲故事七月底的时候有位朋友在wx上找到我&#xff0c;说他的程序内存占用8G&#xff0c;托管才占用1.5G&#xff0c;询问剩下的内存哪里去了&#xff1f;截图如下&#xff1a;从求助内容看&#xff0c;这位朋友真的太客气了&#xff0c;动不动就谈钱&…

直接开撸! 阿里Spring高频面试题泄露,持续更新~

阿里HR面试核心的几个问题&#xff1a; 1、你为什么离职&#xff1f; 2、你怎么看待自己的职业生涯&#xff1f;&#xff08;包括怎么平衡家庭和工作的关系、加班、选择一家公司时的考量、location等&#xff09; 3、薪资期望 4、项目中遇到的最大挑战是什么&#xff1f;怎么…

重磅!Nature盘点年度十佳论文,生命科学占据半壁江山,中国学者表现亮眼

全世界只有3.14 % 的人关注了青少年数学之旅科学是人类进步的阶梯&#xff01;在当今社会&#xff0c;科学技术的发展进步将为人类社会带来巨大的效益&#xff0c;毫不夸张的说&#xff0c;科学指引并推着着人类文明的进程。基础科学作为科学技术的理论基石&#xff0c;其重要性…

zend guard6的使用

1.生成key edit->preferences->license Keys->generate 2.新建product license文件 3.新建Zend Guard项目文件 需要注意新建项目的第二项需要英文路径 4.在项目上按右键 选择configure 初始界面是 如果要用做授权,点击security按键 然后设置license文件等 附上php.in…

理解T-SQL: 脚本和批处理

脚本能够将相应的T-SQL命令组织起来&#xff0c;实现一个完整的功能目标。脚本提供了变量、分支、循环等控制语句&#xff0c;可以用来实现一些复杂的任务。通过组织一系列的SQL命令编成脚本和批处理&#xff0c;也减低了数据库管理的复杂性。 1. USE语句 没啥好讲的&#xff0…

凉凉!面试阿里我被Redis技术专题给搞的昏倒在地~

凉凉&#xff01;面试阿里我被Redis技术专题给弄死了~ &#x1f4da;我本以为我可以像是别的博主一样去阿里面试随随便便&#xff0c;因为Redis&#xff0c;我直接被阿里大佬淦翻在地上 好了不装了 没过没关系 我总结了一些这些最难的知识点&#xff01;&#xff01;&#xff0…

与众不同 制作会唱歌的WinRAR - imsoft.cnblogs

为了使用方便&#xff0c;我们可能会把RAR压缩包制作成自解压文件。WinRAR自带的自解压模块虽然使用很方便&#xff0c;但千篇一律的外观看起来实在 乏味。其实&#xff0c;只要通过简单改造&#xff0c;你就可以制作出与众不同&#xff0c;声色俱佳的WinRAR自解压界面&#xf…

看到这一幕,我甚至都想戒烟了。。 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;

c#3.0关于JSON简单操作的实用帮助类(泛型实现)

关于json的操作&#xff0c;网上一搜一大把。避免重复造轮子&#xff0c;整理发布一个笔者在项目开发中经常用到的一个json实用帮助类&#xff0c;泛型实现&#xff0c;非常方便。不废话了&#xff0c;贴代码&#xff1a;1、json操作的帮助类&#xff1a; Codeusing System;usi…

谁还没个黑历史了。。。 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;

C# 虚方法与抽象方法区别

虚方法是必须要实现的&#xff0c;只有接口或者抽象方法才没有实现体&#xff0c;什么是实现体&#xff0c;就是方法的那对大括号&#xff1a;下面举例&#xff1a;public abstract void Fn1();//定义一个抽象方法 public vitrual void Fn2(){ };//定义一个虚方法(那对大括号必…

京东面试官:呦,你对中间件 Mycat了解的还挺深~

1.数据切分概念 数据的切分[&#xff08;Sharding&#xff09;]根据其切分规则的类型&#xff0c;可以分为两种切分模式。一种是按照不同的表&#xff08;或者Schema&#xff09;来切分到不同的数据库&#xff08;主机&#xff09;)之上&#xff0c;这种切可以称之为数据的垂直…

Redis与Memcached的区别

2019独角兽企业重金招聘Python工程师标准>>> 传统MySQL Memcached架构遇到的问题 实际MySQL是适合进行海量数据存储的&#xff0c;通过Memcached将热点数据加载到cache&#xff0c;加速访问&#xff0c;很多公司都曾经使用过这样的架构&#xff0c;但随着业务数据量…

“高仿版拼多多”宣告破产!曾一年收割1.3亿用户,如今自救失败负债16亿

全世界只有3.14 % 的人关注了青少年数学之旅又一家改变世界的互联网公司跑路了啊&#xff1f;挣扎数月的淘集集最终也回天乏术了。淘集集并购重组最终失败&#xff0c;供应商何时能拿到被拖欠的货款仍然遥遥无期。这家主打比“拼多多”更下沉市场的社交电商&#xff0c;平台上线…

处在哪个阶段?

在职业生涯中&#xff0c;可以划分出多个阶段&#xff0c;但是如下3个阶段也许是最粗糙和实在的&#xff1a; 1、入门级&#xff0c;在别人的领导下可以完成工作。 2、成熟级&#xff0c;可以独立完成工作。 3、精英级&#xff0c;可以领到别人完成工作。 你处在哪个阶段&#…

在VS Code中直接调试Web程序,是怎样一种体验?

前言要在VS Code中启动Chrome或Microsoft Edge中调试任何Web程序&#xff0c;您只需按F5或选择菜单"运行"->"启动调试"来启动会话。你可以选择在Chrome、Edge或Node.js中进行调试。Edge DevTools如果选择了Edge进行调试&#xff0c;你会发现调试工具条上…

面试官问:你做过什么Java线程池实践,我写了一篇博客给他看~

线程池大家都## 标题很熟悉&#xff0c;无论是平时的业务开发还是框架中间件都会用到&#xff0c;大部分都是基于JDK线程池ThreadPoolExecutor做的封装&#xff0c; 都会牵涉到这几个核心参数的设置&#xff1a;核心线程数&#xff0c;等待(任务)队列&#xff0c;最大线程数&am…

【Blog.Idp开源】支持在线密码找回

&#xff08;一个做认证平台&#xff0c;必须会遇到的一个问题&#xff09;BCVP框架&#xff0c;是基于:ASP.NETCore5.0VUE.jsIdentityServer4等核心技术&#xff0c;实现的前后端分离与动态认证鉴权一体化平台。01密码找回认证中心绕不开的话题Architecture Design.无论你是自…

我的狗丢了,所以我能加你微信吗? | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅&#xff08;图源网络&#xff0c;侵权删&#xff09;