Java反射笔记1
概述:
- 反射是指对于任何一个Class类,在运行的时候能直接得到这个类的全部成分,这种运行时动态获取类信息以及动态调用类中成分的能力叫做Java反射
正常方式:引入需要的”包类”名称-》通过New实例化-》取得实例化对象
反射方式:实例化对象-》getClass()方法-》得到完整的包类名称
相关API:
java.lang.Class:代表一个类,Class对象表示某个类加载后在堆里的对象
java.lang.reflect.Method:代表类的方法,Method对象表示某个类的方法
java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
java.lang.reflect.Constructor:代表类的构造器,Constructor对象表示构造器
关键:
反射的第一步是先得到编译后的Class类对象,然后就可以得到Class的全部成分
HelloWorld.java ->javac ->HelloWorld.class
Class c = HelloWorld.class;
即在运行时获取类的字节码文件对象,然后可以解析类中的全部成分
功能:
- 判断任意一个对象所属的类
- 判断任意一个类的成员变量和成员方法
- 调用任意一个对象的方法
- 构造任意一个类的对象
- 动态代理
获取Class类对象的方法:
Class类静态方法forName(String className)(将字节码文件加载进内存返回Class对象)
Class c = Class.forName(全限名:包名+类名)#源代码阶段类名.class,通过类名的属性class#Class对象阶段
Class c1 = Student.class;
3.对象.getClass()获取实例化对象对应类的Class对象#Runtime运行阶段
Student s = new Student();
Class c2 = s.getclass();
1 | public class GetClassName { |
以上三种获取Class类方法中,使用类.class属性会需要我们导入类的包,依懒性较强;如果是使用实例化对象的getClass()方法,需要我们本身创建一个对象,不太方便反而偏离了反射机制,所以一般使用Class.forName方法,此方法不用导入其他类,能加载任意类
获取成员方法Method
Method getMethod(String name, 类<?>… parameterTypes) //返回该类所声明的public方法
Method getDeclaredMethod(String name, 类<?>… parameterTypes) //返回该类所声明的所有方法
//第一个参数获取该方法的名字,第二个参数获取标识该方法的参数类型Method[] getMethods() //获取所有的public方法,包括类自身声明的public方法,父类中的public方法、实现的接口方法
Method[] getDeclaredMethods() // 获取该类中的所有方法
以上是对反射的一些用途简述,但反射是比较抽象,也是看了很多教程才逐渐有个雏形,技术通常是因为需求才诞生的,所以提出一个需求来引出反射。
假如说有一个配置文件re.properties,内容为
1 | classfullpath=com.reflection.Cat |
根据配置文件的信息来创建对象并调用其方法
创建对象的话,常见的方法应该是建一个类然后New一个对象或者Clone一个 ,按照思路走一下,新建一个包(自定义),并创建RefletionDemo.java文件和Cat.java
1 | #Cat.java |
传统创建对象的方式:
1 | Cat cat = new Cat(); |
这里传统方法不适用于我们的需求,可以考虑文件流读取?
1 | Properties properties = new Properties(); |
以上代码获取了配置文件里类的全路径和方法名并输出,输出的结果和配置文件完全一致
然后
1 | new classfullpath()? |
这是错误的,因为new后面接类名,但是classfullpath是字符串类型,是很明显不行的
直接new.类的全路径是的可以,但直接new.classfullpath()是不行的,因为这里是字符串,所以传统的方法是不行的(这里的需求可以提炼为通过外部文件配置,在不修改源码的情况下控制程序)所以这里引出了反射
1 | #加载类,返回一个Class类型的对象cls,Class是一个Class类型的类 |
这里如果在Cat.java里增加新方法
1 | #Cat.java |
如果按照传统方法,只能修改源码将cat.hi()改为cat.cry()
如果按照反射,直接在配置文件里把method的值改为cry就行了
反射机制
- 反射允许程序在执行期借助于Refletion这个API取得任何类的内部信息,并能操作对象的属性以及方法
- 加载完类之后,在堆中产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息,通过这个对象得到类的结构
这里对于Class类再举个小例子,Class也是类,没有区别
1 | p对象 -》类型 person类 |
反射的底层原理:
Java程序在计算机有三个阶段,编译阶段.类加载阶段.Runtime阶段
1 | public class Cat { |
编译阶段中,源码里会有成员变量.成员方法.构造器等元素,经过javac编译后生成Cat.class字节码文件,源码里拥有的元素,字节码文件中也会有
在Runtime运行阶段中如果进行创建对象即
1 | Cat cat = new Cat(); |
会导致类加载,致使Cat.class字节码文件加载到内存里堆去,进入Class类加载阶段,生成一个Class类对象,其含有字节码里所有元素,在从字节码文件得到Class类对象的过程中有一个类加载器ClassLoader作用,在这里就体现了反射机制,在Class类对象中,会把元素比如成员变量当做对象映射为Field[] Field,构造器和成员方法等同理
类加载后就生成了Cat对象,该对象知道他是属于哪个Class对象,其间有一个映射关系,所以我们可以通过这个对象获得所关联的Class对象。当得到Class对象后就能够创建对象,调用对象方法,操作属性等
获取成员变量Field
获取成员变量Field位于java.lang.reflect.Field包中
Field[] getFields() :获取所有public修饰的成员变量
Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符
Field getField(String name) 获取指定名称的 public修饰的成员变量
Field getDeclaredField(String name) 获取指定的成员变量
得到name字段:
1 | #java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量 |
获取构造器对象
Class类中用于获取构造器的方法
Constructor[] getConstructors() :返回所有构造器对象的数组#仅public
Constructor[] getDeclareConstructors():返回所有构造器对象的数组(存在即可)
Constructor[] getConstructor(parameterTypes):返回单个构造器对象#仅public
Constructor[] getDeclareConstructor(parameterTypes):返回单个构造器对象,存在即可
1 | #java.lang.reflect.Constructor:代表类的成员变量,Constructor对象表示构造器 |