Java编程思想(十八) —— 再谈反射

Java编程思想(十五) —— 类型信息之反射Java编程思想(十六)
—— 联系JVM
再谈Class,书上只用了3页就讲完了,还有讲了那么多Class的东西,接下来要从反射中怎么用,自己结合API和其他资料再写多一些。

示例:Test.java

public class Test {
        public Test() {
        }
        public Test(int i) {
          System.out.println(i);
        } private void pri() {
		System.out.println("private");
	}

	public void pub() {
		System.out.println("public");
	}

	protected void pro() {
		System.out.println("protected");
	}

	private String pristr;
	public String pubstr;
	protected String pro;
}

有不同的方法和域。

测试:

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Reflect {
	public static void main(String[] args) {
		try {
			//测试参数类型后面跟着三点的作用
			Test t = new Test();
			t.three();
			t.three("a","b","c");
			//输出[] [a,b,c] 证明...表示的参数可以为空或者是传字符数组  即可传可不传  英文成为varargs

			//将Test装载进命名空间   得到代表Test的Class对象引用
			Class c = Class.forName("Test");

			//Method类  拿到名为pub的方法
			try {
				Method one = c.getMethod("pub");
				try {
					//方法调用 invoke(Object obj, Object... args)  obj为所要调用方法所属的类  args为方法所传的参数
					//所传obj要为实例 ,不然会有object is not an instance of declaring class
					one.invoke(c.newInstance());
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (InstantiationException e) {
					e.printStackTrace();
				}
			} catch (NoSuchMethodException e1) {
				e1.printStackTrace();
			} catch (SecurityException e1) {
				e1.printStackTrace();
			}

			//这种返回的方法的数组   为公共方法 包括那些父类和父接口中继承的方法
			Method[] method = c.getMethods();
			//返回的是该Class对象代表的类声明的所有方法  这样就包括public private protected方法
			Method[] declaredmethod = c.getDeclaredMethods();
			Class[] array = c.getClasses();
			Field[] field = c.getFields();
			Field[] declaredfield = c.getDeclaredFields();
			for (int i = 0; i < array.length; i++) {
				System.out.println("Classes array is "+array[i].getName());
			}

			for (int i = 0; i < method.length; i++) {
				System.out.println("Methods array is "+method[i].getName());
			}

			//getReturnType拿到方法返回的属性。
			for (int i = 0; i < declaredmethod.length; i++) {
				System.out.println("DeclaredMethods array is "+declaredmethod[i].getReturnType().getName()+
						" "+declaredmethod[i].getName());
				try {
					try {
						String s[] ={"1","2"}; 

						//如果没有设置 就会有Class Reflect can not access a member of class Test with
						//modifiers "private"异常
						//Constructor, Field, Method 为AccessibleObject的子类
						//有着访问控制的权限  默认是false  会强制java进行 访问检查  所以private是禁止访问的  Filed同理
						declaredmethod[i].setAccessible(true);
						System.out.println(declaredmethod[i].getName());
						if(declaredmethod[i].getName().equals("three")){
							System.out.println("true");
							//下面这条会报 wrong number of arguments异常
							//这个问题是搞了我最久的东西  中文都是前篇一律的胡说八道
							//invoke(Object obj, Object... args)   数组是协变的 String[]被当成Object[]
							//String[] 被当成了整个参数 而string数组里面的元素又不是object
							//而不是args的第一个参数,其实,我们String数组是Objec数组的第一个元素

							/*这是因为编译器会把字符串数组当作一个可变长度参数传给对象o,而我们取得方法只有一个参数,
							所以就会出现wrong number of
							arguments的异常,我们只要把字符串数组强制转换为一个Object对象就可以解决这个异常了  这个简直就是放屁*/
							//declaredmethod[i].invoke(c.newInstance(),s);
							declaredmethod[i].invoke(c.newInstance(),new Object[]{s});
							declaredmethod[i].invoke(c.newInstance(),(Object)new String[]{"1","2","3"});
							declaredmethod[i].invoke(c.newInstance(),(Object)null);
							declaredmethod[i].invoke(c.newInstance(),(Object)new String[]{});
						}else{
							declaredmethod[i].invoke(c.newInstance());
						}
					} catch (InstantiationException e) {
						e.printStackTrace();
					}
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				}
			}

			//与Method同理,只不过Filed拿到的是域。
			for (int i = 0; i < field.length; i++) {
				System.out.println("Fields array is "+field[i].getName());
			}

			for (int i = 0; i < declaredfield.length; i++) {
				System.out.println("Declaredfieds array is "+declaredfield[i].getName());
				try {
					declaredfield[i].setAccessible(true);
					declaredfield[i].set(t, "a");
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
			System.out.println(t.pro);
			System.out.println(t.pub);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

基本所有的问题和属性方法都在里面,不过wrong number of arguments异常,是我查了最久的异常,静下心来后看懂了。

这是因为编译器会把字符串数组当作一个可变长度参数传给对象o,而我们取得方法只有一个参数,

所以就会出现wrong number of arguments的异常,我们只要把字符串数组强制转换为一个Object对象就可以解决这个异常了。

全部都是这个错误的答案,invoke(Object obj, Object... args)  ,数组是协变的 String[]被当成Object[]。

String[] 被当成了整个参数 ,而string数组里面的元素又不是object 。

而不是args的第一个参数,其实,我们String数组是Objec数组的第一个元素。

慢慢看就理解了。

后面补充:

Constructor<?>[] ct = Class.forName("Test").getConstructors();
			for (int i = 0; i < ct.length; i++) {
				System.out.println("Constructor:" + ct[i].getName());
			}
			System.out.println("new instance");
			try {
				ct[0].newInstance();
				ct[1].newInstance(123);
			} catch (InstantiationException | IllegalAccessException
					| IllegalArgumentException | InvocationTargetException e2) {
				e2.printStackTrace();
			}

Constructor就是构造器,不过不同于Class的newInstace方法,Constructor的newInstance(Object... initargs) 为varargs,即上面提到的三点。可以进行参数的传入。

而有趣的一点是,Constructor数组是有序的,前提是你的构造方法写在类里面最前面,如果前面有其他方法,那么数组的顺序就不是按照排序而排了。

反射的作用,就是运行时检查对象的类型,任意调用对象的方法(Spring和servlet中都用到了,注意到没有,我们在xml配置,然后属性会根据xml注入),同时可以知道方法参数和属性。

虽然这算是打破了所谓的封装性,private属性,不过想想,没有后门,语言岂不是死了?

对于反射,想了解跟多的可以看看国外的反射教程,http://www.programcreek.com/2013/09/java-reflection-tutorial/

时间: 2025-01-05 05:14:30

Java编程思想(十八) —— 再谈反射的相关文章

Java编程思想(十六) —— 联系JVM再谈Class

编程思想这个专栏停了好久了,主要是把精力放在了其他知识上,现在继续补上. 前面两篇写到RTTI和简单的反射介绍,先回顾一下: RTTI,运行时类型信息,多态的应用,类型转换其实是发生在运行期间. Class对象: 编程思想讲到的定义,Java使用Class对象来执行其RTTI,类是程序的一部分,每个类都有一个Class对象,其实每编写和编译一个新类,就会产生一个Class对象,其实这个对象时被保存在同名的.class文件中的.生成这个类对象,其实是JVM(Java虚拟机)使用了"类加载器&quo

Java编程思想(十五) —— 类型信息之反射

讲完.class,Class之后,继续. 1)泛化的Class引用 Class也可以加入泛型,加入之后会进行类型检查. 贴一下书上原话,Class<?>优于Class,虽然他们是等价的,Class<?>的好处是碰巧或疏忽使用了一个非具体的类引用.我搞不懂这个所谓非具体是什么? 后面弄懂了,其实<?>作为通配符,就是未知的,直接写结论的话不能写个具体类型吧,作者的意思其实就是说加了泛型的Class就是选择了非具体的版本. 加入泛型的原因是提供编译期间的类型检查,操作失误的

Java编程思想(十二) —— 字符串(1)

字符串在编程中也是经常使用的. 1)不可变 其实查看API就会发现: public final class String extends Object implements Serializable, Comparable<String>, CharSequence final,不可变的. public class TestString { static String upcase(String s){ return s.toUpperCase(); } public static void

Java编程思想(十二) —— 字符串(2)

上篇讲到String的基本用法及StringBuilder和String的比较.继续. 给大家感受一下RednaxelaFX的厉害,他大学看的书. 嗯,这就是去硅谷的水平,所以,还是继续看书吧. 1)格式化输出 确实,说到C的printf,是不能用重载的+操作符的. printf("%d %f", x , y); %d这些为格式修饰符,%d表示整数,x插入到%d的位置,%f表示浮点数,y查到%f的位置. Java也模仿了C: public class TestString { publ

Java编程思想(十四) —— 类型信息RTTI(1)

译者翻译的时候有些奇怪,Runtime type information (RTTI) allows you to discover and use type information while a program is running. 运行时类型信息(原来的翻译没有括号这里面的内容,Runtime type information,简称RTTI,个人觉得这样注释比较好)可以让你在程序运行的时候发现和使用类型信息.后面直接出现RTTI让人疑惑. 1)为什么需要RTTI 之前的多态的例子中: p

Java编程思想(五) —— 多态(上)

上一章,Java编程思想(四) -- 复用类里面讲到了向上转型,感觉和多态放在一起写更好. 多态,polymorphism.一个重要的特性,篇幅太长了,分上下两篇写. (1)向上转型 class TV{ public static void show(TV tv){ System.out.println("TV"); } } public class LeTV extends TV{ public static void main(String[] args) { LeTV letv

Java编程思想——第17章 容器深入研究(two)

六.队列 排队,先进先出.除并发应用外Queue只有两个实现:LinkedList,PriorityQueue.他们的差异在于排序而非性能. 一些常用方法: 继承自Collection的方法: add 在尾部增加一个元索 如果队列已满,则抛出一个IIIegaISlabEepeplian异常 remove 移除并返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementException异常 element 返回队列头部的元素 如果队列为空,则抛出一个NoSuchElementExce

Java编程思想重点笔记(Java开发必看)

Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面 试过程中,而且在大型项目开发中也是常用的知识,既有简单的概念理解题(比如is-a关系和has-a关系的区别),也有深入的涉及RTTI和JVM底层 反编译知识. 1. Java中的多态性理解(注意与C++区分) Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意

64.JAVA编程思想——优先级

64.JAVA编程思想--优先级 线程的优先级(Priority)告诉调试程序该线程的重要程度有多大.如果有大量线程都被堵塞,都在等候运行,调试程序会首先运行具有最高优先级的那个线程.然而,这并不表示优先级较低的线程不会运行(换言之,不会因为存在优先级而导致死锁).若线程的优先级较低,只不过表示它被准许运行的机会小一些而已. 可用getPriority()方法读取一个线程的优先级,并用setPriority()改变它.在下面程序中,大家会发现计数器的计数速度慢了下来,因为它们关联的线程分配了较低