欢迎关注本人公众号:Bean冷的心,内容包含计算机网络、数据结构与算法、科技资讯和知识扫盲,期待结实各位大佬和对计算机感兴趣的小伙伴~
文章目录
想要搞明白反射到底是什么,首先要知道什么是反射?反射有什么用,为什么需要反射。首先我们看一下反射的定义:
一、定义
JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
二、定义解释
这样文绉绉的定义实在是难理解,而且到底什么是在运行状态,对于任意一个实体类,都能够知道这个类的所有属性和方法呢??
我们首先看个简单例子:
相信大家平时肯定写过这样的代码吧,String类的对象,通过点“ . ”,就可以调用String类封装好了的方法,虽然我们很熟悉这样的方法,可是Idea是怎么知道有这些方法的呢?
答案就是反射,在解释这个问题之前,我们先来了解一下类加载机制是什么。
三、什么是类加载机制
假如我们写了一个类,叫Student,众所周知类一般是有三部分的:
将其浓缩一下就是这样:
.java源文件通过javac编译成.class字节码文件之后,结构不变,还是存储在硬盘中,然后此时,我们就可以使用Student类了
Student stu = new Student();
stu.run();
可是我们常常忽略了字节码文件是如何被加载到内存当中去的,字节码文件通过一个叫做类加载器(ClassLoader)的东西,将类加载到内存当中去。类加载器中有一个Class对象,封装了类的基本信息,包括属性、构造方法、普通方法,所以结构如图:
我们再回头看前面的问题,正是因为Class对象封装了当前类所有的成员方法,所以将具体方法显示到IDEA中,就可以达到通过点“ . ”调用方法时,能看到类的所有成员方法了。
四、获取Class对象的方式
获得Class对象,总共有三种方法:
方法一
Class.forName("全类名");
方法二
类名.class获取
Student.class;
方法三
由于所有类都是继承Object类,所以Object的所有方法,子类都可以用,Object类中有一个方法就是getClass();,所以可以通过对象名调getClass();方法。
对象.getClass();
看个例子
打印结果:
可以看出来这三种方式生成的Class对象其实都是一样的。
结论
同一个字节码文件(*.class)一次编译,只会被加载一次,无论是哪种加载方法,最终生成的Class对象都是一样的。
五、使用Class对象
1.获得成员变量们
Field[] getFields() 获得所有public修饰的成员变量
Field getField(String name) 获得指定名称被public修饰的成员变量
Field[] getDeclaredFields() 获得当前包下所有方法
Field getDeclaredField(String name) 获得当前包下任一方法
2.获得构造方法们
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
3.获得成员方法们
Method[] getMethods()
Method getMethod(String name, Class<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, Class<?>... parameterTypes)
4.获得类名
String getName()
需要注意的是,虽然getDeclaredXXX()方法可以获取私有方法或属性,但是需要调用一个方法,以调用私有方法举例
method.setAccessible(true);
通过这个方法暴力破解!!!~一定要记牢了!!
六、方法调用案例
先介绍一下java.lang.reflect.Method的invoke方法:
public Object invoke(Object obj,
Object... args)
throws IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
invoke方法就相当于你调用你通过Student对象stu调用成员方法run,如果方法有参数,则传入方法所在对象和参数,无参数,则只传入方法所在对象。
以调用方法为例:
package com.bean.ioc.ReflectionDemo;
import java.lang.reflect.Method;
public class Demo03 {
public static void main(String[] args) throws Exception{
Class<Student> studentClass = Student.class;
Student stu = new Student();
//想找到带有参数的run方法,必须有两个必要条件,方法的名字和参数
Method run = studentClass.getMethod("run", String.class);
Method run1 = studentClass.getMethod("run");
//想要调用自己写的方法,需要两个必要条件,类的对象和方法传入的参数
run.invoke(stu,"小斌哥");
run1.invoke(stu);
int i = 1;
//获取当前类的所有public成员方法
Method[] methods = studentClass.getMethods();
for (Method method : methods) {
System.out.print(i+++" ");
System.out.println(method);
// method.setAccessible(true);加了这句话就可以获取私有方法了(暴力破解)
}
}
}
输出结果
小斌哥正在奔跑
我正在run
1 public static void com.bean.ioc.ReflectionDemo.Student.main(java.lang.String[]) throws java.lang.Exception
2 public void com.bean.ioc.ReflectionDemo.Student.run()
3 public void com.bean.ioc.ReflectionDemo.Student.run(java.lang.String)
4 public final void java.lang.Object.wait() throws java.lang.InterruptedException
5 public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
6 public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
7 public boolean java.lang.Object.equals(java.lang.Object)
8 public java.lang.String java.lang.Object.toString()
9 public native int java.lang.Object.hashCode()
10 public final native java.lang.Class java.lang.Object.getClass()
11 public final native void java.lang.Object.notify()
12 public final native void java.lang.Object.notifyAll()
Process finished with exit code 0
方法包括Student类的成员方法以及从Object父类继承来的方法
七、案例
好像说了这么多,也没说反射为什么占着灵魂的作用,我们来看一个例子:
需求
写一个框架,在不改变任意类的前提下,可以帮我们创建任意类的对象,并且执行其中任意的方法。
步骤
1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
2.在程序中加载读取配置文件
3.使用反射技术来加载文件到内存
4.创建对象
5.执行方法
package com.bean.ioc.ReflectionDemo;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 框架类
*/
public class ReflectUtil {
public static void main(String[] args) throws Exception {
//可以创建任意类,可以执行任意方法
/*
前提:不修改源码的前提下
*/
//1.加载配置文件
//1.1创建properties文件
Properties pro = new Properties();
//1.2加载配置文件
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectUtil.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类到内存
Class<?> cls = Class.forName(className);
//4.创建对象
Object o = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(o);
}
}
properties
className=com.bean.ioc.ReflectionDemo.Student
methodName=run
这样就在不改变原有代码的条件下,仅通过修改properties的文件就执行了相应的方法,可是原来的方法只需要两行就能调用run方法,为什么我们要写这么一大堆反射的东西还要弄properties呢?因为将来在开发大型项目的时候,如果修改源码中的一行代码,是需要重新测试的,通过反射加配置文件,仅仅修改物理文件properties是不会对源码产生影响的,可以节省大量的时间。
欢迎关注本人公众号:Bean冷的心,内容包含计算机网络、数据结构与算法、科技资讯和知识扫盲,期待结实各位大佬和对计算机感兴趣的小伙伴~