JAVA反射总结(4)

上两篇博客,主要介绍了反射的基本应用,这篇博客,主要写一些关于利用反射来越过泛型约束,通过配置文件的方式使用反射,编写泛型工具类等!


首先,我们来介绍利用配置文件的方式使用反射,在总结一里面,我们写道如何使用反射,想要使用反射,就要拿到反射入口,而拿反射入口的方式有三种,其中有一种我们不需要利用现有的类或者对象,那就是Class.forName(String className),这个方法,我们只需将一个类的全类名写进去,就能拿到该类的反射入口。

而反射其中有一个作用就是:

在运行时调用任意一个对象的方法;

所以我们现在考虑一下,是否可以将该全类名放入一个配置文件,我们访问通过配置文件的方式来获取该值,然后拿到反射入口呢,答案当然是可以的,我们不仅可以通过访问配置文件的方式拿到反射入口,我们还能将该类对应的属性或者方法都放入配置文件中直接反射操作,这样,无论是什么类,在我们要使用其方法或者是业务需求有更新的时候,我们一般不需要去修改源代码,修改配置文件就能达到我们的目的,说这么多废话,下面我们通过一个demo来完成一个小项目。


我们先建立两个类,TestClass1.java和TestClass2.java。其代码如下所示:

package com.charles.reflectDemo;

public class TestClass1 {
	private String name;
	private int id;
	public TestClass1() {}
	public TestClass1(String name) {
		super();
		this.name = name;
	}
	public TestClass1(String name, int id) {
		super();
		this.name = name;
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public void sayHello()
	{
		System.out.println("This is test 01:Say Hello");
	}

}

该类包含了一些属性以及其set和get方法以及一个sayHello()方法;接下来我们看另外一个类TestClass2.java

package com.charles.reflectDemo;

public class TestClass2 {
	private String name;
	private int id;
	public TestClass2() {}
	public TestClass2(String name) {
		super();
		this.name = name;
	}
	public TestClass2(String name, int id) {
		super();
		this.name = name;
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public void sayHi()
	{
		System.out.println("This is test 02:Say Hi");
	}

}

该类包含了一些属性以及其set和get方法以及一个名为sayHi()的方法


接下来,我们在项目路径下建立一个property.txt的属性文件,其内容如下:

classPath=com.charles.reflectDemo
method=sayHello

我们使用左右相等的形式,即K-V对来保存一些信息,可以看到,第一个等号的右边是一个全类名,第二个等号的右边是一个方法名;


我们再建立测试类

package com.charles.reflectDemo;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Properties;

public class TestClass {

	public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException
	{
		
		//IO操作
		Properties property=new Properties();
		//加载配置文件
		property.load(new FileReader("property.txt"));	
		//获取全类名和方法名
		String clazzName=property.getProperty("classPath");
		String methodName=property.getProperty("method");
		//获取反射入口
		Class<?> testClazz=null;
		testClazz=Class.forName(clazzName);
		//获取一个反射类的方法对象
		Method method=testClazz.getDeclaredMethod(methodName);
		//执行该方法
		method.invoke(testClazz.newInstance());
		
	}

}

我们通过IO操作配置文件的方式,读取到了文件里的类名已经方法名,然后通过反射,执行了该方法(testClass1的sayHello),执行的结果如下:

那么,我们怎么体现出:在运行时调用任意一个对象的方法 呢?

现在,我们打开刚刚写好的配置文件,将其改成:

classPath=com.charles.reflectDemo.TestClass2
method=sayHi

其他代码我么都不用改,直接执行,观察结果:

此时,执行的结果就变成了另一个类的另一个方法,而我们的Java代码丝毫未变,这就是反射的魅力之一所在,也证实了刚刚我们说的话,其实这是个很简单的例子,但是也有最基本的精髓在里面,如果自己想创造出一套框架,就可以利用反射思考,也可以看别人的框架是怎么实现的。当然,关于这个demo中的io操作此处就不详述了,那是j2se中的内容了。接下来我们来介绍用java绕过泛型检查!


我们用一个demo来介绍反射绕过泛型检查,当然,在平常的使用中基本不会着么做,因为这样会破坏正常的代码机制和约定。大家知道就行了。代码如下:

package com.charles.reflectDemo;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

public class templateTest {
	public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
	{
		//定义一个动态数组泛型为Integer对象NumberList
		ArrayList<Integer> NumberList=new ArrayList<Integer>();
		NumberList.add(123456);
		NumberList.add(234567);
		NumberList.add(345678);
		System.out.println(NumberList);
		//开始利用反射越过泛型了
		Class<?> listClazz=ArrayList.class;
		Method method =listClazz.getMethod("add",Object.class);
		method.invoke(NumberList, "charles");
		System.out.println(NumberList);
		
	}

}

执行结果如下:

这里稍稍解释一下,首先我们定义一个动态数组(集合),并利用泛型约束只能存入Integer类型数据,我们存入三个整形数据,然后打印,此时保存的都是整形数据;然后我们开始利用泛型搞事,先拿到该集合的反射入口,然后通过反射入口拿add

这个方法,将第二个参数设置成Object.class,表示将add方法的反射方法的参数设置为Object类型,而在之前,我们通过泛型,其只能是Integer类型。利用invoke来执行该方法,将第一个参数放之前的集合对象,第二个参数就可以设置任何类型了,我们这里用的String类型。最后执行程序,没问题,我们已经越过泛型。

最后,我们来写一个简单的泛型工具类来结束此篇博文,也结束反射的总结!


先说一下我们接下来的这个demo的需求,在本篇博文最开始的那里有两个java类,而且可以看见里面都有很多set方法为类中的属性赋值,其实对于此类类,我们称之为javabean,有的也叫domain,还有其他地方叫做pojo。那么我们下面的demo的目的就是写一个帮助类,为任意的类的成员的任意属性赋值。我们建立JAVA文件propertyUtils.java;代码如下:

package com.charles.reflectDemo;

import java.lang.reflect.Field;

public class propertyUtils {
	public static void setProperty(Object obj,String fieldName,Object value) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
	{
		Class<?> Clazz=obj.getClass();
		Field field=Clazz.getDeclaredField(fieldName);
		field.setAccessible(true);
		field.set(obj, value);
	}
}

解释一下:该类只有一个方法public static void setProperty(Object obj,String fieldName,Object value),该方法一共有三个参数,,第一个参数是你想要为其赋值的对象,第二个参数是该对象的哪个属性名,第三个参数是你想给该参数赋的值,这里至于第一个参数和第三个参数类型是Object类型,这里就不多解释了,依然是j2se中多态的知识了。我们来执行该方法,建立测试类testUtils.java;代码如下:

package com.charles.reflectDemo;

public class testUtils {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException
	{
		TestClass1 test=new TestClass1();
		propertyUtils.setProperty(test, "name", "charles");
		System.out.println(test.getName());
		
	}


}

实例化一个TestClass1对象,调用我们自己写的帮助类的setProperty方法来为该对象的name属性赋值为charles,打印,最后执行结果如下:

至此,反射的相关知识点我个人的话认为是基本总结完成,当然,也有可能存在没有说到的地方,欢迎大家留言,更多可以去看源码或者自己动手玩。

告辞!java反射! 

原创文章 42 获赞 72 访问量 8227

猜你喜欢

转载自blog.csdn.net/weixin_43249548/article/details/97810268