您的位置:首页 > 其它

学习笔记05—框架与反射初步

2013-04-12 16:10 295 查看
本篇中值得关注的时Properties类. 这个类实际上是Map的孙子。。。

Map有个实现类叫HashTable,与HashMap想相比是线程安全的,HashTable有个子类正是Properties

其关键字和值只能是String类型,经常被用来存储和访问配置信息。


框架初步


配置文件

1. 配置文件设置类名
以上篇HashCode相关中的代码为例,改用配置文件提供ArrayList和HashSet类名
右键点击项目名新建File,创建config.properties文件,写入
className=java.util.ArrayList
实际上是个键值对。

然后修改ReflectTest2类。

思路:

需要一个Properties类的对象,用其void load(InputStream inStream)方法载入一个输入流
这个输入流读取config.properties文件,
然后用其StringgetProperty(String key)方法“以键取值”,得到类名.具体代码如下:

InputStream in = new FileInputStream("config.properties");
Properties props = new Properties();
props.load(in);
// 不要忘记关闭流否则会造成系统资源不得释放
in.close();

String className = props.getProperty("className");

Collection c = (Collection) Class.forName(className).newInstance();


这样就实现了某种程度上的“分离”,采用ArrayList还是HashSet只需在config.properties配置即可。

关于config.properties 的路径:
用方法得到工程的绝对路径,然后再根据工程内部相对路径找到文件。


类加载器

2. 加载config.properties文件的另一种方式

在Eclipse中把config.properties文件拖到src目录下,这时Eclipse会自动将其复制到workspace的bin下
相应地,要更改路径

// InputStream in = new FileInputStream("config.properties");
InputStream in =ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
// "cn/itcast/day1/config.properties"不要写成"/cn/itcast/...
// 这里面的原因还不明确


用1中的文件输入流的方式不仅可以读取文件,而且也可以写入文件。
用ClassLoader的方式适合读取配置文件。

但是这种写法仍然过于繁琐,下面是简化的写法,内部仍然用到了getClassLoader()

// 下面的写法可以只写文件名
// 因为文件位于与ReflectTest2.class同一包下(相对于包的路径)
InputStream in =
ReflectTest2.class.getResourceAsStream("config.properties");


此时如果把文件移动到cn.itcast.day1.resources 包下面,就要写成
"resources/config.properties"
因为ReflectTest2.class从自己所在的包出发,即从day1文件夹下出发

如果从classpath下出发,则以"/"开头
"/cn/itcast/day1/resources/config.properties"

总结:

newFileInputStream(String path)需要动态计算项目真实目录然后加相对目录
classLoader.getResourceAsStream从classpath出发不加"/"
X.class.getResourceAsStream从classpath出发要加"/";如果文件路径跟X.class所在的包有关系则X.class所在目录出发


IntroSpector和JavaBean

3. JavaBean
我们不知道JavaBean内部的属性到底叫什么,仅仅可以从get方法的名字上推断。
比如我们去买菠萝,菠萝卖家内部把菠萝叫做凤梨,而外面挂的牌子写着卖菠萝。
我们没有必要关心他们内部叫什么,我们只知道可以买到菠萝。

4. 用简单内省方式操作JavaBean
首先在ReflectPoint中添加x和y的setter和getter
新建测试类InspectorTest

public staticvoidmain(String[] args) throws Exception {
ReflectPoint p1 = new ReflectPoint(3, 5);
String propertyName = "x";
/*
* 普通方法缺点在于手动写出getX 不够智能
*/
Method method = p1.getClass().getMethod("getX");
Object retVal = method.invoke(p1);
System.out.println(retVal);
/*
* 内省方法
*/
PropertyDescriptor pd = new PropertyDescriptor(propertyName, p1.getClass());
Object retVal2 = pd.getReadMethod().invoke(p1);
System.out.println(retVal2);
/*
* set对象的值
*/
pd.getWriteMethod().invoke(p1, 7);
retVal2 = pd.getReadMethod().invoke(p1);
System.out.println(retVal2);
}


可以用Eclipse的Refactor抽取方法。这一点已经在Eclipse使用技巧中的5.
Refactor中提过了。
将读写操作分别抽取成getProperties和setProperties方法。

最好用.getClass,不要用.class

private staticvoidsetProperties(Object p1, String propertyName,
Object value) throws... {
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName, p1.getClass());
pd2.getWriteMethod().invoke(p1, value);
}

private staticObject getProperties(Object p1, String propertyName)
throws...{
PropertyDescriptor pd1 = new PropertyDescriptor(propertyName, p1.getClass());
Object retVal2 = pd1.getReadMethod().invoke(p1);
returnretVal2;
}


总结:

关键之处在于new一个PropertyDescriptor,在构造方法中指明属性和对象

new PropertyDescriptor(propertyName, p1.getClass());


5. 对比: 另一种较为繁琐的操作方式
用Introspector.getBeanInfo(ClassbeanClass)取得BeanInfobeanInfo,
然后用beanInfo取得PropertyDescriptor[],
在这个数组中查找匹配propertyName的属性并做出处理。
下面代码写在getProperties方法里面

// 取得beanInfo
BeanInfo beanInfo = Introspector.getBeanInfo(p1.getClass());
// 取得PropertyDescriptor的数组
PropertyDescriptor[] pds =beanInfo.getPropertyDescriptors();

Object retVal2 = null;
// 遍历数组查找匹配的属性
for (PropertyDescriptor pd : pds) {
if(pd.getName().equals(propertyName)) {
// 得到getter 激发对象使其调用
retVal2 =pd.getReadMethod().invoke(p1);
break;
}
}
return retVal2;


这中扫描的手段跟反射机制中的9.综合案例很相似。
综合案例中的代码片段回顾:

Field[] fields = obj.getClass().getFields();
// 对所有字段进行迭代,判断是否是String类型的,如果是则进一步处理
for (Field field : fields) {
// 因为字节码只有一份,所以用==,用equals会造成语义不明确
if (field.getType() == String.class) {
String oldStr = (String)field.get(obj);
String newStr = oldStr.replace('b', 'a');
field.set(obj, newStr);
}
}


这种用beanInfo取得PropertyDescriptor[]的方式显然不如new PropertyDescriptor便捷。


BeanUtils操作JavaBean

6. BeanUtils
适用于操作JavaBeans的更加强大的工具类
由于在实际开发中对JavaBean的读写操作非常频繁,
人们做出了专门的工具类BeanUtils,用以对JavaBean进行操作。

首先导入beanutils和logging包。在工程下新建一个lib目录,复制两个jar包到其中。
分别右键点击它们Add to Build Path. 这样就可以使用BeanUtils了。
使用方法:

BeanUtils.getProperty(p1,propertyName);
BeanUtils.setProperty(p1,propertyName, "9");


注意:

BeanUtils对JavaBean的属性进行读取和写入的时候都是用的String.
getProperty返回的是String,setProperty的时候可以用String类型。
BeanUtils可以进行类型的自动转换。

7. BeanUtils的另外一个特性是可以进行属性的级联操作。

如果一个对象的属性是个复合类型,BeanUtils可以把这个属性再当作JavaBean。
在ReflectPoint类里面增加一个Date类型的属性birthday,并添加其getter和setter.
birthday 属性本身又可以被当作JavaBean. Date类有setTime方法,就可以认为birthday有time属性。

于是可以这样操作:

BeanUtils.setProperty(p1, "birthday.time","111");


这样运行会提示出错:

No bean specified

原因是birthday还没有实例化成为一个对象。

因此,在增加birthday属性的时候要初始化:

private Date birthday= newDate();


取得属性值:

System.out.println("beanutils getting: " + BeanUtils.getProperty(p1, "birthday.time"));


8. BeanUtils的其他方法
void copyProperties(Object dest,Object orig) 把一个对象属性复制到另一个对象
Map describe(Object bean)把一个bean的属性转换为Map
void populate(Object bean, Map properties)把Map内容填充到bean中

9. BeanUtils操作Map对象
Java1.7关于Map定义的新语法 无法测试通过 暂时找不到解决办法
最可能的原因是这种新语法目前不被支持

Map map = {name :"zxx", age : 18};

这种写法跟Python中创建字典的写法很相似:

phone = {"Bob" : 1234,"Alice" : 5678}

有一种方法可以简便的创建Map,暂时先用这种方法,这种方法看起来也很奇妙:
关于这种写法,请参考fantaxy的空间_前庭 在Java_Map初始化中做了充分的说明,
在此向fantaxy致敬!

Map map = new HashMap() {
{
put("name", "zxx");
put("age", "25");
}
};


BeanUtils.setProperty(map, "name", "jean");
System.out.println(BeanUtils.getProperty(map, "name"));


10.PropertyUtils和BeanUtils的区别

PropertyUtils.setProperty(p1,propertyName, 8);
System.out.println(PropertyUtils.getProperty(p1,propertyName));


如果改为"8"则会提示argument type mismatch

总结:

PropertyUtils与BeanUtils的不同在于,
前者以属性本身的类型进行操作,而BeanUtils总是以字符串进行读写操作。
如果对BeanUtils的自作聪明不满意,需要准确的类型时,可以使用PropertyUtils.

Over.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: