什么是反射
将类的各个组成部分封装为其他对象的过程就叫做 反射,其中 组成部分 指的是我们类的 成员变量(Field)、构造方法(Constructor)、成员方法(Method)。
使用反射的优缺点
- 优点
-
在程序运行过程中可以操作类对象,增加了程序的灵活性;
-
解耦,从而提高程序的可扩展性,提高代码的复用率,方便外部调用;
-
对于任何一个类,当知道它的类名后,就能够知道这个类的所有属性和方法;而对于任何一个对象,都能够调用它的一个任意方法。
- 缺点
-
性能问题:Java 反射中包含了一些动态类型,JVM 无法对这些动态代码进行优化,因此通过反射来操作的方式要比正常操作效率更低。
-
安全问题:使用反射时要求程序必须在一个没有安全限制的环境中运行,如果程序有安全限制,就不能使用反射。
-
程序健壮性:反射允许代码执行一些平常不被允许的操作,破坏了程序结构的抽象性,导致平台发生变化时抽象的逻辑结构无法被识别。
获取 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;
}
public void setScore(float score) {
this.score = score;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
@Override
public 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);
// 第二种方式,类名.class
Class 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()
| 返回包含一个数组 Field
对象反射由此表示的类或接口的所有可访问的公共字段类对象 |
| Field getField(String name)
| 返回一个 Field
对象,它反映此表示的类或接口的指定公共成员字段类对象 |
| Field[] getDeclaredFields()
| 返回的数组 Field
对象反映此表示的类或接口声明的所有字段类对象 |
| Field getDeclaredField(String name)
| 返回一个 Field
对象,它反映此表示的类或接口的指定已声明字段类对象 |
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 对象的使用
*/
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 对象的使用
*/
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()
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);
}
}
}
观察上面的结果可知,该方法可用于获取所有的成员变量,不用考虑修饰符的限制(不包括父类)。
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 对象的使用
*/
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()
来忽略访问全新啊修饰符的安全检查,否则程序将会报错。
获取构造方法
| 方法 | 说明 |
| :-- | :-- |
| Constructor<?>[] getConstructors()
| 返回包含一个数组 Constructor
对象反射由此表示的类的所有公共构造类对象 |
| Constructor<T> getConstructor(类<?>... parameterTypes)
| 返回一个 Constructor
对象,该对象反映 Constructor
对象表示的类的指定的公共类函数 |
| Constructor<?>[] getDeclaredConstructors()
| 返回一个反映 Constructor
对象表示的类声明的所有 Constructor
对象的数组类 |
| Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
| 返回一个 Constructor
对象,该对象反映 Constructor
对象表示的类或接口的指定类函数 |
package com.cunyu;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后:学习总结——MyBtis知识脑图(纯手绘xmind文档)
学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)
除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后:学习总结——MyBtis知识脑图(纯手绘xmind文档)
学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)
[外链图片转存中…(img-e64zGAYR-1711976407883)]
除此之外,前文所提及的Alibaba珍藏版mybatis手写文档以及一本小小的MyBatis源码分析文档——《MyBatis源码分析》等等相关的学习笔记文档,也皆可分享给认可的朋友!
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!