黑马程序员:JAVA反射API

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

1、通过反射API可以获取程序在运行时刻的内部结构。

      使用反射API的时候就好像在看一个JAVA类在水中的倒影,可以知道JAVA类的内部结构,可以实现与它交互,包括创建新的对象和调用对象的方法等。

               1)对构造方法的调用

	//直接在源代码中使用String(StringBuffer buffer)构造方法
	StringBuffer buffer = new StringBuffer("abc");
	String str1 = new String(buffer);
	//通过反射API调用String(StringBuffer buffer)构造方法
	Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
	String str2 = (String)constructor1.newInstance(new StringBuffer("abc"));
	System.out.println(str2.charAt(2));

                2)对属性的调用

	ReflectPoint point1 = new ReflectPoint(3, 4);
	Field fieldY = ReflectPoint.class.getField("y");
	//field的值不是4,因为没有为其指定具体对象,field是代表ReflectPoint类的y字段的字节码
	System.out.println(fieldY.get(point1));
	
	Field fieldX = ReflectPoint.class.getDeclaredField("x");
	fieldX.setAccessible(true);
	System.out.println(fieldX.get(point1));

                改变属性的值

    private static void changeStringValue(Object obj) throws Exception {
	Field[] fields = obj.getClass().getFields();
	for(Field field : fields){
	    //if(field.getType().equals(String.class))
	    //不用equals是因为字节码文件只会产生一个对象,用  == 比较
	    if(field.getType() == String.class){
		String oldValue = (String)field.get(obj);
		String newValue = oldValue.replace('b', 'a');
		field.set(obj, newValue);
	    }
	}
    }

                3)对方法的调用

	System.out.println(str1.charAt(1));
	
	Method methodCharAt = String.class.getMethod("charAt", int.class);
	System.out.println(methodCharAt.invoke(str1, 1));
	//利用反射的方式得到字节码里面的方法,再用方法作用与某个对象
	System.out.println(methodCharAt.invoke(null, 1));
	//这个方法是静态的应写成null

总结:JAVA反射API可以获取程序在运行时刻的内部结构。可以遍历出来一个JAVA类的内部结构,包括构造方法、声明的域和定义的方法等。对应的方法分别getConstructor、getField和getMethod。这三个方法还有对应的getDeclaredXXX,区别在于getDeclaredXXX方法只会获取该类自身所声明的元素,而不会考虑继承下来的。Constructor、Field和Method这三个类分别表示类中的构造方法、域和方法。这些类中的方法可以获取到所对应结构的元数据。

得到类的字节码有三种方法
1 Person.class
2 new Date().getClass()
3 Class.forName("java.util.Data");//反射主要用第三种


2、对接收数组参数的成员方法进行反射时,
由于要兼容JKD1.5以前的版本,java虚拟机会把数组拆开,把数组当成一个包装参数的包,所以需要再用数组进行封装

如:

public class ReflectTest2 {
    public static void main(String[] args) throws Exception {
	//TestArguments.main(new String[] { "111", "222", "333" });
	// 一般的时候我们不知道要执行的类的名字,即TestArguments是不知道,可能是args里面传过来的字符串指明要执行的类的main方法,所以要用反射
	String statringClassName = args[0];
	Method mainMethod = Class.forName(statringClassName).getMethod("main", String[].class);
	//mainMethod.invoke(null, new Object[]{ new String[] { "111", "222", "333" } });
	//main方法是静态的,不需要对象,所以为null
	//对于接收数组类型的成员方法的反射,需要将数组打包,因为要兼容JDK1.5之前的版本
	mainMethod.invoke(null, (Object)new String[] { "111", "222", "333" });
	//方法2,张老师解释为将String[]转换为Object告诉编译器我传的是一个对象,不是参数数组,不需要拆包,这种方式相对于方法1效率略高
    }
}

class TestArguments {
    public static void main(String[] args) {
	for (String arg : args) {
	    System.out.println(arg);
	}
    }
}

3、关于数组的反射

没有办法得到数组的类型
Object[] a = new Object[]{"a", 1};
a[0].getClass();

对象a中可能含有多种类型的元素,所以没有具体的类型。

public class ReflectTest3 {
    public static void main(String[] args) {
	int[] a1 = new int[]{1,2,3};
	int[] a2 = new int[4];
	int[] a3 = new int[3];
	int[][] a4 = new int[2][3];
	
	String[] s1 = new String[]{"aa","bb"};
	
	Object obj1 = a1;
	Object obj2 = a2;
	Object obj3 = a3;
	Object obj4 = a4;
	Object obj5 = s1;
	
	
	Object[] ob1  = a4;
	Object[] ob2 = s1;
	
	System.out.println(a1.getClass() == a2.getClass());
	System.out.println(a1.getClass() == a3.getClass());
	
	System.out.println(a1);
	System.out.println(s1);
	//基本数据类型的父类不是Object,所以不会被解析成list
	//Object[] ob = a1;  同理这句报错
	System.out.println(Arrays.asList(new Object[]{a1}));
	System.out.println(Arrays.asList(s1));
	
	printObject(a1);
    }

    private static void printObject(Object obj) {
	Class cla = obj.getClass();
	
	if(cla.isArray()){
	    int len = Array.getLength(obj);
	    for(int i = 0; i < len; i ++){
		System.out.println(Array.get(obj, i));
	    }
	}
	else{
	    System.out.println(obj);
	}
    }
}

4、ArrayList和HashSet

HashSet是一种基于Hash表的集合,
向其集合添加元素的过程为:每一个元素被存之前,根据哈希算法对元素存储的物理地址,转换成对应的哈希值。

	Collection collection = (Collection)Class.forName(className).newInstance();
	
	ReflectPoint p1 = new ReflectPoint(1, 2) ;
	ReflectPoint p2 = new ReflectPoint(1, 2) ;
	ReflectPoint p3 = new ReflectPoint(2, 2) ;
	
	collection.add(p1);
	collection.add(p2);
	collection.add(p3);
	collection.add(p1);
	
	System.out.println(collection.size());


当一个对象被存储进HashSet集合中以后,就不要修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了。这时候,即使在contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回找不到对象的效果。这会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄漏。

	Collection collection = (Collection)Class.forName(className).newInstance();
	
	ReflectPoint p1 = new ReflectPoint(1, 2) ;
	ReflectPoint p2 = new ReflectPoint(1, 2) ;
	ReflectPoint p3 = new ReflectPoint(2, 2) ;
	
	collection.add(p1);
	collection.add(p2);
	collection.add(p3);
	collection.add(p1);
	
	//p1.y = 4;
	collection.remove(p1);

使用别人写的类有两种方式

1、你调用别人的类
2、别人调用你的类(Struts调用你的类)

5、关于框架
框架要解决的核心问题
因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象,而要用反射方式来做。

public class ReflectTest {
    public static void main(String[] args) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException {
	//InputStream ips = new FileInputStream("src/com/itheima/day3/config.properties");
	//类加载器加载
	//InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("com/itheima/day3/config.properties");
	InputStream ips = ReflectTest.class.getResourceAsStream("config.properties");
	
	
	Properties properties = new Properties();
	properties.load(ips);
	ips.close();//关闭掉操作系统所占用的资源内存
	
	String className = properties.getProperty("className");
	Collection collection = (Collection)Class.forName(className).newInstance();
	
	ReflectPoint p1 = new ReflectPoint(1, 2) ;
	ReflectPoint p2 = new ReflectPoint(1, 2) ;
	ReflectPoint p3 = new ReflectPoint(2, 2) ;
	
	collection.add(p1);
	collection.add(p2);
	collection.add(p3);
	collection.add(p1);
	
	//p1.y = 4;
	collection.remove(p1);
	
	System.out.println(collection.size());
    }
}
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。


你做的门调用锁,锁是工具;
你做的门被房子调用,房子是框架。
房子和锁都是别人提供的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值