反射概述

什么是反射

将类的各个组成部分封装为其他对象的过程就叫做 反射,其中 组成部分 指的是我们类的 成员变量(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

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

  1. Java反射机制Reflection

    Java反射机制 1 .class文件 2 Class类 3 Class类与反射机制 4 Java反射机制的类库支持及简介 5 反射机制的定义与应用 6 反射机制Demo Java反射机制demo(一 ...

  2. java基础-java反射机制

    2019独角兽企业重金招聘Python工程师标准>>> 引述要学习Spring框架的技术内幕,必须事先掌握一些基本的Java知识,正所谓"登高必自卑,涉远必自迩" ...

  3. Class对象和Java反射机制

    一 前言 很多书上都说,在java的世界里,一切皆对象.其实从某种意义上说,在java中有两种对象:实例对象和Class对象.实例对象就是我们平常定义的一个类的实例: 1 2 3 4 5 6 /** ...

  4. java 对象复制 反射_利用Java反射机制实现对象相同字段的复制操作

    一.如何实现不同类型对象之间的复制问题? 1.为什么会有这个问题? 近来在进行一个项目开发的时候,为了隐藏后端数据库表结构.同时也为了配合给前端一个更友好的API接口文档(swagger API文档) ...

  5. 深入理解java反射机制

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

  6. java 反射机制详解

    火星十一郎 海纳百川, 有容乃大,QQ:791909235,Tel:13137910179 posts - 774, comments - 556, trackbacks - 0, articles ...

  7. 利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换

    作者:54dabang 在spring的学习过程之中,我们能够看出通过配置文件来动态管理bean对象的优点(松耦合 能够让零散部分组成一个总体,而这些总体并不在意之间彼此的细节,从而达到了真正的物理上 ...

  8. Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法...

    Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法 www.MyException.Cn   发布于:2012-09-15 ...

  9. Java反射机制分析指南

    一.JAVA是动态语言吗? 一般而言,说到动态言,都是指在程序运行时允许改变程序结构或者变量类型,从这个观点看,JAVA和C++一样,都不是动态语言. 但JAVA它却有着一个非常突出的动态相关机制:反 ...

最新文章

  1. 利用废弃建筑建设数据中心
  2. python 一些方法的时间测试
  3. mfc中加logo以及背景图
  4. 计算string长度 java_夯实基础:掌握从Java 9+开始的String的空间优化
  5. DoraCMS 源码知识点备注
  6. java 实体类 代码重复_java – JPA两个单向@OneToMany关系到同一个实体导致重复输入...
  7. Angular 自定义分页组件,自定义每页显示个数
  8. c语言中转义字符efg,c语言的转义字符与空白符
  9. C++基础知识(二)命名空间
  10. 鸿蒙系统自主研发,华为鸿蒙OS系统规划曝光,原来国产自主研发系统已经如此强大...
  11. python字典合并最高效_Python合并两个字典的常用方法与效率比较
  12. BMP/JPG/PNG/GIF/有损压缩和无损压缩【转载整理】
  13. List<String> 查找重复记录
  14. 机器学习笔试题精选(六)
  15. 苏超 计算机系 南京大学,Ni-Ti基合金薄膜相变行为及其力学特性研究
  16. SMMS:一个不错的免费图床
  17. 《神经网络与深度学习》中文版推荐
  18. Raspberry Pi 支持开源脑机接口
  19. 实现 iOS 内存检测工具
  20. webpack二刷之五、生产环境优化(3.sideEffects 副作用)

热门文章

  1. 分布式事务一致性解决方案
  2. WebAssembly:随风潜入夜
  3. java原子类场景,CAS你知道吗?原子类AtomicInteger的ABA问题谈谈?,原子共面问题...
  4. php symfony urlmatcher-gt;match,symfony路由组件(The Routing Component)
  5. Nginx- 实现跨域访问
  6. 解决 Cmder 的光标跟文字有个间距 及常用配置
  7. 腾讯、百度、小米等7家互联网各大厂的中台建设怎么样了?
  8. ArcGIS实验教程——实验十六:空间数据查询
  9. 多面体 (Multipatch)
  10. SVN之如何添加默认.a文件