黑马程序员-JAVA-反射初探_sisel的博客-程序员秘密

技术标签: 学习  黑马  

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------


JAVA的反射(java.lang.reflect)机制提供了在运行时加载类,取得类信息,构造类实例对象,取得和设置静态和实例字段,取得和引发(invoke)类的静态和实例方法的一系列工具,为各种框架和应用带来了极高的动态扩展能力,并且提供了运行时改变业务逻辑的可能,这个特性不能不了解一下。

下面我分别作一些简单的例子来试试这些功能

为了方便测试 做一个测试的目标和简单输出语句


class ReflectPoint extends Rflt implements Point {
	int x, y;
	String comment;
	int[] val;

	private static int rect(int xl, int yl) {
		return xl * yl;
	}

	@Override
	public String toString() {
		return "ReflectPoint [x=" + x + ", y=" + y + ", comment=" + comment
				+ ", val=" + Arrays.toString(val) + "]";
	}

	public ReflectPoint(int x, int y, String comment, int[] val) {
		super();
		this.setX(x);
		this.y = y;
		this.comment = comment;
		this.val = val;
	}

	@Override
	public int getX() {
		return x;
	}

	private void setX(int x) {
		this.x = x;
	}

	@Override
	public int getY() {
		return y;
	}

	void setY(int y) {
		this.y = y;
	}

	private static boolean not(boolean b) {
		return !b;
	}

	@Override
	protected String getComment() {
		return comment;
	}
}

abstract class Rflt {
	abstract String getComment();
}

interface Point {
	int getX();

	int getY();
}


	public static void sp(Object o) {
		if (o == null) {
			System.out.println("null");
		}
		if (o.getClass().isArray()) {
			StringBuilder sb = new StringBuilder();
			int len = Array.getLength(o);
			sb.append('[');
			for (int i = 0; i < len; i++) {
				if (i != 0) {
					sb.append(':');
				}
				sb.append(Array.get(o, i));
			}
			sb.append(']');
			System.out.println(sb);
		} else {
			System.out.println(o);
		}
	}



首先试试取得类:

	private static void testClass() {
		// 构造一个样例
		int[] val={3,4,7};
		ReflectPoint rp=new ReflectPoint(12, 25, "ref test", val);
		
		//取得类的3种常见方式 1.直接用类名.class 2.用已知类实例的.getClass() 
		//3.用Class的静态方法forName(String className) !!注意这种方式如果在classpath下找不到类会引发ClassNotFoundException  
		Class c1=ReflectPoint.class,c2=rp.getClass(),c3=null;
		try {
			c3=Class.forName("com.itheima.ReflectPoint");
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("ClassNotFound");
		}
		//以下三个输出都是class com.itheima.ReflectPoint
		sp(c1);
		sp(c2);
		sp(c3);
		
		//类路径错误的例子
		try {
			Class.forName("com.test.IdontExist");
		} catch (ClassNotFoundException e) {
			//throw new RuntimeException("ClassNotFound");
			sp("com.test.IdontExists not found");
		}
		
		//三种方式取得的Class实例在同一个类加载器下是单例,所以可以用==来比较,当然用.equals也行
		sp(c1==c2);//true
		sp(c1==c3);//true
	}
	


看来取得类很容易,值得注意的是Class.forName(String className)方法,这里加载的类依赖于字符串输入,而将它绑定到一个类似properties的配置文件或者绑定到命令行参数便轻松实现了启动应用时配置加载的类。再进一步,用一个驻留的后台线程监听配置文件改变,如果发生变化就重新配置或者重新启动应用服务,便达到了在线改变配置的效果,当然要达到这样的效果应该有良好的框架设计以免出现冲突和漏洞。


那么有了类,试试构建实例对象

	private static void testConstructer() {
		// 构造必要的数据
		int[] val = { 3, 4, 7 };
		int x = 12, y = 25;
		String comm = "cons test";

		// 取得Constructor
		// 这里使用了直接指定参数列表取得指定的构造函数的方式,当然如果参数不匹配会导致NoSuchMethodException
		// 而当前运行时上下文无权限访问(修饰符限定)时,会导致SecurityException 就要改用暴力反射才能继续了
		Constructor<ReflectPoint> constructor=null;
		try {
			constructor= ReflectPoint.class.getConstructor(int.class,int.class,String.class,int[].class);
		} catch (NoSuchMethodException | SecurityException e) {
			throw new RuntimeException("Constructor can not get");
		}
		
		//构造实例
		//这里直接用.newInstance()方法即可,当然:参数列表必须匹配
		ReflectPoint rp=null;
		if(constructor!=null){
			try {
				rp=constructor.newInstance(x,y,comm,val);
			} catch (InstantiationException | IllegalAccessException
					| IllegalArgumentException | InvocationTargetException e) {
				throw new RuntimeException("Construct Instance fail");
			}
		}
		
		//测试一下实例
		if(rp!=null){
			sp(rp);
			sp(rp.x);
			sp(rp.y);
			sp(rp.comment);
			sp(rp.val);//OK 构造成功 字段都取得了
		}
	}

取得构造方法还有一个途径就是Class实例方法.getConstructors() 它返回类的所有公共构造方法数组,然后可以遍历整个结果,利用.getGenericParameterTypes()分析参数类型,最后选择适合的构造方法。这种方法适用于黑箱操作,毕竟开发的时候不大可能完全了解可能加载的构造方法列表。


取得字段的例子,这里使用了暴力反射,而且逐个修改了传入的对象的字段:


	/*
	 * 测试时传入的对象 int[] val = { 3, 4, 7 }; ReflectPoint rp = new ReflectPoint(12,
	 * 25, "ref test", val); testFields(rp);
	 */
	private static void testFields(Object o) {
		// 现在,传入了一个Object类型对象 显然信息是极少的
		// 那么就必须用反射来获取他的各种信息
		// 这里测试获取他的字段并作修改

		// 修改前
		sp(o);
		// ReflectPoint [x=12, y=25, comment=ref test, val=[3, 4, 7]]

		// 这里为了取得所有的成员字段,使用了getDeclared系列方法和setAccessible强制访问,所谓的暴力反射
		Field[] fields = o.getClass().getDeclaredFields();
		for (Field field : fields) {
			if (!field.isAccessible()) {
				field.setAccessible(true);
			}
			// 可以根据字段类型判断
			if (field.getType().isPrimitive()) {
				// 也可以根据字段名称判断
				switch (field.getType().getName()) {
				// 简单修改一下整形内容
				case "int":
					int mod = 0;
					switch (field.getName()) {
					case "x":
						mod = 7;
						break;
					case "y":
						mod = -10;
						break;
					default:
						break;
					}
					try {
						// 用get方法取,用set方法写入
						field.set(o, (int) (field.get(o)) + mod);
					} catch (IllegalArgumentException | IllegalAccessException e) {
						throw new RuntimeException("Field " + e.getMessage());
					}
					break;
				default:
					break;
				}
			} else {
				switch (field.getType().getName()) {
				// 修改String 这里类型名是完整的限定名
				case "java.lang.String":
					try {
						field.set(o, (String) (field.get(o)) + "edited");
					} catch (IllegalArgumentException | IllegalAccessException e) {
						throw new RuntimeException("Field " + e.getMessage());
					}
					break;
				// 数组类型名为'['+元素类型名 基本类型用一个大写字符表示, I表示int
				case "[I":
					try {
						Object arr = field.get(o);
						int[] val = new int[Array.getLength(arr) + 2];
						int sum = 0;
						for (int i = 0; i < Array.getLength(arr); i++) {
							int intValue = Array.getInt(arr, i);
							sum += intValue;
							val[i] = intValue;
						}
						val[val.length - 2] = 787;
						val[val.length - 1] = sum + 787;
						field.set(o, val);
					} catch (IllegalArgumentException | IllegalAccessException e) {
						throw new RuntimeException("Field " + e.getMessage());
					}
					break;
				default:
					break;
				}
			}
		}
		// 修改后
		sp(o);
		// ReflectPoint [x=19, y=15, comment=ref testedited, val=[3, 4, 7, 787,
		// 801]]
		// 可以看到修改成功了
	}



可以看到 字段的取得主要靠Field类 每一个Field类实例代表一个类中的字段,而不是字段的实例,要取得字段的实例,需要传入对象实例,或者传入null来取得静态字段。

最后 测试一下方法的反射


	private static void testMethod() {
		// 还是先准备一个对象
		int[] val = { 3, 4, 7 };
		ReflectPoint rp = new ReflectPoint(12, 25, "ref test", val);

		// 还是暴力一下,拿到所有方法
		Method[] mths = rp.getClass().getDeclaredMethods();
		for (Method method : mths) {
			if (!method.isAccessible()) {
				method.setAccessible(true);
			}
			// 先看看相关的信息
			sp(method.getName());
			sp(method.getModifiers());
			sp(method.getParameterTypes());
			sp(method.getReturnType());

			// 调用一下试试看
			Object result = null;
			Class<?>[] paraType = method.getParameterTypes();
			int paraCount = paraType.length;
			Object[] paras = new Object[paraCount];
			for (int i = 0; i < paraCount; i++) {
				paras[i] = defaultValue(paraType[i]);
			}
			switch (method.getParameterTypes().length) {
			case 0:
				try {
					result = method.invoke(rp);
				} catch (IllegalAccessException | IllegalArgumentException
						| InvocationTargetException e) {
					throw new RuntimeException("Method " + e.getMessage());
				}
				break;
			case 1:
				try {
					result = method.invoke(rp, paras[0]);
				} catch (IllegalAccessException
						| IllegalArgumentException | InvocationTargetException e) {
					throw new RuntimeException("Method " + e.getMessage());
				}
				break;
			case 2:
				try {
					result = method.invoke(rp, paras[0], paras[1]);
				} catch (IllegalAccessException
						| IllegalArgumentException | InvocationTargetException e) {
					throw new RuntimeException("Method " + e.getMessage());
				}
				break;
			// 再多参数就不管了
			default:
				break;
			}
			sp(method);
			if (result != null) {
				sp(result);
			}else{
				sp("null|void");
			}
		}
	}

	private static Object defaultValue(Class<?> cls) {
		if (cls.isPrimitive()) {
			switch (cls.getName()) {
			case "byte":
			case "char":
			case "short":
			case "int":
			case "long":
				return (byte) 0;
			case "float":
			case "double":
				return 0.0f;
			case "boolean":
				return false;
			case "void":
				return null;
			}
		} else {
			try {
				return cls.newInstance();
			} catch (InstantiationException | IllegalAccessException e) {
				sp("unable to init default instance");
				return null;
			}
		}
		// TODO Auto-generated method stub
		return null;
	}



可以看到 方法的反射依靠Method的实例方法invoke,而且和字段类似,可以暴力反射拿到没有权限访问的方法。而且,静态方法的invoke与传入的对象无关,一般来说,可以传入null。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/sisel/article/details/45007785

智能推荐

2018华工计算机考研分数,2018年华南理工大学考研复试分数线_心理咨询师单婷的博客-程序员秘密

根据我校2018年硕士研究生入学考试考生成绩、招生计划安排和各学科基本要求,经学校招生工作领导小组讨论决定,对入围复试的考生初试成绩基本要求如下:一、学术学位学科门类总分政治外语业务一业务二02经济学3306055858503法学335606090900401教育学34560601900403体育学300454019005文学3606060909007理学3155050808008工学300505...

Android实现仿360手机卫士悬浮窗效果_wurui8的博客-程序员秘密

大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话。不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多高手喜欢把自己的经验写在网上,供大家来学习,我也是从中受惠了很多,在此我深表感谢。可是我发现我却从来没有将自己平时的一些心得拿出来与大家分享,共同学习,太没有奉献精神了。于是我痛定思痛,决定从今

STM32 ADC采样率如何计算_stm32adc采样频率_2021乐乐的博客-程序员秘密

第一:前言用STM32 采集数据必须依据信号源设置采样频率。根据奈奎斯特定律,采样率必须是信号源最大频率的2倍以上,但是在实际的需求当中,采样率应该是Fs&gt;3Fmax .采样周期=转换时间+读取时间 . 转换时间=采样时间+12个时钟周期(STM32F4)或者 转换时间=采样时间+12.5个时钟周期(STM32F1)采样时间是STM32采集模拟量的时间,采集的时间越长越...

1063: 判断三角形的形状(3级)输入三角型的三条边,判断三角形的形状。_在学习的小董的博客-程序员秘密

输入三角型的三条边,判断三角形的形状,假设输入的三边边长均>0。判断实数相等,应该用两个数相减绝对值小于某数,如fabs(a*a+b*b-c*c)

gitlab-ce 安装_gitlab-ce安装需要哪个perl包_woniyu123的博客-程序员秘密

#gitlab-ce安装指南##1、暗转必要依赖```xmlsudoyuminstall-ycurlpolicycoreutils-pythonopenssh-serverperlsudosystemctlenablesshdsudosystemctlstartsshdsudofirewall-cmd--permanent--add-service=httpsudofirewall-cmd--permanent--add-servi...

随便推点

一条sql语句执行得很慢的原因有哪些?(面试题)_sql语句执行慢的原因面试题_Chackca的博客-程序员秘密

目录1、偶尔很慢1.1、数据库在刷新脏页(flush)1.2、拿不到锁2、一直这么慢2.1、没用到索引2.2、数据库选错了索引一条SQL语句执行的很慢,那是每次执行都很慢呢?还是大多数情况下是正常的,偶尔出现很慢呢?所以我觉得,我们还得分以下两种情况来讨论。1、大多数情况是正常的,只是偶尔会出现很慢的情况。2、在数据量不变的情况下,这条SQL语句一直以...

IPV6——cisco_xxxxxxxxxxxxxxxxxxxu的博客-程序员秘密

Router&amp;gt;enable Router#configure terminal Enter configuration commands, one per line.  End with CNTL/Z.Router(config)#hostname R3R3(config)#interface ethernet 0/0R3(config-if)#no shutdown R3(config-i...

嵌入式GUI ftk-0.1发布 嵌入式GUI FTK 界面设计器 | 李先静的博客_ilvu999的博客-程序员秘密

嵌入式GUI ftk-0.1发布分类: 嵌入式GUI FTK 转载时请注明出处和作者联系方式文章出处:http://www.limodev.cn/blog作者联系方式:李先静   FTK最初是《系统程序员成长计划》的综合练习项目,在一些热心朋友的帮助下,从国庆到元旦,经过三个月的开发和完善,终于实现基本功能,初具现代嵌入式GUI的轮廓了。新年

一次简单的Maven加速构建实战_wangmm0218的博客-程序员秘密

注意:所有的编译、打包、部署全部是通过Jenkins完成的。公司内部有一个项目,开始做的时候已经预计到会有很多客服端。所以开发就搞了如下的结构: fft-api # 公用的API,所有的程序都必须使用fft-client-hubei # 湖北客户端fft-job # 定时任务fft-persistent # 数据库连接层fft-persi

ZJNU 2021-07-14 个人排位赛3 部分题解_StelaYuri的博客-程序员秘密

完全不会数据结构就比较离谱A - RoadblockProblem Link题意FJ的农场由NNN个点MMM条边组成,边存在边权它每次都会沿着最短路径从点111走到点NNN现在FJ的牛想选择这张图中的任意一条边,使其边权翻倍问选择某条边翻倍后,FJ需要多走的距离的最大值是多少(最短路的最大增量)标签DijkstraSPFA思路先在原图中跑一遍SPFA,求出此时的最短路长度originalDistanceoriginalDistanceoriginalDistance并且回溯得到这条

setTimeout计时器_0到59 settimeout_雷滨滨的博客-程序员秘密

&lt;body&gt; &lt;h1&gt;setTimeout&lt;/h1&gt; &lt;span id="content"&gt;时间&lt;/span&gt; &lt;button onclick="start()"&gt;开始&lt;/button&gt; &lt;script&gt; var x = 00, y = 00, z = 00; function start () {..

推荐文章

热门文章

相关标签