------- android培训、java培训、期待与您交流! ----------
反射就是把Java类中的各种成分映射成相应的Java类。
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是class,class代表的是一份字节码。
用反射获得各个字节码对应的实例对象的几种方式:
1、类名.class,例如:System.class;
2、对象.getClass(),例如:new Date().getClass();
3、Class.for Name(“类名”),例如:Class.forName(“java.util.Date”);
4、对于几种基本数据类型还有,如int.class==Integer.TYPE;
数组类型的Class实例对象,Class.isArray();
总之,只要在源程序中出现的类型,都有各自的Class实例对象,例如:int[]、void
实例如下:
public class ReflectTest { public static void main(String[] args)throws Exception { // TODO Auto-generated method stub String str1="abc"; Class cls1=str1.getClass();//获取String类字节码方法一 Class cls2=String.class;//获取String类字节码方法二 Class cls3=Class.forName("java.lang.String");//获取String类的字节码方法三 System.out.println(cls1==cls2);//此处验证为同一份字节码,输出为true System.out.println(cls2==cls3);//此处验证为同一份字节码,输出为true System.out.println(cls1.isPrimitive());//判断是否为基本类型,输出为false System.out.println(int.class.isPrimitive());//判断是否为基本类型,输出为true System.out.println(int.class==Integer.TYPE);//基本类型字节码获取,输出为true System.out.println(int[].class.isPrimitive());//判断是否为基本类型,输出为false System.out.println(int[].class.isArray());//判断字节码是否为数组字节码,输出为true }
用反射获得构造方法并创建对象的步骤:
1、得到某个类的一个构造方法:
得到某个类的所有构造方法,如:如Constructor constructor=Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
获得相应的构造方法时要用到类型
Constructor[] constructor=Class.forName(“java.lang.String”).getConstructors();
2、创建实例对象:
如:String str=(String)constructor.newInstance(new StringBuffer(“abc”));
备注:如调用的是不带参数的构造方法则可以更简单,可以用Class.newInstance()方法,例子如下:String obj=(String)Class.forName(“java.lang.String”).newInstance();该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
实例如下:
public class ReflectTest { public static void main(String[] args)throws Exception { Constructor constructor1=String.class.getConstructor(StringBuffer.class);//获取String类的构造函数 String str2=(String)constructor1.newInstance(new StringBuffer("abc"));//用获取的构造函数创建一个实例对象 System.out.println(str2.charAt(2)); }
用反射获取成员变量(Field)值的步骤:
1、得到某个类中的成员变量,如下例子:
ReflectPoint pt1=new ReflectPoint(3,5);ReflectPoint为自定的一个类;
Field fieldY=pt1.getClass().getField(“y”);
2、通过Field中的方法获取成员变量中的值,如下:
fieldY.get(pt1);
实例如下:
public class ReflectTest { public static void main(String[] args)throws Exception { ReflectPoint pt1=new ReflectPoint(3,5);//创建一个对象,用于实验用反射获取成员变量 Field fieldx=pt1.getClass().getDeclaredField("x");//用反射获取私有的成员变量 fieldx.setAccessible(true);//让私有成员变量显示化 System.out.println(fieldx.get(pt1)); changeStringValue(pt1);//此方法是用反射改变成员变量的内容 System.out.println(pt1); } private static void changeStringValue(Object obj)throws Exception {//用反射改变成员变量的内容 Field[] fields=obj.getClass().getFields(); for(Field field:fields) { if(field.getType()==String.class){//字节码的比较用== String oldValue=(String)field.get(obj); String newValue=oldValue.replace('b', 'a'); field.set(obj, newValue); } } } } public class ReflectPoint { private int x; public int y; public String str1="ball"; public String str2="basketball"; public String str3="itcast"; public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } public String toString(){ return str1+"..."+str2+"..."+str3; } }
用反射获取方法(Method)的步骤:
1、得到某个类中的方法,如下例子:
Method methodCharAt=String.class.getMethod(“charAt”,int.class);后面两个参数为方法名及原方法的参数,如果参数有多个,就写多个;
2、用Method获得的字节码去作用于某个对象
如:methodCharAt.invoke(str1,1);如果invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法。
实例如下:
public class ReflectTest { public static void main(String[] args)throws Exception { Method methodCharAt=String.class.getMethod("charAt",int.class);//用反射获取一个的方法 System.out.println(methodCharAt.invoke(str1, 1));//用于指定是获取哪个对象的方法 //TestArgument.main(new String[]{"123","3343","23245"}); /*用反射怎么获取一个类的主函数?*/ String startingClassName=args[0]; /*如果是通过cdm运行,则需给ReflectTest类中的主函数输入String参数TestArgument, 如用软件也需设置TestArgument作为String类参数传入ReflectTest类的主函数中*/ Method mainMethod=Class.forName(startingClassName).getMethod("main", String[].class); mainMethod.invoke(null, (Object)new String[]{"123","3343","23245"});/*后面的参数也可写成new Object[]{new String[]{"123","3343","23245"}}*/ } } class TestArgument{//此类用于测试用反射调用此类主函数的实验 public static void main(String[] agrs){ for(String str:agrs){ System.out.println(str); } } }
数组的反射:
1、具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
2、代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
3、基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
4、反射中有一工具类Array专门用于对数组的反射操作。
hashCode方法和HashSet类:
为了提高集合中元素的查找效率,出现了一种哈希算法,这种方式将集合分成若干存储区域,每个对象可以计算出一个哈希码值,可以将哈希码值分组,每组分别对应某个存储区域,根据一个对象的哈希码值就可以该对象应该存储在哪个区域,HashSet就是采用哈希算法进行存取对象的集合。
HashSet原理:HashSet内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域;Object类中定义了一个hashCode()方法返回每个Java对象的哈希码,当从HashSet集合中查找某个对象时,Java系统首先调用对象的hashCode()方法获取该对象的哈希码,然后根据哈希码找到相应的存储区域,最后取出该存储区域的每个元素与该对象进行equals比较,这样不用遍历集合中的所有元素就可以得到结论,大大提高了检索性能。
注意事项:
1、只有类的实例对象要被采用哈希算法进行存储和检索时,这个类才需要按要求覆盖hashCode()方法,一般hashCode方法和equals方法都是同时被覆盖。
2、通常来说,一个类的两个实例对象equals()方法比较的结果相等时,它们的哈希码也必须相等,但反之则不成立,即equals方法比较结果不相等的对象可以有相同的哈希码,或者说哈希码相同的两个对象equals方法比较的结果可以不等。
3、当一个对象被存储进HashSet集合以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了,在这种情况下,即使在contains方法使用该对象当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。