反射,指的是对于任意一个类,都可以动态的获得它的所有属性和方法,对于任意一个对象都能调用的它的所有属性和方法,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
想要理解反射首先我们要知道JVM也就是java的虚拟机,java能够跨平台也是因为它,JVM说白了也就是一个进程,只不过是用来跑你的代码的。
上图是java的内存模型,我们关注的点,一个方法区,一个栈,一个堆,初学的时候老师不深入的话只告诉你java的内存分为堆和栈,易懂点吧!
加载一个类主要有三个阶段:编译加载、连接、初始化
假如你写了一段代码 Object o=new Object()
首先你的代码会被编译成一个.class字节码文件,然后这个字节码文件会被类加载器也就是ClassLoader加载到内存中,并且创建一个class对象(每一个类都会有一个且只有一个class对象)。加载器 ClassLoader 加载class文件时,会把类里的一些数值常量、方法、类信息等加载到内存中,称之为类的元数据,最终目的是为了生成一个Class对象用来描述类,这个对象会被保存在.class文件里。
一个类的元数据会被转换成方法区中的运行时数据
成员变量---->Field类的对象,可以有多个,所以Field[]
构造器---->Constructor类的对象,可以有多个,所以Constructor[]
方法---->Method类的对象,可以有多个,所以Method[]
编译完成之后会来到连接阶段,将java类的二进制代码合并到JVM的运行状态中的过程,它会做这几件事
- 验证:验证你的代码是否有问题,是否有安全方面的问题
- 准备:将类变量进行分配内存并初始化,这些内存是在方法区中分配的
- 解析:将常量池中的符号引用替换成直接引用,在编译的时候每个java类都会被编译成一个class文件,但在编译的时候虚拟机并不知道所引用类的地址,所以就用符号引用来代替,而在解析阶段就是为了把这个符号引用转化成真正的地址的阶段。
接下来就是初始化阶段了
- 执行类构造器clinit,类构造器是由编译阶段自动收集所有类变量的复制动作和静态代码块中的动作语句合并产生的(类构造器是构造类信息的,不是该类的构造器)
- 初始化一个类的时候会首先去检查父类是否初始化,如果没有会首先初始化父类
- jvm会保证一个类的clinit方法在多线程环境下被争取的加锁和同步
那么什么时候会发生类的初始化呢?
类的主动引用(一定会发生类的初始化)
类的被动引用(不会发生类的初始化)
类加载器
类加载器的作用:将class字节码文件加载到内存中,并将这些静态数据装换成方法区的运行时数据,然后再堆中生成一个代表这个类的java.lang.Class对象,作为方法区中数据的访问入口
类缓存:标准的JavaSE类加载器可以按照要求查找类,但一旦某一个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过GC垃圾回收机制可以回收这些Class对象
JVM规范定义了如下类型的类加载器
双亲委派机制
当我们需要加载一个类的时候,首先AppClassLoader会检查自己是否加载过,如果加载过则无需加载,否则就会拿到父加载器,父加载器同样会进行检查,一直到Bootstrap classLoader,而不是先看自己是否能加载,到了Bootstrap classLoader之后,如果Bootstrap classLoader不能加载,则会下沉到子加载器,如果可以则加载,如果不可以则继续下沉,一直到AppClassLoader,如果还是无法加载,则抛出异常ClassNotFoundException
我们能够获取的运行时的class类的完整结构结构
有两种类型获取,当然前提是你有这个类
那么这两种方式有什么区别了,区别在于第一种方法只能获取公有的东西,而第二种是获取全部
需要注意的是获取方法的时候,getMethod可以获取到本类及其父类的方法,而getDeclaredMethod只能获取到本类
如何去调用方法和修改属性
方法.invoke(类对象)
Class c1 = Class.forName("experiment.B");
B b = (B) c1.getConstructor(null).newInstance();
Method method=c1.getDeclaredMethod("A",null);
method.invoke(b);
属性.set(类对象,设置成什么值)
Class c1 = Class.forName("experiment.B");
B b = (B) c1.getConstructor(null).newInstance();
Field field=c1.getDeclaredField("name");
field.set(b,"orange");
System.out.println(field.get(b));
newInstance()和new()
JAVA9之后的版本请使用构造器创建对象,class.getDeclaredConstructor().newInstance()
需要注意的是使用newInstance()必须满足两个条件:1.这个这个类已经加载了。2.这个类已经链接
下面说说如何获取泛型信息
getGenericParameterTypes 获取所有泛型参数信息
getGenericReturnType 获取泛型返回值信息(返回值只有一个)
getGenericExceptionTypes 获取所有泛型错误信息
ParameterizedType 真实参数
public class B {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Class c1 = Class.forName("my.genericity");
Method method = c1.getMethod("aaa", Map.class);
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println(genericParameterType);
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
}
泛型还可以越过泛型检查,请看下面
public class B {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
ArrayList<Integer> arr=new ArrayList<>();
Class<? extends ArrayList> aClass = arr.getClass();
Method add = aClass.getMethod("add", Object.class);
add.invoke(arr,"hello");
add.invoke(arr,"world");
add.invoke(arr,"orange");
System.out.println(arr);
}
}
运行结果:
我们定义了一个Integer类型的集合,但是我们居然可以存String类型的数据,这就是反射牛逼之处的体现
通过配置文件来指定内容
public class B {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, InstantiationException {
Properties prop=new Properties();
FileReader fr=new FileReader("data.txt");
prop.load(fr);
fr.close();
String className = prop.getProperty("ClassName");
String methodName = prop.getProperty("MethodName");
Class<?> aClass = Class.forName(className);
Constructor<?> con1 = aClass.getConstructor(String.class);
Object o = con1.newInstance("libo");
Method method = aClass.getMethod(methodName);
System.out.println(method.invoke(o));
}
}
如有不对欢迎联系改正!!!感谢阅读