没人回答, 我自己把源码给改了下, 最终个人分析, 应该是源码的设计没有设计好, 下面是源码, 我把源码改成的类型变量的通配符改成了String, 结果运行不会出错.
public Enumeration<?> propertyNames() {
Hashtable h = new Hashtable();
enumerate(h);
return h.keys();
}
private synchronized void enumerate(Hashtable h) {
if (defaults != null) {
defaults.enumerate(h);
}
for (Enumeration e = keys(); e.hasMoreElements();) {
String key = (String)e.nextElement();
h.put(key, get(key));
}
}
而且源码还有个很大的问题就是, 当我们用Properties对象添加属性时, 我们可以用它的父类Hashtable<Object, Object>的put<Key k, Value v>方法来添加, 如果用put添加, 那么就可以是添加任意类型的键值对了, 但是如果我们真的给键添加了其它类型(非String), 这样在使用propertyNames()方法来获取所有键的枚举时, 会发现有个ClassCastException,类型转换异常, 这个原因是因为当调用 propertryNames()方法时, 该方法内部会创建一个Hashtable对象, 该对象没有记录键值对, 而之前在使用put方法时, Hashtable中有键值对, 那么这时新的对象要拥有键值对,就要把之前put方法添加的键值对添加到新的Hashtable中, 但是在添加过程中, 源码做了一个强转动作 String key = (String)e.nextElement(); 该强转动作要能正确执行, 那么也就是说put方法添加的那个老值就必须是String类型的才能强转. 所以我个人觉得这就是源码上的缺陷, 如果把源码中的 public Enumeration<?> propertyNames()方法中Enumeration的通配符改成String, 那么当用户使用时, 就不会去添加其它类型. 或者不改通配符, 那么就把强转给改以下, 改成步强转, 用Object来接受要传递给新Hashtable中的老值, 这样也不会出错, 下面是出错代码:
import java.util.Enumeration;
import java.util.Hashtable;
class Demo {
public static void main(String[] ags) {
Properties p = new Properties();
p.put(1, "zhangsan");
p.put(2, "zhangsan");
p.put(3, "wangwu");
Enumeration<String> keys = p.propertyNames();
while (keys.hasMoreElements()) {
String key = (String) keys.nextElement();
System.out.println(key);
}
}
}
建议源码修改样式一:
class Properties extends Hashtable<Object, Object> {
protected Properties defaults;
public Enumeration<?> propertyNames() {
Hashtable h = new Hashtable();
enumerate(h);
return h.keys();
}
private synchronized void enumerate(Hashtable h) {
if (defaults != null) {
defaults.enumerate(h);
}
for (Enumeration e = keys(); e.hasMoreElements();) {
//这里不要强转, 用Object来接受, 那么使用Properties对象使用put方法添加元素
//不论添加什么元素, 使用枚举器遍历都不会报错.
Object key = e.nextElement();
h.put(key, get(key));
}
}
}
源码修改样式二:
class Properties extends Hashtable<Object, Object> {
protected Properties defaults;
//这里把枚举的通配符改成String, 那么用户在使用Properties的put方法来添加键值对时,又想用propertyNames
//方法来获取枚举器遍历,那么用户就不会添加其它类型的键, 只会添加String类型的
//其实仅仅更改这里作用不大,
public Enumeration<String> propertyNames() {
Hashtable h = new Hashtable();
enumerate(h);
return h.keys();
}
private synchronized void enumerate(Hashtable h) {
if (defaults != null) {
defaults.enumerate(h);
}
for (Enumeration e = keys(); e.hasMoreElements();) {
//这里依然用强转
String key = (String)e.nextElement();
h.put(key, get(key));
}
}
}
个人认为, 应该是Sun已经放弃了修改这个问题, 因为在JDK1.2后,添加了专门供Properties类添加键值对的方法, 且方法做了限制, 参数类型只能是String类型, 所以, 我们使用时是根本就不用Hashtable中的方法来对Properties类进行各种操作了, 那么Sun公司也就懒得修改这个错误了, 这里也可以总结一下, 虽然要知道Properties是 Hashtable<Object, Object>的子类, 但是在使用它时, 我们应该把它当作一个独立的类来使用;
这个是我的个人总结, 其中有些是自己分析的, 有些问过别人, 总之就是我的个人理解及一些他人答案综合起来的, 如果有看到这个贴的, 我哪里理解错误的还望指出;
|