原文片段剪辑:
File f = Resources.getResourceAsFile(OpenAdDefine.CHANNELID_CONFIG);
Properties props = new Properties();
props.load(new FileInputStream(f));
//下面的test1与test2相关代码是自己遇到问题时的测试
String test1 = seq.getPosition();//seq是读取数据库(采用gbk方式连接读取数据库)时获得的一个对象,有属性为字符串内型的变量position
Iterator<Object> keySetIterator = props.keySet().iterator();
keySetIterator.next();
String test2 = keySetIterator.next().toString();
System.out.println("test1=" + test1 + ", test2=" + test2 + ", " + test1.equals(test2));
String test3 = new String(test1.getBytes(), "GBK");
String test4 = new String(test2.getBytes(), "GBK");
System.out.println("test3=" + test3 + ", test4=" + test4 + ", " + test3.equals(test4));
String test5 = new String(test1.getBytes("GBK"), "GBK");
String test6 = new String(test2.getBytes("GBK"), "GBK");
System.out.println("test5=" + test5 + ", test6=" + test6 + ", " + test5.equals(test6));
String test7 = new String(test1.getBytes("ISO-8859-1"), "GBK");
String test8 = new String(test2.getBytes("ISO-8859-1"), "GBK");
System.out.println("test7=" + test7 + ", test8=" + test8 + ", " + test7.equals(test8));
String test9 = new String(test1.getBytes("UTF-8"), "GBK");
String test10 = new String(test2.getBytes("UTF-8"), "GBK");
System.out.println("test9=" + test9 + ", test10=" + test10 + ", " + test9.equals(test10));
String test11 = new String(test1.getBytes("GBK"), "ISO-8859-1");
System.out.println("test11="+test11 + ", " + test11.equals(test2));
// 测试结束
channelId = props.getProperty(new String(seq.getPosition().getBytes(), "ISO-8859-1"));// 根据位置从配置文件中获取channelId
if (channelId == null)
{
channelId = props.getProperty(new String(seq.getPosition()
.getBytes(), "utf-8"));
}
if (channelId == null)
{
channelId = props.getProperty(new String(seq.getPosition()
.getBytes(), "GBK"));
}
if (channelId == null)
{
throw new Exception("get channelid failed, channelid is "
+ channelId);
}
分析得出结果:
现在问题出现在,当channelidconfig.properties配置文件中key是中文时,那么seq.getPosition即字符串test1(编码为GBK),实例是:
test1="标清广告背景图片“(编码为GBK,因为从数据库中获取来的),而配置文件中的test2(即想用props.getProperty(key)的key)全都会是Java默认的字符编码的”ISO-8859-1",所以当我们用打印语句System.out.println(test1)打印的是中文“标清广告背景图片",而用打印语句System.out.println(test2)打印的却是乱码,而且打印System.out.println(test1.equals(test2))结果也显示为false。
解决问题的方法是这样的:
第一种方法:变test1, String test3 = new String(test1.getBytes("GBK"), "ISO-8859-1"),结果test3.equals(test2)为true ;
第二种方法:变test2, String test4 = new String(test2.getBytes("ISO-8859-1"), "GBK"), 结果test1.equals(test4)为true。
(“备注说明”:上面涉及到的”ISO-8859-1"其实都可以不用写,因为Java字符串默认的字符编码操作就是“ISO-8859-1")
联想探索:
上面一一连串的推测与结论表面上似乎都是对的,但是一个“备注说明”,理论上基于上面的实验与推论应该完全正确,然而结果不是我们想象的那样,经多方面分析,查看源代码,一想再想,还是源代码最能说明问题,其实java.lang.String中的构造方法new String(byte[] bytes)和getBytes()源代码里面都有这样的代码:
String csn = Converters.getDefaultEncodingName();
try {
return decode(csn, ba, off, len);
} catch (UnsupportedEncodingException x) {
Converters.resetDefaultEncodingName();
warnUnsupportedCharset(csn);
}
try {
return decode("ISO-8859-1", ba, off, len);
} catch (UnsupportedEncodingException x) {
// If this code is hit during VM initialization, MessageUtils is
// the only way we will be able to get any kind of error message.
MessageUtils.err("ISO-8859-1 charset not available: "
+ x.toString());
// If we can not find ISO-8859-1 (a required encoding) then things
// are seriously wrong with the installation.
System.exit(1);
return null;
}
虽然现在Converters.getDefaultEncodingName()和类sun.io.Converters本身都已被废弃(查证可以用Charset.defaultCharset().name()代替,更多功能由java.nio.charset.CharSet类代替),但是源代码里毕竟有这样的代码,所以再采用“ISO-8859-1”编码字节数组组成字符串,或者字符串得到字节数组时都会先采用系统默认的字符集编码,经实践证明,我的Windows操作系统由Charset.defaultCharset().name()或者Converters.getDefaultEncodingName()得到的都是GBK,而Linux系统得到的是UTF-8,所以这个String的构造方法new String(byte[] bytes)和getBytes()方法是不能乱用的,依赖系统属性的!正如此,解决问题的方法还是上面所说的两种,而且不能省略“ISO-8859-1”,即不能偷懒用备注中说明的方法解决问题,既然这样不仅限于另一个疑惑,那么java.util.Properties中的Key为什么都是ISO-8859-1编码的字符串呢,再看源代码,发现他底层采用构造字符串的方法是new String(char value[], int offset, int count)并未涉及到字节数组那样转换为字符串的操作,默认字符char编码就是ISO-8859-1吧(这里现在我也不敢断定了)。
回顾总结:
此问题出于Java读取Properties配置文件产生乱码源头,引发对字符串编码的一阵阵研究,对于自己字符串编码方面帮助受益颇多,记下来作为知识的积累!