简单的说,反射机制就是在程序的运行过程中被允许对程序本身进行操作,比如自我检查,进行装载,还可以获取类本身,类的所有成员变量和方法,类的对象,还可以在运行过程中动态的创建类的实例,通过实例来调用类的方法,这就是反射机制一个比较重要的功能了。那么要通过程序来理解反射机制,首先要理解类的加载过程。
在Java程序执行的时候,要经历三个步骤:加载、连接和初始化。首先程序要加载到JVM的方法区中,然后进行连接,最后初始化。这里就主要介绍一下类的加载。如上图,首先,JVM会从硬盘中读取Java源文件并将其加载到方法区中同时生成类名.class文件,也就是类对象,这个类对象中包含了我们创建类的实例时所需要的模板信息,也就是源代码中的成员变量和方法等。Class本身也是一个类,它的主要功能之一就是生成类加载时的class文件,为类的初始化及实例化做准备。而我们在程序中通过关键字new创建的对象创建的是类的对象,而不是类对象,二者的区别如图中所示。
对类的加载有了一个大致的理解之后,我们来看一下实现反射机制的具体操作:
反射机制在我们所学习的框架中有很大的应用,而在我们实际开发中用的并不多,所以理解反射机制对我们学习框架来说很有帮助。
首先我们创建一个实体类User.java
package cn.itcast_01;
public class User extends Person{
public String name;
private int age;
public User(String name, int age) {
super();
this.name = name;
this.age = age;
}
private User(int age)
{
super();
this.age = age;
}
public User(String name)
{
super();
this.name = name;
}
public User() {
super();
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + "]";
}
public void exit()
{
System.out.println(name+"退出系统");
}
public void login(String username,String password)
{
System.out.println("用户名:"+username);
System.out.println("密码:"+password);
}
private String CheckInfo()
{
return "年龄:"+age;
}
}
其中包括成员变量,构造方法和一些成员方法。
利用反射机制可以获取类对象(也就是我们前面介绍的类对象,获取类对象之后我们便获取了类的模板,可以对类进行一些操作),有以下三种方法:
1.类名.class()
2.对象名.getClass()
3.Class.forName(具体的类名)
我们通过代码来看一下具体的操作:
package cn.itcast_01;
public class Demo {
public static void main(String[] args) throws Exception {
//1.类名.class
Class clz = User.class;
System.out.println(clz);
//2.对象名.getClass()
Class clz1 = new User().getClass();
System.out.println(clz==clz1);
//3.Class.forName()
//Class clz2 = Class.forName("User");
Class clz2 = Class.forName("cn.itcast_01.User");
System.out.println(clz==clz2);
}
}
控制台输出如下:
第一行就是所获取的类对象,下面两行true的结果表示类对象只有一个(这里要注意的是第三种方法中类名一定要写全称,包名也要包括进去,不然JVM会无法定位这个类的具体位置)。下面在用一张图来解释这三种方法的执行时机:
在类加载的三个阶段里都可以获取类对象,其中第三种方法,在源码中获取类对象是最常用的,也是反射机制在框架中的应用,鉴于我目前的理解,在框架中的应用可以是通过配置文件写入所创建的类名,再利用第三种方法获取类对象。
获取类对象之后就可以对类进行一些创建对象、调用方法、访问成员变量的操作了:
1.创建对象:
Object obj = 类对象.newInstance();
实例:
package cn.itcast_01;
public class Demo2 {
public static void main(String[] args) throws Exception {
Class clz = Class.forName("cn.itcast_01.User");
System.out.println(clz);
Object obj = clz.newInstance();
System.out.println(obj);
}
}
结果:
2.调用方法:
Method md = 类对象.getMethod("类中的公有方法名");
获取公有方法,其中md是Method类型的方法名,
Object obj1 = lm.invoke(u2,"老赵","aixiaoba");
为获取到的方法命名,方便调用。
Method dm = 类对象.getDeclaredMethod("类中的私有方法名");
获取私有方法名,又叫暴力获取,此方法无视方法的访问权限,即使是被private修饰的方法也会被获取到。
Method lm = 类对象.getMethod("有参方法方法名",参数的类对象...);
获取有参方法,同时要获取参数的类对象,格式为:参数类型.class
Object obj1 = lm.invoke(类的对象,"参数1","参数2");
调用获取到的方法,使用invoke关键字,此处表示调用有参方法。
以上方法中由于不确定获取到的对象类型,所以用Object接收。
实例1:
package cn.itcast_05_Method;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import cn.itcast_01.User;
public class Demo {
public static void main(String[] args) throws Exception {
User u = new User("小巴",18);
User u2 = new User("老赵",20);
Class clz = Class.forName("cn.itcast_01.User");
Method em = clz.getMethod("exit");
Object obj = em.invoke(u2);
System.out.println(obj);
Method lm = clz.getMethod("login",String.class,String.class);
Object obj1 = lm.invoke(u2,"老赵","aixiaoba");
System.out.println(obj1);
}
}
结果:
方法名参考实体类User.java,因为方法为void类型,所以运行结果中会出现null,表示返回值为空。
实例2:
package cn.itcast_05_Method;
import java.lang.reflect.Method;
import cn.itcast_01.User;
public class Demo2 {
public static void main(String[] args) throws Exception {
User u = new User("小巴",18);
User u2 = new User("老赵",20);
Class clz = Class.forName("cn.itcast_01.User");
Method dm = clz.getDeclaredMethod("CheckInfo");
dm.setAccessible(true);
Object obj = dm.invoke(u2);
System.out.println(obj);
}
}
结果:
要注意的是,在暴力获取私有方法后还要获取调用该方法的权限:
dm.setAccessible(true);
将参数设置为true.
3.访问成员变量:
最重要的一个关键字:Field
Field nf = 类对象.getField("成员变量名");
和调用私有方法一样,要访问私有成员变量也要通过暴力获取的的方式,同时也要获取访问私有成员变量的权限。
Field af = 类对象.getDeclaredField("私有成员变量名");
af.setAccessible(true);
实例1:
package cn.itcast_02Field;
import java.lang.reflect.Field;
public class Demo {
public static void main(String[] args) throws Exception {
Class clz = Class.forName("cn.itcast_01.User");
Object obj = clz.newInstance();
Field nf = clz.getField("name");
nf.set(obj, "小巴");
Object object = nf.get(obj);
System.out.println(object);
}
}
结果:
实例2:
package cn.itcast_02Field;
import java.lang.reflect.Field;
import cn.itcast_01.User;
public class Demo2 {
public static void main(String[] args) throws Exception {
User u = new User("小巴",18);
Class clz= Class.forName("cn.itcast_01.User");
Field af = clz.getDeclaredField("age");
af.setAccessible(true);
Object obj = af.get(u);
System.out.println(obj);
}
}
结果:
反射机制中还有很多知识需要学习,比如获取构造方法,获取全部属性,获取全部方法,等等等等,希望能和大家共勉,共同进步!!!