<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>mrzhanghuzi</title>
    <description></description>
    <link>http://mrzhanghuzi.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>多文件上传</title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/197098" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/197098</a>&nbsp;
          发表时间: 2008年05月26日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&lt;HTML&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&lt;HEAD&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&lt;TITLE&gt; New Document &lt;/TITLE&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&lt;META NAME="Generator" CONTENT="EditPlus"&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&lt;META NAME="Author" CONTENT=""&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&lt;META NAME="Keywords" CONTENT=""&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&lt;META NAME="Description" CONTENT=""&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&lt;script type="text/javascript"&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;/*附件添加提示*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; function getFirefoxTip(form)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var tipDiv=document.createElement("div");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tipDiv.style.cssText="width:100px;font:12px Arial;color:#00f;text-decoration:underline";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tipDiv.innerHTML="添加一个附件";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tipDiv.onclick=function()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var i=form.getAttribute("count")||0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createInput(form,parseInt(i)+1);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; };&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form.appendChild(tipDiv);&nbsp;&nbsp; <br />&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;/*删除已经添加的附件项*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; function removeChild(parent,child)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; var i=parent.getAttribute("count");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; parent.removeChild(child);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; i--;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; if(i==0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parent.lastChild.innerHTML="添加一个附件";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; parent.setAttribute("count",i);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; /* 添加移除项*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; function getRemove(form,node)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var span=document.createElement("span");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; span.style.cssText="font:10px Arial;color:#00f;text-decoration:underline;";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; span.innerHTML="移除";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; span.onclick=function(){removeChild(form,node);}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return span;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; /*文件选择框*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; function createInput(form,inputIndex)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var i=inputIndex||0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(i==0)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getFirefoxTip(form);;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var inputDiv=document.createElement("div");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var input=document.createElement("input");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input.setAttribute("type","file");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; input.setAttribute("name","file_"+i);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inputDiv.appendChild(input);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inputDiv.appendChild(getRemove(form,inputDiv));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form.insertBefore(inputDiv,form.lastChild);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form.setAttribute("count",i);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; form.lastChild.innerHTML="再添加一个附件";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; /*初始化*/&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; function init()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; createInput(document.forms['uploadForm']);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&lt;/script&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&lt;/HEAD&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp; <br />&lt;BODY onload="init()"&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&lt;form name="uploadForm" action="/upload.do" target="upload" enctype="multipart/form-data" method="post"&gt;&lt;/form&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&lt;iframe name="upload" style="display:none"&gt;&lt;/iframe&gt;&nbsp;&nbsp;&nbsp;&nbsp; <br />&lt;/BODY&gt;&lt;/HTML&gt; </p>
          <br/>
          <span style="color:red;">
            <a href="http://mrzhanghuzi.javaeye.com/blog/197098#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>Mon, 26 May 2008 15:36:14 +0800</pubDate>
        <link>http://mrzhanghuzi.javaeye.com/blog/197098</link>
        <guid>http://mrzhanghuzi.javaeye.com/blog/197098</guid>
      </item>
      <item>
        <title>freemark开发指南(入门)</title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/194966" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/194966</a>&nbsp;
          发表时间: 2008年05月20日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>1概念 <br />2指令 <br />if, else, elseif <br />switch, case, default, break <br />list, break <br />include <br />Import <br />compress <br />escape, noescape <br />assign <br />global <br />setting <br />macro, nested, return <br />t, lt, rt <br />3一些常用方法或注意事项 <br />表达式转换类 <br />数字循环 <br />对浮点取整数 <br />给变量默认值 <br />判断对象是不是null <br />常用格式化日期 <br />添加全局共享变量数据模型 <br />直接调用java对象的方法 <br />字符串处理(内置方法) <br />在模板里对sequences和hashes初始化 <br />注释标志 <br />sequences内置方法 <br />hashes内置方法 <br />4 freemarker在web开发中注意事项 <br />web中常用的几个对象 <br />view中值的搜索顺序 <br />在模板里ftl里使用标签 <br />如何初始化共享变量 <br />与webwork整合配置 <br />5高级方法 <br />自定义方法 <br />自定义 Transforms <br /><br />1概念 <br />最常用的3个概念 <br />sequence 序列，对应java里的list、数组等非键值对的集合 <br />hash 键值对的集合 <br />namespace 对一个ftl文件的引用,利用这个名字可以访问到该ftl文件的资源 <br /><br />2指令 <br />if, else, elseif <br />语法 <br />&lt;#if condition&gt; <br />... <br />&lt;#elseif condition2&gt; <br />... <br />&lt;#elseif condition3&gt; <br />... <br />... <br />&lt;#else&gt; <br />... <br />&lt;/#if&gt; <br />用例 <br />&lt;#if x = 1&gt; <br />x is 1 <br />&lt;/#if&gt; <br /><br />&lt;#if x = 1&gt; <br />x is 1 <br />&lt;#else&gt; <br />x is not 1 <br />&lt;/#if&gt; <br /><br />switch, case, default, break <br />语法 <br />&lt;#switch value&gt; <br />&lt;#case refValue1&gt; <br />... <br />&lt;#break&gt; <br />&lt;#case refValue2&gt; <br />... <br />&lt;#break&gt; <br />... <br />&lt;#case refValueN&gt; <br />... <br />&lt;#break&gt; <br />&lt;#default&gt; <br />... <br />&lt;/#switch&gt; <br /><br />用例 <br />字符串 <br />&lt;#switch being.size&gt; <br />&lt;#case "small"&gt; <br />This will be processed if it is small <br />&lt;#break&gt; <br />&lt;#case "medium"&gt; <br />This will be processed if it is medium <br />&lt;#break&gt; <br />&lt;#case "large"&gt; <br />This will be processed if it is large <br />&lt;#break&gt; <br />&lt;#default&gt; <br />This will be processed if it is neither <br />&lt;/#switch&gt; <br />数字 <br />&lt;#switch x&gt; <br />&lt;#case x = 1&gt; <br />1 <br />&lt;#case x = 2&gt; <br />2 <br />&lt;#default&gt; <br />d <br />&lt;/#switch&gt; <br /><br />如果x=1 输出 1 2, x=2输出 2, x=3 输出d <br /><br />list, break <br />语法 <br />&lt;#list sequence as item&gt; <br />... <br />&lt;#if item = "spring"&gt;&lt;#break&gt;&lt;/#if&gt; <br />... <br />&lt;/#list&gt; <br />关键字 <br />item_index:是list当前值的下标 <br />item_has_next:判断list是否还有值 <br /><br />用例 <br />&lt;#assign seq = ["winter", "spring", "summer", "autumn"]&gt; <br />&lt;#list seq as x&gt; <br />${x_index + 1}. ${x}&lt;#if x_has_next&gt;,&lt;/#if&gt; <br />&lt;/#list&gt; <br /><br />输出 <br />1. winter, <br />2. spring, <br />3. summer, <br />4. autumn <br /><br /><br />include <br />语法 <br />&lt;#include filename&gt; <br />or <br />&lt;#include filename options&gt; <br />options包含两个属性 <br />encoding=&rdquo;GBK&rdquo; 编码格式 <br />parse=true 是否作为ftl语法解析,默认是true，false就是以文本方式引入.注意在ftl文件里布尔值都是直接赋值的如parse=true,而不是parse=&rdquo;true&rdquo; <br />用例 <br />/common/copyright.ftl包含内容 <br />Copyright 2001-2002 ${me}&lt;br&gt; <br />All rights reserved. <br />模板文件 <br />&lt;#assign me = "Juila Smith"&gt; <br />&lt;h1&gt;Some test&lt;/h1&gt; <br />&lt;p&gt;Yeah. <br />&lt;hr&gt; <br />&lt;#include "/common/copyright.ftl" encoding=&rdquo;GBK&rdquo;&gt; <br />输出结果 <br />&lt;h1&gt;Some test&lt;/h1&gt; <br />&lt;p&gt;Yeah. <br />&lt;hr&gt; <br />Copyright 2001-2002 Juila Smith <br />All rights reserved. <br /><br />Import <br />语法 <br />&lt;#import path as hash&gt; <br />类似于java里的import,它导入文件，然后就可以在当前文件里使用被导入文件里的宏组件 <br /><br />用例 <br /><br />假设mylib.ftl里定义了宏copyright那么我们在其他模板页面里可以这样使用 <br />&lt;#import "/libs/mylib.ftl" as my&gt; <br /><br />&lt;@my.copyright date="1999-2002"/&gt; <br /><br />"my"在freemarker里被称作namespace <br /><br />compress <br />语法 <br />&lt;#compress&gt; <br />... <br />&lt;/#compress&gt; <br />用来压缩空白空间和空白的行 <br />用例 <br />&lt;#assign x = " moo \n\n "&gt; <br />(&lt;#compress&gt; <br />1 2 3 4 5 <br />${moo} <br />test only <br /><br />I said, test only <br /><br />&lt;/#compress&gt;) <br />输出 <br />(1 2 3 4 5 <br />moo <br />test only <br />I said, test only) <br />escape, noescape <br />语法 <br />&lt;#escape identifier as expression&gt; <br />... <br />&lt;#noescape&gt;...&lt;/#noescape&gt; <br />... <br />&lt;/#escape&gt; <br />用例 <br />主要使用在相似的字符串变量输出，比如某一个模块的所有字符串输出都必须是html安全的，这个时候就可以使用该表达式 <br />&lt;#escape x as x?html&gt; <br />First name: ${firstName} <br />&lt;#noescape&gt;Last name: ${lastName}&lt;/#noescape&gt; <br />Maiden name: ${maidenName} <br />&lt;/#escape&gt; <br />相同表达式 <br />First name: ${firstName?html} <br />Last name: ${lastName } <br />Maiden name: ${maidenName?html} <br />assign <br />语法 <br />&lt;#assign name=value&gt; <br />or <br />&lt;#assign name1=value1 name2=value2 ... nameN=valueN&gt; <br />or <br />&lt;#assign same as above... in namespacehash&gt; <br />or <br />&lt;#assign name&gt; <br />capture this <br />&lt;/#assign&gt; <br />or <br />&lt;#assign name in namespacehash&gt; <br />capture this <br />&lt;/#assign&gt; <br />用例 <br />生成变量,并且给变量赋值 <br />给seasons赋予序列值 <br />&lt;#assign seasons = ["winter", "spring", "summer", "autumn"]&gt; <br /><br />给变量test加1 <br />&lt;#assign test = test + 1&gt; <br /><br />给my namespage 赋予一个变量bgColor,下面可以通过my.bgColor来访问这个变量 <br />&lt;#import "/mylib.ftl" as my&gt; <br />&lt;#assign bgColor="red" in my&gt; <br /><br />将一段输出的文本作为变量保存在x里 <br />下面的阴影部分输出的文本将被赋值给x <br />&lt;#assign x&gt; <br />&lt;#list 1..3 as n&gt; <br />${n} &lt;@myMacro /&gt; <br />&lt;/#list&gt; <br />&lt;/#assign&gt; <br />Number of words: ${x?word_list?size} <br />${x} <br /><br />&lt;#assign x&gt;Hello ${user}!&lt;/#assign&gt; error <br />&lt;#assign x=&rdquo; Hello ${user}!&rdquo;&gt; true <br /><br />同时也支持中文赋值，如： <br />&lt;#assign 语法&gt; <br />java <br />&lt;/#assign&gt; <br />${语法} <br />打印输出: <br />java <br />global <br />语法 <br />&lt;#global name=value&gt; <br />or <br />&lt;#global name1=value1 name2=value2 ... nameN=valueN&gt; <br />or <br />&lt;#global name&gt; <br />capture this <br />&lt;/#global&gt; <br /><br />全局赋值语法，利用这个语法给变量赋值，那么这个变量在所有的namespace中是可见的,如果这个变量被当前的assign语法覆盖 如&lt;#global x=2&gt; &lt;#assign x=1&gt; 在当前页面里x=2将被隐藏，或者通过${.global.x}来访问 <br /><br />setting <br />语法 <br />&lt;#setting name=value&gt; <br />用来设置整个系统的一个环境 <br />locale <br />number_format <br />boolean_format <br />date_format, time_format, datetime_format <br />time_zone <br />classic_compatible <br />用例 <br />假如当前是匈牙利的设置，然后修改成美国 <br />${1.2} <br />&lt;#setting locale="en_US"&gt; <br />${1.2} <br />输出 <br />1,2 <br />1.2 <br />因为匈牙利是采用&ldquo;,&rdquo;作为十进制的分隔符，美国是用&ldquo;.&rdquo; <br /><br /><br />macro, nested, return <br />语法 <br /><br />&lt;#macro name param1 param2 ... paramN&gt; <br />... <br />&lt;#nested loopvar1, loopvar2, ..., loopvarN&gt; <br />... <br />&lt;#return&gt; <br />... <br />&lt;/#macro&gt; <br />用例 <br />&lt;#macro test foo bar="Bar" baaz=-1&gt; <br />Test text, and the params: ${foo}, ${bar}, ${baaz} <br />&lt;/#macro&gt; <br />&lt;@test foo="a" bar="b" baaz=5*5-2/&gt; <br />&lt;@test foo="a" bar="b"/&gt; <br />&lt;@test foo="a" baaz=5*5-2/&gt; <br />&lt;@test foo="a"/&gt; <br />输出 <br />Test text, and the params: a, b, 23 <br />Test text, and the params: a, b, -1 <br />Test text, and the params: a, Bar, 23 <br />Test text, and the params: a, Bar, -1 <br />定义循环输出的宏 <br />&lt;#macro list title items&gt; <br />&lt;p&gt;${title?cap_first}: <br />&lt;ul&gt; <br />&lt;#list items as x&gt; <br />&lt;li&gt;${x?cap_first} <br />&lt;/#list&gt; <br />&lt;/ul&gt; <br />&lt;/#macro&gt; <br />&lt;@list items=["mouse", "elephant", "python"] title="Animals"/&gt; <br />输出结果 <br />&lt;p&gt;Animals: <br />&lt;ul&gt; <br />&lt;li&gt;Mouse <br />&lt;li&gt;Elephant <br />&lt;li&gt;Python <br />&lt;/ul&gt; <br />包含body的宏 <br />&lt;#macro repeat count&gt; <br />&lt;#list 1..count as x&gt; <br />&lt;#nested x, x/2, x==count&gt; <br />&lt;/#list&gt; <br />&lt;/#macro&gt; <br />&lt;@repeat count=4 ; c halfc last&gt; <br />${c}. ${halfc}&lt;#if last&gt; Last!&lt;/#if&gt; <br />&lt;/@repeat&gt; <br />输出 <br />1. 0.5 <br />2. 1 <br />3. 1.5 <br />4. 2 Last! <br /><br /><br /><br />t, lt, rt <br />语法 <br />&lt;#t&gt; 去掉左右空白和回车换行 <br /><br />&lt;#lt&gt;去掉左边空白和回车换行 <br /><br />&lt;#rt&gt;去掉右边空白和回车换行 <br /><br />&lt;#nt&gt;取消上面的效果 <br /><br /><br />3一些常用方法或注意事项 <br /><br /><br />表达式转换类 <br />${expression}计算expression并输出 <br />#{ expression }数字计算#{ expression ;format}安格式输出数字format为M和m <br />M表示小数点后最多的位数,m表示小数点后最少的位数如#{121.2322;m2M2}输出121.23 <br /><br /><br /><br />数字循环 <br />1..5 表示从1到5，原型number..number <br />对浮点取整数 <br />${123.23?int} 输出123 <br />给变量默认值 <br />${var?default(&ldquo;hello world&lt;br&gt;&rdquo;)?html}如果var is null那么将会被hello world&lt;br&gt;替代 <br /><br />判断对象是不是null <br />&lt;#if mouse?exists&gt; <br />Mouse found <br />&lt;#else&gt; <br />也可以直接${mouse?if_exists})输出布尔形 <br />常用格式化日期 <br />openingTime必须是Date型,详细查看freemarker文档 Reference-&gt;build-in referece-&gt;build-in for date <br /><br />${openingTime?date} <br />${openingTime?date_time} <br />${openingTime?time} <br /><br />添加全局共享变量数据模型 <br />在代码里的实现 <br />cfg = Configuration.getDefaultConfiguration(); <br />cfg.setSharedVariable("global", "you good"); <br />页面实现可以通过global指令,具体查看指令里的global部分 <br />直接调用java对象的方法 <br />${object.methed(args)} <br /><br />字符串处理(内置方法) <br />html安全输出 <br />&ldquo;abc&lt;table&gt;sdfsf&rdquo;?html <br />返回安全的html输出,替换掉html代码 <br />xml安全输出 <br />var?xml <br />substring的用法 <br />&lt;#assign user=&rdquo;hello jeen&rdquo;&gt; <br />${user[0]}${user[4]} <br />${user[1..4]} <br />输出 : <br />ho <br />ello <br />类似String.split的用法 <br />&ldquo;abc;def;ghi&rdquo;?split(&ldquo;;&rdquo;)返回sequence <br />将字符串按空格转化成sequence,然后取sequence的长度 <br />var?word_list 效果同 var?split(&ldquo; &rdquo;) <br />var?word_list?size <br /><br />取得字符串长度 <br />var?length <br /><br />大写输出字符 <br />var?upper_case <br /><br />小写输出字符 <br />var?lower_case <br /><br />首字符大写 <br />var?cap_first <br /><br />首字符小写 <br />var?uncap_first <br /><br />去掉字符串前后空格 <br />var?trim <br /><br />每个单词的首字符大写 <br />var?capitalize <br /><br />类似String.indexof: <br />&ldquo;babcdabcd&rdquo;?index_of(&ldquo;abc&rdquo;) 返回1 <br />&ldquo;babcdabcd&rdquo;?index_of(&ldquo;abc&rdquo;,2) 返回5 <br />类似String.lastIndexOf <br />last_index_of和String.lastIndexOf类似,同上 <br /><br />下面两个可能在代码生成的时候使用（在引号前加&rdquo;\&rdquo;） <br />j_string: 在字符串引号前加&rdquo;\&rdquo; <br />&lt;#assign beanName = 'The "foo" bean.'&gt; <br />String BEAN_NAME = "${beanName?j_string}"; <br />打印输出: <br />String BEAN_NAME = "The \"foo\" bean."; <br />js_string: <br />&lt;#assign user = "Big Joe's \"right hand\"."&gt; <br />&lt;script&gt; <br />alert("Welcome ${user}!"); <br />&lt;/script&gt; <br />打印输出 <br />alert("Welcome Big Joe\'s \"right hand\"!"); <br /><br />替换字符串 replace <br />${s?replace(&lsquo;ba&rsquo;, &lsquo;XY&rsquo; )} <br />${s?replace(&lsquo;ba&rsquo;, &lsquo;XY&rsquo; , &lsquo;规则参数&rsquo;)}将s里的所有的ba替换成xy 规则参数包含: i r m s c f 具体含义如下: <br />&middot; i: 大小写不区分. <br />&middot; f: 只替换第一个出现被替换字符串的字符串 <br />&middot; r: XY是正则表达式 <br />&middot; m: Multi-line mode for regular expressions. In multi-line mode the expressions ^ and $ match just after or just before, respectively, a line terminator or the end of the string. By default these expressions only match at the beginning and the end of the entire string. <br />&middot; s: Enables dotall mode for regular expressions (same as Perl singe-line mode). In dotall mode, the expression . matches any character, including a line terminator. By default this expression does not match line terminators. <br />&middot; c: Permits whitespace and comments in regular expressions. <br /><br /><br />在模板里对sequences和hashes初始化 <br />sequences <br /><br />1. [&ldquo;you&rdquo;,&rdquo;me&rdquo;,&rdquo;he&rdquo;] <br />2. 1..100 <br />3. [ {&ldquo;Akey&rdquo;:&rdquo;Avalue&rdquo;},{&ldquo;Akey1&rdquo;:&rdquo;Avalue1&rdquo;}, <br />{&ldquo;Bkey&rdquo;:&rdquo;Bvalue&rdquo;},{&ldquo;Bkey1&rdquo;:&rdquo;Bvalue1&rdquo;}, <br />] <br /><br /><br />hashes {&ldquo;you&rdquo;:&rdquo;a&rdquo;,&rdquo;me&rdquo;:&rdquo;b&rdquo;,&rdquo;he&rdquo;:&rdquo;c&rdquo;} <br /><br /><br />注释标志 <br />&lt;#-- <br />这里是注释 <br />--&gt; <br />旧版本的freemarker采用的是&lt;#comment&gt; 注释 &lt;/#comment&gt;方法 <br /><br />sequences内置方法 <br />sequence?first <br />返回sequence的第一个值;前提条件sequence不能是null <br />sequence?last <br />返回sequence最后一个值 <br />sequence?reverse <br />反转sequence的值 <br />sequence?size <br />返回sequence的大小 <br />sequence?sort <br />对sequence按里面的对象toString()的结果进行排序 <br />sequence?sort_by(value) <br />对sequence 按里面的对象的属性value进行排序 <br />如: sequence里面放入的是10 个user对象，user对象里面包含name,age等属性 <br />sequence?sort_by(name) 表示所有的user按user.name进行排序 <br />hashes内置方法 <br />hash?keys <br />返回hash里的所有keys, 返回结果类型sequence <br />hash?values <br />返回hash里的所有value, 返回结果类型sequence <br />4 freemarker在web开发中注意事项 <br />freemarker与webwork整合 <br />web中常用的几个对象 <br />Freemarker的ftl文件中直接使用内部对象: <br />${Request ["a"]} <br />${RequestParameters["a"]} <br />${Session ["a"]} <br />${Application ["a"]} <br />${JspTaglibs ["a"]} <br /><br />与webwork整合之后 通过配置的servlet 已经把request,session等对象置入了数据模型中 <br />在view中存在下面的对象 <br />我们可以在ftl中${req}来打印req对象 <br />&middot; req - the current HttpServletRequest <br />&middot; res - the current HttpServletResponse <br />&middot; stack - the current OgnlValueStack <br />&middot; ognl - the OgnlTool instance <br />&middot; webwork - an instance of FreemarkerWebWorkUtil <br />&middot; action - the current WebWork action <br />&middot; exception - optional the Exception instance, if the view is a JSP exception or Servlet exception view <br />view中值的搜索顺序 <br />${name}将会以下面的顺序查找name值 <br />&middot; freemarker variables <br />&middot; value stack <br />&middot; request attributes <br />&middot; session attributes <br />&middot; servlet context attributes <br />在模板里ftl里使用标签 <br />注意，如果标签的属性值是数字，那么必须采用nubmer=123方式给属性赋值 <br />JSP页面 <br />&lt;%@page contentType="text/html;charset=ISO-8859-2" language="java"%&gt; <br />&lt;%@taglib uri="/WEB-INF/struts-html.tld" prefix="html"%&gt; <br />&lt;%@taglib uri="/WEB-INF/struts-bean.tld" prefix="bean"%&gt; <br /><br />&lt;html&gt; <br />&lt;body&gt; <br />&lt;h1&gt;&lt;bean:message key="welcome.title"/&gt;&lt;/h1&gt; <br />&lt;html:errors/&gt; <br />&lt;html:form action="/query"&gt; <br />Keyword: &lt;html:text property="keyword"/&gt;&lt;br&gt; <br />Exclude: &lt;html:text property="exclude"/&gt;&lt;br&gt; <br />&lt;html:submit value="Send"/&gt; <br />&lt;/html:form&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br />模板ftl页面 <br />&lt;#assign html=JspTaglibs["/WEB-INF/struts-html.tld"]&gt; <br />&lt;#assign bean=JspTaglibs["/WEB-INF/struts-bean.tld"]&gt; <br /><br />&lt;html&gt; <br />&lt;body&gt; <br />&lt;h1&gt;&lt;@bean.message key="welcome.title"/&gt;&lt;/h1&gt; <br />&lt;@html.errors/&gt; <br />&lt;@html.form action="/query"&gt; <br />Keyword: &lt;@html.text property="keyword"/&gt;&lt;br&gt; <br />Exclude: &lt;@html.text property="exclude"/&gt;&lt;br&gt; <br />&lt;@html.submit value="Send"/&gt; <br />&lt;/@html.form&gt; <br />&lt;/body&gt; <br />&lt;/html&gt; <br /><br /><br />如何初始化共享变量 <br />1． 初始化全局共享数据模型 <br />freemark在web上使用的时候对共享数据的初始化支持的不够,不能在配置初始化的时候实现，而必须通过ftl文件来初始化全局变量。这是不能满主需求的，我们需要在servlet init的时候留出一个接口来初始化系统的共享数据 <br />具 体到和webwork整合,因为本身webwork提供了整合servlet,如果要增加全局共享变量，可以通过修改 com.opensymphony.webwork.views.freemarker.FreemarkerServlet来实现,我们可以在这个 servlet初始化的时候来初始化全局共享变量 <br />与webwork整合配置 <br />配置web.xml <br />&lt;servlet&gt; <br />&lt;servlet-name&gt;freemarker&lt;/servlet-name&gt; <br />&lt;servlet-class&gt;com.opensymphony.webwork.views.freemarker.FreemarkerServlet&lt;/servlet-class&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;TemplatePath&lt;/param-name&gt; <br />&lt;param-value&gt;/&lt;/param-value&gt; <br />&lt;!&mdash;模板载入文件夹，这里相对context root，递归获取该文件夹下的所有模板--&gt; <br />&lt;/init-param&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;NoCache&lt;/param-name&gt; &lt;!&mdash;是否对模板缓存--&gt; <br />&lt;param-value&gt;true&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;ContentType&lt;/param-name&gt; <br />&lt;param-value&gt;text/html&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;template_update_delay&lt;/param-name&gt; <br />&lt;!&mdash;模板更新时间,0表示每次都更新,这个适合开发时候--&gt; <br />&lt;param-value&gt;0&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;default_encoding&lt;/param-name&gt; <br />&lt;param-value&gt;GBK&lt;/param-value&gt; <br />&lt;/init-param&gt; <br />&lt;init-param&gt; <br />&lt;param-name&gt;number_format&lt;/param-name&gt; <br />&lt;param-value&gt;0.##########&lt;/param-value&gt;&lt;!&mdash;数字显示格式--&gt; <br />&lt;/init-param&gt; <br />&lt;load-on-startup&gt;1&lt;/load-on-startup&gt; <br />&lt;/servlet&gt; <br />&lt;servlet-mapping&gt; <br />&lt;servlet-name&gt;freemarker&lt;/servlet-name&gt; <br />&lt;url-pattern&gt;*.ftl&lt;/url-pattern&gt; <br />&lt;/servlet-mapping&gt; <br /><br />5高级方法 <br />自定义方法 <br />${timer("yyyy-MM-dd H:mm:ss", x)} <br />${timer("yyyy-MM-dd ", x)} <br /><br />在模板中除了可以通过对象来调用方法外（${object.methed(args)}）也可以直接调用java实现的方法，java类必须实现接 口TemplateMethodModel的方法exec(List args). 下面以把毫秒的时间转换成按格式输出的时间为例子 <br />public class LongToDate implements TemplateMethodModel { <br /><br />public TemplateModel exec(List args) throws TemplateModelException { <br />SimpleDateFormat mydate = new SimpleDateFormat((String) args.get(0))); <br />return mydate.format(new Date(Long.parseLong((String)args.get(1))); <br />} <br />} <br />将LongToDate对象放入到数据模型中 <br />root.put("timer", new IndexOfMethod()); <br />ftl模板里使用 <br />&lt;#assign x = "123112455445"&gt; <br />${timer("yyyy-MM-dd H:mm:ss", x)} <br />${timer("yyyy-MM-dd ", x)} <br /><br />输出 <br />2001-10-12 5:21:12 <br />2001-10-12 <br /><br />自定义 Transforms <br />实现自定义的&lt;@transform&gt;文本或表达式&lt;/@transform&gt;的功能,允许对中间的最终文本进行解析转换 <br /><br />例子：实现&lt;@upcase&gt;str&lt;/@upcase&gt; 将str转换成STR 的功能 <br /><br />代码如下： <br />import java.io.*; <br />import java.util.*; <br />import freemarker.template.TemplateTransformModel; <br /><br />class UpperCaseTransform implements TemplateTransformModel { <br /><br />public Writer getWriter(Writer out, Map args) { <br />return new UpperCaseWriter(out); <br />} <br /><br />private class UpperCaseWriter extends Writer { <br /><br />private Writer out; <br /><br />UpperCaseWriter (Writer out) { <br />this.out = out; <br />} <br /><br />public void write(char[] cbuf, int off, int len) <br />throws IOException { <br />out.write(new String(cbuf, off, len).toUpperCase()); <br />} <br /><br />public void flush() throws IOException { <br />out.flush(); <br />} <br /><br />public void close() { <br />} <br />} <br />} <br />然后将此对象put到数据模型中 <br />root.put("upcase", new UpperCaseTransform()); <br /><br />在view(ftl)页面中可以如下方式使用 <br /><br />&lt;@upcase&gt; <br />hello world <br />&lt;/@upcase&gt; <br /><br />打印输出: <br />HELLO WORLD</p>
<p>&nbsp;</p>
          <br/>
          <span style="color:red;">
            <a href="http://mrzhanghuzi.javaeye.com/blog/194966#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, 20 May 2008 17:32:53 +0800</pubDate>
        <link>http://mrzhanghuzi.javaeye.com/blog/194966</link>
        <guid>http://mrzhanghuzi.javaeye.com/blog/194966</guid>
      </item>
      <item>
        <title>Hibernate性能优化4( 转)</title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/192610" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/192610</a>&nbsp;
          发表时间: 2008年05月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在处理大数据量时，会有大量的数据缓冲保存在 Session 的一级缓存中，这缓存大太时会严重显示性能，所以在使用 Hibernate 处理大数据量的，可以使用 session.clear() 或者 session. Evict(Object) 在处理过程中，清除全部的缓存或者清除某个对象。 <br /><br />2) 对大数据量查询时，慎用 list() 或者 iterator() 返回查询结果， <br />1. 使用 List() 返回结果时， Hibernate 会所有查询结果初始化为持久化对象，结果集较大时，会占用很多的处理时间。 <br />2. 而使用 iterator() 返回结果时，在每次调用 iterator.next() 返回对象并使用对象时， Hibernate 才调用查询将对应的对象初始化，对于大数据量时，每调用一次查询都会花费较多的时间。当结果集较大，但是含有较大量相同的数据，或者结果集不是全部都会使用时，使用 iterator() 才有优势。 <br />3. 对于大数据量，使用 qry.scroll() 可以得到较好的处理速度以及性能。而且直接对结果集向前向后滚动。 <br /><br />3) 对于关联操作， Hibernate 虽然可以表达复杂的数据关系，但请慎用，使数据关系较为简单时会得到较好的效率，特别是较深层次的关联时，性能会很差。 <br /><br />4) 对含有关联的 PO （持久化对象）时，若 default-cascade="all" 或者 “save-update” ，新增 PO 时，请注意对 PO 中的集合的赋值操作，因为有可能使得多执行一次 update 操作。 <br /><br />5) 在一对多、多对一的关系中，使用延迟加载机制，会使不少的对象在使用时 才 会初始化，这样可使得节省内存空间以及减少数据库的负荷，而且若 PO 中的集合没有被使用时，就可减少互数据库的交互从而减少处理时间。 <br /><br /><br />6) 对于大数据量新增、修改、删除操作或者是对大数据量的查询，与数据库的交互次数是决定处理时间的最重要因素，减少交互的次数是提升效率的最好途径，所以在开发过程中，请将 show_sql 设置为 true ，深入了解 Hibernate 的处理过程，尝试不同的方式，可以使得效率提升。 <br /><br /><br />7) Hibernate 是以 JDBC 为基础，但是 Hibernate 是对 JDBC 的优化，其中使用 Hibernate 的缓冲机制会使性能提升，如使用二级缓存以及查询缓存，若命中率较高明，性能会是到大幅提升。 <br /><br />8) Hibernate 可以通过设置 hibernate.jdbc.fetch_size ， hibernate.jdbc.batch_size 等属性，对 Hibernate 进行优化。 <br />9) 不过值得注意的是，一些数据库提供的主键生成机制在效率上未必最佳，大量并发 insert 数据时可能会引起表之间的互锁。数据库提供的主键生成机制，往往是通过在一个内部表中保存当前主键状态（如对于自增型主键而言，此内部表中就维护着当前的最大值和递增量），之后每次插入数据会读取这个最大值，然后加上递增量作为新记录的主键，之后再把这个新的最大值更新回内部表中，这样，一次 Insert 操作可能导致数据库内部多次表读写操作，同时伴随的还有数据的加锁解锁操作，这对性能产生了较大影响。 <br />因此，对于并发 Insert 要求较高的系统，推荐采用 uuid.hex 作为主键生成机制。 <br />10) Dynamic Update 如果选定，则生成 Update SQL 时不包含未发生变动的字段属性，这样可以在一定程度上提升 SQL 执行效能 . Dynamic Insert 如果选定，则生成 Insert SQL 时不包含未发生变动的字段属性，这样可以在一定程度上提升 SQL 执行效能 <br />11) 在编写代码的时候请，对将 POJO 的 getter/setter 方法设定为 public ，如果设定为 private ， Hibernate 将无法对属性的存取进行优化，只能转而采用传统的反射机制进行操作，这将导致大量的性能开销（特别是在 1.4 之前的 Sun JDK 版本以及 IBM JDK 中，反射所带来的系统开销相当可观）。 <br />12) 在 one-to-many 关系中，将 many 一方设为主动方（ inverse=false ）将有助性能的改善 <br />13) 由于多对多关联的性能不佳（由于引入了中间表，一次读取操作需要反复数次查询），因此在设计中应该避免大量使用 . <br />14) Hibernate 支持两种锁机制：即通常所说的“悲观锁（ Pessimistic Locking ）”和“乐观锁（ Optimistic Locking ）”。 悲观锁带来 数据库性能的大量开销，特别是对长事务而言，这样的开销往往无法承受。乐观锁机制在一定程度上解决了这个问题.乐观锁机制避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统整体性能表现。
          <br/>
          <span style="color:red;">
            <a href="http://mrzhanghuzi.javaeye.com/blog/192610#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, 13 May 2008 15:24:09 +0800</pubDate>
        <link>http://mrzhanghuzi.javaeye.com/blog/192610</link>
        <guid>http://mrzhanghuzi.javaeye.com/blog/192610</guid>
      </item>
      <item>
        <title>Hibernate性能优化3( 转)</title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/192609" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/192609</a>&nbsp;
          发表时间: 2008年05月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          作者：Robbin Fan<br />一。 inverse = ?<br />          inverse=false(default)<br />                      用于单向one-to-many关联<br />                      parent.getChildren().add(child) // insert child<br />                      parent.getChildren().delete(child) // delete child<br />           inverse=true<br />                      用于双向one-to-many关联<br />                      child.setParent(parent); session.save(child) // insert child<br />                       session.delete(child)<br />            在分层结构的体系中<br />             parentDao, childDao对于CRUD的封装导致往往直接通过session接口持久化对象，而很少通过关联对象可达性 <br /><br />二。 one-to-many关系<br />                单向关系还是双向关系？<br />                     parent.getChildren().add(child)对集合的触及操作会导致lazy的集合初始化，在没有对集合配置二级缓存的情况下，应避免此类操作<br />                   select * from child where parent_id = xxx;<br />          性能口诀：<br />                  1. 一般情况下避免使用单向关联，尽量使用双向关联<br />                  2. 使用双向关联，inverse=“true”<br />                  3. 在分层结构中通过DAO接口用session直接持久化对象，避免通过关联关系进行可达性持久化<br /><br /> <br /><br />三。many-to-one关系<br />         单向many-to-one表达了外键存储方<br />         灵活运用many-to-one可以避免一些不必要的性能问题<br />         many-to-one表达的含义是：0..n : 1，many可以是0，可以是1，也可以是n，也就是说many-to-one可以表达一对多，一对一，多对一关系<br />          因此可以配置双向many-to-one关系，例如：<br />                1.   一桌四人打麻将，麻将席位和打麻将的人是什么关系？是双向many-to-one的关系<br /><br />四。one-to-one<br />            通过主键进行关联<br />            相当于把大表拆分为多个小表<br />            例如把大字段单独拆分出来，以提高数据库操作的性能<br />            Hibernate的one-to-one似乎无法lazy，必须通过bytecode enhancement<br /><br />五。集合List/Bag/Set <br />            one-to-many<br />               1.    List需要维护index column，不能被用于双向关联，必须inverse=“false”，被谨慎的使用在某些稀有的场合<br /><br />               2.      Bag/Set语义上没有区别<br />               3.       我个人比较喜欢使用Bag<br />           many-to-many <br />               1.      Bag和Set语义有区别<br />               2。   建议使用Set<br /><br />六。集合的过滤<br />             1. children = session.createFilter(parent.getChildren(), “where this.age > 5 and   this.age &lt; 10”).list()<br />         针对一对多关联当中的集合元素非常庞大的情况，特别适合于庞大集合的分页：<br />                   session.createFilter(parent.getChildren(),“”).setFirstResult(0).setMaxResults(10).list();<br />在hibernate 中用 super.getSession().createFilter( , )<br /><br />七。继承关系当中的隐式多态<br />           HQL: from Object<br />             1.     把所有数据库表全部查询出来<br />              2.     polymorphism=“implicit”(default)将当前对象，和对象所有继承子类全部一次性取出<br />              3.      polymorphism=“explicit”，只取出当前查询对象<br /><br />八。Hibernate二级缓存<br />              著名的n+1问题：from Child，然后在页面上面显示每个子类的父类信息，就会导致n条对parent表的查询：<br />                   select * from parent where id = ?<br />                   .......................<br />                   select * from parent where id = ?<br />              解决方案<br />                        1.      eager fetch<br />                         2.      二级缓存<br /><br />九。inverse和二级缓存的关系<br />            当使用集合缓存的情况下：<br />                 1.     inverse=“false”，通过parent.getChildren()来操作，Hibernate维护集合缓存<br />                  2.    inverse=“true”，直接对child进行操作，未能维护集合缓存！导致缓存脏数据<br />                  3.    双向关联，inverse=“true”的情况下应避免使用集合缓存<br /><br />十。Hibernate二级缓存是提升web应用性能的法宝<br />              OLTP类型的web应用，由于应用服务器端可以进行群集水平扩展，最终的系统瓶颈总是逃不开数据库访问；<br /><br />           哪个框架能够最大限度减少数据库访问，降低数据库访问压力， 哪个框架提供的性能就更高；针对数据库的缓存策略：<br />                    1.        对象缓存：细颗粒度，针对表的记录级别，透明化访问，在不改变程序代码的情况下可以极大提升web应用的性能。对象缓存是ORM的制胜法宝。<br />                    2.       对象缓存的优劣取决于框架实现的水平，Hibernate是目前已知对象缓存最强大的开源ORM<br />                    3.        查询缓存：粗颗粒度，针对查询结果集，应用于数据实时化要求不高的场合<br /><br />十一。应用场合决定了系统架构<br />一、是否需要ORM<br />Hibernate or iBATIS？<br />二、采用ORM决定了数据库设计<br />            Hibernate：<br />                    倾向于细颗粒度的设计，面向对象，将大表拆分为多个关联关系的小表，消除冗余column，通过二级缓存提升性能（DBA比较忌讳关联关系的出现，但是 ORM的缓存将突破关联关系的性能瓶颈）；Hibernate的性能瓶颈不在于关联关系，而在于大表的操作<br />            iBATIS：<br />                    倾向于粗颗粒度设计，面向关系，尽量把表合并，通过表column冗余，消除关联关系。无有效缓存手段。iBATIS的性能瓶颈不在于大表操作，而在于关联关系。<br /><br />总结：<br />     性能口诀<br />               1、使用双向一对多关联，不使用单向一对多<br />               2、灵活使用单向多对一关联<br />               3、不用一对一，用多对一取代<br />               4、配置对象缓存，不使用集合缓存<br />               5、一对多集合使用Bag，多对多集合使用Set<br />               6、继承类使用显式多态<br />               7、表字段要少，表关联不要怕多，有二级缓存撑腰<br /><br /> <br /><br />最近开始留意项目中的Hibernate的性能问题，希望可以抽出时间学习一下hiberante的性能优化。主要是对数据库连接池技术、hibernate二级缓存、hibernate的配置优化等问题进行学习！<br /><br /><br />1.关联关系：<br />普通的关联关系：是不包括一个连接表，也就是中间表如：<br />create table Person(personId bigint not null primary key,addressId bigint not null)<br />create table Address(addressId bigint not null primary key)<br />也就是不会还有一个关系表如：<br />create table Person(personId bigint not null primary key)<br />create table Address(addressId bigint not null primary key)<br />create table PersonAddress(personId bigint not null,ddressId bigint not null primary key)<br /><br /><br />单向many-to-one关联是最常见的，而单向one-to-many是不常见的<br /><br /><br />2. inner join (内连接) <br />left (outer) join （左外连接）<br />right (outer) join (右外连接)<br />full join (全连接，并不常用)<br /><br /><br />3.小技巧：<br />统计结果数目：<br />(Integer)session.iterator("select count(*) from ..").next()).intValue();<br />根据一个集合大小来排序：<br />select user.id,user.name<br />from User as user.name <br />    left join user.messages msg<br />group by user.id,user.name<br />having count(msg)>=1
          <br/>
          <span style="color:red;">
            <a href="http://mrzhanghuzi.javaeye.com/blog/192609#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>Tue, 13 May 2008 15:23:15 +0800</pubDate>
        <link>http://mrzhanghuzi.javaeye.com/blog/192609</link>
        <guid>http://mrzhanghuzi.javaeye.com/blog/192609</guid>
      </item>
      <item>
        <title>Hibernate性能优化2( 转)</title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/192608" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/192608</a>&nbsp;
          发表时间: 2008年05月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          关键字: hibernate <br />本文依照HIBERNATE帮助文档，一些网络书籍及项目经验整理而成，只提供要点和思路，具体做法可以留言探讨，或是找一些更详细更有针对性的资料。<br /><br />　　初用HIBERNATE的人也许都遇到过性能问题，实现同一功能，用HIBERNATE与用JDBC性能相差十几倍很正常，如果不及早调整，很可能影响整个项目的进度。<br /><br />　　大体上，对于HIBERNATE性能调优的主要考虑点如下:<br /><br />　　? 数据库设计调整<br /><br />　　? HQL优化<br /><br />　　? API的正确使用(如根据不同的业务类型选用不同的集合及查询API)<br /><br />　　? 主配置参数(日志，查询缓存，fetch_size, batch_size等)<br /><br />　　? 映射文件优化(ID生成策略，二级缓存，延迟加载，关联优化)<br /><br />　　? 一级缓存的管理<br /><br />　　? 针对二级缓存，还有许多特有的策略<br /><br />　　? 事务控制策略。<br /><br />　　1、 数据库设计<br /><br />　　a) 降低关联的复杂性<br /><br />　　b) 尽量不使用联合主键<br /><br />　　c) ID的生成机制，不同的数据库所提供的机制并不完全一样<br /><br />　　d) 适当的冗余数据，不过分追求高范式<br /><br />　　2、 HQL优化<br /><br />　　HQL如果抛开它同HIBERNATE本身一些缓存机制的关联，HQL的优化技巧同普通的SQL优化技巧一样，可以很容易在网上找到一些经验之谈。<br /><br />　　3、 主配置<br /><br />　　a) 查询缓存，同下面讲的缓存不太一样，它是针对HQL语句的缓存，即完全一样的语句再次执行时可以利用缓存数据。但是，查询缓存在一个交易系统(数据变更频繁，查询条件相同的机率并不大)中可能会起反作用:它会白白耗费大量的系统资源但却难以派上用场。<br /><br />　　b) fetch_size，同JDBC的相关参数作用类似，参数并不是越大越好，而应根据业务特征去设置<br /><br />　　c) batch_size同上。<br /><br />　　d) 生产系统中，切记要关掉SQL语句打印。<br /><br />　　4、 缓存<br /><br />　　a) 数据库级缓存:这级缓存是最高效和安全的，但不同的数据库可管理的层次并不一样，比如，在ORACLE中，可以在建表时指定将整个表置于缓存当中。<br /><br />　　b) SESSION缓存:在一个HIBERNATE SESSION有效，这级缓存的可干预性不强，大多于HIBERNATE自动管理，但它提供清除缓存的方法，这在大批量增加/更新操作是有效的。比如，同时增加十万条记录，按常规方式进行，很可能会发现OutofMemeroy的异常，这时可能需要手动清除这一级缓存:Session.evict以及 Session.clear<br /><br />　　c) 应用缓存:在一个SESSIONFACTORY中有效，因此也是优化的重中之重，因此，各类策略也考虑的较多，在将数据放入这一级缓存之前，需要考虑一些前提条件:<br /><br />　　i. 数据不会被第三方修改(比如，是否有另一个应用也在修改这些数据?)<br /><br />　　ii. 数据不会太大<br /><br />　　iii. 数据不会频繁更新(否则使用CACHE可能适得其反)<br /><br />　　iv. 数据会被频繁查询<br /><br />　　v. 数据不是关键数据(如涉及钱，安全等方面的问题)。<br /><br />　　缓存有几种形式，可以在映射文件中配置:read-only(只读，适用于很少变更的静态数据/历史数据)，nonstrict-read- write，read-write(比较普遍的形式，效率一般)，transactional(JTA中，且支持的缓存产品较少)<br /><br />　　d) 分布式缓存:同c)的配置一样，只是缓存产品的选用不同，在目前的HIBERNATE中可供选择的不多，oscache, jboss cache，目前的大多数项目，对它们的用于集群的使用(特别是关键交易系统)都持保守态度。在集群环境中，只利用数据库级的缓存是最安全的。<br /><br />　　5、 延迟加载<br /><br />　　a) 实体延迟加载:通过使用动态代理实现<br /><br />　　b) 集合延迟加载:通过实现自有的SET/LIST，HIBERNATE提供了这方面的支持<br /><br />　　c) 属性延迟加载:<br /><br />　　6、 方法选用<br /><br />　　a) 完成同样一件事，HIBERNATE提供了可供选择的一些方式，但具体使用什么方式，可能用性能/代码都会有影响。显示，一次返回十万条记录(List /Set/Bag/Map等)进行处理，很可能导致内存不够的问题，而如果用基于游标(ScrollableResults)或Iterator的结果集，则不存在这样的问题。<br /><br />　　b) Session的load/get方法，前者会使用二级缓存，而后者则不使用。<br /><br />　　c) Query和list/iterator，如果去仔细研究一下它们，你可能会发现很多有意思的情况，二者主要区别(如果使用了Spring，在HibernateTemplate中对应find,iterator方法):<br /><br />　　i. list只能利用查询缓存(但在交易系统中查询缓存作用不大)，无法利用二级缓存中的单个实体，但list查出的对象会写入二级缓存，但它一般只生成较少的执行SQL语句，很多情况就是一条(无关联)。<br /><br />　　ii. iterator则可以利用二级缓存，对于一条查询语句，它会先从数据库中找出所有符合条件的记录的ID，再通过ID去缓存找，对于缓存中没有的记录，再构造语句从数据库中查出，因此很容易知道，如果缓存中没有任何符合条件的记录，使用iterator会产生N+1条SQL语句(N为符合条件的记录数)<br /><br />　　iii. 通过iterator，配合缓存管理API，在海量数据查询中可以很好的解决内存问题，如:<br /><br />　　while(it.hasNext()){<br /><br />　　YouObject object = (YouObject)it.next();<br /><br />　　session.evict(youObject);<br /><br />　　sessionFactory.evice(YouObject.class, youObject.getId());<br /><br />　　}<br /><br />　　如果用list方法，很可能就出OutofMemory错误了。<br /><br />　　iv. 通过上面的说明，我想你应该知道如何去使用这两个方法了。<br /><br />　　7、 集合的选用<br /><br />　　在HIBERNATE 3.1文档的“19.5. Understanding Collection performance”中有详细的说明。<br /><br />　　8、 事务控制<br /><br />　　事务方面对性能有影响的主要包括:事务方式的选用，事务隔离级别以及锁的选用<br /><br />　　a) 事务方式选用:如果不涉及多个事务管理器事务的话，不需要使用JTA，只有JDBC的事务控制就可以。<br /><br />　　b) 事务隔离级别:参见标准的SQL事务隔离级别<br /><br />　　c) 锁的选用:悲观锁(一般由具体的事务管理器实现)，对于长事务效率低，但安全。乐观锁(一般在应用级别实现)，如在HIBERNATE中可以定义 VERSION字段，显然，如果有多个应用操作数据，且这些应用不是用同一种乐观锁机制，则乐观锁会失效。因此，针对不同的数据应有不同的策略，同前面许多情况一样，很多时候我们是在效率与安全/准确性上找一个平衡点，无论如何，优化都不是一个纯技术的问题，你应该对你的应用和业务特征有足够的了解。<br /><br />　　9、 批量操作<br /><br />　　即使是使用JDBC，在进行大批数据更新时，BATCH与不使用BATCH有效率上也有很大的差别。我们可以通过设置batch_size来让其支持批量操作。<br /><br />　　举个例子，要批量删除某表中的对象，如“delete Account”，打出来的语句，会发现HIBERNATE找出了所有ACCOUNT的ID，再进行删除，这主要是为了维护二级缓存，这样效率肯定高不了，在后续的版本中增加了bulk delete/update，但这也无法解决缓存的维护问题。也就是说，由于有了二级缓存的维护问题，HIBERNATE的批量操作效率并不尽如人意!<br /><br />　　从前面许多要点可以看出，很多时候我们是在效率与安全/准确性上找一个平衡点，无论如何，优化都不是一个纯技术的问题，你应该对你的应用和业务特征有足够的了解，一般的，优化方案应在架构设计期就基本确定，否则可能导致没必要的返工，致使项目延期，而作为架构师和项目经理，还要面对开发人员可能的抱怨，必竟，我们对用户需求更改的控制力不大，但技术/架构风险是应该在初期意识到并制定好相关的对策。<br /><br />　　还有一点要注意，应用层的缓存只是锦上添花，永远不要把它当救命稻草，应用的根基(数据库设计，算法，高效的操作语句，恰当API的选择等)才是最重要的。<br /><br />Hibernate的缓存***********************************<br /><br />1、首先设置EhCache，建立配置文件ehcache.xml，默认的位置在class-path，可以放到你的src目录下：<br /><br />Xml代码 <br />＜?xml version="1.0" encoding="UTF-8"?＞   <br />＜ehcache＞   <br />　＜diskStore path="java.io.tmpdir"/＞   <br />　　＜defaultCache   <br />　　　maxElementsInMemory="10000" ＜!-- 缓存最大数目 --＞   <br />　　　eternal="false" ＜!-- 缓存是否持久 --＞   <br />　　　overflowToDisk="true" ＜!-- 是否保存到磁盘，当系统当机时--＞   <br />　　　timeToIdleSeconds="300" ＜!-- 当缓存闲置n秒后销毁 --＞   <br />　　　timeToLiveSeconds="180" ＜!-- 当缓存存活n秒后销毁--＞   <br />　　　diskPersistent="false"  <br />　　　diskExpiryThreadIntervalSeconds= "120"/＞   <br />＜/ehcache＞  <br /><br />＜?xml version="1.0" encoding="UTF-8"?＞<br />＜ehcache＞<br />　＜diskStore path="java.io.tmpdir"/＞<br />　　＜defaultCache<br />　　　maxElementsInMemory="10000" ＜!-- 缓存最大数目 --＞<br />　　　eternal="false" ＜!-- 缓存是否持久 --＞<br />　　　overflowToDisk="true" ＜!-- 是否保存到磁盘，当系统当机时--＞<br />　　　timeToIdleSeconds="300" ＜!-- 当缓存闲置n秒后销毁 --＞<br />　　　timeToLiveSeconds="180" ＜!-- 当缓存存活n秒后销毁--＞<br />　　　diskPersistent="false"<br />　　　diskExpiryThreadIntervalSeconds= "120"/＞<br />＜/ehcache＞  <br /> <br /><br /> <br /><br />　　2、在Hibernate配置文件中设置：<br /><br />Xml代码 <br />＜!-- 设置Hibernate的缓存接口类，这个类在Hibernate包中 --＞   <br />＜property name="cache.provider_class"＞org.hibernate.cache.EhCacheProvider＜/property＞   <br />　＜!-- 是否使用查询缓存 --＞   <br />　＜property name="hibernate.cache.use_query_cache"＞true＜/property＞   <br />　　如果使用spring调用Hibernate的sessionFactory的话，这样设置：   <br />　　＜!--HibernateSession工厂管理 --＞   <br />　　　＜bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"＞   <br />　　　＜property name="dataSource"＞   <br />　　　　＜ref bean="datasource" /＞   <br />　　　＜/property＞   <br />　　　＜property name="hibernateProperties"＞   <br />　　　＜props＞   <br />　　　　＜prop key="hibernate.dialect"＞org.hibernate.dialect.Oracle9Dialect＜/prop＞   <br />　　　　＜prop key="connection.provider_class"＞org.hibernate.connection.C3P0ConnectionProvider＜/prop＞   <br />　　　　＜prop key="hibernate.show_sql"＞true＜/prop＞   <br />　　　　＜prop key="hibernate.cache.use_query_cache"＞true＜/prop＞   <br />　　　　＜prop key="hibernate.cache.provider_class"＞org.hibernate.cache.EhCacheProvider＜/prop＞   <br />　　　＜/props＞   <br />　＜/property＞   <br />　＜property name="mappingDirectoryLocations"＞   <br />　　＜list＞   <br />　　　＜value＞/WEB-INF/classes/cn/rmic/manager/hibernate/＜/value＞   <br />　　＜/list＞   <br />　＜/property＞   <br />＜/bean＞  <br /><br />＜!-- 设置Hibernate的缓存接口类，这个类在Hibernate包中 --＞<br />＜property name="cache.provider_class"＞org.hibernate.cache.EhCacheProvider＜/property＞<br />　＜!-- 是否使用查询缓存 --＞<br />　＜property name="hibernate.cache.use_query_cache"＞true＜/property＞<br />　　如果使用spring调用Hibernate的sessionFactory的话，这样设置：<br />　　＜!--HibernateSession工厂管理 --＞<br />　　　＜bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"＞<br />　　　＜property name="dataSource"＞<br />　　　　＜ref bean="datasource" /＞<br />　　　＜/property＞<br />　　　＜property name="hibernateProperties"＞<br />　　　＜props＞<br />　　　　＜prop key="hibernate.dialect"＞org.hibernate.dialect.Oracle9Dialect＜/prop＞<br />　　　　＜prop key="connection.provider_class"＞org.hibernate.connection.C3P0ConnectionProvider＜/prop＞<br />　　　　＜prop key="hibernate.show_sql"＞true＜/prop＞<br />　　　　＜prop key="hibernate.cache.use_query_cache"＞true＜/prop＞<br />　　　　＜prop key="hibernate.cache.provider_class"＞org.hibernate.cache.EhCacheProvider＜/prop＞<br />　　　＜/props＞<br />　＜/property＞<br />　＜property name="mappingDirectoryLocations"＞<br />　　＜list＞<br />　　　＜value＞/WEB-INF/classes/cn/rmic/manager/hibernate/＜/value＞<br />　　＜/list＞<br />　＜/property＞<br />＜/bean＞  <br />　　说明一下：如果不设置“查询缓存”，那么hibernate只会缓存使用load()方法获得的单个持久化对象，如果想缓存使用 findall()、list()、Iterator()、createCriteria()、createQuery()等方法获得的数据结果集的话，就需要设置<br />hibernate.cache.use_query_cache true 才行<br /><br />　　3、在Hbm文件中添加＜cache usage="read-only"/＞<br /><br />　　4、如果需要“查询缓存”，还需要在使用Query或Criteria()时设置其setCacheable(true);属性<br /><br />　　5、实践出真知，给一段测试程序，如果成功的话第二次查询时不会读取数据库<br /><br />Java代码 <br />package cn.rmic.hibernatesample;   <br />  <br />import java.util.List;   <br />  <br />import org.hibernate.CacheMode;   <br />import org.hibernate.Criteria;   <br />import org.hibernate.Query;   <br />import org.hibernate.Session;   <br />  <br />import cn.rmic.hibernatesample.hibernate.HibernateSessionFactory;   <br />import cn.rmic.manager.po.Resources;   <br />  <br />public class testCacheSelectList ...{   <br />  <br />　/** *//**  <br />　* @param args  <br />　*/  <br />　public static void main(String[] args) ...{   <br />　　// TODO Auto-generated method stub   <br />  <br />　　Session s=HibernateSessionFactory.getSession();   <br />　　Criteria c=s.createCriteria(Resources.class);   <br />　　c.setCacheable(true);   <br />　　List l=c.list();   <br />　　// Query q=s.createQuery("From Resources r")   <br />　　// .setCacheable(true)   <br />　　// .setCacheRegion("frontpages") ;   <br />　　// List l=q.list();   <br />　　Resources resources=(Resources)l.get(0);   <br />　　System.out.println("-1-"+resources.getName());   <br />　　HibernateSessionFactory.closeSession();   <br />　　try ...{   <br />　　　Thread.sleep(5000);   <br />　　} catch (InterruptedException e) ...{   <br />　　　// TODO Auto-generated catch block   <br />　　　e.printStackTrace();   <br />　　}   <br />　　s=HibernateSessionFactory.getSession();   <br />　　c=s.createCriteria(Resources.class);   <br />　　c.setCacheable(true);   <br />　　l=c.list();   <br />　　// q=s.createQuery("From Resources r").setCacheable(true)   <br />　　// .setCacheRegion("frontpages");   <br />　　// l=q.list();   <br />　　resources=(Resources)l.get(0);   <br />　　System.out.println("-2-"+resources.getName());   <br />　　HibernateSessionFactory.closeSession();   <br />　}   <br />}
          <br/>
          <span style="color:red;">
            <a href="http://mrzhanghuzi.javaeye.com/blog/192608#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, 13 May 2008 15:22:04 +0800</pubDate>
        <link>http://mrzhanghuzi.javaeye.com/blog/192608</link>
        <guid>http://mrzhanghuzi.javaeye.com/blog/192608</guid>
      </item>
      <item>
        <title>Hibernate性能优化1( 转)</title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/192605" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/192605</a>&nbsp;
          发表时间: 2008年05月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          有很多人认为Hibernate天生效率比较低，确实，在普遍情况下，需要将执行转换为SQL语句的 Hibernate的效率低于直接JDBC存取，然而，在经过比较好的性能优化之后，Hibernate的性能还是让人相当满意的，特别是应用二级缓存之后，甚至可以获得比较不使用缓存的JDBC更好的性能，下面介绍一些通常的Hibernate的优化策略：<br />    1.抓取 优化<br />     抓取是指Hibernate如何在关联关系之间进行导航的时候，Hibernate如何获取关联对象的策略，其主要定义了两个方面：如何抓取和何时抓取<br />     1）如何抓取。<br />     Hibernate3主要有两种种抓取方式，分别应用于对象关联实例(many-to-one、one-to-one)和对象关联集合(set、map等)，总共是四种变种<br />     JOIN抓取： 通过在SELECT语句中使用OUTER JOIN来获得对象的关联实例或者关联集合）<br />     SELECT抓取： 另外发送一条SELECT语句来抓取当前对象的关联实体和集合<br />     在我的开发经历中，此处对性能的优化是比较有限的，并不值得过多关注<br />     例：<br />     A.应用于对象关联实例（默认是false）<br />     &lt;many-to-one name=".." outer-join="true/false/auto"   .../> <br />     B.应用于对象关联集合（默认是auto）<br />     &lt;set name=".." fetch="join/select" ... ><br />        ....<br />     &lt;/set><br />     2）何时抓取<br />     主要分为延迟加载和立即抓取，默认的情况下Hibernate3对对象关联实采用延迟加载，普通属性采用立即抓取，通过延迟加载和采用适当的抓取粒度，与不采用优化相比往往可以将性能提升数倍<br />     立即抓取：当抓取宿主对象时，同时抓取其关联对象和关联集以及属性<br />     延迟加载：当抓取宿主对象时，并不抓取其关联对象，而是当对其对象进行调用时才加载<br />     例：<br />     A.应用于对象关联实例（默认是延迟加载）<br />     &lt;many-to-one name=".."   lazy="true/false" .../> <br />     B.应用于对象关联集合（默认是延迟加载）<br />     &lt;set name=".." lazy="true/false" ... ><br />        ....<br />     &lt;/set><br />     对于延迟加载，需要注意的时，对延迟对象的使用必须在Session关闭之前进行，Hibernate的 LazyInitalizationException往往就是由于在Session的生命期外使用了延迟加载的对象。当我们进行Web开发时，可以使用 OpenSessionInView模式，当请求开始时打开session，当请求响应结束时才关闭session，不过，在使用 OpenSessionInView模式时，需要注意如果响应时间比较长（业务比较复杂或者客户端是低速网络），将Session资源(也就是数据库的连接)占用太久的话可以会导致资源耗尽<br />     3）抓取粒度<br />     抓取粒度指的是对象在关联关系之间被导航时一次预先加载的数量，Hibernate程序的性能比较差往往就在于没有对抓取粒度仔细考虑，当加载一个列表并在列表中的每个对象中对其关联进行导航时，往往导致N+1条SQL语句查询。<br />     例：<br />     A.应用于对象关联实例（默认为1），注意，对对象关联实例的设 置是在被关联的对象之上的，譬如<br />     class User<br />     {<br />         Group g;<br />     }<br />     那么抓取粒度应该在Group的配置文件之上，见下<br />     &lt;class name="Group" table="group" batch-size=".."><br />         ...<br />     &lt;/class><br />     对该值并没有一个约定俗成的值，根据情况而定，如果被关联表数据比较少，则可以设置地小一些，3-20，如果比较大则可以设到30-50，注意的时候，并不是越多越好，当其值超过50之后，对性能并没有多大改善但却无谓地消耗内存<br />     假设有如下例子：<br />        List&lt;User> users = query.list();<br />     如果有20个User，并对这20个User及其Group进行遍历，如果不设置batch-size（即batch-size="1"），则在最糟糕的情况<br />     下，需要1 + 20条SQL语句，如果设置batch-size="10"，则最好的情况下只需要1 + 2条SQL语句<br />     B.应用于对象关联集合（默认为1）<br />     &lt;set name=".." batch-size="" ... ><br />        ....<br />     &lt;/set><br />     2.二级缓存<br />     Hibernate 对数据的缓存包括两个级：一级缓存，在Session的级别上进行，主要是对象缓存，以其id为键保存对象，在Session的生命期间存在；二级缓存，在SessionFactory的级别上进行，有对象缓存和查询缓存，查询缓存以查询条件为键保存查询结果，在SessionFactory的生命期间存在。默认地，Hibernate只启用一级缓存，通过正确地使用二级缓存，往往可以获得意想不到的性能。<br />     1）对象缓存：<br />     当抓取一个对象之后，Hiberate将其以id为键缓存起来，当下次碰到抓取id相同的对象时，可以使用如下配置<br />     方法1：在缓存对象上配置<br />     &lt;class ...><br />        &lt;cache useage="read-only/write/...." regions="group" /><br />     &lt;/class><br />     useage 表示使用什么类型的缓存，譬如只读缓存、读写缓存等等（具体参见Hibernate参考指南），值得注意的时，有部分缓存在Hibernate的实现中不支持读写缓存，譬如JBossCache在Hibernate的实现中只是一种只读缓存，具体缓存实现对缓存类型的支持情况，可以参见 org.hibernate.cache包<br />     regions表示缓存分块，大部分的缓存实现往往对缓存进行分块，该部分是可选的，详细参见各缓存实现<br />     方法2：在hibernate.cfg.xml中配置<br />     &lt;cache class=".." useage=".." regions=".."/><br />     我认为第二种更好，可以统一管理<br />     2)查询缓存<br />     查询时候将查询结果以查询条件为键保存起来，需要配置如下<br />     A.在hibernate.cfg.xml中配置（启用查询缓存）<br />     &lt;property name="hibernate.cache.use_query_cache">true&lt;/property>   （前面的属性名可参见常量<br />org.hibernate.cfg.Enviroment.USE_QUERY_CACHE）<br />     B.程序<br />     query.setCacheable(true);<br />     query.setCacheRegions(...);<br />     需要注意的是,查询缓存与对象缓存要结合更有效,因为查询缓存仅缓存查询结果列表的主键数据<br />     一般情况下在开发中，对一些比较稳定而又被频繁引用的数据，譬如数据字典之类的，将其进行二级缓存，对一些查询条件和查询数据变化不频繁而又常常被使用的查询，将其进行二级缓存。由于二级缓存是放在内存中，而且Hibernate的缓存不是弱引用缓存（WeekReference），所以注意不要将大块的数据放入其中，否则可能会被内存造成比较大的压力。<br />     3.批量数据操作<br />     当进行大批量数据操作（几万甚至几十几百万）时，需要注意两点，一，批量提交，二，及时清除不需要的一级缓存数据<br />     1）所谓的批量提交，就是不要频繁使用session的flush，每一次进行flush，Hibernate将PO数据于数据库进行同步，对于海量级数据操作来说是性能灾难（同时提交几千条数据和提交一条数据flush一次性能差别可能会是几十倍的差异）。一般将数据操作放在事务中，当事务提交时 Hibernate自动帮你进行flush操作。<br />     2）及时清除不需要的一级缓存数据：由于Hibernate默认采用一级缓存，而在 session的生命期间，所有数据抓取之后会放入一级缓存中，而当数据规模比较庞大时，抓取到内存中的数据会让内存压力非常大，一般分批操作数据，被一次操作之后将一级缓存清除，譬如<br />     session.clear(User.class)<br />     4.杂项<br />     dynamic-insert，dynamic-update，动态插入和动态更新，指的是让Hibernate插入数据时仅插入非空数据，当修改数据时只修改变化的数据，譬如对于 <br />     class User<br />     {<br />        id<br />        username<br />        password<br />     }<br />     如果u.id=1, u.username="ayufox",u.password=null，那么如果不设置动态插入，则其sql语句是 insert into users(id, username, password) values (1, 'ayufox', ')，如果设置则其 sql语句是insert into users(username) valeus('ayufox')<br />     在如上的情况下，如果修改 u.password='11'，那么如果不设置动态更新，则sql语句为update users set username='ayufox', password='11' where id = 1，如果设置则为update user set password='11' where d = 1<br />     设置是在class的映射文件中，如下<br />     &lt;class name="User" table="users" dynamic-insert="true/false" dynamic-update="true/false" ...><br />     &lt;/class><br />　　该设置对性能的提升比较有限
          <br/>
          <span style="color:red;">
            <a href="http://mrzhanghuzi.javaeye.com/blog/192605#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>Tue, 13 May 2008 15:19:49 +0800</pubDate>
        <link>http://mrzhanghuzi.javaeye.com/blog/192605</link>
        <guid>http://mrzhanghuzi.javaeye.com/blog/192605</guid>
      </item>
      <item>
        <title>面向对象的 Javascript 语言特性:闭包(转)</title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/192597" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/192597</a>&nbsp;
          发表时间: 2008年05月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          转自(http://www.cnblogs.com/zitiger/archive/2007/06/08/776777.html)<br />闭包<br /><br />　　闭包意味着内层的函数可以引用存在于包绕它的函数的变量，即使外层的函数的执行已经终止。这一特殊的论题可能是非常强大又非常复杂的。我强烈推荐你们参考本节后面将提及的站点，因为它有一些关于闭包这一话题的精彩的信息。<br />　　我们先来看程序2-13所示的闭包的两个简单例子。<br />　　<br />　　程序2-13. 闭包改善的代码清晰性的两例<br /><br /><br />//得到id为"main"的元素<br />var obj = document.getElementById("main");<br /><br />//改变它的边框样式<br />obj.style.border = "1px solid red";<br /><br />//初始化一个1秒钟以后被调用的回调函数<br />setTimeout(function(){<br />    //此函数将隐藏该元素<br />    obj.style.display = 'none';<br />}, 1000);<br /><br />//用来延迟显示消息的通用函数<br />function delayedAlert( msg, time ) {<br />    //初始化一个被封套的函数<br />    setTimeout(function(){<br />        //此函数使用了来自封套它的函数的变量msg<br />        alert( msg );<br />    }, time );<br />}<br /><br />//调用函数delayedAlert，带两个参数<br />delayedAlert( "Welcome!", 2000 );<br /><br /><br />　　第一个对setTimeout的函数调用，展示了一个的JavaScript新手遇到问题的通俗的例子。在JavaScript新手的程序里像这样的代码时常可以看到：<br /><br /><br />setTimeout("otherFunction()", 1000);<br /><br />//或者甚至<br />setTimeout("otherFunction(" + num + "," + num2 + ")", 1000);<br /><br /><br />　　使用闭包的概念，完全可能的把这种混乱的代码清理掉。第一个例子很简单；有一个回调函数在调用setTimeout函数以后1000微秒以后被调用，而它仍引用了变量obj（定义在全局范围，指向id为"main"的元素）。定义的第二个函数，delayedAlert，展示了一种解决出现的setTimeout混乱的方案，以及函数作用域内可以有闭包的能力。<br />　　你们应该可以发现，当在代码中使用这种简单的闭包时，你所写的东西的清晰性将会提高，免于陷入语法的迷雾之中。<br />　　我们来看一个闭包可能带来的有有趣的副作用。在某些函数化的编程语言里，有一个叫做currying的概念。本质上讲，currying是就是为函数的一些参数预填入值，创建一个更简单的新函数的方法。代码2-14里有一个简单的currying的例子，创建了向另一个函数预填一个参数而得的新函数。<br /><br />　　代码2-14. 使用闭包的函数currying<br /><br /><br />//生成做加法的新函数的函数<br />function addGenerator( num ) {<br />    //返回一个简单函数用来计算两个数的加法，<br />    //其中第一个数字从生成器中借用<br />    return function( toAdd ) {<br />        return num + toAdd<br />    };<br />}<br /><br />//addFive现在是接受一个参数的函数，<br />//此函数将给参数加5，返回结果数字<br />var addFive = addGenerator( 5 );<br /><br />//这里我们可以看到，当传给它参数4的时候<br />//函数addFive的结果为9<br />alert( addFive( 4 ) == 9 );<br /><br /><br />　　闭包还能解决另一个常见的JavaScript编码方面的问题。JavaScript新手趋向于在全局作用域里放置许多变量。这一般被认为是不好的习惯，因为那些变量可能悄悄地影响其它的库，导致令人迷惑的问题的产生。使用一个自执行的、匿名的函数，你可以从根本上隐藏所有的通常的全局变量，使它们对其它代码不可见，如程序2-15所示。<br />　　<br />　　代码2-15. 使用匿名函数从全局作用域隐藏变量的例子<br /><br /><br />//创建一个用作包装的匿名函数<br />(function(){<br />    //这个变量通常情况下应该是全局的<br />    var msg = "Thanks for visiting!";<br /><br />    //为全局对象绑定新的函数<br />    window.onunload = function(){<br />        //使用了“隐藏”的变量<br />        alert( msg );<br />    };<br /><br />//关闭匿名函数并执行之<br />})();<br /><br /><br />　　最后，让我们来看使用闭包时出现的一个问题。闭包允许你引用存在于父级函数中的变量。然而，它并不是提供该变量创建时的值；它提供的是父级函数中该变量最后的值。你会看到这个问题最通常是在一个for循环中。有一个变量被用作迭代器(比如i)，在for内部新的函数被创建，并使用了闭包来引用该迭代器。问题是，当新的闭包函数被调用时，它们将会引用该iterator最后的值(比如，一个数组的最后位置)，而不是你所期望的那个。程序2-16的例子说明，使用匿名函数激发作用域，在其中创建一个合乎期望的闭包是可能的。<br /><br />　　程序2-16. 使用匿名函数激发一个创建多个闭包函数所需的作用域的例子<br /><br /><br />//id为"main"的一个元素<br />var obj = document.getElementById("main");<br /><br />//用来绑定的items数组<br />var items = [ "click", "keypress" ];<br /><br />//遍历items中的每一项<br />for ( var i = 0; i &lt; items.length; i++ ) {<br />    //用自执行的匿名函数来激发作用域<br />    (function(){<br />        //在些作用域内存储值<br />        var item = items[i];<br />        //为obj元素绑定函数<br />        obj[ "on" + item ] = function() {<br />            //item引用一个父级的变量，<br />            //该变量在此for循环的上文中已被成功地scoped(?)<br />            alert( "Thanks for your " + item );<br />        };<br />    })();<br />}<br /><br /><br />　　闭包的概念并非轻易可以掌握的；我着实花了大量的时间和精力才彻底弄清闭包有多么强大。幸运的是，有一个精彩的资源解释了JavaScript中的闭包是怎么工作的：Jim Jey的"JavaScript闭包"，网址是http://jibbering.com/faq/faq_notes/closures.html。<br /><br />最后，我们将研究上下文的概念，这是许多JavaScript的面向对象特性赖以建立的基石。
          <br/>
          <span style="color:red;">
            <a href="http://mrzhanghuzi.javaeye.com/blog/192597#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>Tue, 13 May 2008 14:55:12 +0800</pubDate>
        <link>http://mrzhanghuzi.javaeye.com/blog/192597</link>
        <guid>http://mrzhanghuzi.javaeye.com/blog/192597</guid>
      </item>
      <item>
        <title>JavaScript：prototype属性使用说明(转)</title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/192594" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/192594</a>&nbsp;
          发表时间: 2008年05月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          转自(http://bokee.shinylife.net/blog/article.asp?id=455)<br />prototype 是在 IE 4 及其以后版本引入的一个针对于某一类的对象的方法，而且特殊的地方便在于：它是一个给类的对象添加方法的方法！这一点可能听起来会有点乱，别急，下面我便通过实例对这一特殊的方法作已下讲解：<br /><br />　　首先，我们要先了解一下类的概念，JavaScript 本身是一种面向对象的语言，它所涉及的元素根据其属性的不同都依附于某一个特定的类。我们所常见的类包括：数组变量(Array)、逻辑变量(Boolean)、日期变量(Date)、结构变量(Function)、数值变量(Number)、对象变量(Object)、字符串变量(String) 等，而相关的类的方法，也是程序员经常用到的（在这里要区分一下类的注意和属性发方法），例如数组的push方法、日期的get系列方法、字符串的split方法等等，<br /><br />　　但是在实际的编程过程中不知道有没有感觉到现有方法的不足？prototype 方法应运而生！下面，将通过实例由浅入深讲解 prototype 的具体使用方法：<br /><br /><br />1、最简单的例子，了解 prototype：<br />(1) Number.add(num)：作用，数字相加<br />实现方法：Number.prototype.add = function(num){return(this+num);}<br />试验：alert((3).add(15)) -> 显示 18<br /><br /><br />(2) Boolean.rev(): 作用，布尔变量取反<br />实现方法：Boolean.prototype.rev = function(){return(!this);}<br />试验：alert((true).rev()) -> 显示 false<br /><br />是不是很简单？这一节仅仅是告诉读者又这么一种方法，这种方法是这样运用的。<br /><br /><br />2、已有方法的实现和增强，初识 prototype：<br />(1) Array.push(new_element)<br />　　作用：在数组末尾加入一个新的元素<br />　　实现方法：<br />　　Array.prototype.push = function(new_element){<br />        this[this.length]=new_element;<br />        return this.length;<br />    }<br />　　让我们进一步来增强他，让他可以一次增加多个元素！<br />　　实现方法：<br />　　Array.prototype.pushPro = function() {<br />        var currentLength = this.length;<br />        for (var i = 0; i &lt; arguments.length; i++) {<br />            this[currentLength + i] = arguments[i];<br />        }<br />        return this.length;<br />    }<br />　　应该不难看懂吧？以此类推，你可以考虑一下如何通过增强 Array.pop 来实现删除任意位置，任意多个元素（具体代码就不再细说了）<br /><br />(2) String.length<br />　　作用：这实际上是 String 类的一个属性，但是由于 JavaScript 将全角、半角均视为是一个字符，在一些实际运用中可能会造成一定的问题，现在我们通过 prototype 来弥补这部不足。<br />　　实现方法：<br />　　String.prototype.cnLength = function(){<br />        var arr=this.match(/[^\x00-\xff]/ig);<br />        return this.length+(arr==null?0:arr.length);<br />    }<br />　　试验：alert("EaseWe空间Spaces".cnLength()) -> 显示 16<br />　　这里用到了一些正则表达式的方法和全角字符的编码原理，由于属于另两个比较大的类别，本文不加说明，请参考相关材料。<br /><br /><br />3、新功能的实现，深入 prototype：在实际编程中所用到的肯定不只是已有方法的增强，更多的实行的功能的要求，下面我就举两个用 prototype 解决实际问题的例子：<br />(1) String.left()<br />　　问题：用过 vb 的应该都知道left函数，从字符串左边取 n 个字符，但是不足是将全角、半角均视为是一个字符，造成在中英文混排的版面中不能截取等长的字符串<br />　　作用：从字符串左边截取 n 个字符，并支持全角半角字符的区分<br />　　实现方法：<br />　　String.prototype.left = function(num,mode){<br />        if(!/\d+/.test(num))return(this);<br />        var str = this.substr(0,num);<br />        if(!mode) return str;<br />        var n = str.Tlength() - str.length;<br />        num = num - parseInt(n/2);<br />        return this.substr(0,num);<br />    }<br />　　试验：<br />     alert("EaseWe空间Spaces".left(8)) -> 显示 EaseWe空间<br />     alert("EaseWe空间Spaces".left(8,true)) -> 显示 EaseWe空<br />　　本方法用到了上面所提到的String.Tlength()方法，自定义方法之间也能组合出一些不错的新方法呀！<br /><br />(2) Date.DayDiff()<br />　　作用：计算出两个日期型变量的间隔时间（年、月、日、周）<br />　　实现方法：<br />　　Date.prototype.DayDiff = function(cDate,mode){<br />        try{<br />            cDate.getYear();<br />        }catch(e){<br />            return(0);<br />        }<br />        var base =60*60*24*1000;<br />        var result = Math.abs(this - cDate);<br />        switch(mode){<br />            case "y":<br />                result/=base*365;<br />                break;<br />            case "m":<br />                result/=base*365/12;<br />                break;<br />            case "w":<br />                result/=base*7;<br />                break;<br />            default:<br />                result/=base;<br />                break;<br />        }<br />        return(Math.floor(result));<br />    }<br />　　试验：alert((new Date()).DayDiff((new Date(2002,0,1)))) -> 显示 329<br />     alert((new Date()).DayDiff((new Date(2002,0,1)),"m")) -> 显示 10<br />　　当然，也可以进一步扩充，得出响应的小时、分钟，甚至是秒。<br /><br />(3) Number.fact()<br />　　作用：某一数字的阶乘<br />　　实现方法：<br />　　Number.prototype.fact=function(){<br />        var num = Math.floor(this);<br />        if(num&lt;0)return NaN;<br />        if(num==0 || num==1)<br />            return 1;<br />        else<br />            return (num*(num-1).fact());<br />    }<br />　　试验：alert((4).fact()) -> 显示 24<br />　　这个方法主要是说明了递归的方法在 prototype 方法中也是可行的！
          <br/>
          <span style="color:red;">
            <a href="http://mrzhanghuzi.javaeye.com/blog/192594#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, 13 May 2008 14:52:44 +0800</pubDate>
        <link>http://mrzhanghuzi.javaeye.com/blog/192594</link>
        <guid>http://mrzhanghuzi.javaeye.com/blog/192594</guid>
      </item>
      <item>
        <title>悟透JavaScript(转帖) </title>
        <author>mrzhanghuzi</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://mrzhanghuzi.javaeye.com">mrzhanghuzi</a>&nbsp;
          链接：<a href="http://mrzhanghuzi.javaeye.com/blog/192592" style="color:red;">http://mrzhanghuzi.javaeye.com/blog/192592</a>&nbsp;
          发表时间: 2008年05月13日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: large">[/size][size=xx-large]</span>转自(http://www.cnblogs.com/leadzen/archive/2008/02/25/1073404.html)<br />引子<br /><br />    编程世界里只存在两种基本元素，一个是数据，一个是代码。编程世界就是在数据和代码千丝万缕的纠缠中呈现出无限的生机和活力。<br /><br />    数据天生就是文静的，总想保持自己固有的本色；而代码却天生活泼，总想改变这个世界。<br /> <br />   你看，数据代码间的关系与物质能量间的关系有着惊人的相似。数据也是有惯性的，如果没有代码来施加外力，她总保持自己原来的状态。而代码就象能量，他存在的唯一目的，就是要努力改变数据原来的状态。在代码改变数据的同时，也会因为数据的抗拒而反过来影响或改变代码原有的趋势。甚至在某些情况下，数据可以转变为代码，而代码却又有可能被转变为数据，或许还存在一个类似E=MC2形式的数码转换方程呢。然而，就是在数据和代码间这种即矛盾又统一的运转中，总能体现出计算机世界的规律，这些规律正是我们编写的程序逻辑。<br /><br />    不过，由于不同程序员有着不同的世界观，这些数据和代码看起来也就不尽相同。于是，不同世界观的程序员们运用各自的方法论，推动着编程世界的进化和发展。<br /> <br />    众所周知，当今最流行的编程思想莫过于面向对象编程的思想。为什么面向对象的思想能迅速风靡编程世界呢？因为面向对象的思想首次把数据和代码结合成统一体，并以一个简单的对象概念呈现给编程者。这一下子就将原来那些杂乱的算法与子程序，以及纠缠不清的复杂数据结构，划分成清晰而有序的对象结构，从而理清了数据与代码在我们心中那团乱麻般的结。我们又可以有一个更清晰的思维，在另一个思想高度上去探索更加浩瀚的编程世界了。<br /><br />    在五祖弘忍讲授完《对象真经》之后的一天，他对众弟子们说：“经已讲完，想必尔等应该有所感悟，请各自写个偈子来看”。大弟子神秀是被大家公认为悟性最高的师兄，他的偈子写道：“身是对象树，心如类般明。朝朝勤拂拭，莫让惹尘埃！”。此偈一出，立即引起师兄弟们的轰动，大家都说写得太好了。只有火头僧慧能看后，轻轻地叹了口气，又随手在墙上写道：“对象本无根，类型亦无形。本来无一物，何处惹尘埃？”。然后摇了摇头，扬长而去。大家看了慧能的偈子都说：“写的什么乱七八糟的啊，看不懂”。师父弘忍看了神秀的诗偈也点头称赞，再看慧能的诗偈之后默然摇头。就在当天夜里，弘忍却悄悄把慧能叫到自己的禅房，将珍藏多年的软件真经传授于他，然后让他趁着月色连夜逃走...<br /><br />    后来，慧能果然不负师父厚望，在南方开创了禅宗另一个广阔的天空。而慧能当年带走的软件真经中就有一本是《JavaScript真经》！<br /><br />回归简单<br /><br />    要理解JavaScript，你得首先放下对象和类的概念，回到数据和代码的本原。前面说过，编程世界只有数据和代码两种基本元素，而这两种元素又有着纠缠不清的关系。JavaScript就是把数据和代码都简化到最原始的程度。<br /><br />    JavaScript中的数据很简洁的。简单数据只有 undefined, null, boolean, number和string这五种，而复杂数据只有一种，即object。这就好比中国古典的朴素唯物思想，把世界最基本的元素归为金木水火土，其他复杂的物质都是由这五种基本元素组成。<br /><br />    JavaScript中的代码只体现为一种形式，就是function。<br /><br />    注意：以上单词都是小写的，不要和Number, String, Object, Function等JavaScript内置函数混淆了。要知道，JavaScript语言是区分大小写的呀!<br /><br />    任何一个JavaScript的标识、常量、变量和参数都只是unfined, null, bool, number, string, object 和 function类型中的一种，也就typeof返回值表明的类型。除此之外没有其他类型了。<br /><br />    先说说简单数据类型吧。<br /><br />    undefined:   代表一切未知的事物，啥都没有，无法想象，代码也就更无法去处理了。<br />                      注意：typeof(undefined) 返回也是 undefined。<br />                              可以将undefined赋值给任何变量或属性，但并不意味了清除了该变量，反而会因此多了一个属性。<br /><br />    null:            有那么一个概念，但没有东西。无中似有，有中还无。虽难以想象，但已经可以用代码来处理了。<br />                      注意：typeof(null)返回object，但null并非object，具有null值的变量也并非object。<br /><br />    boolean:      是就是，非就非，没有疑义。对就对，错就错，绝对明确。既能被代码处理，也可以控制代码的流程。<br /><br />    number:      线性的事物，大小和次序分明，多而不乱。便于代码进行批量处理，也控制代码的迭代和循环等。<br />                      注意：typeof(NaN)和typeof(Infinity)都返回number 。<br />                              NaN参与任何数值计算的结构都是NaN，而且 NaN != NaN 。<br />                              Infinity / Infinity = NaN 。<br /><br />    string:         面向人类的理性事物，而不是机器信号。人机信息沟通，代码据此理解人的意图等等，都靠它了。<br /><br />     简单类型都不是对象，JavaScript没有将对象化的能力赋予这些简单类型。直接被赋予简单类型常量值的标识符、变量和参数都不是一个对象。<br /><br />    所谓“对象化”，就是可以将数据和代码组织成复杂结构的能力。JavaScript中只有object类型和function类型提供了对象化的能力。<br /><br />没有类<br /><br />    object就是对象的类型。在JavaScript中不管多么复杂的数据和代码，都可以组织成object形式的对象。<br /><br />    但JavaScript却没有 “类”的概念！<br /><br />    对于许多面向对象的程序员来说，这恐怕是JavaScript中最难以理解的地方。是啊，几乎任何讲面向对象的书中，第一个要讲的就是“类”的概念，这可是面向对象的支柱。这突然没有了“类”，我们就象一下子没了精神支柱，感到六神无主。看来，要放下对象和类，达到“对象本无根，类型亦无形”的境界确实是件不容易的事情啊。<br /><br />    这样，我们先来看一段JavaScript程序：<br />    var life = {};<br />    for(life.age = 1; life.age &lt;= 3; life.age++)<br />    {<br />        switch(life.age)<br />        {<br />            case 1: life.body = "卵细胞";<br />                    life.say = function(){alert(this.age+this.body)};<br />                    break;<br />            case 2: life.tail = "尾巴";<br />                    life.gill = "腮";<br />                    life.body = "蝌蚪";<br />                    life.say = function(){alert(this.age+this.body+"-"+this.tail+","+this.gill)};<br />                    break;<br />            case 3: delete life.tail;<br />                    delete life.gill;<br />                    life.legs = "四条腿";<br />                    life.lung = "肺";<br />                    life.body = "青蛙";<br />                    life.say = function(){alert(this.age+this.body+"-"+this.legs+","+this.lung)};<br />                    break;<br />        };<br />        life.say();<br />    };<br />    这段JavaScript程序一开始产生了一个生命对象life，life诞生时只是一个光溜溜的对象，没有任何属性和方法。在第一次生命过程中，它有了一个身体属性body，并有了一个say方法，看起来是一个“卵细胞”。在第二次生命过程中，它又长出了“尾巴”和“腮”，有了tail和gill属性，显然它是一个“蝌蚪”。在第三次生命过程中，它的tail和gill属性消失了，但又长出了“四条腿”和“肺”，有了legs和lung属性，从而最终变成了“青蛙”。如果，你的想像力丰富的话，或许还能让它变成英俊的“王子”，娶个美丽的“公主”什么的。不过，在看完这段程序之后，请你思考一个问题：<br /><br />    我们一定需要类吗？<br /><br />    还记得儿时那个“小蝌蚪找妈妈”的童话吗？也许就在昨天晚，你的孩子刚好是在这个美丽的童话中进入梦乡的吧。可爱的小蝌蚪也就是在其自身类型不断演化过程中，逐渐变成了和妈妈一样的“类”，从而找到了自己的妈妈。这个童话故事中蕴含的编程哲理就是：对象的“类”是从无到有，又不断演化，最终又消失于无形之中的...<br /><br />    “类”，的确可以帮助我们理解复杂的现实世界，这纷乱的现实世界也的确需要进行分类。但如果我们的思想被“类”束缚住了，“类”也就变成了“累”。想象一下，如果一个生命对象开始的时就被规定了固定的“类”，那么它还能演化吗？蝌蚪还能变成青蛙吗？还可以给孩子们讲小蝌蚪找妈妈的故事吗？<br /><br />    所以，JavaScript中没有“类”，类已化于无形，与对象融为一体。正是由于放下了“类”这个概念，JavaScript的对象才有了其他编程语言所没有的活力。<br /><br />    如果，此时你的内心深处开始有所感悟，那么你已经逐渐开始理解JavaScript的禅机了。<br /><br />函数的魔力<br /><br />    接下来，我们再讨论一下JavaScript函数的魔力吧。<br /><br />    JavaScript的代码就只有function一种形式，function就是函数的类型。也许其他编程语言还有procedure或 method等代码概念，但在JavaScript里只有function一种形式。当我们写下一个函数的时候，只不过是建立了一个function类型的实体而已。请看下面的程序：<br />    function myfunc()<br />    {<br />        alert("hello");<br />    };<br />    <br />    alert(typeof(myfunc));<br />    这个代码运行之后可以看到typeof(myfunc)返回的是function。以上的函数写法我们称之为“定义式”的，如果我们将其改写成下面的“变量式”的，就更容易理解了：<br />    var myfunc = function ()<br />        {<br />            alert("hello");<br />        };<br />    <br />    alert(typeof(myfunc));<br />    这里明确定义了一个变量myfunc，它的初始值被赋予了一个function的实体。因此，typeof(myfunc)返回的也是function。其实，这两种函数的写法是等价的，除了一点细微差别，其内部实现完全相同。也就是说，我们写的这些JavaScript函数只是一个命了名的变量而已，其变量类型即为function，变量的值就是我们编写的函数代码体。<br /><br />    聪明的你或许立即会进一步的追问：既然函数只是变量，那么变量就可以被随意赋值并用到任意地方啰？<br /><br />    我们来看看下面的代码：<br />    var myfunc = function ()<br />        {<br />            alert("hello");<br />        };<br />    myfunc(); //第一次调用myfunc，输出hello<br />    <br />    myfunc = function ()<br />        {<br />            alert("yeah");<br />        };    <br />    myfunc(); //第二次调用myfunc，将输出yeah<br />    这个程序运行的结果告诉我们：答案是肯定的！在第一次调用函数之后，函数变量又被赋予了新的函数代码体，使得第二次调用该函数时，出现了不同的输出。<br /><br />    好了，我们又来把上面的代码改成第一种定义式的函数形式：<br />    function myfunc ()<br />    {<br />        alert("hello");<br />    };<br />    myfunc(); //这里调用myfunc，输出yeah而不是hello<br />    <br />    function myfunc ()<br />    {<br />        alert("yeah");<br />    };    <br />    myfunc(); //这里调用myfunc，当然输出yeah<br />    按理说，两个签名完全相同的函数，在其他编程语言中应该是非法的。但在JavaScript中，这没错。不过，程序运行之后却发现一个奇怪的现象：两次调用都只是最后那个函数里输出的值！显然第一个函数没有起到任何作用。这又是为什么呢？<br /><br />    原来，JavaScript执行引擎并非一行一行地分析和执行程序，而是一段一段地分析执行的。而且，在同一段程序的分析执行中，定义式的函数语句会被提取出来优先执行。函数定义执行完之后，才会按顺序执行其他语句代码。也就是说，在第一次调用myfunc之前，第一个函数语句定义的代码逻辑，已被第二个函数定义语句覆盖了。所以，两次都调用都是执行最后一个函数逻辑了。<br /><br />    如果把这个JavaScript代码分成两段，例如将它们写在一个html中，并用&lt;script/>标签将其分成这样的两块：<br />&lt;script><br />    function myfunc ()<br />    {<br />        alert("hello");<br />    };<br />    myfunc(); //这里调用myfunc，输出hello<br />&lt;/script><br /><br />&lt;script><br />    function myfunc ()<br />    {<br />        alert("yeah");<br />    };    <br />    myfunc(); //这里调用myfunc，输出yeah<br />&lt;/script><br />    这时，输出才是各自按顺序来的，也证明了JavaScript的确是一段段地执行的。<br /><br />    一段代码中的定义式函数语句会优先执行，这似乎有点象静态语言的编译概念。所以，这一特征也被有些人称为：JavaScript的“预编译”。<br /><br />    大多数情况下，我们也没有必要去纠缠这些细节问题。只要你记住一点：JavaScript里的代码也是一种数据，同样可以被任意赋值和修改的，而它的值就是代码的逻辑。只是，与一般数据不同的是，函数是可以被调用执行的。<br /><br />    不过，如果JavaScript函数仅仅只有这点道行的话，这与C++的函数指针，DELPHI的方法指针，C#的委托相比，又有啥稀奇嘛！然而，JavaScript函数的神奇之处还体现在另外两个方面：一是函数function类型本身也具有对象化的能力，二是函数function与对象 object超然的结合能力。<br /><br />奇妙的对象<br /><br />    先来说说函数的对象化能力。<br /><br />    任何一个函数都可以为其动态地添加或去除属性，这些属性可以是简单类型，可以是对象，也可以是其他函数。也就是说，函数具有对象的全部特征，你完全可以把函数当对象来用。其实，函数就是对象，只不过比一般的对象多了一个括号“()”操作符，这个操作符用来执行函数的逻辑。即，函数本身还可以被调用，一般对象却不可以被调用，除此之外完全相同。请看下面的代码：<br />    function Sing()<br />    {<br />        with(arguments.callee)<br />          alert(author + "：" + poem);<br />    };<br />    Sing.author = "李白";<br />    Sing.poem = "汉家秦地月，流影照明妃。一上玉关道，天涯去不归";<br />    Sing();<br />    Sing.author = "李战";<br />    Sing.poem = "日出汉家天，月落阴山前。女儿琵琶怨，已唱三千年";<br />    Sing();<br />    在这段代码中，Sing函数被定义后，又给Sing函数动态地增加了author和poem属性。将author和poem属性设为不同的作者和诗句，在调用Sing()时就能显示出不同的结果。这个示例用一种诗情画意的方式，让我们理解了JavaScript函数就是对象的本质，也感受到了JavaScript语言的优美。<br /><br />    好了，以上的讲述，我们应该算理解了function类型的东西都是和object类型一样的东西，这种东西被我们称为“对象”。我们的确可以这样去看待这些“对象”，因为它们既有“属性”也有“方法”嘛。但下面的代码又会让我们产生新的疑惑：<br />    var anObject = {};  //一个对象<br />    anObject.aProperty = "Property of object";  //对象的一个属性<br />    anObject.aMethod = function(){alert("Method of object")}; //对象的一个方法<br />    //主要看下面：<br />    alert(anObject["aProperty"]);   //可以将对象当数组以属性名作为下标来访问属性<br />    anObject["aMethod"]();          //可以将对象当数组以方法名作为下标来调用方法<br />    for( var s in anObject)           //遍历对象的所有属性和方法进行迭代化处理<br />        alert(s + " is a " + typeof(anObject[s]));<br />    同样对于function类型的对象也是一样：<br />    var aFunction = function() {};  //一个函数<br />    aFunction.aProperty = "Property of function";  //函数的一个属性<br />    aFunction.aMethod = function(){alert("Method of function")}; //函数的一个方法<br />    //主要看下面：<br />    alert(aFunction["aProperty"]);   //可以将函数当数组以属性名作为下标来访问属性<br />    aFunction["aMethod"]();          //可以将函数当数组以方法名作为下标来调用方法<br />    for( var s in aFunction)           //遍历函数的所有属性和方法进行迭代化处理<br />        alert(s + " is a " + typeof(aFunction[s]));<br />    是的，对象和函数可以象数组一样，用属性名或方法名作为下标来访问并处理。那么，它到底应该算是数组呢，还是算对象？<br /><br />    我们知道，数组应该算是线性数据结构，线性数据结构一般有一定的规律，适合进行统一的批量迭代操作等，有点像波。而对象是离散数据结构，适合描述分散的和个性化的东西，有点像粒子。因此，我们也可以这样问：JavaScript里的对象到底是波还是粒子？<br /><br />    如果存在对象量子论，那么答案一定是：波粒二象性！<br /><br />    因此，JavaScript里的函数和对象既有对象的特征也有数组的特征。这里的数组被称为“字典”，一种可以任意伸缩的名称值对儿的集合。其实， object和function的内部实现就是一个字典结构，但这种字典结构却通过严谨而精巧的语法表现出了丰富的外观。正如量子力学在一些地方用粒子来解释和处理问题，而在另一些地方却用波来解释和处理问题。你也可以在需要的时候，自由选择用对象还是数组来解释和处理问题。只要善于把握JavaScript的这些奇妙特性，就可以编写出很多简洁而强大的代码来。<br /><br />放下对象<br /><br />    我们再来看看function与object的超然结合吧。<br /><br />    在面向对象的编程世界里，数据与代码的有机结合就构成了对象的概念。自从有了对象，编程世界就被划分成两部分，一个是对象内的世界，一个是对象外的世界。对象天生具有自私的一面，外面的世界未经允许是不可访问对象内部的。对象也有大方的一面，它对外提供属性和方法，也为他人服务。不过，在这里我们要谈到一个有趣的问题，就是“对象的自我意识”。<br /><br />    什么？没听错吧？对象有自我意识？<br /><br />    可能对许多程序员来说，这的确是第一次听说。不过，请君看看C++、C#和Java的this，DELPHI的self，还有VB的me，或许你会恍然大悟！当然，也可能只是说句“不过如此”而已。<br /><br />    然而，就在对象将世界划分为内外两部分的同时，对象的“自我”也就随之产生。“自我意识”是生命的最基本特征！正是由于对象这种强大的生命力，才使得编程世界充满无限的生机和活力。<br /><br />    但对象的“自我意识”在带给我们快乐的同时也带来了痛苦和烦恼。我们给对象赋予了太多欲望，总希望它们能做更多的事情。然而，对象的自私使得它们互相争抢系统资源，对象的自负让对象变得复杂和臃肿，对象的自欺也往往带来挥之不去的错误和异常。我们为什么会有这么多的痛苦和烦恼呢？<br /> <br />    为此，有一个人，在对象树下，整整想了九九八十一天，终于悟出了生命的痛苦来自于欲望，但究其欲望的根源是来自于自我意识。于是他放下了“自我”，在对象树下成了佛，从此他开始普度众生，传播真经。他的名字就叫释迦摩尼，而《JavaScript真经》正是他所传经书中的一本。<br /><br />    JavaScript中也有this，但这个this却与C++、C#或Java等语言的this不同。一般编程语言的this就是对象自己，而 JavaScript的this却并不一定！this可能是我，也可能是你，可能是他，反正是我中有你，你中有我，这就不能用原来的那个“自我”来理解 JavaScript这个this的含义了。为此，我们必须首先放下原来对象的那个“自我”。<br /><br />    我们来看下面的代码：<br />    function WhoAmI()       //定义一个函数WhoAmI<br />    {<br />        alert("I'm " + this.name + " of " + typeof(this));<br />    };<br />    <br />    WhoAmI();   //此时是this当前这段代码的全局对象，在浏览器中就是window对象，其name属性为空字符串。输出：I'm of object<br /><br />    var BillGates = {name: "Bill Gates"};<br />    BillGates.WhoAmI = WhoAmI;  //将函数WhoAmI作为BillGates的方法。<br />    BillGates.WhoAmI();         //此时的this是BillGates。输出：I'm Bill Gates of object<br />    <br />    var SteveJobs = {name: "Steve Jobs"};<br />    SteveJobs.WhoAmI = WhoAmI;  //将函数WhoAmI作为SteveJobs的方法。<br />    SteveJobs.WhoAmI();         //此时的this是SteveJobs。输出：I'm Steve Jobs of object<br /><br />    WhoAmI.call(BillGates);     //直接将BillGates作为this，调用WhoAmI。输出：I'm Bill Gates of object<br />    WhoAmI.call(SteveJobs);     //直接将SteveJobs作为this，调用WhoAmI。输出：I'm Steve Jobs of object<br />    <br />    BillGates.WhoAmI.call(SteveJobs);   //将SteveJobs作为this，却调用BillGates的WhoAmI方法。输出：I'm Steve Jobs of object<br />    SteveJobs.WhoAmI.call(BillGates);   //将BillGates作为this，却调用SteveJobs的WhoAmI方法。输出：I'm Bill Gates of object<br /><br />    WhoAmI.WhoAmI = WhoAmI;     //将WhoAmI函数设置为自身的方法。<br />    WhoAmI.name = "WhoAmI";<br />    WhoAmI.WhoAmI();            //此时的this是WhoAmI函数自己。输出：I'm WhoAmI of function<br />        <br />    ({name: "nobody", WhoAmI: WhoAmI}).WhoAmI();    //临时创建一个匿名对象并设置属性后调用WhoAmI方法。输出：I'm nobody of object<br />    从上面的代码可以看出，同一个函数可以从不同的角度来调用，this并不一定是函数本身所属的对象。this只是在任意对象和function元素结合时的一个概念，是种结合比起一般对象语言的默认结合更加灵活，显得更加超然和洒脱。<br /><br />    在JavaScript函数中，你只能把this看成当前要服务的“这个”对象。this是一个特殊的内置参数，根据this参数，您可以访问到“这个”对象的属性和方法，但却不能给this参数赋值。在一般对象语言中，方法体代码中的this可以省略的，成员默认都首先是“自己”的。但JavaScript却不同，由于不存在“自我”，当访问“这个”对象时，this不可省略！<br /><br />    JavaScript提供了传递this参数的多种形式和手段，其中，象BillGates.WhoAmI()和SteveJobs.WhoAmI()这种形式，是传递this参数最正规的形式，此时的this就是函数所属的对象本身。而大多数情况下，我们也几乎很少去采用那些借花仙佛的调用形式。但只我们要明白JavaScript的这个“自我”与其他编程语言的“自我”是不同的，这是一个放下了的“自我”，这就是JavaScript特有的世界观。<br /><br />对象素描<br /><br />    已经说了许多了许多话题了，但有一个很基本的问题我们忘了讨论，那就是：怎样建立对象？<br /><br />    在前面的示例中，我们已经涉及到了对象的建立了。我们使用了一种被称为JavaScript Object Notation(缩写JSON)的形式，翻译为中文就是“JavaScript对象表示法”。<br /><br />    JSON为创建对象提供了非常简单的方法。例如，<br />    创建一个没有任何属性的对象：<br />var o = {};<br />    创建一个对象并设置属性及初始值：<br />var person = {name: "Angel", age: 18, married: false};<br />    创建一个对象并设置属性和方法：<br />var speaker = {text: "Hello World", say: function(){alert(this.text)}};<br />     创建一个更复杂的对象，嵌套其他对象和对象数组等：<br />    var company =<br />    {<br />        name: "Microsoft",<br />        product: "softwares",<br />        chairman: {name: "Bill Gates", age: 53, Married: true},<br />        employees: [{name: "Angel", age: 26, Married: false}, {name: "Hanson", age: 32, Marred: true}],<br />        readme: function() {document.write(this.name + " product " + this.product);}<br />    };<br />    JSON的形式就是用大括“{}”号包括起来的项目列表，每一个项目间并用逗号“,”分隔，而项目就是用冒号“:”分隔的属性名和属性值。这是典型的字典表示形式，也再次表明了 JavaScript里的对象就是字典结构。不管多么复杂的对象，都可以被一句JSON代码来创建并赋值。<br /><br />    其实，JSON就是JavaScript对象最好的序列化形式，它比XML更简洁也更省空间。对象可以作为一个JSON形式的字符串，在网络间自由传递和交换信息。而当需要将这个JSON字符串变成一个JavaScript对象时，只需要使用eval函数这个强大的数码转换引擎，就立即能得到一个JavaScript内存对象。正是由于JSON的这种简单朴素的天生丽质，才使得她在AJAX舞台上成为璀璨夺目的明星。<br /><br />    JavaScript就是这样，把面向对象那些看似复杂的东西，用及其简洁的形式表达出来。卸下对象浮华的浓妆，还对象一个眉目清晰！<br /><br />构造对象<br /> <br />    好了，接下我们来讨论一下对象的另一种创建方法。<br /><br />    除JSON外，在JavaScript中我们可以使用new操作符结合一个函数的形式来创建对象。例如：<br />    function MyFunc() {};         //定义一个空函数<br />    var anObj = new MyFunc();  //使用new操作符，借助MyFun函数，就创建了一个对象<br />    JavaScript的这种创建对象的方式可真有意思，如何去理解这种写法呢？<br /> <br />   其实，可以把上面的代码改写成这种等价形式：<br />    function MyFunc(){};<br />    var anObj = {};     //创建一个对象<br />    MyFunc.call(anObj); //将anObj对象作为this指针调用MyFunc函数<br />    我们就可以这样理解，JavaScript先用new操作符创建了一个对象，紧接着就将这个对象作为this参数调用了后面的函数。其实，JavaScript内部就是这么做的，而且任何函数都可以被这样调用！但从 “anObj = new MyFunc()” 这种形式，我们又看到一个熟悉的身影，C++和C#不就是这样创建对象的吗？原来，条条大路通灵山，殊途同归啊！<br /><br />    君看到此处也许会想，我们为什么不可以把这个MyFunc当作构造函数呢？恭喜你，答对了！JavaScript也是这么想的！请看下面的代码： <br /> 1     function Person(name)   //带参数的构造函数<br /> 2     {<br /> 3         this.name = name;   //将参数值赋给给this对象的属性<br /> 4         this.SayHello = function() {alert("Hello, I'm " + this.name);};   //给this对象定义一个SayHello方法。<br /> 5     };<br /> 6 <br /> 7     function Employee(name, salary)     //子构造函数<br /> 8     {<br /> 9         Person.call(this, name);        //将this传给父构造函数<br />10         this.salary = salary;       //设置一个this的salary属性<br />11         this.ShowMeTheMoney = function() {alert(this.name + " $" + this.salary);};  //添加ShowMeTheMoney方法。<br />12     };<br />13     <br />14     var BillGates = new Person("Bill Gates");   //用Person构造函数创建BillGates对象<br />15     var SteveJobs = new Employee("Steve Jobs", 1234);   //用Empolyee构造函数创建SteveJobs对象<br />16 <br />17     BillGates.SayHello();   //显示：I'm Bill Gates<br />18     SteveJobs.SayHello();   //显示：I'm Steve Jobs<br />19     SteveJobs.ShowMeTheMoney();   //显示：Steve Jobs $1234<br />20 <br />21     alert(BillGates.constructor == Person);  //显示：true<br />22     alert(SteveJobs.constructor == Employee);  //显示：true<br />23     <br />24     alert(BillGates.SayHello == SteveJobs.SayHello); //显示：false<br />    这段代码表明，函数不但可以当作构造函数，而且还可以带参数，还可以为对象添加成员和方法。其中的第9行，Employee构造函数又将自己接收的this作为参数调用Person构造函数，这就是相当于调用基类的构造函数。第21、22行还表明这样一个意思：BillGates是由Person构造的，而SteveJobs是由Employee构造的。对象内置的constructor属性还指明了构造对象所用的具体函数！<br /><br />    其实，如果你愿意把函数当作“类”的话，她就是“类”，因为她本来就有“类”的那些特征。难道不是吗？她生出的儿子各个都有相同的特征，而且构造函数也与类同名嘛！<br /><br />    但要注意的是，用构造函数操作this对象创建出来的每一个对象，不但具有各自的成员数据，而且还具有各自的方法数据。换句话说，方法的代码体(体现函数逻辑的数据)在每一个对象中都存在一个副本。尽管每一个代码副本的逻辑是相同的，但对象们确实是各自保存了一份代码体。上例中的最后一句说明了这一实事，这也解释了JavaScript中的函数就是对象的概念。<br /><br />    同一类的对象各自有一份方法代码显然是一种浪费。在传统的对象语言中，方法函数并不象JavaScript那样是个对象概念。即使也有象函数指针、方法指针或委托那样的变化形式，但其实质也是对同一份代码的引用。一般的对象语言很难遇到这种情况。<br /><br />    不过，JavaScript语言有大的灵活性。我们可以先定义一份唯一的方法函数体，并在构造this对象时使用这唯一的函数对象作为其方法，就能共享方法逻辑。例如：<br />    function SayHello()     //先定义一份SayHello函数代码<br />    {<br />        alert("Hello, I'm " + this.name);<br />    };<br />    <br />    function Person(name)   //带参数的构造函数<br />    {<br />        this.name = name;   //将参数值赋给给this对象的属性<br />        this.SayHello = SayHello;   //给this对象SayHello方法赋值为前面那份SayHello代码。<br />    };<br /><br />    var BillGates = new Person("Bill Gates");   //创建BillGates对象<br />    var SteveJobs = new Person("Steve Jobs");   //创建SteveJobs对象<br />    <br />    alert(BillGates.SayHello == SteveJobs.SayHello); //显示：true<br />    其中，最后一行的输出结果表明两个对象确实共享了一个函数对象。虽然，这段程序达到了共享了一份方法代码的目的，但却不怎么优雅。因为，定义SayHello方法时反映不出其与Person类的关系。“优雅”这个词用来形容代码，也不知道是谁先提出来的。不过，这个词反映了程序员已经从追求代码的正确、高效、可靠和易读等基础上，向着追求代码的美观感觉和艺术境界的层次发展，程序人生又多了些浪漫色彩。<br /><br />   显然，JavaScript早想到了这一问题，她的设计者们为此提供了一个有趣的prototype概念。<br /><br />初看原型<br /><br />    prototype源自法语，软件界的标准翻译为“原型”，代表事物的初始形态，也含有模型和样板的意义。JavaScript中的prototype概念恰如其分地反映了这个词的内含，我们不能将其理解为C++的prototype那种预先声明的概念。<br /><br />    JavaScript的所有function类型的对象都有一个prototype属性。这个prototype属性本身又是一个object类型的对象，因此我们也可以给这个prototype对象添加任意的属性和方法。既然prototype是对象的“原型”，那么由该函数构造出来的对象应该都会具有这个“原型”的特性。事实上，在构造函数的prototype上定义的所有属性和方法，都是可以通过其构造的对象直接访问和调用的。也可以这么说，prototype提供了一群同类对象共享属性和方法的机制。<br /><br />    我们先来看看下面的代码：<br />    function Person(name)<br />    {<br />        this.name = name;   //设置对象属性，每个对象各自一份属性数据<br />    };<br />    <br />    Person.prototype.SayHello = function()  //给Person函数的prototype添加SayHello方法。<br />    {<br />        alert("Hello, I'm " + this.name);<br />    }<br /><br />    var BillGates = new Person("Bill Gates");   //创建BillGates对象<br />    var SteveJobs = new Person("Steve Jobs");   //创建SteveJobs对象<br /><br />    BillGates.SayHello();   //通过BillGates对象直接调用到SayHello方法<br />    SteveJobs.SayHello();   //通过SteveJobs对象直接调用到SayHello方法<br /><br />    alert(BillGates.SayHello == SteveJobs.SayHello); //因为两个对象是共享prototype的SayHello，所以显示：true<br />    程序运行的结果表明，构造函数的prototype上定义的方法