一、什么是反射?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
二、能利用反射做什么?
我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其constructor(构造方法),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
三、前期了解知识
(一)类的加载
想知道如何使用反射,就先要了解类的加载。
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来对这个类进行初始化。
- 加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。 - 连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用 - 初始化 基本的的初始化步骤
(二)类加载器
-
类加载器:负责将.class文件加载到内在中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。 -
类加载器的组成
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器 -
类加载器的作用
(1) Bootstrap ClassLoader 根类加载器,也被称为引导类加载器。负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中(2)Extension ClassLoader 扩展类加载器。负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
(3)Sysetm ClassLoader 系统类加载器。负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所 指定的jar包和类路径
四、如何使用反射?
现有一个Person类
public class Person {
private String name;
int age;
public String address;
public Person() {
}
private Person(String name) {
this.name = name;
}
Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public void show() {
System.out.println("show");
}
public void method(String s) {
System.out.println("method " + s);
}
public String getString(String s, int i) {
return s + "-->" + i;
}
private void function() {
System.out.println("function");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", address=" + address
+ "]";
}
}
那么对于这个类,如何使用反射呢?
(一)获取该类的字节码文件在内存中对应的Class对象
/**
*有三种方式可以获得Class对象
*/
//1、通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object
// 类型的对象,而我不知道你具体是什么类,用这种方法
Person p1 = new Person();
Class c1 = p1.getClass();
//2、直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高
// 这说明任何一个类都有一个隐含的静态成员变量 class
Class c2 = Person.class;
//3、通过 Class 对象的 forName() 静态方法来获取,用的最多,
// 但可能抛出 ClassNotFoundException 异常
Class c3 = Class.forName("com.ys.reflex.Person");
注意:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1,c2,c3进行 == 比较,结果都是true
(二)通过Class类提供的方法获取构造方法
- 获取构造方法
/**
*反射之获取无参构造方法,并使用
*/
public class TestConstructor01 {
public static void main(String[] args) throws Exception {
//获取Class对象
Class c = Class.forName("study01.Person");
//获取无参构造函数
Constructor con = c.getDeclaredConstructor();
//创建实例
Object obj = con.newInstance();
}
}
/**
* 如果只需要利用无参构造来创建实例,那么也可以不用获取构造器,直接使用newInstance方法
*/
public class TestConstructor01 {
public static void main(String[] args) throws Exception {
Class c = Class.forName("com.zuobiao.testReflection.Person");
Person p = (Person)c.newInstance();
}
}
/**
*反射之获取带参构造方法,并使用
*/
public class TestConstructor02 {
public static void main(String[] args) throws Exception {
//获取Class对象
Class c = Class.forName("study01.Person");
//public Constructor getDeclaredConstructor(Class... parameterTypes)
//获取构造方法
Constructor con = c.getDeclaredConstructor(String.class, int.class, String.class);
//创建实例
Object obj = con.newInstance("张三", 27, "北京");
}
}
/**
*反射之获取private修饰的构造方法,并使用
*/
public class TestConstructor03 {
public static void main(String[] args) throws Exception{
//获取Class对象
Class c = Class.forName("study01.Person");
//获取构造方法
Constructor con = c.getDeclaredConstructor(String.class);
//设置访问权限
con.setAccessible(true); //值为true则指示反射的对象在使用时应该取消Java语言访问检查。
//创建实例
Object obj = con.newInstance("林青霞");
}
}
(三)通过Class类提供的方法操作成员变量
- 给成员变量赋值
public class TestField01 {
public static void main(String[] args) throws Exception{
test();
}
public static void test() throws Exception {
//获取Class对象
Class c = Class.forName("study01.Person");
//获取Constructor
Constructor con = c.getDeclaredConstructor();
//创建实例
Object obj = con.newInstance();
System.out.println(obj);
//获取Field
Field addressField = c.getDeclaredField("address");
//赋值Field
addressField.setAccessible(true);
addressField.set(obj, "北京");
System.out.println(obj);
Field nameField = c.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(obj, "张三");
System.out.println(obj);
Field ageField = c.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(obj, 27);
System.out.println(obj);
}
}
测试结果如下:
- 获取成员变量
public class TestField02 {
public static void main(String[] args) throws Exception{
test();
}
public static void test() throws Exception {
//获取Class对象
Class c = Class.forName("study01.Person");
//获取Constructor
Constructor con = c.getDeclaredConstructor();
//创建实例
Object obj = con.newInstance();
System.out.println(obj);
//获取Field
Field addressField = c.getDeclaredField("address");
//赋值Field
addressField.setAccessible(true);
addressField.set(obj, "北京");
System.out.println(obj);
System.out.println(addressField.get(obj));
}
}
测试结果如下:
(四) 通过Class类提供的方法调用成员方法
/**
* 反射之获取无参无返回值成员方法,并调用
*/
public class TestMethod01 {
public static void main(String[] args) throws Exception {
test0();
}
//获取单个方法对象
/**
* 1、public 方法 getMethod(String name, Class... parameterTypes):获取所有方法,包括父类的
* 参数 : name:方法的名称 parameterTypes:该方法的参数列表
* 2、public 方法 getDeclaredMethod(String name, Class... parameterTypes):获取所有子类定义的方法
* 参数 : name:方法的名称 parameterTypes:该方法的参数列表
*/
public static void test() throws Exception {
//获取Class对象
Class c = Class.forName("study01.Person");
//获取构造方法
Constructor con = c.getDeclaredConstructor();
//创建实例
Object obj = con.newInstance();
//获取需要的方法对象
Method m = c.getDeclaredMethod("show");
//调用方法
/**
* public Object invoke(Object obj, Object... args)
* obj - 被调用的对象 args - 用于方法调用的参数
*/
m.invoke(obj);
}
}
测试结果如下:
/**
* 反射之获取有参有返回值成员方法,并调用
*/
public class TestMethod02 {
public static void main(String[] args) throws Exception {
//获取Class对象
Class c = Class.forName("study01.Person");
//获取构造方法
Constructor con = c.getDeclaredConstructor();
//创建实例
Object obj = con.newInstance();
//获取需要的方法对象
//public void method(String s)
Method m = c.getDeclaredMethod("method", String.class);
m.invoke(obj, "java");
//public String getString(String s, int i)
Method m1 = c.getDeclaredMethod("getString", String.class, int.class);
Object rst = m1.invoke(obj, "hello", 200);
System.out.println(rst);
}
}
测试结果如下:
/**
* 反射之获取privste成员方法,并调用
*/
public class TestMethod02 {
public static void main(String[] args) throws Exception {
//获取Class对象
Class c = Class.forName("study01.Person");
//获取构造方法
Constructor con = c.getDeclaredConstructor();
//创建实例
Object obj = con.newInstance();
//获取需要的方法对象
//private void function()
Method m2 = c.getDeclaredMethod("function");
m2.setAccessible(true);
m2.invoke(obj);
}
}
测试结果如下:
五、补充
当使用反射调用静态变量和静态方法时,传递的对象出只需要传递null即可。
六、总结
灵活使用反射能让我们代码更加灵活,但是凡事都有两面性,反射也会消耗系统的性能,增加复杂性等,合理使用才是真!
Java新手,若有错误,欢迎指正!