﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-ALL  ABOUT JAVA-随笔分类-J2SE 基础知识</title><link>http://www.blogjava.net/wdlfellow/category/21227.html</link><description>JAVA 技术</description><language>zh-cn</language><lastBuildDate>Mon, 16 Apr 2007 18:01:32 GMT</lastBuildDate><pubDate>Mon, 16 Apr 2007 18:01:32 GMT</pubDate><ttl>60</ttl><item><title>彻底明白Java的IO系统-转</title><link>http://www.blogjava.net/wdlfellow/archive/2007/04/13/110305.html</link><dc:creator>泡面</dc:creator><author>泡面</author><pubDate>Thu, 12 Apr 2007 16:07:00 GMT</pubDate><guid>http://www.blogjava.net/wdlfellow/archive/2007/04/13/110305.html</guid><wfw:comment>http://www.blogjava.net/wdlfellow/comments/110305.html</wfw:comment><comments>http://www.blogjava.net/wdlfellow/archive/2007/04/13/110305.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wdlfellow/comments/commentRss/110305.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wdlfellow/services/trackbacks/110305.html</trackback:ping><description><![CDATA[<p>转载自<a href="http://hi.baidu.com/ahunspun/blog/item/0069084e9882a0cbd0c86a66.html"><font color=#0000ff>http://hi.baidu.com/ahunspun/blog/item/0069084e9882a0cbd0c86a66.html</font></a><br><br></p>
<p><font color=#000000>一． Input和Output<br>1. stream代表的是任何有能力产出数据的数据源，或是任何有能力接收数据的接收源。在Java的IO中，所有的stream（包括Input和Out </font></p>
<p><font color=#000000>stream）都包括两种类型：</font></p>
<p><font color=#000000>1.1 以字节为导向的stream<br>以字节为导向的stream，表示以字节为单位从stream中读取或往stream中写入信息。以字节为导向的stream包括下面几种类型：<br>1) inputstream：<br>1) ByteArrayInputStream：把内存中的一个缓冲区作为InputStream使用<br>2) StringBufferInputStream：把一个String对象作为InputStream<br>3) FileInputStream：把一个文件作为InputStream，实现对文件的读取操作<br>4) PipedInputStream：实现了pipe的概念，主要在线程中使用<br>5) SequenceInputStream：把多个InputStream合并为一个InputStream</font></p>
<p><font color=#000000>2) Outputstream<br>1) ByteArrayOutputStream：把信息存入内存中的一个缓冲区中<br>2) FileOutputStream：把信息存入文件中<br>3) PipedOutputStream：实现了pipe的概念，主要在线程中使用<br>4) SequenceOutputStream：把多个OutStream合并为一个OutStream</font></p>
<p><font color=#000000>1.2 以Unicode字符为导向的stream<br>以Unicode字符为导向的stream，表示以Unicode字符为单位从stream中读取或往stream中写入信息。以Unicode字符为导向的stream包括下面几</font></p>
<p><font color=#000000>种类型：<br>1) Input　Stream<br>1) CharArrayReader：与ByteArrayInputStream对应<br>2) StringReader：与StringBufferInputStream对应<br>3) FileReader：与FileInputStream对应<br>4) PipedReader：与PipedInputStream对应</font></p>
<p><font color=#000000>2) Out　Stream<br>1) CharArrayWrite：与ByteArrayOutputStream对应<br>2) StringWrite：无与之对应的以字节为导向的stream<br>3) FileWrite：与FileOutputStream对应<br>4) PipedWrite：与PipedOutputStream对应<br>以字符为导向的stream基本上对有与之相对应的以字节为导向的stream。两个对应类实现的功能相同，字是在操作时的导向不同。如</font></p>
<p><font color=#000000>CharArrayReader：和ByteArrayInputStream的作用都是把内存中的一个缓冲区作为InputStream使用，所不同的是前者每次从内存中读取一个</font></p>
<p><font color=#000000>字节的信息，而后者每次从内存中读取一个字符。<br>1.3 两种不现导向的stream之间的转换<br>InputStreamReader和OutputStreamReader：把一个以字节为导向的stream转换成一个以字符为导向的stream。<br>2. stream添加属性<br>2.1 &#8220;为stream添加属性&#8221;的作用<br>运用上面介绍的Java中操作IO的API，我们就可完成我们想完成的任何操作了。但通过FilterInputStream和FilterOutStream的子类，我们可以</font></p>
<p><font color=#000000>为stream添加属性。下面以一个例子来说明这种功能的作用。<br>如果我们要往一个文件中写入数据，我们可以这样操作：<br>FileOutStream fs = new FileOutStream(&#8220;test.txt&#8221;);<br>然后就可以通过产生的fs对象调用write()函数来往test.txt文件中写入数据了。但是，如果我们想实现&#8220;先把要写入文件的数据先缓存到内存</font></p>
<p><font color=#000000>中，再把缓存中的数据写入文件中&#8221;的功能时，上面的API就没有一个能满足我们的需求了。但是通过FilterInputStream和FilterOutStream的</font></p>
<p><font color=#000000>子类，为FileOutStream添加我们所需要的功能。<br>2.2 FilterInputStream的各种类型<br>2.2.1 用于封装以字节为导向的InputStream<br>1) DataInputStream：从stream中读取基本类型（int、char等）数据。<br>2) BufferedInputStream：使用缓冲区<br>3) LineNumberInputStream：会记录input stream内的行数，然后可以调用getLineNumber()和setLineNumber(int)<br>4) PushbackInputStream：很少用到，一般用于编译器开发<br>2.2.2 用于封装以字符为导向的InputStream<br>1) 没有与DataInputStream对应的类。除非在要使用readLine()时改用BufferedReader，否则使用DataInputStream<br>2) BufferedReader：与BufferedInputStream对应<br>3) LineNumberReader：与LineNumberInputStream对应<br>4) PushBackReader：与PushbackInputStream对应<br>2.3 FilterOutStream的各种类型<br>2.2.3 用于封装以字节为导向的OutputStream<br>1) DataIOutStream：往stream中输出基本类型（int、char等）数据。<br>2) BufferedOutStream：使用缓冲区<br>3) PrintStream：产生格式化输出<br>2.2.4 用于封装以字符为导向的OutputStream<br>1) BufferedWrite：与对应<br>2) PrintWrite：与对应<br>3. RandomAccessFile<br>1) 可通过RandomAccessFile对象完成对文件的读写操作<br>2) 在产生一个对象时，可指明要打开的文件的性质：r，只读；w，只写；rw可读写<br>3) 可以直接跳到文件中指定的位置<br>4. I/O应用的一个例子<br>import java.io.*;<br>public class TestIO{<br>public static void main(String[] args)<br>throws IOException{<br>//1.以行为单位从一个文件读取数据<br>BufferedReader in = <br>new BufferedReader(<br>new FileReader("F:\\nepalon\\TestIO.java"));<br>String s, s2 = new String();<br>while((s = in.readLine()) != null)<br>s2 += s + "\n";<br>in.close();</font></p>
<p><font color=#000000>//1b. 接收键盘的输入<br>BufferedReader stdin = <br>new BufferedReader(<br>new InputStreamReader(System.in));<br>System.out.println("Enter a line:");<br>System.out.println(stdin.readLine());</font></p>
<p><font color=#000000>//2. 从一个String对象中读取数据<br>StringReader in2 = new StringReader(s2);<br>int c;<br>while((c = in2.read()) != -1)<br>System.out.println((char)c);<br>in2.close();</font></p>
<p><font color=#000000>//3. 从内存取出格式化输入<br>try{<br>DataInputStream in3 = <br>new DataInputStream(<br>new ByteArrayInputStream(s2.getBytes()));<br>while(true)<br>System.out.println((char)in3.readByte()); <br>}<br>catch(EOFException e){<br>System.out.println("End of stream");<br>}</font></p>
<p><font color=#000000>//4. 输出到文件<br>try{<br>BufferedReader in4 =<br>new BufferedReader(<br>new StringReader(s2));<br>PrintWriter out1 =<br>new PrintWriter(<br>new BufferedWriter(<br>new FileWriter("F:\\nepalon\\ TestIO.out")));<br>int lineCount = 1;<br>while((s = in4.readLine()) != null)<br>out1.println(lineCount++ + "：" + s);<br>out1.close();<br>in4.close();<br>}<br>catch(EOFException ex){<br>System.out.println("End of stream");<br>}</font></p>
<p><font color=#000000>//5. 数据的存储和恢复<br>try{<br>DataOutputStream out2 = <br>new DataOutputStream(<br>new BufferedOutputStream(<br>new FileOutputStream("F:\\nepalon\\ Data.txt")));<br>out2.writeDouble(3.1415926);<br>out2.writeChars("\nThas was pi:writeChars\n");<br>out2.writeBytes("Thas was pi:writeByte\n");<br>out2.close();<br>DataInputStream in5 =<br>new DataInputStream(<br>new BufferedInputStream(<br>new FileInputStream("F:\\nepalon\\ Data.txt")));<br>BufferedReader in5br =<br>new BufferedReader(<br>new InputStreamReader(in5));<br>System.out.println(in5.readDouble());<br>System.out.println(in5br.readLine());<br>System.out.println(in5br.readLine());<br>}<br>catch(EOFException e){<br>System.out.println("End of stream");<br>}</font></p>
<p><font color=#000000>//6. 通过RandomAccessFile操作文件<br>RandomAccessFile rf =<br>new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");<br>for(int i=0; i&lt;10; i++)<br>rf.writeDouble(i*1.414);<br>rf.close();</font></p>
<p><font color=#000000>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");<br>for(int i=0; i&lt;10; i++)<br>System.out.println("Value " + i + "：" + rf.readDouble());<br>rf.close();</font></p>
<p><font color=#000000>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "rw");<br>rf.seek(5*8);<br>rf.writeDouble(47.0001);<br>rf.close();</font></p>
<p><font color=#000000>rf = new RandomAccessFile("F:\\nepalon\\ rtest.dat", "r");<br>for(int i=0; i&lt;10; i++)<br>System.out.println("Value " + i + "：" + rf.readDouble());<br>rf.close();<br>}<br>}<br>关于代码的解释（以区为单位）：<br>1区中，当读取文件时，先把文件内容读到缓存中，当调用in.readLine()时，再从缓存中以字符的方式读取数据（以下简称&#8220;缓存字节读取方</font></p>
<p><font color=#000000>式&#8221;）。<br>1b区中，由于想以缓存字节读取方式从标准IO（键盘）中读取数据，所以要先把标准IO（System.in）转换成字符导向的stream，再进行</font></p>
<p><font color=#000000>BufferedReader封装。<br>2区中，要以字符的形式从一个String对象中读取数据，所以要产生一个StringReader类型的stream。<br>4区中，对String对象s2读取数据时，先把对象中的数据存入缓存中，再从缓冲中进行读取；对TestIO.out文件进行操作时，先把格式化后的信</font></p>
<p><font color=#000000>息输出到缓存中，再把缓存中的信息输出到文件中。<br>5区中，对Data.txt文件进行输出时，是先把基本类型的数据输出屋缓存中，再把缓存中的数据输出到文件中；对文件进行读取操作时，先把文</font></p>
<p><font color=#000000>件中的数据读取到缓存中，再从缓存中以基本类型的形式进行读取。注意in5.readDouble()这一行。因为写入第一个writeDouble()，所以为了</font></p>
<p><font color=#000000>正确显示。也要以基本类型的形式进行读取。<br>6区是通过RandomAccessFile类对文件进行操作。</font></p>
<img src ="http://www.blogjava.net/wdlfellow/aggbug/110305.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wdlfellow/" target="_blank">泡面</a> 2007-04-13 00:07 <a href="http://www.blogjava.net/wdlfellow/archive/2007/04/13/110305.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java 通用线程池－转自CSDN</title><link>http://www.blogjava.net/wdlfellow/archive/2007/04/12/110297.html</link><dc:creator>泡面</dc:creator><author>泡面</author><pubDate>Thu, 12 Apr 2007 15:33:00 GMT</pubDate><guid>http://www.blogjava.net/wdlfellow/archive/2007/04/12/110297.html</guid><wfw:comment>http://www.blogjava.net/wdlfellow/comments/110297.html</wfw:comment><comments>http://www.blogjava.net/wdlfellow/archive/2007/04/12/110297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wdlfellow/comments/commentRss/110297.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wdlfellow/services/trackbacks/110297.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 感谢ryang的劳动！Java实现通用线程池线程池通俗的描述就是预先创建若干空闲线程，等到需要用多线程去处理事务的时候去唤醒某些空闲线程执行处理任务，这样就省去了频繁创建线程的时间，因为频繁创建线程是要耗费大量的CPU资源的。如果一个应用程序需要频繁地处理大量并发事务，不断的创建销毁线程往往会大大地降低系统的效率，这时候线程池就派上用场了。&nbsp;&nbsp;&nbsp;&nbsp;&...&nbsp;&nbsp;<a href='http://www.blogjava.net/wdlfellow/archive/2007/04/12/110297.html'>阅读全文</a><img src ="http://www.blogjava.net/wdlfellow/aggbug/110297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wdlfellow/" target="_blank">泡面</a> 2007-04-12 23:33 <a href="http://www.blogjava.net/wdlfellow/archive/2007/04/12/110297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA虚拟机加载类的方式解析</title><link>http://www.blogjava.net/wdlfellow/archive/2007/04/11/109819.html</link><dc:creator>泡面</dc:creator><author>泡面</author><pubDate>Wed, 11 Apr 2007 01:47:00 GMT</pubDate><guid>http://www.blogjava.net/wdlfellow/archive/2007/04/11/109819.html</guid><wfw:comment>http://www.blogjava.net/wdlfellow/comments/109819.html</wfw:comment><comments>http://www.blogjava.net/wdlfellow/archive/2007/04/11/109819.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/wdlfellow/comments/commentRss/109819.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wdlfellow/services/trackbacks/109819.html</trackback:ping><description><![CDATA[虚拟机加载类的途径：<br><br>1、Dog dog ＝ new Dog（）；<br>这个动作会导致常量池的解析，Dog类被隐式装载。<br>如果当前ClassLoader无法找到Dog，则抛出NoClassDefFoundError。<br><br>2、Class clazz ＝ Class.forName（&#8220;Dog&#8221;）；<br>&nbsp;&nbsp; Object dog ＝clazz.newInstance（）；<br>通过反射加载类型，并创建对象实例<br>如果无法找到Dog，则抛出ClassNotFoundException。<br><br>3、Class clazz ＝ classLoader.loadClass（&#8220;Dog&#8221;）；<br>&nbsp;&nbsp; Object dog ＝clazz.newInstance（）；<br>通过反射加载类型，并创建对象实例<br>如果无法找到Dog，则抛出ClassNotFoundException。<br><br>那么，1和2和3究竟有什么区别呢？分别用于什么情况呢？<br><br>1和2使用的类加载器是相同的，都是当前类加载器。（即：this.getClass.getClassLoader）。<br>3由用户指定类加载器。<br><br>如果需要在当前类路径以外寻找类，则只能采用第3种方式。第3种方式加载的类与当前类分属不同的命名空间。<br>当前类加载器命名空间对其不可见。当然，如果被加载类的超类对于当前类命名空间可见的话，则可以进行强制转型。<br><br>第1和第2种情况区别不大。如果，Dog类在编译时无法得到，则使用第2种方式。<br><br>另外，第1种和第2种都会导致类被初始化，即：执行类的静态初始化语句，而第3种情况不会。<br>另外注意，第1种抛出Error，第2、3种抛出Exception，它们分属于不同的异常/错误分支。<br>
<img src ="http://www.blogjava.net/wdlfellow/aggbug/109819.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wdlfellow/" target="_blank">泡面</a> 2007-04-11 09:47 <a href="http://www.blogjava.net/wdlfellow/archive/2007/04/11/109819.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>java字符集(转自键者天行)</title><link>http://www.blogjava.net/wdlfellow/archive/2007/04/03/108287.html</link><dc:creator>泡面</dc:creator><author>泡面</author><pubDate>Tue, 03 Apr 2007 15:33:00 GMT</pubDate><guid>http://www.blogjava.net/wdlfellow/archive/2007/04/03/108287.html</guid><wfw:comment>http://www.blogjava.net/wdlfellow/comments/108287.html</wfw:comment><comments>http://www.blogjava.net/wdlfellow/archive/2007/04/03/108287.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/wdlfellow/comments/commentRss/108287.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/wdlfellow/services/trackbacks/108287.html</trackback:ping><description><![CDATA[<div class=postText><font color=#993300>1.&nbsp;概述&nbsp;<br><br>本文主要包括以下几个方面：编码基本知识，java，系统软件，url，工具软件等。&nbsp;<br><br>在下面的描述中，将以"中文"两个字为例，经查表可以知道其GB2312编码是"d6d0&nbsp;cec4"，Unicode编码为"4e2d&nbsp;6587"，UTF编码就是"e4b8ad&nbsp;e69687"。注意，这两个字没有iso8859-1编码，但可以用iso8859-1编码来"表示"。&nbsp;<br><br>2.&nbsp;编码基本知识&nbsp;<br><br>最早的编码是iso8859-1，和ascii编码相似。但为了方便表示各种各样的语言，逐渐出现了很多标准编码，重要的有如下几个。&nbsp;<br><br>2.1.&nbsp;iso8859-1&nbsp;<br><br>属于单字节编码，最多能表示的字符范围是0-255，应用于英文系列。比如，字母'a'的编码为0x61=97。&nbsp;<br><br>很明显，iso8859-1编码表示的字符范围很窄，无法表示中文字符。但是，由于是单字节编码，和计算机最基础的表示单位一致，所以很多时候，仍旧使用iso8859-1编码来表示。而且在很多协议上，默认使用该编码。比如，虽然"中文"两个字不存在iso8859-1编码，以gb2312编码为例，应该是"d6d0&nbsp;cec4"两个字符，使用iso8859-1编码的时候则将它拆开为4个字节来表示："d6&nbsp;d0&nbsp;ce&nbsp;c4"（事实上，在进行存储的时候，也是以字节为单位处理的）。而如果是UTF编码，则是6个字节"e4&nbsp;b8&nbsp;ad&nbsp;e6&nbsp;96&nbsp;87"。很明显，这种表示方法还需要以另一种编码为基础。&nbsp;<br><br>2.2.&nbsp;GB2312/GBK&nbsp;<br><br>这就是汉子的国标码，专门用来表示汉字，是双字节编码，而英文字母和iso8859-1一致（兼容iso8859-1编码）。其中gbk编码能够用来同时表示繁体字和简体字，而gb2312只能表示简体字，gbk是兼容gb2312编码的。&nbsp;<br><br>2.3.&nbsp;unicode&nbsp;<br><br>这是最统一的编码，可以用来表示所有语言的字符，而且是定长双字节（也有四字节的）编码，包括英文字母在内。所以可以说它是不兼容iso8859-1编码的，也不兼容任何编码。不过，相对于iso8859-1编码来说，uniocode编码只是在前面增加了一个0字节，比如字母'a'为"00&nbsp;61"。&nbsp;<br><br>需要说明的是，定长编码便于计算机处理（注意GB2312/GBK不是定长编码），而unicode又可以用来表示所有字符，所以在很多软件内部是使用unicode编码来处理的，比如java。&nbsp;<br><br>2.4.&nbsp;UTF&nbsp;<br><br>考虑到unicode编码不兼容iso8859-1编码，而且容易占用更多的空间：因为对于英文字母，unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了utf编码，utf编码兼容iso8859-1编码，同时也可以用来表示所有语言的字符，不过，utf编码是不定长编码，每一个字符的长度从1-6个字节不等。另外，utf编码自带简单的校验功能。一般来讲，英文字母都是用一个字节表示，而汉字使用三个字节。&nbsp;<br><br>注意，虽然说utf是为了使用更少的空间而使用的，但那只是相对于unicode编码来说，如果已经知道是汉字，则使用GB2312/GBK无疑是最节省的。不过另一方面，值得说明的是，虽然utf编码对汉字使用3个字节，但即使对于汉字网页，utf编码也会比unicode编码节省，因为网页中包含了很多的英文字符。&nbsp;<br><br>3.&nbsp;java对字符的处理&nbsp;<br><br>在java应用软件中，会有多处涉及到字符集编码，有些地方需要进行正确的设置，有些地方需要进行一定程度的处理。&nbsp;<br><br>3.1.&nbsp;getBytes(charset)&nbsp;<br><br>这是java字符串处理的一个标准函数，其作用是将字符串所表示的字符按照charset编码，并以字节方式表示。注意字符串在java内存中总是按unicode编码存储的。比如"中文"，正常情况下（即没有错误的时候）存储为"4e2d&nbsp;6587"，如果charset为"gbk"，则被编码为"d6d0&nbsp;cec4"，然后返回字节"d6&nbsp;d0&nbsp;ce&nbsp;c4"。如果charset为"utf8"则最后是"e4&nbsp;b8&nbsp;ad&nbsp;e6&nbsp;96&nbsp;87"。如果是"iso8859-1"，则由于无法编码，最后返回&nbsp;"3f&nbsp;3f"（两个问号）。&nbsp;<br><br>3.2.&nbsp;new&nbsp;String(charset)&nbsp;<br><br>这是java字符串处理的另一个标准函数，和上一个函数的作用相反，将字节数组按照charset编码进行组合识别，最后转换为unicode存储。参考上述getBytes的例子，"gbk"&nbsp;和"utf8"都可以得出正确的结果"4e2d&nbsp;6587"，但iso8859-1最后变成了"003f&nbsp;003f"（两个问号）。&nbsp;<br><br>因为utf8可以用来表示/编码所有字符，所以new&nbsp;String(&nbsp;str.getBytes(&nbsp;"utf8"&nbsp;),&nbsp;"utf8"&nbsp;)&nbsp;===&nbsp;str，即完全可逆。&nbsp;<br><br>3.3.&nbsp;setCharacterEncoding()&nbsp;<br><br>该函数用来设置http请求或者相应的编码。&nbsp;<br><br>对于request，是指提交内容的编码，指定后可以通过getParameter()则直接获得正确的字符串，如果不指定，则默认使用iso8859-1编码，需要进一步处理。参见下述"表单输入"。值得注意的是在执行setCharacterEncoding()之前，不能执行任何getParameter()。java&nbsp;doc上说明：This&nbsp;method&nbsp;must&nbsp;be&nbsp;called&nbsp;prior&nbsp;to&nbsp;reading&nbsp;request&nbsp;parameters&nbsp;or&nbsp;reading&nbsp;input&nbsp;using&nbsp;getReader()。而且，该指定只对POST方法有效，对GET方法无效。分析原因，应该是在执行第一个getParameter()的时候，java将会按照编码分析所有的提交内容，而后续的getParameter()不再进行分析，所以setCharacterEncoding()无效。而对于GET方法提交表单是，提交的内容在URL中，一开始就已经按照编码分析所有的提交内容，setCharacterEncoding()自然就无效。&nbsp;<br><br>对于response，则是指定输出内容的编码，同时，该设置会传递给浏览器，告诉浏览器输出内容所采用的编码。&nbsp;<br><br>3.4.&nbsp;处理过程&nbsp;<br><br>下面分析两个有代表性的例子，说明java对编码有关问题的处理方法。&nbsp;<br><br>3.4.1.&nbsp;表单输入&nbsp;<br><br>User&nbsp;input&nbsp;&nbsp;*(gbk:d6d0&nbsp;cec4)&nbsp;&nbsp;browser&nbsp;&nbsp;*(gbk:d6d0&nbsp;cec4)&nbsp;&nbsp;web&nbsp;server&nbsp;&nbsp;iso8859-1(00d6&nbsp;00d&nbsp;000ce&nbsp;00c4)&nbsp;&nbsp;class，需要在class中进行处理：getbytes("iso8859-1")为d6&nbsp;d0&nbsp;ce&nbsp;c4，new&nbsp;String("gbk")为d6d0&nbsp;cec4，内存中以unicode编码则为4e2d&nbsp;6587。&nbsp;<br><br>l&nbsp;用户输入的编码方式和页面指定的编码有关，也和用户的操作系统有关，所以是不确定的，上例以gbk为例。&nbsp;<br><br>l&nbsp;从browser到web&nbsp;server，可以在表单中指定提交内容时使用的字符集，否则会使用页面指定的编码。而如果在url中直接用?的方式输入参数，则其编码往往是操作系统本身的编码，因为这时和页面无关。上述仍旧以gbk编码为例。&nbsp;<br><br>l&nbsp;Web&nbsp;server接收到的是字节流，默认时（getParameter）会以iso8859-1编码处理之，结果是不正确的，所以需要进行处理。但如果预先设置了编码（通过request.&nbsp;setCharacterEncoding&nbsp;()），则能够直接获取到正确的结果。&nbsp;<br><br>l&nbsp;在页面中指定编码是个好习惯，否则可能失去控制，无法指定正确的编码。&nbsp;<br><br>3.4.2.&nbsp;文件编译&nbsp;<br><br>假设文件是gbk编码保存的，而编译有两种编码选择：gbk或者iso8859-1，前者是中文windows的默认编码，后者是linux的默认编码，当然也可以在编译时指定编码。&nbsp;<br><br>Jsp&nbsp;&nbsp;*(gbk:d6d0&nbsp;cec4)&nbsp;&nbsp;java&nbsp;file&nbsp;&nbsp;*(gbk:d6d0&nbsp;cec4)&nbsp;&nbsp;compiler&nbsp;read&nbsp;&nbsp;uincode(gbk:&nbsp;4e2d&nbsp;6587;&nbsp;iso8859-1:&nbsp;00d6&nbsp;00d&nbsp;000ce&nbsp;00c4)&nbsp;&nbsp;compiler&nbsp;write&nbsp;&nbsp;utf(gbk:&nbsp;e4b8ad&nbsp;e69687;&nbsp;iso8859-1:&nbsp;*)&nbsp;&nbsp;compiled&nbsp;file&nbsp;&nbsp;unicode(gbk:&nbsp;4e2d&nbsp;6587;&nbsp;iso8859-1:&nbsp;00d6&nbsp;00d&nbsp;000ce&nbsp;00c4)&nbsp;&nbsp;class。所以用gbk编码保存，而用iso8859-1编译的结果是不正确的。&nbsp;<br><br>class&nbsp;&nbsp;unicode(4e2d&nbsp;6587)&nbsp;&nbsp;system.out&nbsp;/&nbsp;jsp.out&nbsp;&nbsp;gbk(d6d0&nbsp;cec4)&nbsp;&nbsp;os&nbsp;console&nbsp;/&nbsp;browser。&nbsp;<br><br>l&nbsp;文件可以以多种编码方式保存，中文windows下，默认为ansi/gbk。&nbsp;<br><br>l&nbsp;编译器读取文件时，需要得到文件的编码，如果未指定，则使用系统默认编码。一般class文件，是以系统默认编码保存的，所以编译不会出问题，但对于jsp文件，如果在中文windows下编辑保存，而部署在英文linux下运行/编译，则会出现问题。所以需要在jsp文件中用pageEncoding指定编码。&nbsp;<br><br>l&nbsp;Java编译的时候会转换成统一的unicode编码处理，最后保存的时候再转换为utf编码。&nbsp;<br><br>l&nbsp;当系统输出字符的时候，会按指定编码输出，对于中文windows下，System.out将使用gbk编码，而对于response（浏览器），则使用jsp文件头指定的contentType，或者可以直接为response指定编码。同时，会告诉browser网页的编码。如果未指定，则会使用iso8859-1编码。对于中文，应该为browser指定输出字符串的编码。&nbsp;<br><br>l&nbsp;browser显示网页的时候，首先使用response中指定的编码（jsp文件头指定的contentType最终也反映在response上），如果未指定，则会使用网页中meta项指定中的contentType。&nbsp;<br><br>3.5.&nbsp;几处设置&nbsp;<br><br>对于web应用程序，和编码有关的设置或者函数如下。&nbsp;<br><br>3.5.1.&nbsp;jsp编译&nbsp;<br><br>指定文件的存储编码，很明显，该设置应该置于文件的开头。例如：&lt;%@page&nbsp;pageEncoding="GBK"%&gt;。另外，对于一般class文件，可以在编译的时候指定编码。&nbsp;<br><br>3.5.2.&nbsp;jsp输出&nbsp;<br><br>指定文件输出到browser是使用的编码，该设置也应该置于文件的开头。例如：&lt;%@&nbsp;page&nbsp;contentType="text/html;&nbsp;charset=&nbsp;GBK"&nbsp;%&gt;。该设置和response.setCharacterEncoding("GBK")等效。&nbsp;<br><br>3.5.3.&nbsp;meta设置&nbsp;<br><br>指定网页使用的编码，该设置对静态网页尤其有作用。因为静态网页无法采用jsp的设置，而且也无法执行response.setCharacterEncoding()。例如：&lt;META&nbsp;http-equiv="Content-Type"&nbsp;content="text/html;&nbsp;charset=GBK"&nbsp;/&gt;&nbsp;<br><br>如果同时采用了jsp输出和meta设置两种编码指定方式，则jsp指定的优先。因为jsp指定的直接体现在response中。&nbsp;<br><br>需要注意的是，apache有一个设置可以给无编码指定的网页指定编码，该指定等同于jsp的编码指定方式，所以会覆盖静态网页中的meta指定。所以有人建议关闭该设置。&nbsp;<br><br>3.5.4.&nbsp;form设置&nbsp;<br><br>当浏览器提交表单的时候，可以指定相应的编码。例如：&lt;form&nbsp;accept-charset=&nbsp;"gb2312"&gt;。一般不必不使用该设置，浏览器会直接使用网页的编码。&nbsp;<br><br>4.&nbsp;系统软件&nbsp;<br><br>下面讨论几个相关的系统软件。&nbsp;<br><br>4.1.&nbsp;mysql数据库&nbsp;<br><br>很明显，要支持多语言，应该将数据库的编码设置成utf或者unicode，而utf更适合与存储。但是，如果中文数据中包含的英文字母很少，其实unicode更为适合。&nbsp;<br><br>数据库的编码可以通过mysql的配置文件设置，例如default-character-set=utf8。还可以在数据库链接URL中设置，例如：&nbsp;useUnicode=true&amp;characterEncoding=UTF-8。注意这两者应该保持一致，在新的sql版本里，在数据库链接URL里可以不进行设置，但也不能是错误的设置。&nbsp;<br><br>4.2.&nbsp;apache&nbsp;<br><br>appache和编码有关的配置在httpd.conf中，例如AddDefaultCharset&nbsp;UTF-8。如前所述，该功能会将所有静态页面的编码设置为UTF-8，最好关闭该功能。&nbsp;<br><br>另外，apache还有单独的模块来处理网页响应头，其中也可能对编码进行设置。&nbsp;<br><br>4.3.&nbsp;linux默认编码&nbsp;<br><br>这里所说的linux默认编码，是指运行时的环境变量。两个重要的环境变量是LC_ALL和LANG，默认编码会影响到java&nbsp;URLEncode的行为，下面有描述。&nbsp;<br><br>建议都设置为"zh_CN.UTF-8"。&nbsp;<br><br>4.4.&nbsp;其它&nbsp;<br><br>为了支持中文文件名，linux在加载磁盘时应该指定字符集，例如：mount&nbsp;/dev/hda5&nbsp;/mnt/hda5/&nbsp;-t&nbsp;ntfs&nbsp;-o&nbsp;iocharset=gb2312。&nbsp;<br><br>另外，如前所述，使用GET方法提交的信息不支持request.setCharacterEncoding()，但可以通过tomcat的配置文件指定字符集，在tomcat的server.xml文件中，形如：&lt;Connector&nbsp;...&nbsp;URIEncoding="GBK"/&gt;。这种方法将统一设置所有请求，而不能针对具体页面进行设置，也不一定和browser使用的编码相同，所以有时候并不是所期望的。&nbsp;<br><br>5.&nbsp;URL地址&nbsp;<br><br>URL地址中含有中文字符是很麻烦的，前面描述过使用GET方法提交表单的情况，使用GET方法时，参数就是包含在URL中。&nbsp;<br><br>5.1.&nbsp;URL编码&nbsp;<br><br>对于URL中的一些特殊字符，浏览器会自动进行编码。这些字符除了"/?&amp;"等外，还包括unicode字符，比如汉子。这时的编码比较特殊。&nbsp;<br><br>IE有一个选项"总是使用UTF-8发送URL"，当该选项有效时，IE将会对特殊字符进行UTF-8编码，同时进行URL编码。如果改选项无效，则使用默认编码"GBK"，并且不进行URL编码。但是，对于URL后面的参数，则总是不进行编码，相当于UTF-8选项无效。比如"中文.html?a=中文"，当UTF-8选项有效时，将发送链接"%e4%b8%ad%e6%96%87.html?a=\x4e\x2d\x65\x87"；而UTF-8选项无效时，将发送链接"\x4e\x2d\x65\x87.html?a=\x4e\x2d\x65\x87"。注意后者前面的"中文"两个字只有4个字节，而前者却有18个字节，这主要时URL编码的原因。&nbsp;<br><br>当web&nbsp;server（tomcat）接收到该链接时，将会进行URL解码，即去掉"%"，同时按照ISO8859-1编码（上面已经描述，可以使用URLEncoding来设置成其它编码）识别。上述例子的结果分别是"\ue4\ub8\uad\ue6\u96\u87.html?a=\u4e\u2d\u65\u87"和"\u4e\u2d\u65\u87.html?a=\u4e\u2d\u65\u87"，注意前者前面的"中文"两个字恢复成了6个字符。这里用"\u"，表示是unicode。&nbsp;<br><br>所以，由于客户端设置的不同，相同的链接，在服务器上得到了不同结果。这个问题不少人都遇到，却没有很好的解决办法。所以有的网站会建议用户尝试关闭UTF-8选项。不过，下面会描述一个更好的处理办法。&nbsp;<br><br>5.2.&nbsp;rewrite&nbsp;<br><br>熟悉的人都知道，apache有一个功能强大的rewrite模块，这里不描述其功能。需要说明的是该模块会自动将URL解码（去除%），即完成上述web&nbsp;server（tomcat）的部分功能。有相关文档介绍说可以使用[NE]参数来关闭该功能，但我试验并未成功，可能是因为版本（我使用的是apache&nbsp;2.0.54）问题。另外，当参数中含有"?&amp;&nbsp;"等符号的时候，该功能将导致系统得不到正常结果。&nbsp;<br><br>rewrite本身似乎完全是采用字节处理的方式，而不考虑字符串的编码，所以不会带来编码问题。&nbsp;<br><br>5.3.&nbsp;URLEncode.encode()&nbsp;<br><br>这是Java本身提供对的URL编码函数，完成的工作和上述UTF-8选项有效时浏览器所做的工作相似。值得说明的是，java已经不赞成不指定编码来使用该方法（deprecated）。应该在使用的时候增加编码指定。&nbsp;<br><br>当不指定编码的时候，该方法使用系统默认编码，这会导致软件运行结果得不确定。比如对于"中文"，当系统默认编码为"gb2312"时，结果是"%4e%2d%65%87"，而默认编码为"UTF-8"，结果却是"%e4%b8%ad%e6%96%87"，后续程序将难以处理。另外，这儿说的系统默认编码是由运行tomcat时的环境变量LC_ALL和LANG等决定的，曾经出现过tomcat重启后就出现乱码的问题，最后才郁闷的发现是因为修改修改了这两个环境变量。&nbsp;<br><br>建议统一指定为"UTF-8"编码，可能需要修改相应的程序。&nbsp;<br><br>5.4.&nbsp;一个解决方案&nbsp;<br><br>上面说起过，因为浏览器设置的不同，对于同一个链接，web&nbsp;server收到的是不同内容，而软件系统有无法知道这中间的区别，所以这一协议目前还存在缺陷。&nbsp;<br><br>针对具体问题，不应该侥幸认为所有客户的IE设置都是UTF-8有效的，也不应该粗暴的建议用户修改IE设置，要知道，用户不可能去记住每一个web&nbsp;server的设置。所以，接下来的解决办法就只能是让自己的程序多一点智能：根据内容来分析编码是否UTF-8。&nbsp;<br><br>比较幸运的是UTF-8编码相当有规律，所以可以通过分析传输过来的链接内容，来判断是否是正确的UTF-8字符，如果是，则以UTF-8处理之，如果不是，则使用客户默认编码（比如"GBK"），下面是一个判断是否UTF-8的例子，如果你了解相应规律，就容易理解。&nbsp;<br><br>public&nbsp;static&nbsp;boolean&nbsp;isValidUtf8(byte[]&nbsp;b,int&nbsp;aMaxCount){&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;lLen=b.length,lCharCount=0;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;i=0;i&lt;lLen&nbsp;&amp;&amp;&nbsp;lCharCount&lt;aMaxCount;++lCharCount){&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte&nbsp;lByte=b[i++];//to&nbsp;fast&nbsp;operation,&nbsp;++&nbsp;now,&nbsp;ready&nbsp;for&nbsp;the&nbsp;following&nbsp;for(;;)&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(lByte&gt;=0)&nbsp;continue;//&gt;=0&nbsp;is&nbsp;normal&nbsp;ascii&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(lByte&lt;(byte)0xc0&nbsp;||&nbsp;lByte&gt;(byte)0xfd)&nbsp;return&nbsp;false;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;lCount=lByte&gt;(byte)0xfc?5:lByte&gt;(byte)0xf8?4&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;:lByte&gt;(byte)0xf0?3:lByte&gt;(byte)0xe0?2:1;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(i+lCount&gt;lLen)&nbsp;return&nbsp;false;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for(int&nbsp;j=0;j&lt;lCount;++j,++i)&nbsp;if(b[i]&gt;=(byte)0xc0)&nbsp;return&nbsp;false;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;true;&nbsp;<br><br>}&nbsp;<br><br>相应地，一个使用上述方法的例子如下：&nbsp;<br><br>public&nbsp;static&nbsp;String&nbsp;getUrlParam(String&nbsp;aStr,String&nbsp;aDefaultCharset)&nbsp;<br><br>throws&nbsp;UnsupportedEncodingException{&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(aStr==null)&nbsp;return&nbsp;null;&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;byte[]&nbsp;lBytes=aStr.getBytes("ISO-8859-1");&nbsp;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;new&nbsp;String(lBytes,StringUtil.isValidUtf8(lBytes)?"utf8":aDefaultCharset);&nbsp;<br><br>}&nbsp;<br><br>不过，该方法也存在缺陷，如下两方面：&nbsp;<br><br>l&nbsp;没有包括对用户默认编码的识别，这可以根据请求信息的语言来判断，但不一定正确，因为我们有时候也会输入一些韩文，或者其他文字。&nbsp;<br><br>l&nbsp;可能会错误判断UTF-8字符，一个例子是"学习"两个字，其GBK编码是"&nbsp;\xd1\xa7\xcf\xb0"，如果使用上述isValidUtf8方法判断，将返回true。可以考虑使用更严格的判断方法，不过估计效果不大。&nbsp;<br><br>有一个例子可以证明google也遇到了上述问题，而且也采用了和上述相似的处理方法，比如，如果在地址栏中输入"http://www.google.com/search?hl=zh-CN&amp;newwindow=1&amp;q=学习"，google将无法正确识别，而其他汉字一般能够正常识别。&nbsp;<br><br>最后，应该补充说明一下，如果不使用rewrite规则，或者通过表单提交数据，其实并不一定会遇到上述问题，因为这时可以在提交数据时指定希望的编码。另外，中文文件名确实会带来问题，应该谨慎使用。&nbsp;<br><br>6.&nbsp;其它&nbsp;<br><br>下面描述一些和编码有关的其他问题。&nbsp;<br><br>6.1.&nbsp;SecureCRT&nbsp;<br><br>除了浏览器和控制台与编码有关外，一些客户端也很有关系。比如在使用SecureCRT连接linux时，应该让SecureCRT的显示编码（不同的session，可以有不同的编码设置）和linux的编码环境变量保持一致。否则看到的一些帮助信息，就可能是乱码。&nbsp;<br><br>另外，mysql有自己的编码设置，也应该保持和SecureCRT的显示编码一致。否则通过SecureCRT执行sql语句的时候，可能无法处理中文字符，查询结果也会出现乱码。&nbsp;<br><br>对于Utf-8文件，很多编辑器（比如记事本）会在文件开头增加三个不可见的标志字节，如果作为mysql的输入文件，则必须要去掉这三个字符。（用linux的vi保存可以去掉这三个字符）。一个有趣的现象是，在中文windows下，创建一个新txt文件，用记事本打开，输入"连通"两个字，保存，再打开，你会发现两个字没了，只留下一个小黑点。&nbsp;<br><br>6.2.&nbsp;过滤器&nbsp;<br><br>如果需要统一设置编码，则通过filter进行设置是个不错的选择。在filter&nbsp;class中，可以统一为需要的请求或者回应设置编码。参加上述setCharacterEncoding()。这个类apache已经给出了可以直接使用的例子SetCharacterEncodingFilter。&nbsp;<br><br>6.3.&nbsp;POST和GET&nbsp;<br><br>很明显，以POST提交信息时，URL有更好的可读性，而且可以方便的使用setCharacterEncoding()来处理字符集问题。但GET方法形成的URL能够更容易表达网页的实际内容，也能够用于收藏。&nbsp;<br><br>从统一的角度考虑问题，建议采用GET方法，这要求在程序中获得参数是进行特殊处理，而无法使用setCharacterEncoding()的便利，如果不考虑rewrite，就不存在IE的UTF-8问题，可以考虑通过设置URIEncoding来方便获取URL中的参数。&nbsp;<br><br>6.4.&nbsp;简繁体编码转换&nbsp;<br><br>GBK同时包含简体和繁体编码，也就是说同一个字，由于编码不同，在GBK编码下属于两个字。有时候，为了正确取得完整的结果，应该将繁体和简体进行统一。可以考虑将UTF、GBK中的所有繁体字，转换为相应的简体字，BIG5编码的数据，也应该转化成相应的简体字。当然，仍旧以UTF编码存储。&nbsp;<br><br>例如，对于"语言&nbsp;?言"，用UTF表示为"\xE8\xAF\xAD\xE8\xA8\x80&nbsp;\xE8\xAA\x9E\xE8\xA8\x80"，进行简繁体编码转换后应该是两个相同的&nbsp;"\xE8\xAF\xAD\xE8\xA8\x80&gt;"。&nbsp;</font><br>&nbsp;<br><br>
<p id=TBPingURL>Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1145171</p>
</div>
<img src ="http://www.blogjava.net/wdlfellow/aggbug/108287.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/wdlfellow/" target="_blank">泡面</a> 2007-04-03 23:33 <a href="http://www.blogjava.net/wdlfellow/archive/2007/04/03/108287.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>