首页 > 代码库 > java解惑之字符之谜(谜题18)

java解惑之字符之谜(谜题18)

谜题18:字符串奶酪

        下面这个程序从一个字节序列创建一个字符串,然后迭代遍历字符串中的字符,并将它们作为数字打印。请描述程序打印的数字序列:

public class StringCheese{
	public static void mian(String[] args){
		byte bytes[] new byte[256];
		for(int i = 0; i < 256; i++){
			bytes[i] = (byte)i;
		}
		String str = new String(bytes);
		for(int i = 0,n = str.length(); i < n; i++){
			System.out.print((int)str.charAt(i) + " ");
		}
	}
}


        现在,我们来分析一下这个程序。从这个程序来看,用从0到255中每一个可能的byte数值初始化byte数组,然后将这些byte数值通过String构造器转换成char数值。最后将char数值转型为int数值并打印。打印的数值肯定是非负整数,因为char数值是无符号的,因此,我们可能会描述这个程序将按照顺序打印0到255的整数。那这样描述是否是正确的呢?

        显然这样的描述是不正确的,但是我们运行程序后可能会发现确实是顺序打印了0到255的整数啊,为什么不对呢?但是你再多运行几次,就可能看到不一样的整数序列了。而书中作者在四台不同的机器上进行测试时,得到的是四个不同的序列,包括前面描述的那个序列。而且这个程序甚至不能保证正常终止,比打印其他任何特定字符串都更缺乏保证,它的行为是不确定的。产生这个问题的原因也就是我们这次要来探讨的谜题了:

        String(byte[])构造器。这正是产生该谜题的罪魁祸首。有关它的规范描述:“在通过解码使用平台缺省字符集的指定byte数组来构造一个新的String时,该新String的长度是字符集的一个函数,因此,它可能不等于byte数组的长度。当给定的所有字节在缺省字符集中并非全部有效时,这个构造器的行为是不确定的”,这是在API中对该构造器的一个描述。那到底什么是字符集呢?作者讲到,从技术角度上讲,它是“被编码的字符集合和字符编码模式的结合物”,这也是来自API中的描述。通俗一点来说,字符集就是一个包,包含了字符、表示字符的数字编码以及在字符编码序列和字节序列之间来回转换的方式。转换模式在字符集之间存在着很大的区别:某些是在字符和字节之间一对一的映射,但是大多数都不是这样。ISO-8859-1是惟一能够让该程序按顺序打印从0到255的整数的缺省字符集,它更为大家所熟知的名字是Latin-1(ISO-8859-1)。

        那么顺便来介绍下什么是ISO-8859-1吧,书中也没详细的介绍:

        ISO-8859-1,正式编号为ISO/IEC 8859-1:1998,又称Latin-1或“西欧语言”,是国际标准化组织内ISO/IEC 8859的第一个8位字符集。它以ASCII为基础,在空置的0xA0~0xFF的范围内,加入96个字母以及符号,藉以供使用附加符号的拉丁字母语言使用。这也是维基百科中对其的一些基础介绍,想要详细了解的直接进维基百科吧:http://zh.wikipedia.org/wiki/ISO/IEC_8859-1,我们还是回归谜题的探讨吧。

        J2SE运行期环境(JRE)的缺省字符集依赖于底层的操作系统和语言。如果想知道你的JRE的缺省字符集,并且使用的是5.0或更新的版本,那么你可以通过调用java.nio.charset.Charset.defaultCharset()来了解;如果使用的是较早版本,那么你可以通过阅读系统属性"file.encoding"来了解。幸运的是,没有强制要求必须容忍各种稀奇古怪的缺省字符集。在char序列和byte序列之间转换时,可以且通常应该显示地指定字符集。除了接受byte数组之外,还可以接受一个字符集名称的String构造器就是专为此目的而设计的。如果你用下面的构造器替换最初程序中的String构造器,那么不管缺省的字符集是什么,该程序都保证能够按照顺序打印从0到255的整数:

String str = new String(bytes,"ISO-8859-1");

        声明这个构造器会抛出UnsupportedEncodingException异常,因此必须捕获它,或者更适宜的方式是声明main方法抛出它,否则程序不能通过编译。尽管如此,该程序实际上不会抛出异常。Charset的规范要求java平台的每一种实现都要支持某些种类的字符集,ISO-8859-1就位列其中。到此为止,我们可以从中得到的教训就是:每当要将一个byte序列转换成一个String时,你都在使用一个字符集,不管是否显示地指定了它。如果想让你的程序行为可预知,那么在每次使用字符集时都明确地指定它。

java解惑之字符之谜(谜题18)