前言
最近在学习Spring框架的AOP时,想先去了解一下动态代理,看其它博客都说动态代理的思想和Java反射机制相关,本着去找其它博客节省时间的原则,然后我就越看越懵逼了,因为Java反射机制是很重要的,也急不得,因此我选择看视频+复习其它优质文章来着重学习一下,想写出小白也能看懂的博文,也欢迎各位大佬指正~一 Java反射机制基础
1.理解反射
如果一上来就堆一堆概念,小白看了落泪,大佬看得"心累"。
那我们就来讲讲它的应用场景:我们都知道,我们在编译器编写的代码往往都是经过:编写—>编译—>运行,细心的人知道,如果我们build代码后,会生成对应的.class文件,再将生成的.class文件通过类加载器加载到JVM中.
也就是说,如果我的代码一旦编译运行,生成.class对象后里面的代码就等于是“写死”的了,举一个狂神视频中的例子:游戏对局开始后,有人开外挂,外挂中的代码如何动态地加入会调用原来的代码呢【不通过重新编译】?同样地,有时候程序编译运行后我们需要动态地获得已经编译好的对象,这时Java给我们提供了一个解决方案:反射
Java是一门静态语言,但是通过反射机制它变成了一门准动态语言
2.概念介绍
通过上面的简单理解后,我们来精简清楚地阐明一下反射的概念:
反射机制允许程序在执行期间借助Reflection API(反射API)来取得任何类的内部信息,并且能够操作任意对象内部属性和方法。
3.应用场景
- 在运行时需要判断一个对象所属的类
- 在运行时需要判断一个类的成员变量和方法
- 在运行时需要获取泛型的信息
- 在运行时需要构造一个类的对象
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
二 实战演练
1. 得到Class类的三种方式
- 通过Class.forName()静态方法获取
- 通过实例的getClass()方法获取
- 通过类的".class"属性进行获取
实例演示
package com.hang.reflection;
import com.hang.reflection.pojo.Animal;
import com.hang.reflection.pojo.Monkey;
public class ReflectionDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
//1.通过Class.forName获取
Class class1 = Class.forName("com.hang.reflection.pojo.User");
//2.通过实例的getClass方法获取
Animal monkey=new Monkey("金丝猴",14.5,800);
Class class2 = monkey.getClass();
//3.通过类的".class"属性获取
Class class3 = Animal.class;
Class class4 = Monkey.class;
//通过它们之间的hashCode来判断对象所属是否是同一个Class
System.out.println(class1.hashCode());
System.out.println(class2.hashCode());
System.out.println(class3.hashCode());
System.out.println(class4.hashCode());
}
}
结果分析
通过三种方式获得了对应的class,并且我们注意到第二个hashCode和第四个hashCode是相同的,说明了一个加载的类在JVM中只有一个Class实例。
2.反射获得运行时结构及方法
从上面的三种方式获得了Class后,才是到了反射的重要部分,因为我们不可能消耗资源和性能仅仅是重新获得运行时的Class类,而是要通过Class类来做一些编译时没有完成的事情。
- 获得类的名字
- 获得类的属性
- 获得指定属性的值
- 获得类的方法
- 获得类的指定方法
- 获得类的构造器
- 获得类的指定的构造器
代码:
package com.hang.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionDemo2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class class1 = Class.forName("com.hang.reflection.pojo.User");
/***获得类的名字
* 1.getName()获取的是:包名+类名
* 2.getSimpleName():只获得类名
*/
System.out.println(class1.getName());
System.out.println(class1.getSimpleName());
System.out.println("---------------------------");
/***获得类的属性
* 1.getFields():只能获得public属性
* 2.getDeclaredFields():能获得所有属性
*/
Field[] fields = class1.getFields();
for (Field field : fields) {
System.out.println("public:"+field);
}
Field[] declaredFields = class1.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
System.out.println("------------------------------");
/****获得指定属性的值
* 1.getFiled(String name):找到指定公共属性的值
* 2.getDeclaredFiled(String name):找到指定所有属性的值
*/
// Field name = class1.getField("name");
// System.out.println(name);
Field age = class1.getDeclaredField("age");
System.out.println(age);
System.out.println("------------------------------");
/****获得类的方法
* 1.getMethods(): 获得所有public方法
* 2.getDeclaredMethods(): 获得所有方法
* ps:注意获得的方法中也会包括父类的方法
*/
Method[] methods = class1.getMethods();
for (Method method : methods) {
System.out.println(method);
}
Method[] declaredMethods = class1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
System.out.println("------------------------------");
/****获得类的指定方法
* 1.getMethods(): 获得指定的public方法
* 2.getDeclaredMethods(): 获得指定的方法
*/
//String.class --->避免重载带来的二义性
Method setName = class1.getMethod("setName", String.class);
System.out.println(setName);
System.out.println("-------------------------------");
//获得包括private的指定方法就不再演示了
/*****获得类的构造器
* 1.getConstructors():获得类中公共类型的构造器
* 2.getDeclaredConstructors():获得类的所有的构造器
*/
Constructor[] constructors = class1.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("---------------------------------");
/****获得类中指定类型的构造器
* 1.getConstructor([xxx.class,xxx.class,...]);
* 2.getDeclaredConstructor([xxx.class,xxx.class,...])
*/
Constructor declaredConstructor = class1.getDeclaredConstructor(String.class,String.class,int.class);
System.out.println(declaredConstructor);
}
}
3.反射应用的实战
用无限循环模拟程序在运行使用中,然后通过反射机制对已编译的代码进行动态生成对象并调用方法等操作
package com.hang.reflection;
import com.hang.reflection.pojo.User;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;
public class ReflectionDemo3 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Scanner sc=new Scanner(System.in);
System.out.println("1.创建一个对象;2.获取对象的有参构造器;3.调用对象的指定方法;4.调用对象的指定参数;5.退出程序");
Class class1=Class.forName("com.hang.reflection.pojo.User");
User user2 = null;
//使用循环模拟程序的运行时
out:while(true){
int choice=sc.nextInt();
switch (choice){
case 1:{
User user = (User) class1.newInstance();
System.out.println("新建了一个对象user,这个user的信息:"+user);
continue;
}
case 2:{
Constructor constructor = class1.getDeclaredConstructor(String.class, String.class, int.class);
user2 = (User) constructor.newInstance("航航", "1000", 22);
System.out.println("通过有参构造器构造了一个新的user,这个user信息是:"+user2);
continue;
}
case 3:{
Method method = class1.getMethod("setName", String.class);
//激活得到的方法
method.invoke(user2,"航航2号");
System.out.println("修改用户信息后的user2:"+user2);
continue;
}
case 4:{
/**通过反射操作属性
* 通常类的属性是私有的,我们不能直接操作它,因此下面的方法也是通过暴力方式操作属性
* 是不安全的,不建议使用
*/
Field id = class1.getDeclaredField("id");
//开启修改私有的权限
id.setAccessible(true);
id.set(user2,"1001");
System.out.println("修改用户属性后的user2:"+user2);
continue;
}
case 5:{
break out;
}
}
}
}
}
运行结果:
总结
理解Java的反射机制是非常重要的,不仅仅要理解反射机制的应用,还要对它的原理和对JVM都要有一定的了解。通过对java的反射机制较为简单的学习后,后面我也会出一篇关于代理模式的博客简讲,作为巩固和share~