反射的定义
在java程序运行时,动态获取其对象信息以及动态调用对象的一种反射机制。我们可以利用反射机制获取类中的成员变量,成员方法等,是java中的一种重要的机制。
反射的使用场景
初学者阶段我们可以使用反射获取某些类的私有化方法,私有化的成员变量。当我们学习框架后,可以发现发射几乎遍布整个框架的底层,我们可以使用反射去生成代理,动态获取对象,大大简化我们的开发流程,因此学好反射是非常重要滴!!!!
Java程序运行的三个阶段
前面讲了那么多的反射概念,我们现在先忘却一下反射的概念,先剖析一下java程序的一个运行流程。
当我们新建一个类的时候,我们会在类中定义成员变量,成员方法以及构造方法等。(如图所示)当我们在其他类中创建了这个Person对象之后,Person.java文件会被javac编译成Person.class文件。
这时javac会将Person类进行拆分管理,通过调用Class类的ClassLoader类加载器将Person通过放入Class对象当中,并根据类型进行管理,成员方法放入对应Class中Method[ ]数组中,成员变量放入Field[ ]数组中,而构造方法则放入Constructor[ ] 数组中。
这样来看,我们就可以从各种类型的数组中取出我们想要的类的方法和变量,甚至是该类的构造方法,无论是被private修饰的还是被public所修饰的。
反射的使用
对类的操作
新建一个Person类
public class Person {
public int userId=1;
private String userName="秃头披风侠";
int age;
public Person() {}
public Person(String userName) {
this.userName = userName;
}
public int getUserId() {
return userId;
}
public void test(){
System.out.println("测试。。。");
}
private void test2(){
System.out.println("测试2.。。。。");
}
}
获取Person的字节码对象的方式
类名.class
Person.class
对象名.getClass()
public class TestDemo {
public static void main(String[] args) {
Person person = new Person();
person.getClass();
}
}
Class.forName()
public class TestDemo {
public static void main(String[] args) throws ClassNotFoundException {
Class person = Class.forName("Person");//包名.类名
System.out.println(person);
}
}
获取类的名字
当我们获取字节码对象时,最好不要用class作为名称来引用,会与class关键字造成冲突。
package test;
public class TestDemo {
public static void main(String[] args) {
Person person = new Person();
Class clazz = person.getClass();
System.out.println(clazz.getName());//打印结果为 package(包名).类名==》test.Person
}
}
对类中成员变量的操作
获取类中的成员变量
clazz.getField()
通过变量名称获取变量的字节码对象
该方法只能获取被public修饰的成员变量
public static void main(String[] args) throws IllegalAccessException {
Person person = new Person();
Class clazz = person.getClass();
Field userId = clazz.getField("userId");
//getField会报一个权限异常,我们先抛出去。后面我们会再对权限深入探讨
}
clazz.getDeclaredField()
通过变量名获取变量的字节码对象
public static void main(String[] args) throws NoSuchFieldException {
Person person = new Person();
Class clazz = person.getClass();
Field userId = clazz.getDeclaredField("age");
//getDeclaredField也会报一个找不到字节码对象的异常。
}
获取类中的所有成员变量
clazz.getFields()
获取类中所有被public的成员变量并返回一个Field[ ]数组
返回的字节码对象也是只有被public修饰的成员变量的字节码对象
public static void main(String[] args){
Person person = new Person();
Class clazz = person.getClass();
Field[] fields = clazz.getFields();//获取字节码对象数组
for (Field field:fields){
System.out.println(field);//所以我们的数组中只有age这个成员变量
}
}
clazz.getDeclaredFields()
获取类中所有的成员变量并返回一个Field[ ]数组
该方法可以获取类中的所有成员变量
public static void main(String[] args) {
Person person = new Person();
Class clazz = person.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field:fields){
System.out.println(field);
}
}
获取类中成员变量的值
获取成员变量的值
Field.get()
通过对应变量的字节码对象获取其对应的值
当我们获取字节码对象数组时可以使用for()遍历数组,通过get获取,不需要通过类型去匹配
public static void main(String[] args) throws Exception {
//这里会抛出NoSuchFieldException和IllegalAccessException两个异常,我们统一使用Exception进行抛出
Person person = new Person();
Class clazz = person.getClass();
Field userId = clazz.getField("userId");
userId.setAccessible(true);//这里需要开放权限,默认是不允许我们通过反射获取变量值
Object o = userId.get(person);
System.out.println(o);//打印的结果为1001,该方法适用于所有类型的数据
}
对类中成员方法的操作
1.反射获取类中的方法并调用
clazz.getMethod()
通过方法名获取方法对象,当然也只能获取被public修饰的方法
clazz.getDeclaredMethod(“方法名”)
通过方法名获取方法对象,该方法可以获取所有所有类型方法
method.invoke(类对象,方法入参);
反射实现方法的调用
public static void main(String[] args) throws Exception {
Person person = new Person();
Class clazz = person.getClass();
Method method = clazz.getMethod("test");//获取方法对像
System.out.println(method.getName)//获取方法的名称
method.invoke(person);
//对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。(官方文档)
//我们可以理解为,要实现方法的调用需要将调用该方法的类的对象和该方法需要的参数传入invoke方法中 去实现方法的调用
Method method2 = clazz.getDeclaredMethod("test2");
method2.setAccessible(true);//该方法是私有化方法需要对其权限进行解锁
method2.invoke(person)
//通过反射调用非public方法一定要记住需要对权限进行解锁不然会报错
}
2.获取方法数组
clazz.getMethods() 该方法会返回包括从超类或超接口中继承的公共方法数组Method[ ]
public static void main(String[] args) throws Exception {
Person person = new Person();
Class clazz = person.getClass();
Method[] methods = clazz.getMethods();
//遍历Method数组后我们会发现,数组中会有很多的方法是由于所有的类都默认继承顶层父类Object,而 Object中存在的公共方法都会被放入Method[]数组中
for (Method method:methods){
System.out.println(method.getName());
}
}
clazz.getDeclaredMethods() 该方法会返回类中的所有方法,但不返回继承于超类或超接口的方法
public static void main(String[] args) throws Exception {
Person person = new Person();
Class clazz = person.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (Method method:methods){
System.out.println(method.getName);
}
}
在学习过框架之后我们会发现,这种实现方法调用的方式几乎遍布了框架的底层,学好了反射调用方法会更加有利于我们读懂框架的底层,让我们在面对问题时更加得心应手。
对类中构造方法的操作
1.反射获取类的构造方法
clazz.getConstructor(); 该方法将返回一个构造器对象
public static void main(String[] args) throws Exception {
Person person = new Person();
Class clazz = person.getClass();
Constructor constructor = clazz.getConstructor();
Object o = constructor.newInstance();//这里我们就完成了对对象的创建
System.out.println(o);
//我们可以通过放入对应类型的参数类型的字节码对象获取对应的有参构造器对象
Constructor conArgs = clazz.getConstructor(String.class);
conArgs.newInstance("秃头帅哥");
//在反射创建对象时,一定要注意通过字节码对象获取的构造器对象是有参还是无参构造,实现构造器时填入 对应的参数
}
clazz.getDeclaredConstructor();
该方法会根据方法的入参返回对应的构造器对象,其中包括私有化构造器。私有化普遍应用于各种设计模式,当我们讲设计模式时再对私有化构造器这种设计模式进行讲解这里便不做讲解。当然在获取时还是要对权限进行处理,不然会报权限异常。
public static void main(String[] args) throws Exception {
Person person = new Person();
Class clazz = person.getClass();
Constructor constructor = clazz.getDeclaredConstructor();
}
clazz.getConstrutors(); 返回公共构造器对象Construtor[ ]数组
public static void main(String[] args) throws Exception {
Person person = new Person();
Class clazz = person.getClass();
Constructor[] constructors = clazz.getConstructors();
for(Constructor constructor:constructors){
System.out.println(constructor);//会打印出所有公共构造方法以及其构造方法的入参
}
}
clazz.getDeclaredConstrutors(); 返回所有构造器对象Construtor[ ]数组
public static void main(String[] args) throws Exception {
Person person = new Person();
Class clazz = person.getClass();
Constructor[] constructors = clazz.getConstructors();
for(Constructor constructor:constructors){
System.out.println(constructor);//会打印出所有构造方法以及其构造方法的入参
}
}
总结
反射几乎贯穿了我们整个java学习的过程中,如果我们对源代码进行深度研究有利于我们更好的理解java的底层原理,了解了原理还能怕编写Java程序时出BUG吗?当然这也是我们入职JAVA工程师的第一道门槛,如有不明白的问题大家可以私信我,有时间我会为大家一一解决,本文如出现错误欢迎指正。
作者:头秃程序员
版权声明:
本文为博主原创文章,转载请附上源文链接!