JAVA反射

1.步入正题之前先扯会淡

        之前,在论坛看到别人说过这样一句话:“用专有名词去解释专有名词其实就是一件扯淡的事儿” 。 其实我觉得不无道理,好多技术文档都是用专有名词去解释专有名词,我在看其他博主或者技术文档时也要同时google着里面不理解的一些专有名词。

     我个人认为,用自己的语言来描述自己所理解的东西才是最好的,闲聊毕,接下来我们步入正题来学习java反射。

2.java反射的定义

        JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

        然后,这里要加入我的个人理解: 所谓java反射,其实就是在运行过程中,随便给我一个类,我就可以知道这个类中的所有属性和方法,然后调用这些方法或者修改属性,注意这里指的是运行过程中,也就是.class字节码文件运行在虚拟机的过程中。

        考虑一下,如果随便给你一个字节码文件(.class) 你可以用什么方法知道这个字节码文件中有什么方法和属性? 该怎么调用他们?  没错,就是反射。

3.了解定义之后,我们来看一下反射的代码实现和使用

        反射的用途有很多,下面我只是介绍最简单的用途,如果你可以理解下面的例子,我想你可以利用反射模拟Spring管理beans(当然这可能需要用到一些其他的知识)

  (1).如何反射获得一个类的字节码文件 

                这里我们需要知道java为我们提供了一个类叫做Class   我们来看一下官方文档的描述(jdk1.9) :

            大概的意思是说Class类的实例表示一个正在运行的Java类或者接口,也就是我们说的已经加载到jvm中的.class文件对象。 并且文中有提到该类没有公共的构造方法,也就是说不允许我们new该类对象,原因很简单,我们不能自己创造class字节码文件,这些工作都有java编译器和虚拟机来进行。

        说的好不如做的好,接下来,我们开始码代码 

        新建一个项目,该项目中包含两个类,一个是People类(被反射的类)  一个是测试类

People类代码:

package com.javastudy.reflect;
/**
 * 
 * @author stranger_bai
 * 演示类 
 */
public class People {
	public String name;
	public int age;
	private String sex;
	public void show() {
		System.out.println("i am "+name+" , i am "+age+" years old  and i am a"+sex);
	}
	public void run() {
		System.out.println("i can run");
	}
	public void swim() {
		System.out.println("i can swim");
	}
	public void sayHello(String s) {
		System.out.println( s+" say hello to me !");
	}
}

 注意,People类没有实际意义,只是用来做一个演示。 接下来我们来介绍如何获得People的字节码对象Class

  第一种方式: Class类的静态方法 forName()                        

  第二种方式: 直接 使用 类名.class 获取        两种方式获取的是同一个对象

package com.javastudy.reflect;
/**
 * 
 * @author stranger_bai
 *测试类
 */
@SuppressWarnings("all")
public class Test {

	public static void main(String[] args) throws Exception{
		Class peopleClass = Class.forName("com.javastudy.reflect.People");  //第一种 forName 获取  
		Class secondClass = People.class;  //第二种,直接类名.class获取
		System.out.println(peopleClass == secondClass); //true 获取到的是同一对象
		
	}

}

(2)获取到字节码后,我们就可以获取该类的方法和属性了

        既然是Class类对象,那我们就要看看Class类都为我们提供了那些方法。

        (这里我们只关心获取属性和方法的方法,其余的方法请自行理解)

     

             以上这几个方法就是我们用来获得属性和方法的方法,当然还有其他的方法,这里不再赘述。  

    这里演示一个获取方法并执行的例子,其余大同小异,请读者自行尝试,如果有问题,欢迎讨论!

      首先,我们看到,Class为我们提供了两个获取方法的方法,一个是getMethod 另一个是getMethods 顾名思义,一个是获取所有方法,一个是获取单一方法。获取单一方法又要求我们传入至少一个参数name 然后还需要再传入一个parameterTypes可变参数,其中name指的是方法名,后面的可变参数指的是方法的参数(这里是为了区分重载方法)。返回值都是Method类型 

接下来,测试类代码: 

package com.javastudy.reflect;

import java.lang.reflect.Method;

/**
 * 
 * @author stranger_bai
 *测试类
 */
@SuppressWarnings("all")
public class Test {

	public static void main(String[] args) throws Exception{
		Class peopleClass = Class.forName("com.javastudy.reflect.People");  //第一种 forName 获取  
		Class secondClass = People.class;  //第二种,直接类名.class获取
		System.out.println(peopleClass == secondClass); //true 获取到的是同一对象
		
		Method[] methods = peopleClass.getMethods(); //获取所有方法
		for(Method m:methods) {
			System.out.println(m.getName());   //遍历输出所有获取到的方法名 
		}
		Method showmethod = peopleClass.getMethod("show"); //通过方法名获取方法 
 		System.out.println(showmethod.getName());
	}

}

我们来看一下输出结果:

我们发现,不仅仅是我们写的方法被获取到,连继承自object类的方法也会被找到。  也就是说,不论我们进行多少次继承,其父类的方法都会被找到(请大家自行测试)

获取属性这里不再赘述,利用getFields和getField 返回Field类实例,具体操作参考Field类方法

(3)获取到方法或者属性,怎么调用方法或者修改属性?

    我们知道,获得的方法是Method类的实例,那我们不妨去看一下Method类的文档 

同样,Method类也有很多方法,我们只介绍我们要用到的invoke方法,这个方法是用来调用当前对象所代表的方法。参数obj是方法所在类的对象(这里比较复杂,涉及到对象的单例和多例,所以需要知道要调用的是哪个实例的哪个方法),参数args是我们传给被调用方法的参数。 

那么,至此为止,我们要想调用方法,还有一个问题没解决,就是怎么实例化方法所在类对象 ,这里有两种方式,还记得我们上面在获取方法时有一个getConstructor 返回一个 Constructor对象吗,Constructor类提供一个newInstance 方法返回一个该类的实例  

或者我们可以直接使用Class类提供的newInstance方法,都可以返回该类实例,那么我们有了该类的实例,有了要调用的方法的Method实例,我们就可以通过invoke调用该方法了!

package com.javastudy.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * 
 * @author stranger_bai
 *测试类
 */
@SuppressWarnings("all")
public class Test {

	public static void main(String[] args) throws Exception{
		Class peopleClass = Class.forName("com.javastudy.reflect.People");  //第一种 forName 获取  
		Class secondClass = People.class;  //第二种,直接类名.class获取
		System.out.println(peopleClass == secondClass); //true 获取到的是同一对象
		
		Method[] methods = peopleClass.getMethods(); //获取所有方法
		for(Method m:methods) {
			System.out.println(m.getName());   //遍历输出所有获取到的方法名 
		}
		Method showmethod = peopleClass.getMethod("show"); //通过方法名获取方法 
 		System.out.println(showmethod.getName());
 		
 		//调用方法  
 		//1.获取方法所在类实例 
 		People p1 = (People)peopleClass.newInstance(); //方式一
 		Constructor con  = peopleClass.getConstructor(); //方式二   参数是构造器参数,这里构造器没有参数
 		People p2 = (People)con.newInstance();
 		//2.获取到要调用的方法  
 		Method helloMethod = peopleClass.getMethod("sayHello", String.class);
 		//3.调用invoke 
 		helloMethod.invoke(p2, "小明");//执行p2 对象的sayHello方法,方法的参数是 小明
	}

}

 

 那么至此我们就成功利用反射调用到了一个类的方法。属性操作也是大同小异,具体请参考官方jdk文档所提供的方法。

4.举一反三

  1. 我们知道,java中的泛型是不会被编译到class文件中去的,也就是说泛型只在源码阶段存在,反射是在运行时起作用,那么大家可以尝试一下向某个泛型的集合中插入不是该泛型的数据(例如向String集合中插入Integer)当然,这可能没有意义,但是建议大家尝试一下 。(泛型擦除)

  2.编写一个程序,运行时动态输入类名、方法和属性名,动态输入参数,实现调用。 (基本 类似Spring框架管理bean)

  3. 参考Field类方法,实现修改peivate 属性的值(不使用get/set访问器)  (暴力反射) 

 

文章属于个人理解,若有错误,欢迎指正,欢迎交流 。

JDK1.9 官方文档: https://docs.oracle.com/javase/9/docs/api 

JDK1.9 中文翻译版: https://pan.baidu.com/s/1E8U2_Ckzq-AHX2bZtE6NkA   密码 ds29

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值