一、反射的概述与初衷
1、反射的概述
Java反射机制是在运行状态中,对于任意一个类都能知道它的所有属性和方法;对于任意一个对象都能调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java反射机制。
反射是一种与类进行动态交互能力的一种机制,为什么要强调动态加载呢?动态加载也就是在程序运行的时候才会加载,而不是在编译的时候,动态加载是在需要的时候才进行加载获取,或者说我们可以在任何时候加载一个不存在的类到内存中,然后进行各种交互,或者获取一个没有公开的类(private)的所有信息,总之一句话,反射就相当于游戏外挂开发者可以随时随意的利用反射进行一些特殊的事情!
2、反射的初衷
反射的初衷并不是为了方便我们创建一个对象,而是让我们在写代码的时候可以更加灵活,降低耦合,提高代码的自适应能力。
二、前置知识
学习反射之前,我们要知道什么是Java字节码文件对象,既然是对象,它属于哪个类?它的类型又是什么?怎么获得这个字节码文件对象呢?带着这样的疑问,跟我一起来探索吧!
首先,来看一下什么是字节码文件对象,具体如下图:
那么它到底属于哪个类?
public class Student { public static void main(String[] args) { Student student = new Student(); }}
上述代码的"student "是一个对象,他的对象类型是"Student " 。而字节码文件对象就好比"student "对象,只不过他的对象的类型是Class,这里的Class是一个类,并不是"public class Student"中的class,为了方便理解我们可以写成"Class 字节码文件 = new Class();"的形式。接下来就是怎么获得这个字节码文件对象,Java给我们提供了三个方法:
1、Object类的getClass()方法
2、类型.class属性
3、Class类的forName()方法
public class Demo { public static void main(String[] args) throws ClassNotFoundException { Student student = new Student(); Class studentClass = student.getClass(); System.out.println("Object类的getClass()方法:"+studentClass); System.out.println("类型.class属性:"+int.class); Class studentClass1 = Class.forName("com.wy.Student"); System.out.println("Class类的forName()方法:"+studentClass1); }}对应的输出内容: Object类的getClass()方法:class com.wy.Student 类型.class属性:int Class类的forName()方法:class com.wy.Student
这里小编给大家普及一下字节码文件的加载时机:
1、new一个类的时候
2、访问一个类的静态成员的时候
3、调用一个类的静态方法的时候
4、通过反射的方式创建一个类的字节码对象的时候
5、创建一个子类对象的时候
划重点:一个类的字节码文件对象在整个程序的一次运行中,只会加载一次!
三、字节码文件对象的组成
由于反射最终也必须有类参与,因此反射的组成一般有以下几个方面组成:
1、java.lang.Class.java:类对象;
2、java.lang.reflect.Constructor.java;类的构造器对象
3、java.lang.reflect.Method.java;类的方法对象;
4、java.lang.reflect.Field.java:类的属性对象;
四、反射的使用
1、Constructor的使用:
实体类(Person.java)注意这里的构造方法是私有的
package com.reflection;public class Person { private Person(){} public String toString(){ return "小白学Java"; }}
测试类(TestReflection.java)
package com.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class TestReflection { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //第一步 得到字节码文件对象 Class clazz = Person.class; //第二步 获得构造器对象 //Constructor constructor = clazz.getConstructor();这个方法只能得到public修饰的构造方法 //getDeclaredConstructor()可以得到私有构造方法对象 Constructor constructor = clazz.getDeclaredConstructor(); /** * 设置暴力访问权限,一般情况我们并不能对类的私有字段进行操作, * 利用反射也不例外,但有时候,我们又必须区处理这些字段,这时候我们就需要调用setAccessible()方法 * 来允许这种访问,而用于反射类中的Filed,Method和Constructor继承AccessiblObject,因此通过在这些类上 * 调用setAccessible()方法,我们可以实现对这些字段的操作 */ constructor.setAccessible(true); //调用newInstance()方法让加载完的类在内存中创建对应的实例 传统方法:(Person person = new Person("小白")) Object object = constructor.newInstance(); System.out.println(object); }}输出结果:小白学Java
2、Method的使用:
实体类(Student .java)
package com.reflection;public class Student { private String name; private int age; private String getName() { return name; } public int getAge() { return age; } public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } private void testMethod(){ System.out.println("我在测试Method()方法呀"); } @Override public String toString() { return "小白学Java"; }}
测试类(TestReflection .java)
package com.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class TestReflection { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //第一步 得到字节码文件对象 Class clazz = Student.class; //第二步 获得特定的方法 Method method = clazz.getDeclaredMethod("testMethod",null); //暴力访问 method.setAccessible(true); //正常情况调用方法:对象.方法名(参数) //反射调用方法:方法.对象-----》方法对象 invoke(对象,参数); method.invoke(clazz.newInstance(),null); }}输出结果:我在测试Method()方法呀
3、Field的使用:
实体类(Teacher .java)
package com.reflection;public class Teacher { public String name; private int age; private void testField(){ System.out.println("我在测试Field()方法呀"); } @Override public String toString() { return "小白学Java"; }}
测试类(TestReflection .java)
package com.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class TestReflection { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { //使用反射访问对象的成员变量 Filed对象 //第一步 得到字节码文件对象 Class clazz = Teacher.class; //第二步 得到Filed对象 Field field1 = clazz.getField("name"); Field field2 = clazz.getDeclaredField("age"); //用反射的方式创建该类的实例 Object object = clazz.newInstance(); //第三步 设置Filed值 field1.set(object,"小白"); field2.setAccessible(true); field2.set(object,18); System.out.println(((Student)object).name+((Student)object).age); }}输出结果:小白18
五、反射的作用及应用
前面我们大概了解了反射的概念以及使用,那么他的作用是什么呢?什么场景下我们会用到反射?
1、需要访问隐藏属性或者调用方法改变原来的逻辑,这个在开发中很常见,由于一些原因,系统并没有开放一些接口出来,这个时候利用反射是一个有效的解决方法。
2、自定义注解,注解就是在运行时利用反射机制来获取的。
3、反射是java提供的一个重要功能,可以在运行时检查类、接口、方法和变量等信息,无需知道类的名字,方法名等。还可以在运行时实例化新对象,调用方法以及设置和获取变量值。反射非常强大和有用,很多java框架中都有反射的影子,例如spring、mybatis,JDBC利用反射将数据库的表字段映射到java对象的getter/setter方法等等。
都读到这里了,不如就点个赞再走吧!