java高新技术第一天——反射

反射的基石->Class类
java程序中的各个java类属于同一类事物,描述这类事物的java类名
就是Class。
对比提问:众多的人用一个什么类表示?众多的java类用一个什么类表示?
   人-->Person
   Java-->Class
对比提问:Person类代表人,它是实例对象就是张三,李四这样一个个
   具体的人,Class类代表java类,它的各个实例对象又分别对应什么呢?


  对应各个类在内存中的字节码,例如,Person类的字节码,ArrayList类
  字节码,等等。
  一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的
  内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中
  的内容是不同的,这一个个的空间可分别用一个个的对象来表示,
  这些对象显然具有相同的类型,这个类型是什么呢?


如何得到各个字节码对应的实例对象(Class类型)
类名.class,例如,System.class
对象.getClass(),例如,new Date().getClass();
Class.forName("类名"),例如,Class.forName("java.util.Date");


九个预定义Class实例对象:
  参看Class.isPrimitive方法的帮助
  int.class==Integer.TYPE;


数组类型的Class实例对象
Class.isArray()
总之只要是在源程序中出现的类型,都有各目的的Class实例对象
例如  int[]   void[]


Class
java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,
至于这个属性的值是什么,则是由这个类的实例对象来确定,不同的
实例对象有不同的属性值。java程序中的各个java类,它们是否属于同一类事物
是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,
要注意与小写class关键字的区别哦。Class类描述了哪些方面的信息呢?
类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的

列表等等。学习反射,首先就要明白Class这个类。


---------------------------------------------------------------
反射


反射就是把java类中的各种成分映射成相应的java类。例如,
一个java类中用一个Class类的对象来表示,一个类中的组成部分:
成员变量,方法,构造方法,包等等信息也用一个个的java类来表示
就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。
表示java类的Class显然要提供一系列的方法,来获得其中的变量、方法、构造方法、
修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、
Method、Constructor、Package等等。


一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示
通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象后
有什么用?怎么用?这是学习和应用反射的要点
------------------------------------------------


Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例:Constructor[] constructors=Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例:Constructor constructor=Class.forName("java.lang.String").getConstrutor(StringBuffer.class);
Class.forName("java.lang.String").getConstructor(StringBuffer.class);
//获得方法时要用到类型




创建实例对象:
通常方式:String str=new String(new StringBuffer("abc"));
反射方式:String str=(String)constrctor.newinstance(new StringBuffer("abc"));
//这里获得方法时要用到上面相同的实例对象
Class.newInstance()方法:
例子String obj=(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实例对象。




作业:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”。


字节码用==做比较!!!


Constructor constructor1=String.class.getConstructor(StringBuffer.class.);
String str2=(String)constructor1.newInsatance(new StringBuffer("abc"));
str2.charAt(2);

------------------------------------------------------------
Method类(成员方法的反射)
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子:Method charAt=
     Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法:
通常方式:System.out.println(str.charAt(str,1));
反射方式:System.out.println(chatAt.invoke(str,1));
如果传递给Method对象的invoke()方法的一个参数为null,这有着什么样
     的意义呢?说明该Method对象对应的是一个静态方法。
jdk1.4和jdk1.5的invoke方法的区别:
1.5:public Object invoke(Object obj,Object... args)
        1.4: public Object invoke(Object obj,Object[] args)
既按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法时,
数组中的每个元素分别对应被 






Method methodCharAt=String.class.getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str1,1));//b
System.out.println(methodCharAt.invoke(str1,new Object[][2]));






--------------------------------------------------------------------
用反射方式执行某个类中的main方法
目标:
    写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
问题:
启动java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),
     通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?
     按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的语法,数组中的每一个元素对应一个参数,当把
     一个字符串数组作为参数传递给invoke方法时,javac会按照哪种语法进行处理?
     jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组
     打散成为若干个单独的参数。所以,在给main方法传递参数时,不能使用
     代码mainMethod.invoke(null,new String[]{"xxx"},javac只把它当做jdk
     1.4的语法进行理解,而不把它当做jdk1.5的语法解释,因此会出现参数类型
     不对的问题。
解决办法:
   mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
   mainMethod.invoke(null,(Object)new String[]{"xxx"});
   编译器会作特殊处理,编译时不把参数当做数组看待,也就不会数组打散成
   若干个参数了

=============================================
数组的反射
1.具有相同维数和元素类型的数组属于同一个类型,即
具有相同的Class实例对象。
2.代表数组的Class实例对象的getSuperClass()方法返回的
父类为Object类对应的Class。
3.基本类型的一位数组可以被当做Object类型使用,不能当做Object[]
类型使用;非基本类型的一维数组,既不可以当做Object类型使用,
又可以Object[]类型使用。
4.Arrays.asList()方法处理int[]和String[]时的差异。
5.Array工具类用于完成对数组的反射操作。
思考:怎么得到数组中的元素类型?


Object[] a = new Object[]{"a",1};
a[0].getClass().getName();
//这样只能得到某一数组元素的类型,不能得到所有元素的类型。


反射的作用->实现框架功能
框架与框架要解决的核心问题
我做房子,用户自己安装门窗和空调,我做的房子就是框架。
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。




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


综合案例
  先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成
ReflectPoint类的equals和hashCode方法,比较两个集合的运行结果差异。
  然后改为采用配置文件加反射的方式创建ArrayLIst和HashSet的实例对象,
比较观察运行结果差异。

package cn.itcast.day1;

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;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}

	@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;
	}

	@Override
	public String toString(){
		return str1 + ":" +str2 +":"+str3;
	}
}

package cn.itcast.day1;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

public class ReflectTest {
	public static void main(String[] args) throws Exception {
		String str1 = "abc";
		Class cls1 = str1.getClass();
		Class cls2 = String.class;
		Class cls3 = Class.forName("java.lang.String");
		System.out.println(cls1);//class java.lang.String
		System.out.println(cls2);//class java.lang.String
		System.out.println(cls3);//class java.lang.String
		
		System.out.println(cls1==cls2);//true
		System.out.println(cls1==cls3);//true
		
		System.out.println(cls1.isPrimitive());//false   isPrimitive()判断字节码是不是基本类型的
		System.out.println(int.class);//int
		System.out.println(Integer.class);//class java.lang.Integer
		System.out.println(int.class.isPrimitive());//true
		System.out.println(int.class==Integer.class);//false
		System.out.println(int.class==Integer.TYPE);//true
		System.out.println(int[].class);//class [I
		System.out.println(int[].class.isArray());//true  isArray()判断一个类型是不是数组
		
		//构造方法的反射
		String.class.getConstructor(StringBuffer.class);
		//new String(new StringBuffer("abc"));
		Constructor constructor1 = String.class.getConstructor(StringBuffer.class);//可变参数
		String str2 = (String)constructor1.newInstance(new StringBuffer("abc"));
		System.out.println(str2.charAt(2));
		//成员变量的反射
		ReflectPoint pt1 = new ReflectPoint(3,5);
		Field fieldY = pt1.getClass().getField("y");
		//fieldY的值是多少?是5,错!fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应
		System.out.println(fieldY.get(pt1));
		Field fieldX = pt1.getClass().getDeclaredField("x");
		fieldX.setAccessible(true);//暴力反射
		System.out.println(fieldX.get(pt1));
		
		
		changeStringValue(pt1);		
		System.out.println(pt1);
		
		//调用str1的charAt(1);
		Method methodCharAt = String.class.getMethod("charAt",int.class);
		System.out.println(methodCharAt.invoke(str1, 1));
	
		System.out.println(methodCharAt.invoke(str1, new Object[]{2}));//此为jdk1.4的语法
	
		
		String startingClassName = args[0];
		Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
		mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});
		//mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
		
		
		int[] a1 = new int[]{1,2,3};
		int[] a2 = new int[4];
		int[][] a3 = new int[2][3];
		String [] a4 = new String[]{"a","b","c"};
		System.out.println(a1.getClass()==a2.getClass());//true
		//System.out.println(a1.getClass()==a4.getClass());//编译器在1.4一下不会报错
		//System.out.println(a1.getClass()==a3.getClass());
		System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object
		System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object
	
		Object aObj1 = a1;
		Object aObj2 = a4;
		Object aObj3 = a1;
		Object[] aObj4 = a3;
		Object[] aObj5 = a4;
		
		System.out.println(a1);
		System.out.println(a4);
		System.out.println(Arrays.asList(a1));
		System.out.println(Arrays.asList(a4));
		
		//Object obj = null;
		printObject(a4);//数组反射
		printObject("xjs");
	}
	//用反射打印数组
	private static void printObject(Object obj) {
		Class clazz = obj.getClass();
		if(clazz.isArray()){//若所给obj为数组
			int len = Array.getLength(obj);//通过Array反射得到数组的长度
			for(int i=0;i<len;i++){
				System.out.println(Array.get(obj, i));//反射取出obj数组中的第i个元素
			}			
		}else{
			System.out.println(obj);
		}
		
	}
	//将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”。
	private static void changeStringValue(Object obj) throws Exception{
		Field[] fields = obj.getClass().getFields();
		for(Field field : fields){
			//if(field.getType().equals(String.class)){
			if(field.getType()==String.class){//字节码比较用==
				String oldValue = (String)field.get(obj);
				String newValue = oldValue.replace('b','a');
				field.set(obj, newValue);
			}
		}
		
	}

}

//写一个程序,这个程序能够根据用户提供的类名,去执行类中的main方法.
class TestArguments{
	public static void main(String[] args){
		for(String arg : args){
			System.out.println(arg);
		}
	}
}


反射测试二:

src目录下放入config.properties文件:

className=java.util.HashSet

package cn.itcast.day1;


import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;

public class ReflectTest2 {

	/**
	 * ArrayList是有顺序的集合,引用变量
	 * HashSet先判断是否含有元素,若含有此元素则不放入进去
	 * 当ReflectPoint当中重写了equals时,在ReflectTest类中的集合中就不能被放入两个完全相同的ReflectPoint对象了。
	 * HashCode()方法是针对hash的集合存在的,若没有HashSet类似的hash集合存在,则hashcode()也没有存在的必要
	 * 当两个对象的equals相同时,但是没有hashcode方法时,两个相同对象会在内存中被存放在两个不同的区域,所以为了
	 * 让两个相等的对象(equals相等时)也肯定被放在内存中相同的区域,就应该加上hashcode方法
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		
		/*
		 * getRealPath();
		 * 一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。
		 * */
		//InputStream ips = new FileInputStream("config.properties");//加载文件方法一
		
		//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
		//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("resources/config.properties");
		InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("/cn/itcast/day1/config.properties");//绝对路径
	
		Properties props = new Properties();
		props.load(ips);
		ips.close();
		String className = props.getProperty("className");
		Collection collections = (Collection) Class.forName(className).newInstance();
		
		//Collection collections = new ArrayList();//打印4
		
		//Collection collections = new HashSet();//打印3
		
		ReflectPoint pt1 = new ReflectPoint(3,3);
		ReflectPoint pt2 = new ReflectPoint(5,5);
		ReflectPoint pt3 = new ReflectPoint(3,3);
		collections.add(pt1);
		collections.add(pt2);
		collections.add(pt3);
		collections.add(pt1);
		
		
		//pt1.y = 7;
		//collections.remove(pt1);//会造成内存泄露,当一个对象被使用完毕之后没有被释放会造成内存泄露
		System.out.println(collections.size());
		
	}

}
/**
 * 通常要求hashcode和equals方法一并被同时覆盖
 * 通常来说,一个类的两个实例对象用equals()方法比较的结果相等时,它们的哈希码也必须相等,但反之则不成立,
 * 即equals方法比较结果不相等的对象可以有相同的哈希码,或者说哈希码相同的两个对象的equals方法比较的结果可以不相等,
 * 例如,字符串"BB"和"Aa"的equals方法比较结果肯定不相等,但它们的hashCode方法返回值却相等。
 * 
 * 
 * 当一个对象被存储进hashset集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后
 * 的哈希值与最初存储进Hashset集合中时的哈希值就不同了,在这种情况下,即使contains方法使用该对象的当前引用作为的
 * 参数去HashSet集合中检索对象,也将返回找不到对象的结果,这会导致无法从hashset集合中单独删除当前对象,从而造成
 * 内存泄露
 */





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值