反射
一、Class类
1.定义
java程序中的各个java类也属于同一类事物,而描述这类事物的Java类就是Class。(此Class类中可以描述java中所
有类的属性,如,他们都有类名、都有方法、都有属性、都有所继承的类、所实现的接口、都有所属的包等等,这些
都是他们共有的属性,所以可以用一个类来统一进行描述)。
2.Class对象
Class的每一个实例对象,代表一份字节码即类在内存中的二进制表示。
3.与class的区别
Class是一个类名,描述一类事物;而class是一个类文件的后缀名,表示文件的类型,表示是一个类文件。
4.创建Class实例对象的三种方法
以类Date为例
(1)类名.class
Class c=Date.class;
(2)对象.getClass();
Class c2=new Date().getClass();
(3)Class.forName("类名")
Class c3=Class.forName("java.util.Data");
此方法得到的字节码可以分为两种情况:
第一,当此字节码在内存中时,则直接返回此字节码即可;
第二,当此字节码不在内存中时,则用类加载器先将此字节码加载进内存,缓存在jvm中,然后再返回此字节码。缓
存在jvm中,这样以后再用到此字节码的时候就不用再加载了,直接用即可。
5.9种基本数据类型的字节码
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
这9种基本数据类型的字节码也都是Class类的实例对象。
6.Class类中的常用方法
例子:
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类。
类中的变量,方法,构造函数,包等等信息,就是用相应类的实例对象来表示的,他们是Constructor、Field、
Method、Package等等,Method的每一个实例对象,就代表一个类中具体的方法。
2.构造方法的反射应用
(1)构造方法对应的类为Constructor
(2)得到类中所有的构造方法,利用方法getConstructors()
Constructor[] constructors=String.class.getConstructors();得到类中的某一个构造方法,利用方法getConstructor(对应的构造方法需要的参数类型的字节码)
Constructor constructor=String.class.getConstructor(对应的构造方法需要的参数类型的字节码);
(3)通过反射来创建对象的过程如下:
1> 得到类的字节码
2> 利用类的字节码调用方法getConstructor(构造方法需要的参数类型的字节码)来得到具体的要用到的构造方
法,即得到Constructor的实例对象
3> 通过得到的Constructor的实例对象再调用方法newInstance(创建对象时要传入的参数),来创建此类的对象。
即为:Class -> Constructor->创建对象
注意上面两处传入的参数类型是一致的。
例子:
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();
实现原理:
该方法内部先得到默认的构造方法,然后利用该构造方法创建实例对象
该方法内部用到了缓存机制来保存默认构造方法的实例对象,这样以后可以直接使用了。
3.成员变量的反射
(1)成员变量对应的类为Field类。此类的对象,代表某一个类中的一个成员变量即某一个类中的某一个字段
(2)通过反射得到某一个类得对象中的某一个字段的值
步骤如下:即为:类对象->Class->Field->get(对象)(即得到此对象上次字段的值)
对于字段是公有的:
1> 得到类的字节码
2> 通过方法getField(字段的字符串形式)得到类中的某个字段
3> 通过得到的字段来调用方法get(对象)来得到此类的某一个对象的此字段的值。
对于字段是私有的:
1> 得到类的字节码
2> 通过方法getDeclaredField(字段的字符串形式)来得到类中的私有的字段(即此字段可以被看见了)
注意,不管是私有的还是公有的字段,此方法都适用。
3> 通过调用方法setAccessible(true)来实现能够拿到此变量的值。
4> 通过得到的字段来调用方法get(对象)来得到此类的某一个对象的此字段的值。
以上这种方法为暴力反射。
(3)暴力反射
暴力反射就是将某个类私有字段的访问检查去掉
例如Person类中有一个私有的age字段,如果要反射出这个字段,则需要调用getDeclaredField(),
如果使用getField则会抛NOSuchFieldException未找到字段异常,
如果调用该字段的get或者set方法会抛IllegalAccessException非法的访问异常,
我们可以通过该字段的setAccessible(true)方法来告诉编译器是否进行访问检查.true就代表不进行访问检查.
例子:
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;
}
}
(4)成员变量反射的综合案例
需求:利用反射将一个类中所有的字符串字段中的b改为a。
步骤:
1> 得到一个想要进行操作的对象。
2> 得到此对象的所有的字段,利用方法getFields()
3> 遍历这些字段,并判断这些字段哪些属于字符串
4> 利用方法replace(old,new)来完成替换功能。
注意要想通过反射来得到字段,则必须明确是要操作哪个对象,因为不同的对象其字段的值也不同。
例子:
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;
}
}
4.成员方法的反射
(1)成员方法对应的类为Method,此类对象是为类中的对象而不是具体对象的方法。
(2)需求:利用反射来达到str.charAt(1)的效果
步骤:
1> 通过调用字节码中的方法getMethod(方法名字符串,此方法的参数类型的字节码)来获取类中对应的方法。方
法的参数类型字节码主要是用来区分重载函数的。
2> 通过方法invoke(对象,参数值)来决定此方法作用的对象。
例子:
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,参数值)表示此成员方法是静态的,不用通过对象调用。
5.对接收数组参数的成员方法进行反射
需求:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法
分析:要想达到根据用户提供的类名来执行该类中的main方法,则即为在运行环境中用户来输入想要运行的类,则此
类就传入到了主类的main函数的参数数组中,即以字符串的形式存在了,而调用此类的main函数,要么通过类名调用
,要么通过此类的对象调用,但由于此类已经被封装成了字符串,所以这两种都是没办法用的,所以用反射的形式来
完成调用此类的main函数,
步骤为:
得到此类的字节码Class->得到相应的main方法Method->main方法作用于什么对象,
并指定传入main方法的参数即调用invoke()。
注意:
用户传入的类名是在运行环境中的参数列表中传入即写入:cn.itcast.day1.StartClassName
则此类就传入到了MethodTest类中的main方法参数中。
例子:
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);
}
}
}
(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);方法使用于打印数据类型为任何数据类型的数组中的元素。
例子:
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]
}
}
(6)
数组的反射应用
Array.get(数组名称,角标)(即得到数组中某个角标的元素)
例子:
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);
}
}
}
1.ArryaList集合特点:此集合中的元素是有序的,即先进来的元素就放在集合前面,后进来的元素就放在后面;元
素可重复。
2.HashSet集合特点:集合中的元素是无序的,并且是不可重复的。通过实现方法hashcode和方法equals来达到元素
唯一的目的。
3.hashCode方法的作用:
第一:它使用于运用hash值的结构中;
第二:它将集合分成了几个区域,当存入对象时首先先算出此对象的hash值,看此对象属于哪个区域,并放进相应的
位置;当对集合中的元素进行删除或者比较的时候,直接算出所要进行比较或者删除的元素的hash值,看此hash值属
于哪个区域,直接去这个区域找这个元素即可,提高了效率。
第三:保证了存入集合中元素的唯一性。因为对于存入的元素先算hash值,跟集合中有的元素的hash值相等,则再调
用equals方法,若equals比较的也是相等,则视为相同元素,从而不再存入此集合,所以保证了集合元素的唯一性。
4.hashcode中算出的hash值可能会导致内存泄露。
因为在对于hashCode方法算hash值时,注意:当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的
那些参与计算哈希值的字段了,都则,对象修改以后的哈希值与最初存储进HashSet集合中的哈希值就不同了。那么
,当删除此元素时,由于算出的目前的hash值和存储进集合中的哈希值不一样,所以删除不掉此元素,这样就会导致
,不用的元素一直存在,释放不了内存空间,所以导致内存泄露。
例子:
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类中的各种方法来对此类进行想要操作的动作。
例子:
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());//根据配置文件中的集合类型来决定最后的集合长度
}
}
4.用类加载器的方式管理资源和配置文件
每一个框架都有配置文件,而加载配置文件的原理是:框架内部利用类加载器加载的配置文件,所以配置文件一般都是放在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),这些信息在类中用私有字段来存储,如果读取或者设置这些字段的值,则需要通
过一些相应的方法来访问。
6.对JavaBean的简单内省操作
(1)JDK中提供了对JavaBeen进行操作的一些API,这套API就成为内省。如果对于私有的x,只是
通过getX方法来访问,有一定的难度,用内省这套API操作JavaBeen比用普通类的方式更方便。
(2)对于类中私有的属性进行取值
步骤:
第一,通过创建对象PropertyDescriptor来得到对于哪一个对象上的哪一个属性进行描述;
第二,通过此对象调用方法getReadMethod()来得到getX()(即X属性的读方法);
第三,用此方法调用invoke(类名)来说明此方法是作用于哪个对象上并调用此方法。
(3)对于类中的私有属性进行设值
步骤:
第一,通过创建对象PropertyDescriptor来得到对于哪一个对象上的哪一个属性进行描述;
第二,通过此对象调用方法getWriteMethod()来得到setX()(即X属性的写方法);
第三,用此方法调用invoke(类名,要设置的值)来说明此方法是给什么对象上的此属性赋值。
例子:
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;
}
}
7.使用BeanUtils工具包操作JavaBean
对于上面代码的那么多的步骤:即得到要操作的属性->得到对于操作此属性的各种方法->运用此方法。BeenUtils工具包将以上这样复杂的过程装换成了一步
例如:对于类中私有的属性进行取值:BeenUtils.getProperty(gsd,"x");
对于类中的私有属性进行设值:BeenUtils.setProperty(gsd,"x","7");
注意:参数是字段名称和赋的值都是以字符串形式传入。