<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>hitqiang</title>
    <description></description>
    <link>http://hitqiang.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>Java与随机数</title>
        <author>hitqiang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hitqiang.javaeye.com">hitqiang</a>&nbsp;
          链接：<a href="http://hitqiang.javaeye.com/blog/197604" style="color:red;">http://hitqiang.javaeye.com/blog/197604</a>&nbsp;
          发表时间: 2008年05月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          java中产生随机数 <br />关键字:   随机数 java     <br />import java.util.Random; <br />/** <br />* Java实用工具类库中的类java.util.Random提供了产生各种类型随机数的方法。 <br />* 它可以产生int、long、float、double以及Goussian等类型的随机数。 <br />* java.lang.Math中的方法random()只产生double型的随机数。 <br />*/ <br />public class RandomNumber{<br /><br />public static void main(String[] args) {<br /><br />// 使用java.lang.Math的random方法生成随机数 <br />System.out.println("Math.random(): " + Math.random());<br /><br />// 使用不带参数的构造方法构造java.util.Random对象 <br />System.out.println("使用不带参数的构造方法构造的Random对象:"); <br />Random rd1 = new Random(); <br />// 产生各种类型的随机数 <br />// 按均匀分布产生整数 <br />System.out.println("int: " + rd1.nextInt()); <br />// 按均匀分布产生长整数 <br />System.out.println("long: " + rd1.nextLong()); <br />// 按均匀分布产生大于等于0，小于1的float数[0, 1) <br />System.out.println("float: " + rd1.nextFloat()); <br />// 按均匀分布产生[0, 1)范围的double数 <br />System.out.println("double: " + rd1.nextDouble()); <br />// 按正态分布产生随机数 <br />System.out.println("Gaussian: " + rd1.nextGaussian());<br /><br />// 生成一系列随机数 <br />System.out.print("随机整数序列:"); <br />for (int i = 0; i &lt; 5; i++) { <br />System.out.print(rd1.nextInt() + " "); <br />} <br />System.out.println();<br /><br />// 指定随机数产生的范围 <br />System.out.print("[0,10)范围内随机整数序列: "); <br />for (int i = 0; i &lt; 10; i++) { <br />// Random的nextInt(int n)方法返回一个[0, n)范围内的随机数 <br />System.out.print(rd1.nextInt(10) + " "); <br />} <br />System.out.println(); <br />System.out.print("[5,23)范围内随机整数序列: "); <br />for (int i = 0; i &lt; 10; i++) { <br />// 因为nextInt(int n)方法的范围是从0开始的， <br />// 所以需要把区间[5,28)转换成5 + [0, 23)。 <br />System.out.print(5 + rd1.nextInt(23) + " "); <br />} <br />System.out.println(); <br />System.out.print("利用nextFloat()生成[0,99)范围内的随机整数序列: "); <br />for (int i = 0; i &lt; 10; i++) { <br />System.out.print((int) (rd1.nextFloat() * 100) + " "); <br />} <br />System.out.println(); <br />System.out.println();<br /><br />// 使用带参数的构造方法构造Random对象 <br />// 构造函数的参数是long类型，是生成随机数的种子。 <br />System.out.println("使用带参数的构造方法构造的Random对象:"); <br />Random ran2 = new Random(10); <br />// 对于种子相同的Random对象，生成的随机数序列是一样的。 <br />System.out.println("使用种子为10的Random对象生成[0,10)内随机整数序列: "); <br />for (int i = 0; i &lt; 10; i++) { <br />System.out.print(ran2.nextInt(10) + " "); <br />} <br />System.out.println(); <br />Random ran3 = new Random(10); <br />System.out.println("使用另一个种子为10的Random对象生成[0,10)内随机整数序列: "); <br />for (int i = 0; i &lt; 10; i++) { <br />System.out.print(ran3.nextInt(10) + " "); <br />} <br />System.out.println(); <br />// ran2和ran3生成的随机数序列是一样的，如果使用两个没带参数构造函数生成的Random对象， <br />// 则不会出现这种情况，这是因为在没带参数构造函数生成的Random对象的种子缺省是当前系统时间的毫秒数。<br /><br />// 另外，直接使用Random无法避免生成重复的数字，如果需要生成不重复的随机数序列，需要借助数组和集合类 <br />//本书第4章将给出解决方法。 <br />} <br />}<br /><br />运行结果： <br />C:\>java RandomNumber <br />Math.random(): 0.525171492959965 <br />使用不带参数的构造方法构造的Random对象: <br />int: 636539740 <br />long: -752663949229005813 <br />float: 0.87349784 <br />double: 0.4065973309853902 <br />Gaussian: 0.4505871918488808 <br />随机整数序列:1936784917 1339857386 -1185229615 1883411721 1409219372 <br />[0,10)范围内随机整数序列: 1 1 5 5 9 0 1 0 2 4 <br />[5,23)范围内随机整数序列: 9 13 26 18 11 27 26 12 21 8 <br />利用nextFloat()生成[0,99)范围内的随机整数序列: 1 47 72 59 49 86 80 88 55 82<br /><br />使用带参数的构造方法构造的Random对象: <br />使用种子为10的Random对象生成[0,10)内随机整数序列: <br />3 0 3 0 6 6 7 8 1 4 <br />使用另一个种子为10的Random对象生成[0,10)内随机整数序列: <br />3 0 3 0 6 6 7 8 1 4
          <br/>
          <span style="color:red;">
            <a href="http://hitqiang.javaeye.com/blog/197604#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 27 May 2008 20:34:54 +0800</pubDate>
        <link>http://hitqiang.javaeye.com/blog/197604</link>
        <guid>http://hitqiang.javaeye.com/blog/197604</guid>
      </item>
      <item>
        <title>如何提高java I/O的执行效率，MappedByteBuffer</title>
        <author>hitqiang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hitqiang.javaeye.com">hitqiang</a>&nbsp;
          链接：<a href="http://hitqiang.javaeye.com/blog/197583" style="color:red;">http://hitqiang.javaeye.com/blog/197583</a>&nbsp;
          发表时间: 2008年05月27日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          如何提高java I/O的执行效率，MappedByteBuffer<br />复制内容到剪贴板代码:<br />import java.nio.*;<br />import java.nio.channel.*;<br />import java.io.*;<br />public static void copy(File source, File dest) throws IOException {<br />FileChannel in = null, out = null;<br />try { <br />  in = new FileInputStream(source).getChannel();<br />  out = new FileOutputStream(dest).getChannel();<br /><br />  long size = in.size();<br />  MappedByteBuffer buf = in.map(FileChannel.MapMode.READ_ONLY, 0, size);<br /><br />  out.write(buf);<br />  if (in != null) in.close();<br />  if (out != null) out.close();<br />}<br />}谈谈MappedByteBuffer <br /><br />JDK1.4中加入了一个新的包:NIO(java.nio.*).这个库最大的功能就是增加了对异步套接字的支持. <br />其实在其他语言中,包括在最原始的SOCKET实现(BSD SOCKET),这是一个早有的功能:异步回调读/写事件,通过选择器动态选择感兴趣的事件,等等.不过好在SUN终于也开始支持它了.我想这也是开放的好处之一吧(NIO是作为JSR-51项目引入的). <br /><br />这里简单讲一下操作流程: <br /><br />通过把一个套接字通道(SocketChannel)注册到一个选择器(Selector)中,不时调用后者的选择(select)方法就能返回满足的选择键(SelectionKey),键中包含了SOCKET事件信息. <br /><br /><br />异步套接字对服务器程序来说更具吸引力.一般同步SOCKET服务器的实现都是采用线程池来处理客户请求的,基于请求超时时间和并发线程数目的限制,如果并发处理能力能够达到上千就已经是不错了.异步服务器的能力则至少是它的数倍(有人测试一个简单的ECHO服务程序,说可以达到上万个并发,不知道是否真的能达到). <br /><br />SocketChannel的读写是通过一个类叫ByteBuffer(java.nio.ByteBuffer)来操作的.这个类本身的设计是不错的,比直接操作byte[]方便多了. <br /><br />ByteBuffer有两种模式:直接/间接.间接模式最典型(也只有这么一种)的就是HeapByteBuffer,即操作堆内存(byte[]).但是内存毕竟有限,如果我要发送一个1G的文件怎么办?不可能真的去分配1G的内存.这时就必须使用"直接"模式,即MappedByteBuffer,文件映射. <br /><br />先中断一下,谈谈操作系统的内存管理.一般操作系统的内存分两部分:物理内存;虚拟内存.虚拟内存一般使用的是页面映像文件,即硬盘中的某个(某些)特殊的文件.操作系统负责页面文件内容的读写,这个过程叫"页面中断/切换". <br /><br />MappedByteBuffer也是类似的,你可以把整个文件(不管文件有多大)看成是一个ByteBuffer.这是一个很好的设计,除了一点,令人头疼的一点. <br /><br /><br /><br /><br />MappedByteBuffer只能通过调用FileChannel的map()取得,再没有其他方式.但是令人奇怪的是,SUN提供了map()却没有提供unmap().这样会导致什么后果呢? <br /><br />举个例子,文件test.tmp是一个临时构建的文件,在业务处理(通过SocketChannel发送)完之后将不再有效.一般的做法都是这样的: <br /><br />(1)File file = new File("test.tmp"); <br />FileInputStream in = new FileInputStream(file); <br />FileChannel ch = in.getChannel(); <br />MappedByteBuffer buf = ch.map(FileChannel.MapMode.READ_ONLY, 0, file.length()); <br /><br /><br />(2)SocketChannel sch = 已经构造好了; <br />while (buf.hasRemaining()) <br />sch.write(buf); <br /><br />(3)ch.close(); <br />in.close(); <br />file.delete(); <br /><br />上面的操作都会正常的完成,除了最后一步:文件无法删除!即使你通过资源管理器直接强制删除也不行,说"文件正在使用". <br /><br /><br />为什么会出现这种情况? <br />说"文件正在使用",说明文件句柄没有清零,还有在使用它的地方---就是被MappedByteBuffer占用了!尽管FileChannel,FileInputStream都已经关闭了,但是在map里还打开着一个文件句柄.但是在外部看不见也无法操作它.那么这个句柄在什么时候才会正常地关闭呢?根据JAVADOC的说明,是在垃圾收集的时候.而众所周知垃圾收集是程序根本无法控制的. <br /><br /><br />既然MappedByteBuffer是从FileChannel中map()出来的,为什么它又不提供unmap()呢?SUN自己也没有讲清楚为什么.O'Reilly的&lt;&lt;Java NIO>>中说是因为"安全"的原因,但是到底unmap()会怎么不安全,作者也没有讲清楚. <br /><br /><br />在SUN的BUG库中,这个问题在02年就有人提交了BUG报告,但是SUN自己不认为是BUG,而只是一个RFE(Request For Enhancement),有待改进. <br />好在网上牛人多.在BUG报告(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038)中,有网友提出了一个解决的办法(具体参看上面的URL),可行,至少我在WINDOWS2000下测试是可以的.唯一的不足是并不是每次都能马上生效(文件彻底被删除),有的时候要延迟一会再试. <br /><br /><br />再抱怨两句.对于网友们的BUG报告,SUN似乎不怎么重视.粗看一下上面的BUG报告,会发现居然上世纪90年代的报告还赫然在列.有兴趣的朋友不妨仔细研究研究. <br /><br /><br />还有一点忘了说了.ByteBuffer是无法派生的.因为这个抽象类中定义了几个包抽象方法,即实现类只能位于java.nio包中.本来自己实现MappedByteBuffer也不难,只是效率比SUN实现的肯定要低一些.毕竟后者是可以直接与操作系统打交道的.而要是自己实现的化,只能通过一个中间的堆缓冲区进行过渡. <br /><br /><br />我不知道为什么SUN不提供ByteBuffer的派生.毕竟这是一个很实用的类,如果允许派生,那么我就可以操作的就不仅仅限于堆内存和文件了,我可以扩展到任何存储设备.复制内容到剪贴板代码:<br /><br />public boolean copyTo(String strSourceFileName, String strDestDir) {<br />File fileSource = new File(strSourceFileName);<br />File fileDest = new File(strDestDir);<br /><br /><br />// 如果源文件不存或源文件是文件夹<br />if (!fileSource.exists() || !fileSource.isFile()) {<br />System.out.println("错误: FileOperator.java copyTo函数,\n原因: 源文件["<br />+ strSourceFileName + "],不存在或是文件夹!");<br />return false;<br />}<br /><br /><br />// 如果目标文件夹不存在<br />if (!fileDest.isDirectory() || !fileDest.exists()) {<br />if (!fileDest.mkdirs()) {<br />System.out.println("错误: FileOperator.java copyTo函数,\n原因:目录文件夹不存，在创建目标文件夹时失败!");<br />return false;<br />}<br />}<br /><br />try {<br />String strAbsFilename = strDestDir + File.separator + fileSource.getName();<br /><br />FileInputStream fileInput = new FileInputStream(strSourceFileName);<br />FileOutputStream fileOutput = new FileOutputStream(strAbsFilename);<br /><br />int i = 0;<br />int count = -1;<br /><br />long nWriteSize = 0;<br />long nFileSize = fileSource.length();<br /><br />byte[] data = new byte[BUFFER];<br /><br />while (-1 != (count = fileInput.read(data, 0, BUFFER))) {<br />fileOutput.write(data, 0, count);<br />nWriteSize += count;<br />long size = (nWriteSize * 100) / nFileSize;<br />long t = nWriteSize;<br />String msg = null;<br />if (size &lt;= 100 && size >= 0) {<br />msg = "\r拷贝文件进度: " + size + "% \t" + "\t 已拷贝: " + t;<br />} else if (size > 100) {<br />msg = "\r拷贝文件进度: " + 100 + "% \t" + "\t 已拷贝: " + t;<br />}<br />}<br /><br />fileInput.close();<br />fileOutput.close();<br /><br />System.out.println("\n拷贝文件成功!");<br />return true;<br /><br />} catch (Exception e) {<br />System.out.println("异常信息：[");<br />e.printStackTrace();<br />return false;<br />}<br />}<br />http://www.loohost.com/redirect.php?tid=1967&goto=lastpost
          <br/>
          <span style="color:red;">
            <a href="http://hitqiang.javaeye.com/blog/197583#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 27 May 2008 19:29:48 +0800</pubDate>
        <link>http://hitqiang.javaeye.com/blog/197583</link>
        <guid>http://hitqiang.javaeye.com/blog/197583</guid>
      </item>
      <item>
        <title>jar文件使用详解</title>
        <author>hitqiang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hitqiang.javaeye.com">hitqiang</a>&nbsp;
          链接：<a href="http://hitqiang.javaeye.com/blog/193907" style="color:red;">http://hitqiang.javaeye.com/blog/193907</a>&nbsp;
          发表时间: 2008年05月17日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          式被广泛使用，因此易与使用，有很多中工具可以操作这种格式的文件。也正是因为这个原因，jar文件本身并不能表达所包含应用程序的标签信息。<br /><br />　　Manifest 因此得以出现<br /><br />　　为了要提供存档的标签信息，jar 文件指定了一个特定目录来存放标签信息：META-INF 目录，其中我们来关注该目录中的MANIFEST.MF文件，他就是JAR的manifest文件，他包含了JAR文件的内容描述，并在运行时向JVM提供应用程序的信息，大多数JAR文件含有一个默认生成的manifest 文件,执行JAR命令或使用zip工具，都可以产生它<br /><br />　　如果是由jar命令产生的 manifest 文件，形如:<br /><br />　　Manifest-Version: 1.0<br /><br />　　Created-By:1.4.0-beta<br /><br />　　(Sun Microsystems Inc.)<br /><br />　　这些信息没甚么用,仅仅告诉我们使用的是1.0的manifest文件,第一行定义manifest的格式，第二行说明使用 SUN 的JDK1.4的jar工具生成该文件，如果manifest文件是由其他 （如ant） 创建的，那将会出现 “Created-By: Ant 1.2” 之类的内容，如果你是自己创建manifest文件，你可以加入自己的一些相关信息.<br /><br />　　基础格式<br /><br />　　manifest 文件的格式 是很简单的，每一行都是 名－值 对应的 “属性名:[空格]属性值” ，属性名后接着是 ":" ，然后是一空格，再是属性值，每行最多72个字符，如果需要增加，你可以在下一行续行，续行以空格开头，以空格开头的行都会被视为前一行的续行。<br /><br />　　所有在开头的属性都是全局的，你也可以定义特定class 或package的属性，稍后将介绍这种<br /><br />manifest文件作用：<br /><br />　　1、把manifest文件插入JAR文件，用来描述jar文件<br /><br />　　使用 m 选项，把指定文件名的manifest文件 传入，例如<br /><br />　　jar cvfm myapplication.jar myapplication.mf -C classdir<br /><br />　　如果你使用ant来创建时，在ant 的build.xml 加入如下条目<br /><br />　　&lt;target name="jar"><br /><br />　　&lt;jar jarfile ="myapplication.jar"<br /><br />　　manifest="myapplication.mf"><br /><br />　　&lt;fileset dir="classdir"<br /><br />　　includes="**/*.class"/><br /><br />　　&lt;/jar><br /><br />　　&lt;/target><br /><br />　　运行Java程序<br /><br />　　现在我们来体验一下manifest文件的作用，如果现在我们有一个Java 应用程序打包在myapplication.jar中， main class为 com.example.myapp.MyAppMain ，那么我们可以用以下的命令来运行<br /><br />　　java -classpath myapplication.jar com.example.myapp.MyAppMain<br /><br />　　这显然太麻烦了，现在我们来创建自己的manifest文件，如下：<br /><br />　　Manifest-Version: 1.0<br /><br />　　Created-By: JDJ example<br /><br />　　Main-Class: com.example.myapp.MyAppMain<br /><br />　　这样我们就可以使用如下的命令来运行程序了：（明显简单多了，也不会造成无谓的拼写错误）<br /><br />　　java -jar myapplication.jar<br /><br />管理JAR的依赖资源<br /><br />　　很少Java应用会仅仅只有一个jar文件，一般还需要 其他类库。比如我的应用程序用到了Sun 的 Javamail classes ，在classpath中我需要包含activation.jar 和 mail.jar,这样在运行程序时,相比上面的例子,我们要增加一些:<br /><br />　　java -classpath mail.jar:activation.jar -jar myapplication.jar<br /><br />　　在不同的操作系统中,jar包间的分隔符也不一样，在UNIX用“：”，在window中使用 “；”，这样也不方便<br /><br />　　同样，我们改写我们的manifest文件，如下<br /><br />　　Manifest-Version: 1.0<br /><br />　　Created-By: JDJ example<br /><br />　　Main-Class: com.example.myapp.MyAppMain<br /><br />　　Class-Path: mail.jar activation.jar<br /><br />　　（加入了Class-Path: mail.jar activation.jar，用空格分隔两个jar包）<br /><br />　　这样我们仍然可以使用和上例中相同的命令来执行该程序：<br /><br />　　java -jar myapplication.jar<br /><br />　　Class-Path属性中包含了用空格分隔的jar文件，在这些jar文件名中要对特定的字符使用逃逸符，比如空格，要表示成"%20"，在路径的表示中，都采用“/”来分隔目录，无论是在什么操作系统中，(即使在window中)，而且这里用的是相对路径（相对于本身的JAR文件）：<br /><br />　　Manifest-Version: 1.0<br /><br />　　Created-By: JDJ example<br /><br />　　Main-Class: com.example.myapp.MyAppMain<br /><br />　　Class-Path: ext/mail.jar ext/activation.jar<br /><br />       上面的方法打包后生成的jar文件中不包括ext/mail.jar ext/activation.jar两个包，要运行生成的包，必须同时存在Class-Path中的包，且相对位置不能改变。这样运行一个包，要附带几个包，很不方便，那有什么办法让这些包都集成一个包中呢？当然可以。这里介绍一种方法：将所引用到的包全部解压到同一个目录下（当前目录切换到此目录），自己的包也在该目录下，manifest.mf文件(此时无须再包含Class-Path属性)和图片文件也放在此目录下（当前目录不包含不相关文件），然后将当前目录下所有文件打包，命令如下：<br /><br />当前目录>jar cvfm my.jar manifest.mf -C ./ *<br /><br />用法：jar {ctxu}[vfm0Mi] [jar-文件] [manifest-文件] [-C 目录] 文件名<br /><br />"-C ./"中"./"代表当前目录，其与后面的"*"之间有空格，"*"代表所有文件，这样就将引用的包加进来了，可以双击运行。虽然图片文件也打到包中了，但好象没有用，当存在于包外时有用。<br /><br />另外,你也可以加入一些其他必要的文件:jar cvmf MANIFEST.MF a.jar *.class *.png   这样你就可以把程序中需要的一些图片文件打包到jar包中了。<br /><br />但是把图片打包到jar中可能会存在这样的情况，就是不打包时还可以加载图片，打了包之后程序加载不上图片，那么你的程序需要稍微改一下，在新建图标对象的时候：<br /><br />Icon pica=new ImageIcon(a.class.getResource("pica.png"));<br />Icon picb=new ImageIcon(a.class.getResource("picb.png"));<br /><br />其中a.class是你的主类名，这样就可以加载上图片了。<br /><br /><br /><br /><br />　　Implementation-Title<br /><br />　　Implementation-Version<br /><br />　　Implementation-Vendor<br /><br />　　当要提供一个类库或编程接口时，描述信息显得是很重要，见以下范例：<br /><br />　　Manifest-Version: 1.0<br /><br />　　Created-By: JDJ example<br /><br />　　Class-Path: mail.jar activation.jar<br /><br />　　Name: com/example/myapp/<br /><br />　　Specification-Title: MyApp<br /><br />　　Specification-Version: 2.4<br /><br />　　Specification-Vendor: example.com<br /><br />　　Implementation-Title: com.example.myapp<br /><br />　　Implementation-Version: 2002-03-05-A<br /><br />　　Implementation-Vendor: example.com<br /><br />　　Package Version 查询<br /><br />　　在manifest文件中加入package描述后，就可以使用Java提供的java.lang.Package class进行Package 的信息查询，这里列举3个最基本的获取package object的方法<br /><br />　　1.Package.getPackages():返回系统中所有定义的package列表<br /><br />　　2.Package.getPackage(String packagename):按名返回package<br /><br />　　3.Class.getPackage():返回给定class所在的package<br /><br />　　使用者这方法就可以动态的获取package信息.<br /><br />　　需要注意的是如果给定的package中没有class被加载,则也无法获得package 对象<br /><br />　　Manifest 技巧<br /><br />　　总是以Manifest-Version属性开头<br /><br />　　每行最长72个字符，如果超过的化，采用续行<br /><br />　　确认每行都以回车结束，否则改行将会被忽略<br /><br />　　如果Class-Path 中的存在路径，使用"/"分隔目录，与平台无关<br /><br />　　使用空行分隔主属性和package属性<br /><br />　　使用"/"而不是"."来分隔package 和class ,比如 com/example/myapp/<br /><br />　　class 要以.class结尾，package 要以 / 结尾 <br /><br /><br /><br /><br />　　Multiple Main Classes（多主类）<br /><br />　　还有一种Multiple Main Classes情况，如果你的应用程序可能有命令行版本 和GUI版本，或者一些不同的应用却共享很多相同的代码，这时你可能有多个Main Class，我们建议你采取这样的策略：把共享的类打成lib包，然后把不同的应用打成不同的包，分别标志主类：如下<br /><br />　　Manifest for myapplicationlib.jar:<br /><br />　　Manifest-Version: 1.0<br /><br />　　Created-By: JDJ example<br /><br />　　Class-Path: mail.jar activation.jar<br /><br />　　Manifest for myappconsole.jar:<br /><br />　　Manifest-Version: 1.0<br /><br />　　Created-By: JDJ example<br /><br />　　Class-Path: myapplicationlib.jar<br /><br />　　Main-Class: com.example.myapp.MyAppMain<br /><br />　　Manifest for myappadmin.jar:<br /><br />　　Manifest-Version: 1.0<br /><br />　　Created-By: JDJ example<br /><br />　　Class-Path: myapplicationlib.jar<br /><br />　　Main-Class: com.example.myapp.MyAdminTool<br /><br />　　在myappconsole.jar 和 myappadmin.jar的manifest文件中分别注明各自的 Main Class<br /><br />　　2、用来描述包 Package Versioning<br /><br />　　完成发布后，如果使用者想了解 ，哪些代码是谁的？目前是什么版本？使用什么版本的类库？解决的方法很多 ，manifest提供了一个较好的方法，你可以在manifest文件中描述每一个包的信息。<br /><br />　　Java 秉承了实现说明与描述分离的原则，package 的描述 定义了package 是什么，实现说明 定义了谁提供了描述的实现，描述和实现包含 名、版本号和提供者。要得到这些信息，可以查看JVM的系统属性（使用 java.lang.System.getProperty() ）<br /><br />　　在manifest文件中，我可以为每个package定义描述和实现版本，声明名字，并加入描述属性和实现属性，这些属性是<br /><br />　　Specification-Title<br /><br />　　Specification-Version<br /><br />　　Specification-Vendor<br />引用自：http://hi.baidu.com/suofang/blog/item/271e8682f943c291f603a629.html
          <br/>
          <span style="color:red;">
            <a href="http://hitqiang.javaeye.com/blog/193907#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 17 May 2008 16:54:38 +0800</pubDate>
        <link>http://hitqiang.javaeye.com/blog/193907</link>
        <guid>http://hitqiang.javaeye.com/blog/193907</guid>
      </item>
      <item>
        <title>java jar文件分析</title>
        <author>hitqiang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hitqiang.javaeye.com">hitqiang</a>&nbsp;
          链接：<a href="http://hitqiang.javaeye.com/blog/192311" style="color:red;">http://hitqiang.javaeye.com/blog/192311</a>&nbsp;
          发表时间: 2008年05月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          常常在网上看到有人询问：如何把 java 程序编译成 .exe 文件。通常回答只有两种，一种是制作一个可执行的 JAR 文件包，然后就可以像.chm 文档一样双击运行了；而另一种是使用 JET 来进行 编译。但是 JET 是要用钱买的，而且据说 JET 也不是能把所有的 Java 程序都编译成执行文件，性能也要打些折扣。所以，使用制作可执行 JAR 文件包的方法就是最佳选择了，何况它还能保持 Java 的跨平台特性。 <br /><br />下面就来看看什么是 JAR 文件包吧： <br /><br />1. JAR 文件包 <br /><br />JAR 文件就是 Java Archive File，顾名思意，它的应用是与 Java 息息相关的，是 Java 的一种文档格式。JAR 文件非常类似 ZIP 文件——准确的说，它就是 ZIP 文件，所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中，包含了一个 META-INF/MANIFEST.MF 文件，这个文件是在生成 JAR 文件的时候自动创建的。举个例子，如果我们具有如下目录结构的一些文件： <br /><br />　　== <br /><br />　　`-- test <br /><br />　　　 `-- Test.class <br /><br />把它压缩成 ZIP 文件 test.zip，则这个 ZIP 文件的内部目录结构为： <br /><br />　　test.zip <br /><br />　　`-- test <br /><br />　　　 `-- Test.class <br /><br />如果我们使用 JDK 的 jar 命令把它打成 JAR 文件包 test.jar，则这个 JAR 文件的内部目录结构为： <br /><br />　　test.jar <br /><br />　　|-- META-INF <br /><br />　　|　 `-- MANIFEST.MF <br /><br />　　`-- test <br /><br />　　　　`--Test.class <br /><br />2. 创建可执行的 JAR 文件包 <br /><br />制作一个可执行的 JAR 文件包来发布你的程序是 JAR 文件包最典型的用法。 <br /><br />Java 程序是由若干个 .class 文件组成的。这些 .class 文件必须根据它们所属的包不同而分级分目录存放；运行前需要把所有用到的包的根目录指定给 CLASSPATH 环境变量或者 java 命令的 -cp 参数；运行时还要到控制台下去使用 java 命令来运行，如果需要直接双击运行必须写 Windows 的批处理文件 (.bat) 或者 Linux 的 Shell 程序。因此，许多人说，Java 是一种方便开发者苦了用户的程序设计语言。 <br /><br />其实不然，如果开发者能够制作一个可执行的 JAR 文件包交给用户，那么用户使用起来就方便了。在 Windows 下安装 JRE (Java Runtime Environment) 的时候，安装文件会将 .jar 文件映射给 javaw.exe 打开。那么，对于一个可执行的 JAR 文件包，用户只需要双击它就可以运行程序了，和阅读 .chm 文档一样方便 (.chm 文档默认是由 hh.exe 打开的)。那么，现在的关键，就是如何来创建这个可执行的 JAR 文件包。 <br /><br />创建可执行的 JAR 文件包，需要使用带 cvfm 参数的 jar 命令，同样以上述 test 目录为例，命令如下： <br /><br />jar cvfm test.jar manifest.mf test <br /><br />这里 test.jar 和 manifest.mf 两个文件，分别是对应的参数 f 和 m，其重头戏在 manifest.mf。因为要创建可执行的 JAR 文件包，光靠指定一个 manifest.mf 文件是不够的，因为 MANIFEST 是 JAR 文件包的特征，可执行的 JAR 文件包和不可执行的 JAR 文件包都包含 MANIFEST。关键在于可执行 JAR 文件包的 MANIFEST，其内容包含了 Main-Class 一项。这在 MANIFEST 中书写格式如下： <br /><br />Main-Class: 可执行主类全名(包含包名) <br /><br />例如，假设上例中的 Test.class 是属于 test 包的，而且是可执行的类 (定义了 public static void main(String[]) 方法)，那么这个 manifest.mf 可以编辑如下： <br /><br />Main-Class: test.Test &lt;回车>; <br /><br />这个 manifest.mf 可以放在任何位置，也可以是其它的文件名，只需要有 Main-Class: test.Test 一行，且该行以一个回车符结束即可。创建了 manifest.mf 文件之后，我们的目录结构变为： <br /><br />　　== <br /><br />　　|-- test <br /><br />　　|　 `-- Test.class <br /><br />　　`-- manifest.mf <br /><br />这时候，需要到 test 目录的上级目录中去使用 jar 命令来创建 JAR 文件包。也就是在目录树中使用“==”表示的那个目录中，使用如下命令： <br /><br />jar cvfm test.jar manifest.mf test <br /><br />之后在“==”目录中创建了 test.jar，这个 test.jar 就是执行的 JAR 文件包。运行时只需要使用 java -jar test.jar 命令即可。 <br /><br />需要注意的是，创建的 JAR 文件包中需要包含完整的、与 Java 程序的包结构对应的目录结构，就像上例一样。而 Main-Class 指定的类，也必须是完整的、包含包路径的类名，如上例的 test.Test；而且在没有打成 JAR 文件包之前可以使用 java &lt;类名>; 来运行这个类，即在上例中 java test.Test 是可以正确运行的 (当然要在 CLASSPATH 正确的情况下)。 <br /><br />3. jar 命令详解 <br /><br />jar 是随 JDK 安装的，在 JDK 安装目录下的 bin 目录中，Windows 下文件名为 jar.exe，Linux 下文件名为 jar。它的运行需要用到 JDK 安装目录下 lib 目录中的 tools.jar 文件。不过我们除了安装 JDK 什么也不需要做，因为 SUN 已经帮我们做好了。我们甚至不需要将 tools.jar 放到 CLASSPATH 中。 <br /><br />使用不带任何的 jar 命令我们可以看到 jar 命令的用法如下： <br /><br />jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ... <br /><br />其中 {ctxu} 是 jar 命令的子命令，每次 jar 命令只能包含 ctxu 中的一个，它们分别表示： <br /><br />-c　创建新的 JAR 文件包 <br /><br />-t　列出 JAR 文件包的内容列表 <br /><br />-x　展开 JAR 文件包的指定文件或者所有文件 <br /><br />-u　更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中) <br /><br />[vfm0M] 中的选项可以任选，也可以不选，它们是 jar 命令的选项参数 <br /><br />-v　生成详细报告并打印到标准输出 <br /><br />-f　指定 JAR 文件名，通常这个参数是必须的 <br /><br />-m　指定需要包含的 MANIFEST 清单文件 <br /><br />-0　只存储，不压缩，这样产生的 JAR 文件包会比不用该参数产生的体积大，但速度更快 <br /><br />-M　不产生所有项的清单（MANIFEST〕文件，此参数会忽略 -m 参数 <br /><br />[jar-文件] 即需要生成、查看、更新或者解开的 JAR 文件包，它是 -f 参数的附属参数 <br /><br />[manifest-文件] 即 MANIFEST 清单文件，它是 -m 参数的附属参数 <br /><br />[-C 目录] 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令，它只能在创建和更新 JAR 文件包的时候可用。　　 <br /><br />文件名 ... 指定一个文件/目录列表，这些文件/目录就是要添加到 JAR 文件包中的文件/目录。如果指定了目录，那么 jar 命令打包的时候会自动把该目录中的所有文件和子目录打入包中。 <br /><br />下面举一些例子来说明 jar 命令的用法： <br /><br />1) jar cf test.jar test <br /><br />该命令没有执行过程的显示，执行结果是在当前目录生成了 test.jar 文件。如果当前目录已经存在 test.jar，那么该文件将被覆盖。 <br /><br />2) jar cvf test.jar test <br /><br />该命令与上例中的结果相同，但是由于 v 参数的作用，显示出了打包过程，如下： <br /><br />标明清单(manifest) <br /><br />增加：test/(读入= 0) (写出= 0)(存储了 0%) <br /><br />增加：test/Test.class(读入= 7) (写出= 6)(压缩了 14%) <br /><br />3) jar cvfM test.jar test <br /><br />该命令与 2) 结果类似，但在生成的 test.jar 中没有包含 META-INF/MANIFEST 文件，打包过程的信息也略有差别： <br /><br />增加：test/(读入= 0) (写出= 0)(存储了 0%) <br /><br />增加：test/Test.class(读入= 7) (写出= 6)(压缩了 14%) <br /><br />4) jar cvfm test.jar manifest.mf test <br /><br />运行结果与 2) 相似，显示信息也相同，只是生成 JAR 包中的 META-INF/MANIFEST 内容不同，是包含了 manifest.mf 的内容 <br /><br />5) jar tf test.jar <br /><br />在 test.jar 已经存在的情况下，可以查看 test.jar 中的内容，如对于 2) 和 3) 生成的 test.jar 分别应该此命令，结果如下； <br /><br />对于 2) <br /><br />META-INF/ <br /><br />META-INF/MANIFEST.MF <br /><br />test/ <br /><br />test/Test.class <br /><br />对于 3) <br /><br />test/ <br /><br />test/Test.class <br /><br />6) jar tvf test.jar <br /><br />除显示 5) 中显示的内容外，还包括包内文件的详细信息，如： <br /><br />0 Wed Jun 19 15:39:06 GMT 2002 META-INF/ <br /><br />86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF <br /><br />0 Wed Jun 19 15:33:04 GMT 2002 test/ <br /><br />7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class <br /><br />7) jar xf test.jar <br /><br />解开 test.jar 到当前目录，不显示任何信息，对于 2) 生成的 test.jar，解开后的目录结构如下： <br /><br />　　== <br /><br />　　|-- META-INF <br /><br />　　|　 `-- MANIFEST <br /><br />　　`-- test <br /><br />　　　　`--Test.class <br /><br /> jar xvf test.jar <br /><br />运行结果与 7) 相同，对于解压过程有详细信息显示，如： <br /><br />创建：META-INF/ <br /><br />展开：META-INF/MANIFEST.MF <br /><br />创建：test/ <br /><br />展开：test/Test.class <br /><br />9) jar uf test.jar manifest.mf <br /><br />在 test.jar 中添加了文件 manifest.mf，此使用 jar tf 来查看 test.jar 可以发现 test.jar 中比原来多了一个 manifest。这里顺便提一下，如果使用 -m 参数并指定 manifest.mf 文件，那么 manifest.mf 是作为清单文件 MANIFEST 来使用的，它的内容会被添加到 MANIFEST 中；但是，如果作为一般文件添加到 JAR 文件包中，它跟一般文件无异。 <br /><br />10) jar uvf test.jar manifest.mf <br /><br />与 9) 结果相同，同时有详细信息显示，如： <br /><br />增加：manifest.mf(读入= 17) (写出= 19)(压缩了 -11%) <br /><br />4. 关于 JAR 文件包的一些技巧 <br /><br />1) 使用 unzip 来解压 JAR 文件 <br /><br />在介绍 JAR 文件的时候就已经说过了，JAR 文件实际上就是 ZIP 文件，所以可以使用常见的一些解压 ZIP 文件的工具来解压 JAR 文件，如 Windows 下的 WinZip、WinRAR 等和 Linux 下的 unzip 等。使用 WinZip 和 WinRAR 等来解压是因为它们解压比较直观，方便。而使用 unzip，则是因为它解压时可以使用 -d 参数指定目标目录。 <br /><br />在解压一个 JAR 文件的时候是不能使用 jar 的 -C 参数来指定解压的目标的，因为 -C 参数只在创建或者更新包的时候可用。那么需要将文件解压到某个指定目录下的时候就需要先将这具 JAR 文件拷贝到目标目录下，再进行解压，比较麻烦。如果使用 unzip，就不需要这么麻烦了，只需要指定一个 -d 参数即可。如： <br /><br />unzip test.jar -d dest/ <br /><br />2) 使用 WinZip 或者 WinRAR 等工具创建 JAR 文件 <br /><br />上面提到 JAR 文件就是包含了 META-INF/MANIFEST 的 ZIP 文件，所以，只需要使用 WinZip、WinRAR 等工具创建所需要 ZIP 压缩包，再往这个 ZIP 压缩包中添加一个包含 MANIFEST 文件的 META-INF 目录即可。对于使用 jar 命令的 -m 参数指定清单文件的情况，只需要将这个 MANIFEST 按需要修改即可。 <br /><br />3) 使用 jar 命令创建 ZIP 文件 <br /><br />有些 Linux 下提供了 unzip 命令，但没有 zip 命令，所以需要可以对 ZIP 文件进行解压，即不能创建 ZIP 文件。如要创建一个 ZIP 文件，使用带 -M 参数的 jar 命令即可，因为 -M 参数表示制作 JAR 包的时候不添加 MANIFEST 清单，那么只需要在指定目标 JAR 文件的地方将 .jar 扩展名改为 .zip 扩展名，创建的就是一个不折不扣的 ZIP 文件了，如将上一节的第 3) 个例子略作改动： <br /><br />jar cvfM test.zip test
          <br/>
          <span style="color:red;">
            <a href="http://hitqiang.javaeye.com/blog/192311#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 12 May 2008 17:47:54 +0800</pubDate>
        <link>http://hitqiang.javaeye.com/blog/192311</link>
        <guid>http://hitqiang.javaeye.com/blog/192311</guid>
      </item>
      <item>
        <title>字符编码原理</title>
        <author>hitqiang</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://hitqiang.javaeye.com">hitqiang</a>&nbsp;
          链接：<a href="http://hitqiang.javaeye.com/blog/191892" style="color:red;">http://hitqiang.javaeye.com/blog/191892</a>&nbsp;
          发表时间: 2008年05月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          编码字符集与乱码问题根源之所在 <br />本文介绍了编码字符集的概念以及Java与编码字符集之间的关系，文章的内容来自于本人工作过程中的经验积累以及网络中的相关文章介绍，如果文章中有任何纰漏欢迎读者指正，让我们共同讨论学习J<br />1．      字符<br />字符是抽象的最小文本单位。它没有固定的形状（可能是一个字形），而且没有值。“A”是一个字符，“€”（德国、法国和许多其他欧洲国家通用货币的标志）也是一个字符。“中”“国”这是两个汉字字符。字符仅仅代表一个符号，没有任何实际值的意义。<br /><br /><br />２．      字符集<br />字符集是字符的集合。例如，汉字字符是中国人最先发明的字符，在中文、日文、韩文和越南文的书写中使用。这也说明了字符和字符集之间的关系，字符组成字符集。<br /><br /><br />３．      编码字符集<br />编码字符集是一个字符集（有时候也被简称位字符集），它为每一个字符分配一个唯一数字。最早的编码是iso8859-1，和ascii编码相似。但为了方便表示各种各样的语言，逐渐出现了很多标准编码。<br /> <br />iso8859-1:属于单字节编码字符集，最多能表示的字符范围是0-255，应用于英文系列，除了iso8859-1以外还有其他iso8859系列的编码，这些编码都是为了满足欧洲国家语言字符的需要而设计的。<br /> <br />GB2312/GBK/ GB18030:前面提到的iso8859-1最多只能表示256个字符，这对于汉字来说实在是有些抱歉，所以就有了现在要介绍的汉字国标码，专门用来表示汉字，是双字节编码字符集，而英文字母和iso8859-1一致（兼容iso8859-1编码）。其中GBK编码能够用来同时表示繁体字和简体字，而GB2312只能表示简体字，GBK是兼容GB2312编码的。而GB18030-2000则是一个更复杂的字符集，采用变长字节的编码方式，能够支持更多的字符。需要注意的是中国政府要求所有在中国出售的软件必须支持GB18030。<br /> <br />Unicode：这是最统一的编码字符集，可以用来表示所有语言的字符，不兼容任何前面提到的编码字符集。Unicode 标准始终使用十六进制数字，而且在书写时在前面加上前缀“U+”，所以“A”的编码书写为“U+0041”。注意：在JAVA语言中书写时应该使用转义符‘\u’表示，如 char charA = ‘\u0041’; 这种表示方法等与 char charA = ‘A’; 。<br /> <br />从ASCII（英文） ==> 西欧文字 ==> 东欧字符集（俄文，希腊语等） ==> 东亚字符集（GB2312 BIG5 SJIS等）==> 扩展字符集GBK GB18030这个发展过程基本上也反映了字符集标准的发展过程，但这么随着时间的推移，尤其是互联网让跨语言的信息的交互变得越来越多的时候，太多多针对本地语言的编码标准的出现导致一个应用程序的国际化变得成本非常高。尤其是你要编写一个同时包含法文和简体中文的文档，这时候一般都会想到要是用一个通用的字符集能够显示所有语言的所有文字就好了，而且这样做应用也能够比较方便的国际化，为了达到这个目标，即使应用牺牲一些空间和程序效率也是非常值得的。UNICODE就是这样一个通用的解决方案。<br /> <br />４．      Unicode编码字符集<br />Unicode 因为必须将中、韩、日、英、法、阿拉伯……等许多国家所使用的文字都纳入，目前已经包含了六万多个字符，所以 Unicode 使用了16个位来为字符编码。因为 Unicode 使用了 16 位编码，所以每个字符都用 16 位来储存或传输是很自然的事，这种储存或传输的格式称为UTF-16（一种Unicode的字符编码方案，在这里所说的UTF-16并不涉及增补字符的表示，本文将会在稍后介绍）。但是如果你使用到的字符都是西方字符，那么你一定不会想用 UTF-16 的格式，因为体积比8位的iso8859-1多了一倍，如此一来就必须考虑程序运行时各种字符在内存中所占空间的性能问题，这便引入了字符编码方案的概念：<br /><br /><br />字符编码方案是从一个或多个编码字符集到一个或多个固定宽度代码单元序列的映射。<br /><br /><br />最常用的代码单元是字节，所以可以简单的认为字符编码方案是为了告诉计算机如何将编码字符集（如Unicode）映射到计算机可以识别的数据格式中，如字节。这种编码方案往往能够为他所对应的字符集在计算机处理时提供更为优化的空间以及性能上的解决方案。Unicode编码字符集有三种字符编码方案，下面将逐一介绍：<br /> <br />l         UTF-32* 即将每一个Unicode编码表示为相同值的32位整数。很明显，它是内部处理最方便的表达方式，但是，如果作为一般字符串表达方式，则要消耗更多的内存。显而易见，对于英文字母的表示将需要多个0字节，仅仅因为我们需要4个字节32位来表示一个Unicode字符。<br /> <br />l         UTF-16 使用一个或两个未分配的 16位代码单元的序列对Unicode编码进行编码。值U+0000至U+FFFF 编码为一个相同值的16位单元。增补字符*编码为两个代码单元，第一个单元来自于高代理范围（U+D800 至 U+DBFF），第二个单元来自于低代理范围（U+DC00 至U+DFFF）。这在概念上可能看起来类似于多字节编码，但是其中有一个重要区别：值 U+D800 至 U+DFFF 保留用于 UTF-16；没有这些值分配字符作为代码点。这意味着，对于一个字符串中的每个单独的代码单元，软件可以识别是否该代码单元表示某个单单元字符，或者是否该代码单元是某个双单元字符的第一个或第二单元。这相当于某些传统的多字节字符编码来说是一个显著的改进，在传统的多字节字符编码中，字节值0x41 既可能表示字母“A”，也可能是一个双字节字符的第二个字节。<br /> <br />l         UTF-8 使用一至四个字节的序列对编码Unicode进行编码。U+0000至U+007F使用一个字节编码，U+0080至U+07FF 使用两个字节，U+0800 至U+FFFF使用三个字节，而U+10000至U+10FFFF使用四个字节。UTF-8设计原理为：字节值0x00至0x7F始终表示代码点U+0000至U+007F（Basic Latin 字符子集，它对应 ASCII 字符集）。这些字节值永远不会表示其他Unicode编码字符，这一特性使 UTF-8 可以很方便地在软件中将特殊的含义赋予某些 ASCII 字符。UTF-8 的格式在编码英文时，只需要8位，但是中文则是24位，其他更加偏僻的字符才又可能是32位，这也是UTF-8最大的编码特点，可以最高效率的利用计算机空间，因为在计算机处理的时候大多数情况下还是只使用英文进行运算和处理，这也是为什么还需要UTF-8的主要原因，因为毕竟互联网70％以上的信息仍然是英文。如果连英文都用2个字节存取(UCS-2)，空间浪费不就太多了？<br /> <br /> <br />*  UTF­-32 表示Unicode Transformation Form 32-bit form，UTF-16，UTF-8依此类推。<br />*  Unicode 最初设计是作为一种固定宽度的 16 位字符编码。在 Java 编程语言中，基本数据类型 char 初衷是通过提供一种简单的、能够包含任何字符的数据类型来充分利用这种设计的优点。不过，现在看来，16 位编码的所有65,536个字符并不能完全表示全世界所有正在使用或曾经使用的字符。于是，Unicode 标准已扩展到包含多达 1,112,064个字符。那些超出原来的16位限制的字符被称作增补字符。<br /><br /><br />５．      Java与编码字符集<br />从上面的介绍我们知道了Unicode编码字符集可以用来表示世界上所有的语言文字。Java内部处理字符使用的字序方式是Unicode，这是一种通行全球的编码方式，他使Java语言能够描述世界上所有的文字。在Java程序中对各种字符在内存中处理是使用Unicode的UTF-8编码方式，这也是因为UTF-8的特点所决定的，Class File（也就是bytecode）中有一栏位叫做常数区（Constant Pool），一律使用UTF-8为子元编码。<br /> <br />这看起来一切正常，Java可以处理世界上所有的字符，一切都是按照秩序在运行，但是，从前面的讨论我们知道，世界上并不是仅仅只有Unicode编码字符集，同时存在的还有iso8859-1、GBK等编码字符集，就是在Unicode中也同样存在着UTF-8，UTF-16，UTF-32等多种编码，如果传入的字节编码采用的是GB18030，而采用的解码方式为UTF-8那会有什么后果呢，看看下面的代码片段：<br /> <br />public static final String TEST_RESOURCE = "你好";<br /> <br />    public static void testEncoding() {<br />        try {<br />            byte[] bytes = TEST_RESOURCE.getBytes("GB18030");<br />            String result = new String(bytes, "UTF-8");<br />            System.out.println("Receive value: [" + result + "].");<br />        } catch (UnsupportedEncodingException e) {<br />            // TODO Auto-generated catch block<br />            e.printStackTrace();<br />        }<br />    }<br />执行以上的代码片段，在我的机器（Win XP中文版）上面得到的结果是：<br />       Receive value: [���].<br />明白了吧，这就是久负盛名的乱码问题的根源，目前在市面上存在有多种编码字符集，以及编码字符集的编码方案，所以虽然在Java中内部是以Unicode的UTF-8来处理各种字符的表示以及运算，但是这仅仅是在Java内部而以，如果Java程序需要和外部应用系统进行交互，比如与操作系统，数据库系统之间的交互，那么在这些交互过程中如何处理字符集的编码解码是解决好Java应用程序乱码问题的根源。<br />如果将上面的代码块修改成如下的代码块：<br />    public static void testEncoding() {<br />        try {<br />            byte[] bytes = TEST_RESOURCE.getBytes("GB18030");<br />            String result = new String(bytes, "GB18030");<br />            System.out.println("Receive value: [" + result + "].");<br />        } catch (UnsupportedEncodingException e) {<br />            // TODO Auto-generated catch block<br />            e.printStackTrace();<br />        }<br />    }<br />                     注意红色标注的地方，执行以上的代码块将会受到预期的结果：<br />                            Receive value: [你好].<br />统一字符的编码类型和解码类型，如此一来任何乱码问题都不会再是问题了。在网上可以搜索到N多的关于如何解决J2EE乱码问题的文章，我在这里也就不废话了，我只是想说说Java乱码问题的根源之所在。<br />如果你仔细想想Java的开发过程，原文件编写、javac编译、java执行，这每一步骤都会涉及到编码的转换过程，这个过程总是存在的，只是有的时候用默认的参数进行。<br />我们从javac这个命令来开始我们的分析，编译的时候，如果你不说明源文件编码方式的话，javac 编译器在读进此原始程序文件开始编译之前，会先去询问操作系统档案预设的编码方式为何。以我的操作系统WIN XP 中文版来说，javac 会先询问WIN XP，得知当前的编码是用GB18030的方式编码。然后就可以将源文件由GB18030转成Unicode编码方式，开始进行编译。在这里就会发生一下一些编码问题：<br /> <br />l         如果操作系统的国籍资料设定错误，会造成javac编译器取得的编码信息是错误的，这里也有可能由于系统属性file.encoding设置错误，在我的系统中该属性为GB18030，可以通过代码System.out.println(System.getProperties());输出可能的系统属性。<br /> <br />l         较差劲的编译器可能没有主动询问操作系统的编码方式，而是采用编译器预设的编码方式，当然这种情况对于目前先进的编译器来说已经不存在了，但是这确实是一种可能的原因。<br /> <br />l         源代码是在英文操作系统上书写采用编码iso8859-1，写好以后再将源代码传递给中文操作系统进行编译，这样由于两个操作系统的编码方式不同，也会造成javac执行错误。<br /> <br />明白了吧，这些问题在我们日常的代码编写过程中，往往由于默认的属性都正好能满足我们的需要，即源代码的书写以及编译都采用操作系统默认的编码方式，所以可能很多人到目前为止都没有遇见过诸如此类的问题，但是我们要知道，这些问题确实是存在的。<br />Java编译器在执行过程中给我们提供了可选的encoding参数来告诉编译器该采用何种编码方式将读入的源文件转换成Unicode编码方式，然后再进行后续的编译工作。<br />javac –encoding GB18030 ….<br /> <br />６．      Inside<br />OK，通过前面的介绍希望读者能够对Java以及各种字符编码之间的关系有个简单的了解，下面我在继续总结一下：<br /> <br />l         Javac是以系统默认编码（file.encoding系统属性）读入源文件，然后按Unicode进行编码的。<br /> <br />l         在JAVA运行的时候，JAVA也是采用Unicode编码的，为了高度利用内存空间提高效率对Unicode字符编码采用了UTF-8的方式编码，并且默认输入和输出的都是操作系统的默认编码。<br /> <br />l         也就是说在new String(bytes,encode)中，系统认为输入的是编码为encode的字节流，换句话说，如果按encode来翻译bytes才能得到正确的结果；而在new String(bytes)中采用的就是根据file.encoding系统属性读入的编码方式来进行编码，同样也必须根据系统默认的编码才能得到正确的结果，这个结果最后要在JAVA中保存，它还是要从这个encode转换成Unicode，因为在JAVA中各种字符均是以Unicode的形式来处理的。<br /> <br />l         也就是说有bytes-->encode字符-->Unicode字符的转换；而在String.getBytes([encode])中，系统要做一个Unicode字符-->encode字符-->bytes的转换。<br /> <br />希望通过本文的介绍能够使你对字符集编码的概念以及Java与字符集编码之间的关系有个清楚的认识，我相信，如果搞清楚了他们之间的关系，那个在Java world鼎鼎有名的乱码问题将一去不再复返了J<br /><br />乱码原因<br />　　java内核是unicode的。但Java总是根据操作系统的默认编码字符集来决定字符串的初始编码，而且Java系统的输入和输出的都是采取操作系统的默认编码，而数据库、文件、网络传输中的字节流……采用的编码更是各不相同。所以不可避免的就会出现烦人的乱码问题了。 <br /><br />解决办法<br />　　1、GB2312、GBK、Unicode（UTF8）？<br />　　从字符集的大小比较 GB2312 &lt; GBK &lt; UTF8，很显然，如果我们采用UTF8作为系统编码的话，是不会有错的。而且如果你要考虑国际化的话，UTF8似乎是你唯一的选择<br />　　2、开发和编译代码时指定字符集为UTF-8<br />　　JBuilder和Eclipse都可以在项目属性中设置。<br />　　3、使用过滤器<br />　　编写过滤器<br />package com.javer.test.language;<br /><br />import javax.servlet.Filter;<br />import javax.servlet.FilterChain;<br />import javax.servlet.FilterConfig;<br />import javax.servlet.ServletRequest;<br />import javax.servlet.ServletResponse;<br /><br />public class EncodingFilter<br />implements Filter<br />{<br />FilterConfig config;<br /><br />public void init(FilterConfig parm1)<br />throws javax.servlet.ServletException<br />{<br />this.config = parm1;<br />}<br /><br />public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)<br />throws java.io.IOException, javax.servlet.ServletException<br />{<br />if (req.getCharacterEncoding() == null || !req.getCharacterEncoding().equals("UTF-8"))<br />{<br />req.setCharacterEncoding("UTF-8");<br />}<br />chain.doFilter(req, res);<br />}<br /><br />public void destroy()<br />{<br />this.config = null;<br />}<br />} <br />　　在web.xml文件中配置该过滤器<br />&lt;?xml version="1.0" encoding="UTF-8"?><br />&lt;!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"><br />&lt;web-app><br />&lt;display-name>javer‘s project&lt;/display-name><br />&lt;filter><br />&lt;filter-name>EncodingFilter&lt;/filter-name><br />&lt;display-name>EncodingFilter&lt;/display-name><br />&lt;description>对编码进行转换&lt;/description><br />&lt;filter-class>com.javer.test.language.EncodingFilter&lt;/filter-class><br />&lt;/filter><br />&lt;filter-mapping><br />&lt;filter-name>EncodingFilter&lt;/filter-name><br />&lt;url-pattern>/*&lt;/url-pattern><br />&lt;/filter-mapping><br />&lt;welcome-file-list><br />&lt;welcome-file>index.jsp&lt;/welcome-file><br />&lt;/welcome-file-list><br />&lt;/web-app> <br />　　4、在JSP 中进行声明<br />　　在JSP头部声明&lt;%@ page contentType="text/html;charset= UTF-8" %><br />　　在Jsp的html代码中声明&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"><br />　　5、数据库管理<br />　　一般数据库都可以通过管理设置设定UTF-8 <br />　　也可以通过jdbc链接时指定编码参数，如：mysql：jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8<br />　　6、其它<br />　　其他所有和外界交互时能够设定编码时就设定UTF-8，例如读取文件，操作XML等。总之，记住一个原则：在所有系统的出入口处都用UTF8编码进行“翻译”！<br />  <br />引用自：http://beauty9235.javaeye.com/blog/161659
          <br/>
          <span style="color:red;">
            <a href="http://hitqiang.javaeye.com/blog/191892#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 11 May 2008 10:18:57 +0800</pubDate>
        <link>http://hitqiang.javaeye.com/blog/191892</link>
        <guid>http://hitqiang.javaeye.com/blog/191892</guid>
      </item>
  </channel>
</rss>