反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。
反射相关概念: /** * 描述类结构的类(Class),相当于数据类型 * 一般的类结构: * Name 类名 * Constructor 构造器 * Field 类中域 * Method 类中方法 * Class类对象:java.lang.Math,java.lang.String,java.lang.Integer等类的字节码 * 反射:就是把Java类中的各种成分映射成相应的Java类对象. * @author Administrator */
JDK参考手册中介绍:
0.Class类用于表示.class文件,如我们用Integer包装类描述数字.Class类描述类的信息(字节码文件) 1.如何得到某个class文件对应的class对象。 •类名.class, •对象.getClass() •Class.forName(“类名”) 2.数组类型的Class实例对象 •Class.isArray() 3.总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int,void…
Class对象就是Java源文件编译过的.class文件(字节码文件),有关字节码格式规则介绍:
Java字节码格式
参照他人的博客:http://my.oschina.net/indestiny/blog/194260
0.Constructor类的实例对象代表类的一个构造方法。 1.得到某个类所有的构造方法,例: Constructor [] constructors= Class.forName("java.lang.String").getConstructors(); 2.得到某一个构造方法,例: Constructor constructor = Class.forName(“java.lang.String”).getConstructor(StringBuffer.class); 3.利用构造方法创建实例对象: String str = (String)constructor.newInstance(“abc”); 4.Class类的newInstance()方法也可创建类的实例,其内部工作原理是先得无参的构造方法,再用构造方法创建实例对象 String obj =(String)Class.forName("java.lang.String").newInstance();
1.Field类代表某个类中的一个成员变量 ???问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。(注意访问权限的问题) 2.示例代码: ReflectPoint point = new ReflectPoint(1,7); Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y"); System.out.println(y.get(point)); //Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x"); Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x"); x.setAccessible(true); System.out.println(x.get(point));
1.Method类代表某个类中的一个成员方法 2.得到类中的某一个方法: 例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class); 3.调用方法: a.通常方式:System.out.println(str.charAt(1)); b.反射方式: System.out.println(charAt.invoke(str, 1)); 如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一 个静态方法! 4.jdk1.4和jdk1.5的invoke方法的区别: Jdk1.5:public Object invoke(Object obj,Object... args) Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为参数 传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码 也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
package com.itcast.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ClassTest { @SuppressWarnings("unused") public static void main(String[] args) { /** * 三个不同方法获取Class对象,String类的字节码 */ @SuppressWarnings("rawtypes") Class clazz=String.class; @SuppressWarnings("rawtypes") Class clazz1=new String().getClass(); try { @SuppressWarnings("rawtypes") Class clazz2=Class.forName("java.lang.String"); } catch (ClassNotFoundException e) { e.printStackTrace(); } /**Constructor---->new String(StringBuffer strb); //如果利用不带构造方法生成相应地对象,则Class.newInstance(); * */ @SuppressWarnings("rawtypes") Constructor con=null; try { //选择String类中哪个构造方法(编译器并不知道是String的构造方法) con=String.class.getConstructor(StringBuffer.class); //生成String对象时传入StringBuffer对象参数 Object obj=con.newInstance(new StringBuffer("absc")); String str=(String)obj; System.out.println(str); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } /** * Field---->(获取类中域中成员变量) * Default to 0 */ Field field=null; try { field = String.class.getDeclaredField("hash"); field.setAccessible(true); System.out.println(field.get("hash")); } catch (NoSuchFieldException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } /** * Method----->获取类中方法 */ try { Method method=String.class.getMethod("charAt", int.class); char ch=(Character) method.invoke("hash", 1); System.out.println(ch); //Method方法中参数是数组时 Method startMethod=Class.forName("com.itcast.reflect."+args[0]).getMethod("main", String[].class); //startMethod.invoke(null, new String[]{"1212","784","7889"}); //java.lang.IllegalArgumentException: wrong number of arguments //需要拆包:1、new Object[]{new String[]{"1212","784","7889"}} // 提示编译器不是数组,所以不需要拆包 // 2、(Object)new String[]{"1212","784","7889"} startMethod.invoke(null, (Object)new String[]{"1212","784","7889"}); }catch (Exception e) { e.printStackTrace(); } } } class startClass{ public static void main(String[] args) { for(String arg:args){ System.out.println(arg); } } }
测试结果:absc 0 a 1212 784 7889 Run Configurations:
package com.blog; import java.lang.reflect.Array; /** * 获取数组中元素 */ public class ArrayReflect { @SuppressWarnings("unused") public static void main(String[] args) { Object obj=new int[]{45,78,78}; Object obj1=new String[]{"abc","daf","fdsafewrq"}; printObject(obj1); } public static void printObject(Object obj) { @SuppressWarnings("rawtypes") Class clazz=obj.getClass(); if(clazz.isArray()){ for(int i=0,len=Array.getLength(obj);i<len;i++){ System.out.println(Array.get(obj, i)); } }else{ System.out.println(obj); } } } //output:abc daf fdsafewrq ~//
一般用在框架上面,例如Spring框架就是很典型的运用反射技术.我们需要通过XML配置文件设置相关Bean依赖注入.只需要Bean的完整名称.至于相关依赖注入反射实现,黎老师视频中有源码实现Spring部分功能.
下面将有一个案例,读取config.properties配置文件.获取JavaBean实例.
package com.blog; import java.io.IOException; import java.io.InputStream; import java.util.Collection; import java.util.Properties; public class DemoReflect { public static void main(String[] args) { InputStream in = DemoReflect.class.getClassLoader().getResourceAsStream("config.properties"); Properties prop = new Properties(); try { prop.load(in); in.close(); String className = prop.getProperty("className"); Collection collection = (Collection) Class.forName(className).newInstance(); System.out.println(collection.isEmpty()); collection.add("itcast"); System.out.println(collection.isEmpty()); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
知识点:有关于ArrayList,HashSet区别和HashCode的分析 答:ArrayList,HashSet存储的是对象引用地址 ArrayList存储引用地址(可以相同的的,重复的) HashSet存储引用地址(首先检查有没有相同地引用),相同则不放入其中 由于默认检查的是调用Object.equals()方法,只是比较两者的引用地址而没有比较两者的内容 可以重写对象的equals() @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ReflectPoint other = (ReflectPoint) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } 但是由于HashSet是按着Hash算法分组存储对象,所以每个对象都有一个HashCode值,属于某一个区域组 (比较对象时,也同时比较HashCode的值) 就必须要重写hashCode(),同时参与hash运算的变量不能参与其他运算 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } 如果参与运算的话,HashSet.remove(对象)会操作失败,导致内存泄漏;
类加载器和资源,配置文件: 1、工程的绝对路径+内部的相对路径 FileInputStream in=new FileInputStream(realpath+path); 2、this.getClass().getClassLoader().getResourceAsStream(config.properties); 缺点:只可以读取,不可以写入。而且web项目启动,很难修改配置文件.就要从新部署.