简单的说,反射机制就是在程序的运行过程中被允许对程序本身进行操作,比如自我检查,进行装载,还可以获取类本身,类的所有成员变量和方法,类的对象,还可以在运行过程中动态的创建类的实例,通过实例来调用类的方法,这就是反射机制一个比较重要的功能了。那么要通过程序来理解反射机制,首先要理解类的加载过程。

java反射没有入参的方法 反射java invoke_反射机制

在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);
	}

}

控制台输出如下:

java反射没有入参的方法 反射java invoke_User_02

第一行就是所获取的类对象,下面两行true的结果表示类对象只有一个(这里要注意的是第三种方法中类名一定要写全称,包名也要包括进去,不然JVM会无法定位这个类的具体位置)。下面在用一张图来解释这三种方法的执行时机:

java反射没有入参的方法 反射java invoke_类对象_03

在类加载的三个阶段里都可以获取类对象,其中第三种方法,在源码中获取类对象是最常用的,也是反射机制在框架中的应用,鉴于我目前的理解,在框架中的应用可以是通过配置文件写入所创建的类名,再利用第三种方法获取类对象。

获取类对象之后就可以对类进行一些创建对象、调用方法、访问成员变量的操作了:

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);
	}

}

结果:

java反射没有入参的方法 反射java invoke_java反射没有入参的方法_04

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);
	}

}

结果:

java反射没有入参的方法 反射java invoke_反射机制_05

方法名参考实体类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);
	}

}

结果:

java反射没有入参的方法 反射java invoke_java反射没有入参的方法_06

要注意的是,在暴力获取私有方法后还要获取调用该方法的权限:

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);
	}

}

结果:

java反射没有入参的方法 反射java invoke_User_07

实例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);
		
		
		
	}

}

结果:

java反射没有入参的方法 反射java invoke_类对象_08

反射机制中还有很多知识需要学习,比如获取构造方法,获取全部属性,获取全部方法,等等等等,希望能和大家共勉,共同进步!!!