黑马程序员-java基础加强-反射的深入讲解

-------------------------ASP.Net+Unity开发、.Net培训、期待与您交流!--------------------------

透彻分析反射的基础_Class类

Class类
1、定义
java程序中的各个java类也属于同一类事物,而描述这类事物的Java类就是
   Class。(此Class类中可以描述java中所有类的属性,如,他们都有类名、都有方法、都有属性、都有所继承的类、所实现的接口、
   都有所属的包等等,这些都是他们共有的属性,所以可以用一个类来统一进行描述)。
2、Class的每一个实例对象,代表一份字节码即类在内存中的二进制表示。
3、与class的区别
Class是一个类名,描述一类事物;而class是一个类文件的后缀名,表示文件的类型,表示是一个类文件。
4、创建Class实例对象的三种方法
以类Date为例
a、类名.class
  Class c=Date.class;
b、对象.getClass();
Class c2=new Date().getClass();
C、Class.forName("类名")
Class c3=Class.forName("java.util.Data");
此方法得到的字节码可以分为两种情况:第一,当此字节码在内存中时,则直接返回此字节码即可;
第二,当此字节码不在内存中时,则用类加载器先将此字节码加载进内存,缓存在jvm中,然后再返回此字节码。
缓存在jvm中,这样以后再用到此字节码的时候就不用再加载了,直接用即可。
5、九中基本数据类型的字节码
boolean->boolean.class, byte->byte.class, char->char.class, short->short.class, int->int.class,
long->long.class,  float->float.class,  double->double.class,  void->void.class 
这九种基本数据类型的字节码也都是Class类的实例对象。
6、Class类中的常用方法

例子:

package cn.itcast.day1;

    public class ReflectTest {
	public static void main(String[] args) throws Exception{
		// TODO Auto-generated method stub
		String s1=new String();
		Class c1=String.class;
		Class c2=s1.getClass();
		Class c3=Class.forName("java.lang.String");
		System.out.println(c2);//打印结果为class java.lang.String
		System.out.println(c1==c2);//结果为true
		System.out.println(c1==c3);//结果为true
		//以下是方法的使用
		//1、Class.isPrimitive()返回此字节码是不是基本数据类型的字节码
		System.out.println(c1.isPrimitive());//结果为false                    
		System.out.println(int.class.isPrimitive());//结果为true
		//2、返回此字节码是否是数组类型的字节码
		System.out.println(int[].class.isArray());//结果为true
		//3、判断
		System.out.println(int.class==Integer.class);//结果为false
		//每个基本数据类型的字节码都与它对应的引用数据类型.TYPE相等
		System.out.println(int.class==Integer.TYPE);//结果为true
	}
}
总之,在源程序中出现的类型,都有各自的Class实例对象,例如:int[],void,int
-------------------------------------------------------------------------
理解反射的概念
1、概念
反射就是将java类中的各个成分映射成相应的java类。
类中的变量,方法,构造函数,包等等信息,就是用相应类的实例对象来表示的,他们是Field,
Method,Constructor,Package等等Method的每一个实例对象,就代表一个类中具体的方法。
---------------------------------------------------------------------------
19.构造方法的反射应用
1、构造方法对应的类为Constructor
2、得到类中所有的构造方法,利用方法getConstructors()
Constructor[] constructors=String.class.getConstructors();
得到类中的某一个构造方法,利用方法getConstructor(对应的构造方法需要的参数类型的字节码)
Constructor constructor=String.class.getConstructor(对应的构造方法需要的参数类型的字节码);
3、通过反射来创建对象的过程如下:
a、得到类的字节码
b、利用类的字节码调用方法getConstructor(构造方法需要的参数类型的字节码)来得到具体的要
  用到的构造方法,即得到Constructor的实例对象
c、通过得到的Constructor的实例对象再调用方法newInstance(创建对象时要传入的参数),来创建此类的对象。
即为:Class -> Constructor->创建对象
注意上面两处传入的参数类型是一致的。
例子:
   package cn.itcast.day1;
    import java.lang.reflect.Constructor;
	/**
	 *需求:利用反射创建一个对象String s=new String(new StringBuffer("abc"));
	 * */
    public class ConstructorTest {
	public static void main(String[] args)throws Exception {
		// TODO Auto-generated method stub
		//第一步:得到类的字节码
		Class cs=String.class;
		/*第二步:通过字节码得到想要的构造方法;
		注意此步骤中,必须写入构造方法中所要传入的变量类型的字节码*/
		Constructor constructor=cs.getConstructor(StringBuffer.class);
		/*第三步:得到相应的构造方法之后,创建想要的类的对象;
		    注意此步骤中,必须写上(String)类型转换,因为编译器并不知道
		    你所创建的对象的类型,它只是检查语法是否正确;
		  调用newInstance方法时必须传入变量的具体值*/
		String s=(String)constructor.newInstance(new StringBuffer("abc"));
		//建立好了String类型的对象之后,则可以使用对象所有的方法。
		System.out.println(s.charAt(2));//结果为c
	}
}
4、Class.newInstance()
Class.newInstance()为无参的构造方法,此方法即为Class->创建对象.比以上方法减少一步
若需求为:利用反射实现String s=new String();
则可以写成:String s=String.class.newInstance();
实现原理:
该方法内部先得到默认的构造方法,然后利用该构造方法创建实例对象
该方法内部用到了缓存机制来保存默认构造方法的实例对象,这样以后可以直接使用了。
----------------------------------------------------------------------------
成员变量的反射
1、成员变量对应的类为Field类。此类的对象,代表某一个类中的一个成员变量即某一个类中的某一个字段
2、通过反射得到某一个类得对象中的某一个字段的值
步骤如下:即为:类对象->Class->Field->get(对象)(即得到此对象上次字段的值)
对于字段是公有的:
a、得到类的字节码
b、通过方法getField(字段的字符串形式)得到类中的某个字段
c、通过得到的字段来调用方法get(对象)来得到此类的某一个对象的此字段的值。
对于字段是私有的:
a、得到类的字节码
b、通过方法getDeclaredField(字段的字符串形式)来得到类中的私有的字段(即此字段可以被看见了)
注意,不管是私有的还是公有的字段,此方法都适用。
c、通过调用方法setAccessible(true)来实现能够拿到此变量的值。
d、通过得到的字段来调用方法get(对象)来得到此类的某一个对象的此字段的值。
以上这种方法为暴力反射。
3、 暴力反射 
暴力反射就是将某个类私有字段的访问检查去掉
 例如Person类中有一个私有的age字段,如果要反射出这个字段,则需要调用getDeclaredField(),
 如果使用getField则会抛NOSuchFieldException未找到字段异常,
 如果调用该字段的get或者set方法会抛IllegalAccessException非法的访问异常,
我们可以通过该字段的setAccessible(true)方法来告诉编译器是否进行访问检查.true就代表不进行访问检查.
例子:
     package cn.itcast.day1;
     import java.lang.reflect.Field;
	 public class FieldTest {
	  public static void main(String[] args)throws Exception{
		//FieldCode fc=new FieldCode(3,4);
		//通过反射来创建类的对象
		FieldCode fc=FieldCode.class.getConstructor(int.class,int.class).newInstance(4,5);
		
		//得到此类的字节码
		Class c=fc.getClass();
		//通过字节码来调用函数getField(String name)方法来得到类的某个字段
		Field fieldY=c.getField("y");
		//通过调用方法get(对象)来得到此对象对应的此字段的值
		System.out.println(fieldY.get(fc));
		
		//通过调用方法getDeclaredField(String name)来得到私有成员变量的字段
		Field fieldX=c.getDeclaredField("x");
		//通过方法setAccessible(true)来使此私有字段可访问
		fieldX.setAccessible(true);
		//通过调用方法get(对象)来取得对应的值
		System.out.println(fieldX.get(fc));				
	}
}
class FieldCode{
	private int x;
	public int y;
	public FieldCode(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}	
}
-----------------------------------------------------------------------------
成员变量反射的综合案例
需求:利用反射将一个类中所有的字符串字段中的b改为a。
步骤:
1、得到一个想要进行操作的对象。
2、得到此对象的所有的字段,利用方法getFields()
3、遍历这些字段,并判断这些字段哪些属于字符串
4、利用方法replace(old,new)来完成替换功能。
注意要想通过反射来得到字段,则必须明确是要操作哪个对象,因为不同的对象其字段的值也不同。
例子:

package cn.itcast.day1;
import java.lang.reflect.Field;
public class FieldTest {
	public static void main(String[] args)throws Exception{
		//将类FieldCode中的所有字符串字段中的b替换成a
		//1、得到此类的对象
		FieldCode f=FieldCode.class.getConstructor(int.class,int.class).
				             newInstance(5,6);
		//2、的到此类中的所有的字段
		Field[] fields=f.getClass().getFields();
		//3、遍历此类的所有字段
		for (Field field : fields) {
			if (field.getType()==String.class) {
				//4.得到此字段的值
				String oldStr=(String)field.get(f);
				//5.替换
				String newStr=oldStr.replace('b', 'a');
				//6、将替换后的值给此对象,让此对象知道
				field.set(f, newStr);	
			}	
		}
		System.out.println(f);	
	}
}
class FieldCode{
	private int x;
	public int y;
	public String str1="abcde";
	public String str2="basketball";
	public String str3="itcast";
	public FieldCode(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}	
	public String toString(){
		return str1+","+str2+","+str3;
	}
}
-----------------------------------------------------------------------------
成员方法的反射
1、成员方法对应的类为Method,此类对象是为类中的对象而不是具体对象的方法。
2、需求:利用反射来达到str.charAt(1)的效果
步骤:
a、通过调用字节码中的方法getMethod(方法名字符串,此方法的参数类型的字节码)
来获取类中对应的方法。方法的参数类型字节码主要是用来区分重载函数的。
b、通过方法invoke(对象,参数值)来决定此方法作用的对象。
例子:
           package cn.itcast.day1;
           import java.lang.reflect.Method;
           public class MethodTest {
			public static void main(String[] args)throws Exception {
				// TODO Auto-generated method stub
				//String str=new String("abcd");或者使用以下方式来获取对象
				String str=String.class.getConstructor(StringBuffer.class).
						newInstance(new StringBuffer("abcd"));
				
				//1、得到类中相应的方法
				Method method1=str.getClass().getMethod("charAt", int.class);
				//2、调用方法对象的方法invoke(对象,参数值)来决定此方法要作用在哪个对象上
				//以及传入方法中的参数值
				System.out.println(method1.invoke(str, 1));
			}
		}
3、jdk1.4和jdk1.5中invoke()方法的区别
主要区别为参数列表的形式不同
jdk1.4中invoke()方法的参数形式用的是数组型的
格式:public Object invoke(Object obj,Object[] args)//obj为此成员方法要作用什么对象身上,
//数组即为方法的参数列表值,
如写成:method1.invoke(str,new Object{1});
jdk1.5中invoke()方法的参数列表运用的是泛型
格式:public Object invoke(Object obj,Object...args)
注意:1、invoke方法是方法对象的方法,即是方法Method类的对象method1上的对象。
 2、invoke(null,参数值)表示此成员方法是静态的,不用通过对象调用。
-----------------------------------------------------------------------------
对接收数组参数的成员方法进行反射
需求:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法
分析:要想达到根据用户提供的类名来执行该类中的main方法,则即为在运行环境中
 用户来输入想要运行的类,则此类就传入到了主类的main函数的参数数组中,
 即以字符串的形式存在了,而调用此类的main函数,要么通过类名调用,要么
 通过此类的对象调用,但由于此类已经被封装成了字符串,所以这两种都是没
 办法用的,所以用反射的形式来完成调用此类的main函数,
 步骤为:得到此类的字节码Class->得到相应的main方法Method->main方法作用
 于什么对象,并指定传入main方法的参数即调用invoke()。
 注意:用户传入的类名是在运行环境中的参数列表中传入即写入:cn.itcast.day1.StartClassName
则此类就传入到了MethodTest类中的main方法参数中。
 例子:
        package cn.itcast.day1;
	import java.lang.reflect.Method;
	public class MethodTest {
	public static void main(String[] args)throws Exception {
		// TODO Auto-generated method stub
		//String str=new String("abcd");或者使用以下方式来获取对象
		String str=String.class.getConstructor(StringBuffer.class).
				newInstance(new StringBuffer("abcd"));
		//得到类中相应的方法
		Method method1=str.getClass().getMethod("charAt", int.class);
		//调用方法对象的方法invoke(对象,参数值)来决定此方法要作用在哪个对象上
		//以及传入方法中的参数值
		System.out.println(method1.invoke(str, 1));
		System.out.println("--------------main数组测试------------------");
		//1、得到用户传来的类名的字符串形式
		String startclassname=args[0];
		//2、通过反射得到此类的main方法
		Method method=Class.forName(startclassname).getMethod("main",String[].class);
		//3、将此方法作用此什么对象上,并传入什么样的参数(即调用此方法,并传入参数)
		method.invoke(null,(Object)new String[]{"123","456","789"});
		//注意,由于main方法中的参数是一个字符串数组型的,那么该如何为
		//invoke方法传递参数?若传入的参数为new String[]{"123","456","789"}),则java会
		//将它进行拆包,当成三个参数传入main方法的一个参数中,而不是单独的一个数组,所
		//以会出现:IllegalArgumentException: wrong number of arguments的异常现象,
		//所以解决的办法有两种:第一是:method.invoke(null,(Object)new String[]{"123","456","789"});
		//即告诉编译器,我的这个数组是一个整体,你不要再把我拆包,拆成三个了
		//第二是method.invoke(null,new Object[]{new String[]{"123","456","789"}});
		//即表示,你不是要拆包吗,我再包装一层数组,你拆吧,拆开之后是一个数组整体。
	}
}
class StartClassName{
	public static void main(String[] args){
		for (String string : args) {
			System.out.println(string);		
		}	
	}
}
-----------------------------------------------------------------------------
数组与Object的关系及其反射类
1、对于数组:具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
2、代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
3、基本类型的一维数组可以被当做Object类型使用,但不能当做Object[]类型使用。而非
  基本类型的一维数组既可以被当做Object类型使用也可以当做Object[]类型使用。
4、将数组直接打印出来而不是再写for循环,即利用的是方法
   public static List asList(Object obj),即返回的是List集合
或者方法Arrays.toString(i);
 对于System.out.println(Arrays.asList(s1));//[a, b, c]
  System.out.println(Arrays.asList(i));//[[I@12452e8]
 利用方法public static List asList(Object obj),返回的是List集合
  而List集合中存放的是对象,对于数组S1它里面的对象为String,而String是一个
  引用数据类型的,所以直接将此数组中的每个元素分别存入集合中;而i数组中的元素
  类型为int型的,是基本数据类型,不是引用数据类型,所以它里面的元素不能直接被当做
  对象来存入集合中,所以把整个数组当做了一个整体,作为一个对象存入到了集合中。
5、即总结:public static List asList(Object obj)方法使用的是数据类型是引用数据类型
  Arrays.toString(i);方法使用于打印数据类型为任何数据类型的数组中的元素。
  例子:
package cn.itcast.day1;
import java.util.Arrays;
public class ReflectArrayTest {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] a1=new int[2];
		int[] a2=new int[3];
		int[][] a3=new int[2][3];
		String[] a4=new String[2];
		System.out.println(a1.getClass()==a2.getClass());//结果为true
		//System.out.println(a1.getClass()==a3.getClass());//结果为false
		//System.out.println(a1.getClass()==a4.getClass());//结果为false
		//得到数组a1的字节码
		System.out.println(a1.getClass());//结果为class [I
		//得到字节码的名字
		System.out.println(a1.getClass().getName());//结果为[I
		System.out.println("-------第二部分------------------------");
		//结果都为:java.lang.Object
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(a4.getClass().getSuperclass().getName());
		System.out.println("----------第三部分---------------------");
		//3、注意有下面的等式
		Object obj1=a1;//此式是成立的
		//Object[] obj2=a2//此式是不成立的
		Object obj3=a3;//此式是成立的
		Object obj4=a4;//此式是成立的
		Object[] obj5=a4;//此式是成立的
		Object[] obj6=a3;//此式是成立的	
		System.out.println("-------------第四部分------------------");
		//4、要想直接打印出数组中的元素,则以下方法
		String[] s1=new String[]{"a","b","c"};
		int[] i=new int[]{1,2,3};
		System.out.println(Arrays.asList(s1));//[a, b, c]
		System.out.println(Arrays.asList(i));//[[I@12452e8]
		System.out.println(Arrays.toString(i));//[1,2,3]
		System.out.println(Arrays.toString(s1));//[a,b,c]
	}
}
-----------------------------------------------------------------------------
数组的反射应用
对数组进行反射的类为Array,主要方法有:Array.getLength(数组名字)
Array.get(数组名称,角标)(即得到数组中某个角标的元素)
例子:
package cn.itcast.day1;
import java.lang.reflect.Array;
import java.util.Arrays;
public class ReflectArrayTest {
	public static void main(String[] args) {
		int[] i=new int[]{1,2,3};
		System.out.println("-------------第五部分-对数组进行反射操作的类Array------------------");
		printTest("xyz");
		printTest(i);	
	}
	//即对于对象,看是否是数组,是,则拆包并打印每个数组元素;不是,则直接打印此对象。
	private static void printTest(Object obj) {
		// TODO Auto-generated method stub
		//1.得到此对象的字节码
		Class clazz=obj.getClass();
		if (clazz.isArray()) {
			//得到此数组的长度
			int len=Array.getLength(obj);
			for (int y = 0; y < len; y++) {
				System.out.println(Array.get(obj, y));		
			}		
		} else {
			System.out.println(obj);
		}	
	}
}
-----------------------------------------------------------------------------
ArrayList_HashSet的比较及Hashcode分析
1、ArryaList集合特点:此集合中的元素是有序的,即先进来的元素就放在集合前面,后进来的
元素就放在后面;元素可重复。
2、HashSet集合特点:集合中的元素是无序的,并且是不可重复的。通过实现方法hashcode和方法
equals来达到元素唯一的目的。
3、hashCode方法的作用:
第一:它使用于运用hash值的结构中;
第二:它将集合分成了几个区域,当存入对象时首先先算出此对象的hash值,看此对象属于
 哪个区域,并放进相应的位置;当对集合中的元素进行删除或者比较的时候,直接算出
 所要进行比较或者删除的元素的hash值,看此hash值属于哪个区域,直接去这个区域找
 这个元素即可,提高了效率。
第三:保证了存入集合中元素的唯一性。因为对于存入的元素先算hash值,跟集合中有的元素的
 hash值相等,则再调用equals方法,若equals比较的也是相等,则视为相同元素,从而不再
 存入此集合,所以保证了集合元素的唯一性。
4、hashcode中算出的hash值可能会导致内存泄露。
因为在对于hashCode方法算hash值时,注意:当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的
那些参与计算哈希值的字段了,都则,对象修改以后的哈希值与最初存储进HashSet集合中的哈希值就不同了。
那么,当删除此元素时,由于算出的目前的hash值和存储进集合中的哈希值不一样,所以删除不掉此元素,这样就
会导致,不用的元素一直存在,释放不了内存空间,所以导致内存泄露。
例子:
package cn.itcast.day1;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class HashCodeTest {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		System.out.println("------------ArrayList-----------------");
		Collection collections=new ArrayList();
		HashCodeDemo hcd1=new HashCodeDemo(3,3);
		HashCodeDemo hcd2=new HashCodeDemo(5,6);
		HashCodeDemo hcd3=new HashCodeDemo(3,3);
		collections.add(hcd1);
		collections.add(hcd2);
		collections.add(hcd3);
		collections.add(hcd1);
		System.out.println(collections.size());//结果为4
		
		System.out.println("------------HashCode-----------------");
		Collection collections2=new HashSet();
		HashCodeDemo hcd4=new HashCodeDemo(3,3);
		HashCodeDemo hcd5=new HashCodeDemo(5,6);
		HashCodeDemo hcd6=new HashCodeDemo(3,3);
		collections2.add(hcd4);
		collections2.add(hcd5);
		collections2.add(hcd6);
		collections2.add(hcd4);
		System.out.println(collections2.size());//结果为2
		//注意此集合的长度,当没复写hashCode和equals的方法时结果为3,因为hashCode
		//默认的是对象的内存地址值得到的hash值,所以hcd4和hcd6是不同的元素,所以都被
		//加入进去了。
		//改变参与hash值运算的变量的值。 
		hcd4.x=7;
		collections2.remove(hcd4);
		System.out.println(collections2.size());//结果仍为2,因为并未去掉。	
	}
}
class HashCodeDemo{
	public int x;
	public int y;
	public HashCodeDemo(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;
		HashCodeDemo other = (HashCodeDemo) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}
}
-----------------------------------------------------------------------------
框架的概念及用反射技术开发框架的原理
1、框架的概念:
框架即为实现一个功能,但是这个功能内部调用的类或者其他的事物并不明确具体
是什么,而是由以后具体用到的时候再具体给出。这就是框架。
2、框架要解决的问题:
第一:我在写框架时,你这个类可能还未被定义出来,那么该怎么调用你这样的一个类
第二:因为写框架的时候并不知道类名,所以,在程序中无法直接new某个类
的实例对象了,那么该怎么对此类进行各种操作呢?
3、要想解决以上问题就用反射来做。
步骤:第一,写一个配置文件,将以后要用到的类,可以写在配置文件中,用哪个就写哪个
 这样就不用再改框架中的代码了。配置文件创建步骤:工程->new->file->
 文件名称:config.properties,这样将配置文件创建在了此工程下。用户只要在运行
 的时候通过记事本修改配置文件中的信息即可。
 第二:通过流和Properties类将配置文件加载进此工程的目录下并获取类名字符串即可。
 第三:通过反射来得到类的字节码,然后通过Class类中的各种方法来对此
类进行想要操作的动作。

例子:

package cn.itcast.day1;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
public class ReflectTest3 {
	public static void main(String[] args)throws Exception{
		System.out.println("------------第一步加载进配置文件-----------------");
		//让输入流和文件进行相关联,来读取这个文件。
		InputStream is=new FileInputStream("config.properties");
		Properties pro=new Properties();
		//将文件加载进此类的目录中
		pro.load(is);
		is.close();//注意此处是关闭的是用到的系统的底层资源,而不是流。
		System.out.println("------------第二步得到配置文件的信息-----------------");
		//得到此配置文件中的信息
		String className=pro.getProperty("className");
		System.out.println("------------第三步通过反射来创建对象并进行操作-----------------");
		//Collection collections=new ArrayList();以前的做法
		//现在用反射的做法来创建集合对象,由于是无参的构造函数,所以可以
		//直接利用Class.newInstance()来创建对象。
		Collection collections=(Collection)Class.forName(className).newInstance();
		HashCodeDemo hcd1=new HashCodeDemo(3,3);
		HashCodeDemo hcd2=new HashCodeDemo(5,6);
		HashCodeDemo hcd3=new HashCodeDemo(3,3);
		collections.add(hcd1);
		collections.add(hcd2);
		collections.add(hcd3);
		collections.add(hcd1);
		System.out.println(collections.size());//根据配置文件中的集合类型来决定最后的集合长度		
	}
}
-----------------------------------------------------------------------------
用类加载器的方式管理资源和配置文件
每一个框架都有配置文件,而加载配置文件的原理是:框架内部利用类加载器加载的配置文件,所以
配置文件一般都是放在classpath路径下(因为类加载器是在加载的类的目录下加载配置文件的);若
用eclipse开发程序,则配置文件一般放在source目录下即源文件目录下或者source子目录下(即子包),
它会自动的将配置文件加载进classpath的目录下。
如下:InputStream is=ReflectTest3.class.getClassLoader().getResourceAsStream("具体目录名");
或者:InputStream is=ReflectTest3.class.getResourceAsStream("相对目录名或者具体目录名");
-----------------------------------------------------------------------------     
由内省引出JavaBean的讲解
内省的英文字母为:IntroSpector:检查、视察
1、定义
JavaBeen是一种特殊的java类,主要用于传递数据信息,这样java类中的方法
主要用于访问私有的字段,且方法名符合某种命名规则,即必须有以get和set为前缀的方法
例如:下面的类即为JavaBeen类,因为里面有以set和get为前缀的方法。
class Person
	{
		private int age;
		public void setAge(int age)
		{
			this.age=age;
		}
		public int getAge()
		{
			return age;
		}
	}
2、JavaBeen可以当做一般的类进行操作,而一般的类不一定可以当做JavaBeen类来进行操作
因为只有当一般的类中有分别以get和set为前缀的方法才可以。
3、JavaBeen的属性是什么呢?
JavaBeen的属性是去掉get和set前缀剩下的那部分,即为JavaBeen的
属性,而不是类中的私有成员变量是它的属性。所以即为设置和获取age属性值。
4、对于获取的属性,该如何用?
当得到的属性名的第二个字母是小写,而第二个字母是大写则把第一个字母也小写,
如:getAge去掉get为Age->age
当得到的属性名的第二个字母是大写,则并不把第一个字母变为小写,如:getCPU和setCPU
则属性名为CPU
当得到的属性名的第一个字母是小写,都是小写,则这就是属性名了,不再变大小写
如:gettime和settime,则属性名为time。
5、什么时候使JavaBeen呢?
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBeen中,这种JavaBeen的
实例对象通常称之为值对象(Value Object,简称VO),这些信息在类中用私有字段来存储,如
果读取或者设置这些字段的值,则需要通过一些相应的方法来访问。
-----------------------------------------------------------------------------     
对JavaBean的简单内省操作
1、JDK中提供了对JavaBeen进行操作的一些API,这套API就成为内省。如果对于私有的x,只是
通过getX方法来访问,有一定的难度,用内省这套API操作JavaBeen比用普通类的方式更方便。
2、对于类中私有的属性进行取值
步骤:
第一,通过创建对象PropertyDescriptor来得到对于哪一个对象上的哪一个属性进行描述;
第二,通过此对象调用方法getReadMethod()来得到getX()(即X属性的读方法);
第三,用此方法调用invoke(类名)来说明此方法是作用于哪个对象上并调用此方法。
3、对于类中的私有属性进行设值
步骤:
第一,通过创建对象PropertyDescriptor来得到对于哪一个对象上的哪一个属性进行描述;
第二,通过此对象调用方法getWriteMethod()来得到setX()(即X属性的写方法);
第三,用此方法调用invoke(类名,要设置的值)来说明此方法是给什么对象上的此属性赋值。
例子:
package cn.itcast.day1;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class IntroSpectorTest {
	public static void main(String[] args) throws Exception{
		// TODO Auto-generated method stub
		 GetSetDemo gsd=new GetSetDemo(4,5);
		System.out.println("---------对x属性进行读取值---------------");
		//第一部分:创建PropertyDescriptor对象,传入的gsd.getClass()表示要被当做JavaBeen类的类
		//说明要进行描述的属性
		String propertyName="x";
		PropertyDescriptor pts=new PropertyDescriptor(propertyName,gsd.getClass());
		//第二部分:通过此对象调用getReadMethod()来得到对x属性的读方法
		Method methodRead=pts.getReadMethod();
		//第三部分:调用invoke(类名),来获取此属性的值
		System.out.println(methodRead.invoke(gsd));
		System.out.println("---------对x属性进行设置值---------------");
		//第一步创建PropertyDescriptor对象,说明要进行描述的属性
		PropertyDescriptor pts1=new PropertyDescriptor(propertyName,gsd.getClass());
		//第二部分:通过此对象调用getWriteMethod()来得到对x属性的写方法
		Method methodWrite=pts.getWriteMethod();
		//第三部分:调用invoke(类名),来设置此属性的值
		methodWrite.invoke(gsd,7);
		System.out.println(gsd.getX());
	}
}
class GetSetDemo{
	private int x;
	private int y;
	public GetSetDemo(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
	public int getY() {
		return y;
	}
	public void setY(int y) {
		this.y = y;
	}	
}
-----------------------------------------------------------------------------  
使用BeanUtils工具包操作JavaBean
对于上面代码的那么多的步骤:即得到要操作的属性->得到对于操作此属性的各种方法->
运用此方法。BeenUtils工具包将以上这样复杂的过程装换成了一步
例如:对于类中私有的属性进行取值:BeenUtils.getProperty(gsd,"x");
 对于类中的私有属性进行设值:BeenUtils.setProperty(gsd,"x","7");
 //注意参数是字段名称和赋的值都是以字符串形式传入。


-------------------------ASP.Net+Unity开发、.Net培训、期待与您交流!--------------------------


详情请查看:http://edu.csdn.net


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章