黑马程序员——高新技术(反射,用反射获取构造函数、方法等)

时间:2021-09-19 12:25:52

------- android培训java培训、期待与您交流! ----------

反射就是把Java类中的各种成分映射成相应的Java类。

Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是classclass代表的是一份字节码。


用反射获得各个字节码对应的实例对象的几种方式:

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集合中单独删除当前对象,从而造成内存泄露。