byterat

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  54 随笔 :: 0 文章 :: 15 评论 :: 0 Trackbacks

2006年10月24日 #

Eclipse及其插件介绍和下载

0.Eclipse下载
EMF,GEF - Graphical Editor Framework,UML2,VE - Visual Editor都在这里下载
http://www.eclipse.org/downloads/index.php

0.5.lomboz J2EE插件,开发JSP,EJB
http://forge.objectweb.org/projects/lomboz
1.MyEclipse J2EE开发插件,支持SERVLET/JSP/EJB/数据库操纵等
http://www.myeclipseide.com

2.Properties Editor 编辑java的属性文件,并可以自动存盘为Unicode格式
http://propedit.sourceforge.jp/index_en.html

3.Colorer Take 为上百种类型的文件按语法着色
http://colorer.sourceforge.net/

4.XMLBuddy 编辑xml文件
http://www.xmlbuddy.com

5.Code Folding 加入多种代码折叠功能(比eclipse自带的更多)
http://www.coffee-bytes.com/servlet/PlatformSupport

6.Easy Explorer 从eclipse中访问选定文件、目录所在的文件夹
http://easystruts.sourceforge.net/

7.Fat Jar 打包插件,可以方便的完成各种打包任务,可以包含外部的包等
http://fjep.sourceforge.net/

8.RegEx Test 测试正则表达式
http://brosinski.com/stephan/archives/000028.php

9.JasperAssistant 报表插件(强,要钱的)
http://www.jasperassistant.com/

10.Jigloo GUI Builder JAVA的GUI编辑插件
http://cloudgarden.com/jigloo/

11.Profiler 性能跟踪、测量工具,能跟踪、测量BS程序
http://sourceforge.net/projects/eclipsecolorer/

12.AdvanQas 提供对if/else等条件语句的提示和快捷帮助(自动更改结构等)
http://eclipsecolorer.sourceforge.net/advanqas/index.html

13.Log4E Log4j插件,提供各种和Log4j相关的任务,如为方法、类添加一个logger等
http://log4e.jayefem.de/index.php/Main_Page

14.VSSPlugin VSS插件
http://sourceforge.net/projects/vssplugin

15.Implementors 提供跳转到一个方法的实现类,而不是接中的功能(实用!)
http://eclipse-tools.sourceforge.net/implementors/
16.Call Hierarchy 显示一个方法的调用层次(被哪些方法调,调了哪些方法)
http://eclipse-tools.sourceforge.net/call-hierarchy/index.html

17.EclipseTidy 检查和格式化HTML/XML文件
http://eclipsetidy.sourceforge.net/

18.Checkclipse 检查代码的风格、写法是否符合规范
http://www.mvmsoft.de/content/plugins/checkclipse/checkclipse.htm

19.Hibernate Synchronizer Hibernate插件,自动映射等
http://www.binamics.com/hibernatesync/

20.VeloEclipse Velocity插件
http://propsorter.sourceforge.net/

21.EditorList 方便的列出所有打开的Editor
http://editorlist.sourceforge.net/

22.MemoryManager 内存占用率的监视
http://cloudgarden.com/memorymanager/

23.swt-designer java的GUI插件
http://www.swt-designer.com/

24.TomcatPlugin 支持Tomcat插件
http://www.eclipsetotale.com/tomcatPlugin.html

25.XML Viewer
http://tabaquismo.freehosting.net/ignacio/eclipse/xmlview/index.html

26.quantum 数据库插件
http://quantum.sourceforge.net/

27.Dbedit 数据库插件
http://sourceforge.net/projects/dbedit

28.clay.core 可视化的数据库插件
http://www.azzurri.jp/en/software/index.jsp
http://www.azzurri.jp/eclipse/plugins

29.hiberclipse hibernate插件
http://hiberclipse.sourceforge.net
http://www.binamics.com/hibernatesync

30.struts-console Struts插件
http://www.jamesholmes.com/struts/console/

31.easystruts Struts插件
http://easystruts.sourceforge.net

32.veloedit Velocity插件
http://veloedit.sourceforge.net/

33.jalopy 代码整理插件
http://jalopy.sourceforge.net/

34.JDepend 包关系分析
http://andrei.gmxhome.de/jdepend4eclipse/links.html

35.Spring IDE Spring插件
http://springide-eclip.sourceforge.net/updatesite/

36.doclipse 可以产生xdoclet 的代码提示
http://beust.com/doclipse/
posted @ 2008-06-05 15:44 比特鼠| 编辑 收藏

     摘要: 有这样一个函数, 它接受一个函数(或者说闭包)作为参数  阅读全文
posted @ 2008-05-30 15:19 比特鼠| 编辑 收藏

当谈到表格数据的设计时,没有太多的网页设计师会有太大的兴趣。今天我们已经收集了20多个功能超大且看上去挺漂亮的Ajax/CSS表格设计,并且教你一些表格设计中所运用的技巧,例如表格数据的排序和过滤等。

OK,让我们来看一下这些表格:

1. Tablecloth

Tablecloth 由CSS Globe 开发,是一个轻巧易于使用的表格,简洁的将表格样式添加到你的HTML 表格元素中。

21个新奇漂亮的Ajax/CSS表格设计-Tablecloth

2. Ask the CSS Guy Table

Ask the CSS Guy Table教给我们要如何去创建能够清晰显出去资料之间的相关联系的表格,例如:点击一个表格元素时,将突了显示这个元素,并且在纵列和横列都显示出相关的类别关系。

21个新奇漂亮的Ajax/CSS表格设计-Ask the CSS Guy Table

#3. A CSS styled table version 2

Veerle Duoh 为我们展示了一个漂亮的表格设计,并教我们如何使用CSS来吸引用户的眼球。

21个新奇漂亮的Ajax/CSS表格设计-A CSS styled table version 2

#4. Sortable Table

Sortable Table 演示了如何按升序或降序排列以及如何过滤表格中的数据。

21个新奇漂亮的Ajax/CSS表格设计-Sortable Table

5. Row Locking with CSS and JavaScript

Css Guy再次对表格使用了聚焦高亮的效果,除非用户再次点击,否则表单数据将一直保持亮高。

21个新奇漂亮的Ajax/CSS表格设计-Row Locking with CSS and JavaScript

他还给了我们另一个示例:another example to Lock rows with radios .

#6. Vertical scrolling tables

如果您有大量的表格数据,但却没有太大的空间来展示它,这可能是个比较好的方法:一个纯CSS的表格与固定的标题和页脚,以及滚动显示的内容。

21个新奇漂亮的Ajax/CSS表格设计-Vertical scrolling tables

7. Replicating a Tree table

这是一个使用HTML 和CSS 设计的树形状表格。

21个新奇漂亮的Ajax/CSS表格设计-Replicating a Tree table

8 ) Paginate, sort and search a table with Ajax and Rails

这个表格提供了一个动态的界面,而不需要重新刷新整个页面。

21个新奇漂亮的Ajax/CSS表格设计-ajax tables

9. Collapsible tables with DOM and CSS

此表格加上箭头形象的脚本提示,用来控制表格的伸展和收缩。

21个新奇漂亮的Ajax/CSS表格设计-Collapsible tables with DOM and CSS

10. TableSorter plug-in for jQuery

它的主要特性包括多列排序,支持<TH>的rowspan和colspan属性以及许多其他功能。

21个新奇漂亮的Ajax/CSS表格设计-TableSorter plug-in for jQuery

11. Stripe your tables the OO way

使用了Javascript 为表格中的行进行颜色交替,并且添加了onmouseoveronmouseout 事件,当鼠标点击时,切换背景颜色。

21个新奇漂亮的Ajax/CSS表格设计-Stripe your tables the OO way

12. MooTools Table Row & Column highlighting

基于MooTools 框架,高亮显示鼠标悬停时的单元格所在的行和列。

21个新奇漂亮的Ajax/CSS表格设计-MooTools Table Row & Column highlighting

13. CSS Table Gallery

93 styled tables是一个专门收集表格样式的站点,下面是来自一个表格样式的截图:

21个新奇漂亮的Ajax/CSS表格设计-CSS Table Gallery

14. jQuery Table Filter

可以对数据进行各种不同的排序、过滤。

21个新奇漂亮的Ajax/CSS表格设计-jQuery Table Filter

15. Sortable/Resizable/Editable TableKit

TableKit基于Prototype框架,专门收集各种HTML表格,可以利用Ajax实时的进行表格栏目大小、排序等编辑。

21个新奇漂亮的Ajax/CSS表格设计-sortable, resizable, editable

16. Make all your tables sortable

21个新奇漂亮的Ajax/CSS表格设计-sortable table

17. Zebra Tables

alistapart为我们提供了一个极好的例子,如何使用JavaScript和DOM的改变背景色风格,以突出显示单元格。

21个新奇漂亮的Ajax/CSS表格设计-Zebra Tables

18. Standardista Table Sorting

Standardista Table Sorting 是一个Javascript模块,让您可以对HTML数据表的任何栏目进行排序。

21个新奇漂亮的Ajax/CSS表格设计-Standardista Table Sorting

19. GridView3 Example

21个新奇漂亮的Ajax/CSS表格设计-GridView3 Example

20. Mootable

21个新奇漂亮的Ajax/CSS表格设计-Mootable

21. Drag & Drop Sortable Lists with JavaScript and CSS

21个新奇漂亮的Ajax/CSS表格设计-Drag & Drop Sortable Lists with JavaScript and CSS

可能还会有一些你更想寻找的详细资料,下面是一些相关的资源链接:

如果你知道其它更强大的Ajax/CSS表格,欢迎在此留言。

posted @ 2008-01-23 17:46 比特鼠 阅读(3187) | 评论 (0)编辑 收藏

一个在线调色工具
posted @ 2008-01-23 17:44 比特鼠 阅读(380) | 评论 (1)编辑 收藏

/**
 * 加码解码工具
 * @author lwm
 *
 */

public class Encode {
 
 /*
  * 对应javascript的escape()函数, 加码后的串可直接使用javascript的unescape()进行解码
  */
 public static String escape(String src) {
  int i;
  char j;
  StringBuffer tmp = new StringBuffer();
  tmp.ensureCapacity(src.length() * 6);
  for (i = 0; i < src.length(); i++) {
   j = src.charAt(i);
   if (Character.isDigit(j) || Character.isLowerCase(j)
     || Character.isUpperCase(j))
    tmp.append(j);
   else if (j < 256) {
    tmp.append("%");
    if (j < 16)
     tmp.append("0");
    tmp.append(Integer.toString(j, 16));
   } else {
    tmp.append("%u");
    tmp.append(Integer.toString(j, 16));
   }
  }
  return tmp.toString();
 }

 /*
  * 对应javascript的unescape()函数, 可对javascript的escape()进行解码
  */
 public static String unescape(String src) {
  StringBuffer tmp = new StringBuffer();
  tmp.ensureCapacity(src.length());
  int lastPos = 0, pos = 0;
  char ch;
  while (lastPos < src.length()) {
   pos = src.indexOf("%", lastPos);
   if (pos == lastPos) {
    if (src.charAt(pos + 1) == 'u') {
     ch = (char) Integer.parseInt(src
       .substring(pos + 2, pos + 6), 16);
     tmp.append(ch);
     lastPos = pos + 6;
    } else {
     ch = (char) Integer.parseInt(src
       .substring(pos + 1, pos + 3), 16);
     tmp.append(ch);
     lastPos = pos + 3;
    }
   } else {
    if (pos == -1) {
     tmp.append(src.substring(lastPos));
     lastPos = src.length();
    } else {
     tmp.append(src.substring(lastPos, pos));
     lastPos = pos;
    }
   }
  }
  return tmp.toString();
 }

}

posted @ 2008-01-11 17:08 比特鼠 阅读(1817) | 评论 (0)编辑 收藏

读 YUI ,EXT等源码的时候看JS天旋地转,那可不是51JS上那种挪挪位置就能理解的,此刻如果没有JavaScrip的基础,更是像没有星光的黑夜…….

自以为觉得Js对象是很好理解的东东,然而真实践起来却一片糊涂。
通过查阅经典书籍《Professional JavaScript For Web Developers》稍微有些理解了

JavaScript的基本类型
原始类型如: Undefined Null Boolean Number String 等 用 typeof方法能辨别之
引用类型如: Object Function Array Boolean Number String Date等,用insanceof方法辨别之

严格来讲,JavaScript没有对象(Object),但是由于和OO术语对应,所以也称之为对象。所以Array,Function,基本类型,引用类型,函数,以及函数的属性 等等这些都是对象。

而对象分类,则可以分为内置对象(Built-in Object) 和宿主对象(host object)。
内置对象如 Math,Data啊。
宿主对象则如 BOM,DOM之类.

重新回顾了下这些基本概念之后,在做简单实践就有些理解了。
因此对象的使用,创建方式不尽相同,最简单的归类如下:

1 基本创建方式

function Class() {
window.alert("Hello Class!");
}
var clz= new Class();

2 访问对象成员

function Class(){
this.x = " this is x";
this.y = "this is y";
this.z = viewXY;
function viewXY(){
alert("x+","+y);
}
}
var clz= new Class();
clz.viewXY();

3 对象继承

function Parent() {
this.type= "human!";
}
function Child(){
this.age = "26";
this.sex ="male";
this.say= myInfo;
function myInfo(msg){
alert(msg+this.type+ ","+this.age+","+this.sex);
}
}
Child.prototype = new Parent();
var clild = new Child();
clild.say("I'm ");

4.重用原对象 (书上的例子太好了,搬来了)

Funcion.prototype.toString() = function(){
return "Function code hidden";
}
function sayHi(){
alert("hi");
}
alert(sayHi.toString());
posted @ 2008-01-02 11:06 比特鼠 阅读(255) | 评论 (0)编辑 收藏

希望能做到以下几点:

1. 在Java服务端架构的设计, 选型, 方案等方面有所突破! -- 这是最主要的!
2. 也想玩一玩Web前端的AJAX编程, RIA(富互联网应用)等等
3. 熟悉Linux/Unix系统的命令行操作
4. 在Java中跑脚本语言Python, JRuby等等
5. 项目管理

暂时就这么多吧!

posted @ 2007-12-28 09:41 比特鼠 阅读(197) | 评论 (0)编辑 收藏

为 Ajax 安全性所提出的经验法则:
  1. 如果你使用身份验证, 确定你在请求页上检查!
  2. 为 SQL 注入检查。
  3. 为 JavaScript 注入检查。
  4. 保留商务逻辑在服务器上!
  5. 不要假设每个请求是真正的!
  6. 确认检查数据!
  7. 审查请求的数据而且确定它是正确的。
posted @ 2007-12-19 17:10 比特鼠 阅读(262) | 评论 (0)编辑 收藏

1. jvm内部分为主工作区和线程工作区。主工作区是实例的所有线程共有,线程工作区是实例的每个线程专有的工作区,其中包括一些主工作区的一些实例字段数据的拷贝。

2. 服务器一般都有线程池,线程资源是可以重复利用的。你2000个用户在线,不见得能又200个用户同时(或者说并发)访问。再说,只要对象不是太大,我宁愿用200个拷贝,也不想让用户在这个200个任务的队列里等待。

3. 两个DB之间的复制数据,每个DB各自使用自己的Sequane来生成id。复制数据时,如果DB中的外键是由DB维护的,则不会产生id冲突,如果外键是由外部程序维护的,则可能会产生错误!

4. 对于非static的类的数据成员来说,在该类产生的实例中都有一份,并且相互独立(修改后并不影响其他实例), 但static的数据成员则变成了每个类只有一份,即在该类产生的所有实例共享这一个数据成员, 该数据成员的改变会影响到其他的实例. 而static的方法则是让你不用创建对象及能调用这个方法.

5. ThreadLocal的作用就是将经常要用到的对象的引用放到属于线程自己的一个存储空间中,在该线程的执行过程中,可以通过类的静态的ThreadLocal来方便的获取到这个对象,而不用通过参数的形式传来传去。
posted @ 2007-12-19 14:54 比特鼠 阅读(244) | 评论 (0)编辑 收藏

很多高分辨率的图像真的能够扮靓一个Web网站。但是它们也可能会降低网站的(响应)速度——图像都是文件,文件就要占用带宽,而带宽与等待时间直接相关。现在是你进行自我学习,了解如何利用一种叫做图像预加载的小技巧给网站提速的时候了。

图像的预加载

       浏览器通常的工作方式是:只有当要求加载图像的HTTP请求被发送的时候,图像才会被加载,而不论它是被动地通过<img>标记加载,还是主动地通过方法调用加载。所以,如果你有一段JavaScript,需要在鼠标悬停的时候切换图像,或者在超时之后自动地更换图像,那么你就可能会在从服务器取回图像的时候随时碰到等待,时间会从数秒钟到几分钟不等。当你以较慢的速度连接到Internet上的时候,或者被取回的图像非常巨大的时候,这种状况尤其显著,而这种数据延迟通常都会毁掉你所期望的效果。

        有些浏览器会试图转嫁这一问题,比如把图像保存在本地缓冲区里,这样以后对它的调用就能够很快进行了,但是需要第一次调用图像的时候仍然会产生延迟。预加载是一项在需要图像之前就把它下载到缓冲区里的技术。通过这种方式,当真的需要图像的时候,它可以被从缓冲区里取出来,并立即显示出来。

Image()对象
        预加载图像最简单的方法用JavaScript将一个新的Image()对象实例化,并把你想要预加载的图像的URL传递给它。假设我们有一个叫做
http://www.host01.com/Get/jsp/00040004/heavyimagefile.jpg的图像,我们希望,当用户把鼠标放在一个已经显示过的图像上的时,系统能够显示出这个图像。为了预加载这个图像,以便实现更快的响应时间,我们只用创建一个新的Image()对象,将其命名为heavyImage,并使用onLoad()事件处理程序把它同时加载到页面上。

1 < html >< head >< script  language  = "JavaScript" > function  preloader()  {heavyImage  =   new  Image(); heavyImage.src = " http://www.host01.com/Get/jsp/00040004/heavyimagefile.jpg " ;} </ script ></ head >< body  onLoad ="javascript:preloader()" >< href ="#"  onMouseOver ="javascript:document.img01.src='http://www.host01.com/Get/jsp/00040004/heavyimagefile.jpg'" >< img  name ="img01"  src =http://www.host01.com/Get/jsp/00040004/"justanotherfile.jpg" ></ a ></ body ></ html >
2

 

          要注意的是,图像标记自身并不会处理onMouseOver()和onMouseOut()事件,这就是为什么上面例子里的<img>标记被放在一个<a>标记里,后者的确加入了对这些事件类型的支持。
用数组加载多个图像


           在实际操作中,你可能需要预加载一幅以上的图像;例如,在包含有多个图像翻滚(rollover)的菜单条里,或者如果你正在尝试创建平滑的动态效果。这并不困难;你所需要做的就是使用JavaScript的数组,就像下面例子里的一样:

 

1 < script language = " JavaScript " > function  preloader()  //  counter var i = 0; // create object imageObj = new Image(); // set image list images = new Array(); images[0]="image1.jpg" images[1]="image2.jpg" images[2]="image3.jpg" images[3]="image4.jpg" // start preloading for(i=0; i<=3; i++) { imageObj.src=images[i]; }
2 }
  </ script >


         在上面的例子里,你先定义变量i和叫做imageObj的Image()对象。然后定义一个叫做images[]的新数组,在这个数组里,每个数组元素都保存着需要预加载的图像来源。最后,创建一个for()循环,让它在数组里循环,并将它们中的每一个都指派给Image()对象,这样就能够把它预加载到缓冲区里。
onLoad()事件处理程序
        就和JavaScript里的其它很多对象一样,Image()对象也带有多个事件处理程序。这其中最有用的毫无疑问的就是onLoad()处理程序了,它会在完成图像加载的时候被调用。这个处理程序可以与自定义的函数一起使用,以便在完成图像加载之后进行特定的任务。下面的例子通过在图像加载的时候显示“请等待(please wait)”提示信息来说明这个问题,然后在图像完成加载之后就向浏览器发送一个新的URL。

 

< html >< head >< script  language ="JavaScript" > //  create an image objectobjImage = new Image(); // set what happens once the image has loaded objImage.onLoad=imagesLoaded(); // preload the image fileobjImage.src='http://www.host01.com/Get/jsp/00040004/images/image1n.gif';// function invoked on image loadfunction imagesLoaded(){ document.location.href='index2.html';}</script></head><body>Please wait, loading images</body></html>

 


       当然,你还可以创建一个图像数组,对它进行循环,预加载每个图像,并在每个阶段对已加载图像的数量保持跟踪。一旦加载了所有的图像,事件处理程序就能够按照设定把浏览器带到下一个页面(或者进行其他的任务)。

预加载与多状态菜单

          现在,把你刚刚学到的理论付诸真正的实践怎么样?下面一部分内容就是我碰巧编写的一段代码——一个由多个按钮(图像链接)组成的菜单条——其中每个按钮都可能处于三种状态中的一种:正常(normal)、hover(悬停)和点击(click)。由于所有的按钮都有多个状态,所以就有必要使用图像预加载来确保菜单能够根据其切换到的状态进行快速的响应。列表A里的代码就说了这一点。

           列表A里的HTML代码会建立一个由四个按钮组成的菜单条,每个按钮都有三种状态:正常、悬停和点击。其要求如下:

          但鼠标移动到处于正常状态的按钮上时,按钮会变为悬停状态。当鼠标移开的时候,按钮又会恢复到正常状态。当鼠标点击按钮的时候,按钮就会变为点击状态。它会一直保持这个状态,直到另外一个按钮被点击。如果有一个按钮被点击,那么其他的按钮就都不能处于点击状态。其他的按钮只能够处于悬停或者正常状态。一次只能有一个按钮可以被点击。一次只能有一个按钮处于悬停状态。
        第一项任务是建立保存有菜单每个状态的图像的数组。与这些数组元素相对应的<img>元素也都在HTML文档的主体里被创建,并按顺序命名。要注意的是,对数组值的索引是从0开始的,而相应的<img>元素是从1开始命名的——这就需要在脚本后面的一段里进行某种计算上的调整。

        PreloadImages()函数会负责把所有的图像都加载到缓冲区里,这样的话对鼠标移动的响应时间会被减到最小。一个for()循环被用在第一步里创建的图像里进行迭代,并预加载每一个图像。

            ResetAll()函数是把所有图像恢复都到它们正常状态的方便方法。这是有必要的,因为当菜单的项目被点击的时候,菜单里其他所有的项目都必须在被点击项目能够切换到点击状态之前恢复到正常状态。

        SetNormal()、setHover()和setClick()函数负责把特定图像(图像的编号被作为函数的自变量进行传递)的来源分别改为正常、悬停或者点击状态。由于被点击的图像必须一直保持点击状态,直到另外一个图像被点击(见第二项要求),所以它们暂时不会对鼠标移动作出反应;这样的话,如果按钮还不是处在点击状态,那么setNormal()和setHover()函数所包括的代码就只能用来改变按钮的状态。

         上面所提到的预加载只是提高你JavaScript效果响应时间的多种方法之一。就在你的网站上使用上面列出的技巧,并根据你的要求在需要的地方更改它们吧。祝你好运!

posted @ 2007-12-19 10:40 比特鼠 阅读(240) | 评论 (0)编辑 收藏

这些东西都是Java Script大部分都是由老外写的,并且封装得很好,在运用上也很方便,而且也都兼容FF与OPERA,档案中少部分是由中国的高手写的。

 

  一、多样化摺叠菜单:下载

  一个由老外写的折叠式垂直菜单,多样化,多功能,可自订,使用容易,支持FF。

国内外 Java Script 经典封装
图1

  二、CSS圆角边框:下载

  以CSS为主要,用Java Script封装的相当完整,也是老外写的,支持多浏览器,可以自订样式,目前有十多种可以运用。

国内外 Java Script 经典封装
图2

国内外 Java Script 经典封装
图3

  三、模拟视窗:下载

  用层模拟的视窗,是一个中国高手写的,Java Script封装的相当好,使用上也很容易

国内外 Java Script 经典封装
图4
  

  四、支持FF的省略符:下载

  说到省略符,那非CSS莫属,有个老外用Java Script来实现,并且是批量处理的,重点是支持FF。

国内外 Java Script 经典封装
图5

  五、TAB选项卡:下载

  用Java Script模仿各种作业系统的选项卡,老外就是牛,不仅支援多样式的即时切换,同时也支援每个选项卡是否附带图示的切换选项,选项卡也可以上下切换。

国内外 Java Script 经典封装
图6
  

  六、最佳化多样式Windows:下载

  用层模拟视窗的最佳代表作,这是我看过功能最多的模拟式窗,内附多达74项功能与样式,你完完全全可以把它当成是一个真正的视窗来应用,可以根据你的需求来应用,快丢掉你那认为好用的层视窗,这套封装非常完整的视窗绝对可以满足你的各种需求。

国内外 Java Script 经典封装
图7

国内外 Java Script 经典封装
图8

  七、多样化的垂直菜单:附件

  别具风格的方块式垂直折叠菜单,目前有8种风格可以运用,如果你已经厌烦WEB上平凡的菜单,这套在国外颇受欢迎的菜单肯定是你的最佳首选。

国内外 Java Script 经典封装
图9
  

  八、多样化的连结提示效果:下载

  这个连结提示样式允许你直接写入css与html,共有14项功能可以让你自订。

国内外 Java Script 经典封装
图10

  九、侧栏式折叠菜单:下载

  这是一个侧栏式的折叠菜单,它允许你设置它是否有过渡效果、侧栏菜单是否自动伸缩、菜单项切换是否允许动画过渡、是否轮替切换等多项设置,并且也有多种样式可以运用。

  这个脚本有个很好玩的东东,下载并且解压後,请进入samples的目录并打show.html看看效果,我不知道这效果容不容易实现,但是这效果很牛,菜单全自动运行的~

国内外 Java Script 经典封装
图11
  

  十、图形滚动条:下载

  老外写的图形滚动条,有多种样式,在ie里头还支持滚轮滚动。

国内外 Java Script 经典封装
图12

  十一、图片倒影效果:下载
  说到图片倒影,不外乎就是直接作成图片跟css滤镜来实现,但是这个是用Java Script实现的,值得借镜。

国内外 Java Script 经典封装
图13

  十二、代码自动高亮:下载

  虽说这不是什麽新东西,但总是会有人需要吧,而且想学正则表达的人,这肯定是最佳借镜的作品。

国内外 Java Script 经典封装
图14
  
  

  十三、酷似flash效果的图片展示:下载

  这个老外牛到有点变态,这图片展示效果已经跟FLASH没什麽两样,用Java Script写的耶。

国内外 Java Script 经典封装
图15

  十四、让ie6支援png图档:下载

国内外 Java Script 经典封装
图16

  这个问题之前被很多人讨论过,我就不多说什麽了,有需要下吧。

posted @ 2007-12-13 17:29 比特鼠 阅读(5319) | 评论 (5)编辑 收藏

在一个老外的Blog上看到了这个网站,发现原来是一个以C语言为基准的性能比较网站!

Java还算不错,Ruby就不怎么样了, 在脚本语言中居然排在了最后!

看来,解析性的语言玩起来是简单方便了,可是却是以损失性能为代价的!
posted @ 2007-12-13 16:34 比特鼠 阅读(453) | 评论 (0)编辑 收藏

BIG-ENDIAN(大字节序、高字节序)
LITTLE-ENDIAN(小字节序、低字节序)
主机字节序
网络字节顺序
JAVA字节序

1.BIG-ENDIAN、LITTLE-ENDIAN跟多字节类型的数据有关的比如int,short,long型,而对单字节数据byte却没有影响。BIG-ENDIAN就是低位字节排放在内存的低端,高位字节排放在内存的高端。而LITTLE-ENDIAN正好相反。
比如 int a = 0x05060708
在BIG-ENDIAN的情况下存放为:
字节号 0 1 2 3
数据 05 06 07 08
在LITTLE-ENDIAN的情况下存放为:
字节号 0 1 2 3
数据 08 07 06 05

2.BIG-ENDIAN、LITTLE-ENDIAN、跟CPU有关的,每一种CPU不是BIG-ENDIAN就是LITTLE-ENDIAN、。IA架构的CPU中是Little-Endian,而PowerPC 、SPARC和Motorola处理器。这其实就是所谓的主机字节序。而网络字节序是指数据在网络上传输时是大头还是小头的,在Internet的网络字节序是BIG-ENDIAN。所谓的JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序也是BIG-ENDIAN。

3.所以在用C/C++写通信程序时,在发送数据前务必用htonl和htons去把整型和短整型的数据进行从主机字节序到网络字节序的转换,而接收数据后对于整型和短整型数据则必须调用ntohl和ntohs实现从网络字节序到主机字节序的转换。如果通信的一方是JAVA程序、一方是C/C++程序时,则需要在C/C++一侧使用以上几个方法进行字节序的转换,而JAVA一侧,则不需要做任何处理,因为JAVA字节序与网络字节序都是BIG-ENDIAN,只要C/C++一侧能正确进行转换即可(发送前从主机序到网络序,接收时反变换)。如果通信的双方都是JAVA,则根本不用考虑字节序的问题了。

4.如果网络上全部是PowerPC,SPARC和Motorola CPU的主机那么不会出现任何问题,但由于实际存在大量的IA架构的CPU,所以经常出现数据传输错误。

5.文章开头所提出的问题,就是因为程序运行在X86架构的PC SERVER上,发送数据的一端用C实现的,接收一端是用JAVA实现的,而发送端在发送数据前未进行从主机字节序到网络字节序的转换,这样接收端接收到的是LITTLE-ENDIAN的数据,数据解释自然出错。
具体数据如下,实际发送的数据为23578
发送端发送数据: 1A 5C
接收端接收到数据后,按BIG-ENDIAN进行解释具体数据是多少?你们自己去计算并比较吧!


===============================================================================================

Big Endian and Little Endian

    谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian呢?

    其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB),即常说的低位在先,高位在后。
    用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:

Big Endian

  低地址                           高地址
  ----------------------------------------->
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     12     |      34    |     56      |     78    |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian

  低地址                           高地址
  ----------------------------------------->
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     78     |      56    |     34      |     12    |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    从上面两图可以看出,采用big endian方式存储数据是符合我们人类的思维习惯的。而little endian,!@#$%^&*,见鬼去吧 -_-|||

    为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?尤其是当你把你在微机上运算的结果运用到计算机群上去的话。在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的 0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。

    无独有偶,所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了四个转换字节序的宏。


========================================================================================================

/**
* 通信格式转换
*
* Java和一些windows编程语言如c、c++、delphi所写的网络程序进行通讯时,需要进行相应的转换
* 高、低字节之间的转换
* windows的字节序为低字节开头
* linux,unix的字节序为高字节开头
* java则无论平台变化,都是高字节开头
*/

public class FormatTransfer {
/**
  * 将int转为低字节在前,高字节在后的byte数组
  * @param n int
  * @return byte[]
  */
public static byte[] toLH(int n) {
  byte[] b = new byte[4];
  b[0] = (byte) (n & 0xff);
  b[1] = (byte) (n >> 8 & 0xff);
  b[2] = (byte) (n >> 16 & 0xff);
  b[3] = (byte) (n >> 24 & 0xff);
  return b;
}

/**
  * 将int转为高字节在前,低字节在后的byte数组
  * @param n int
  * @return byte[]
  */
public static byte[] toHH(int n) {
  byte[] b = new byte[4];
  b[3] = (byte) (n & 0xff);
  b[2] = (byte) (n >> 8 & 0xff);
  b[1] = (byte) (n >> 16 & 0xff);
  b[0] = (byte) (n >> 24 & 0xff);
  return b;
}

/**
  * 将short转为低字节在前,高字节在后的byte数组
  * @param n short
  * @return byte[]
  */
public static byte[] toLH(short n) {
  byte[] b = new byte[2];
  b[0] = (byte) (n & 0xff);
  b[1] = (byte) (n >> 8 & 0xff);
  return b;
}

/**
  * 将short转为高字节在前,低字节在后的byte数组
  * @param n short
  * @return byte[]
  */
public static byte[] toHH(short n) {
  byte[] b = new byte[2];
  b[1] = (byte) (n & 0xff);
  b[0] = (byte) (n >> 8 & 0xff);
  return b;
}

 

/**
  * 将将int转为高字节在前,低字节在后的byte数组

public static byte[] toHH(int number) {
  int temp = number;
  byte[] b = new byte[4];
  for (int i = b.length - 1; i > -1; i--) {
    b = new Integer(temp & 0xff).byteValue();
    temp = temp >> 8;
  }
  return b;
}

public static byte[] IntToByteArray(int i) {
    byte[] abyte0 = new byte[4];
    abyte0[3] = (byte) (0xff & i);
    abyte0[2] = (byte) ((0xff00 & i) >> 8);
    abyte0[1] = (byte) ((0xff0000 & i) >> 16);
    abyte0[0] = (byte) ((0xff000000 & i) >> 24);
    return abyte0;
}


*/

/**
  * 将float转为低字节在前,高字节在后的byte数组
  */
public static byte[] toLH(float f) {
  return toLH(Float.floatToRawIntBits(f));
}

/**
  * 将float转为高字节在前,低字节在后的byte数组
  */
public static byte[] toHH(float f) {
  return toHH(Float.floatToRawIntBits(f));
}

/**
  * 将String转为byte数组
  */
public static byte[] stringToBytes(String s, int length) {
  while (s.getBytes().length < length) {
    s += " ";
  }
  return s.getBytes();
}


/**
  * 将字节数组转换为String
  * @param b byte[]
  * @return String
  */
public static String bytesToString(byte[] b) {
  StringBuffer result = new StringBuffer("");
  int length = b.length;
  for (int i=0; i<length; i++) {
    result.append((char)(b & 0xff));
  }
  return result.toString();
}

/**
  * 将字符串转换为byte数组
  * @param s String
  * @return byte[]
  */
public static byte[] stringToBytes(String s) {
  return s.getBytes();
}

/**
  * 将高字节数组转换为int
  * @param b byte[]
  * @return int
  */
public static int hBytesToInt(byte[] b) {
  int s = 0;
  for (int i = 0; i < 3; i++) {
    if (b >= 0) {
    s = s + b;
    } else {
    s = s + 256 + b;
    }
    s = s * 256;
  }
  if (b[3] >= 0) {
    s = s + b[3];
  } else {
    s = s + 256 + b[3];
  }
  return s;
}

/**
  * 将低字节数组转换为int
  * @param b byte[]
  * @return int
  */
public static int lBytesToInt(byte[] b) {
  int s = 0;
  for (int i = 0; i < 3; i++) {
    if (b[3-i] >= 0) {
    s = s + b[3-i];
    } else {
    s = s + 256 + b[3-i];
    }
    s = s * 256;
  }
  if (b[0] >= 0) {
    s = s + b[0];
  } else {
    s = s + 256 + b[0];
  }
  return s;
}


/**
  * 高字节数组到short的转换
  * @param b byte[]
  * @return short
  */
public static short hBytesToShort(byte[] b) {
  int s = 0;
  if (b[0] >= 0) {
    s = s + b[0];
    } else {
    s = s + 256 + b[0];
    }
    s = s * 256;
  if (b[1] >= 0) {
    s = s + b[1];
  } else {
    s = s + 256 + b[1];
  }
  short result = (short)s;
  return result;
}

/**
  * 低字节数组到short的转换
  * @param b byte[]
  * @return short
  */
public static short lBytesToShort(byte[] b) {
  int s = 0;
  if (b[1] >= 0) {
    s = s + b[1];
    } else {
    s = s + 256 + b[1];
    }
    s = s * 256;
  if (b[0] >= 0) {
    s = s + b[0];
  } else {
    s = s + 256 + b[0];
  }
  short result = (short)s;
  return result;
}

/**
  * 高字节数组转换为float
  * @param b byte[]
  * @return float
  */
public static float hBytesToFloat(byte[] b) {
  int i = 0;
  Float F = new Float(0.0);
  i = ((((b[0]&0xff)<<8 | (b[1]&0xff))<<8) | (b[2]&0xff))<<8 | (b[3]&0xff);
  return F.intBitsToFloat(i);
}

/**
  * 低字节数组转换为float
  * @param b byte[]
  * @return float
  */
public static float lBytesToFloat(byte[] b) {
  int i = 0;
  Float F = new Float(0.0);
  i = ((((b[3]&0xff)<<8 | (b[2]&0xff))<<8) | (b[1]&0xff))<<8 | (b[0]&0xff);
  return F.intBitsToFloat(i);
}

/**
  * 将byte数组中的元素倒序排列
  */
public static byte[] bytesReverseOrder(byte[] b) {
  int length = b.length;
  byte[] result = new byte[length];
  for(int i=0; i<length; i++) {
    result[length-i-1] = b;
  }
  return result;
}

/**
  * 打印byte数组
  */
public static void printBytes(byte[] bb) {
  int length = bb.length;
  for (int i=0; i<length; i++) {
    System.out.print(bb + " ");
  }
  System.out.println("");
}

public static void logBytes(byte[] bb) {
  int length = bb.length;
  String ut = "";
  for (int i=0; i<length; i++) {
    ut = out + bb + " ";
  }

}

/**
  * 将int类型的值转换为字节序颠倒过来对应的int值
  * @param i int
  * @return int
  */
public static int reverseInt(int i) {
  int result = FormatTransfer.hBytesToInt(FormatTransfer.toLH(i));
  return result;
}

/**
  * 将short类型的值转换为字节序颠倒过来对应的short值
  * @param s short
  * @return short
  */
public static short reverseShort(short s) {
  short result = FormatTransfer.hBytesToShort(FormatTransfer.toLH(s));
  return result;
}

/**
  * 将float类型的值转换为字节序颠倒过来对应的float值
  * @param f float
  * @return float
  */
public static float reverseFloat(float f) {
  float result = FormatTransfer.hBytesToFloat(FormatTransfer.toLH(f));
  return result;
}

}

posted @ 2007-10-24 09:57 比特鼠 阅读(3392) | 评论 (0)编辑 收藏

实现Leader/Fellows模式的项目--CAJ, 地址是:http://caj.cosylab.com/
posted @ 2007-10-23 14:57 比特鼠 阅读(194) | 评论 (0)编辑 收藏

网址:http://httpd.apache.org/docs/2.0/programs/ab.html
posted @ 2007-10-23 14:53 比特鼠 阅读(263) | 评论 (0)编辑 收藏

缓冲区基础

抽象类Buffer是java.nio包支持缓冲区的基础。 Buffer 的工作方式就象内存中用于读写基本数据类型的 RandomAccessFile 。象 RandomAccessFile 一样,使用 Buffer ,所执行的下一个操作(读/写)在当前某个位置发生。执行读/写操作中的任一个都会改变那个位置,所以在写操作之后进行读操作不会读到刚才所写的内容,而会读到刚才所写内容之后的数据。 Buffer 提供了四个指示方法,用于访问线性结构(从最高值到最低值):

capacity() :表明缓冲区的容量大小, 一旦确定了大小, 将不能再改变;
limit() :告诉您到目前为止已经往缓冲区填了多少字节,或者让您用 :limit(int newLimit) 来改变这个限制
position() :告诉您当前的位置,以执行下一个读/写操作
mark() :为了稍后用 reset() 进行重新设置而记住某个位置
flip() :交换限制指针和位置指针,然后将位置置为 0,并废弃已经做的mark标记

缓冲区的基本操作是读 get() 和写 put() ;然而,这些方法在子类中都是针对每种数据类型的特定方法。为了说明这一情况,让我们研究一个简单示例,该示例演示了从同一个缓冲区读和写一个字符。在清单 1 中, flip() 方法交换限制和位置,然后将位置置为 0,并废弃标记,让您读刚才所写的数据:


清单 1. 读/写示例
import java.nio.*;
...
CharBuffer buff = ...;
buff.put('A');
buff.flip();
char c = buff.get();
System.out.println("An A: " + c);
 


现在让我们研究一些具体的 Buffer 子类。

 

缓冲区类型

Merlin 具有 7 种特定的 Buffer 类型,每种类型对应着一个基本数据类型(不包括 boolean):

ByteBuffer       //存放任何除boolean类型外的其他基本类型
CharBuffer       //存放char
DoubleBuffer     //存放double
FloatBuffer      //存放float
IntBuffer        //存放int
LongBuffer       //存放long
ShortBuffer      //存放short

在本文后面,我将讨论第 8 种类型 MappedByteBuffer ,它用于内存映射文件。如果您必须使用的类型不是这些基本类型,则可以先从 ByteBuffer 获得字节类型,然后将其转换成 Object 或其它任何类型。


创建缓冲区
一共有两种类型的缓冲区,直接缓冲区和非直接缓冲区。

在创建缓冲区时,可以要求创建直接缓冲区,创建直接缓冲区的成本要比创建间接缓冲区高,但这可以使运行时环境直接在该缓冲区上进行较快的本机 I/O 操作。因为创建直接缓冲区所增加的成本,所以直接缓冲区只用于长生存期的缓冲区,而不用于短生存期、一次性且用完就丢弃的缓冲区。而且,只能在 ByteBuffer 这个级别上创建直接缓冲区,如果希望使用其它类型,则必须将 Buffer 转换成更具体的类型。

判断一个缓冲区是否是直接缓冲区,可以调用isDirect()方法。

有三种方式来获取一个缓冲区的对象:
a. 调用allocate()或者allocateDirect()方法直接分配,其中allocateDirect()返回的是直接缓冲区。
b. 包装一个数组,如:
      byte[] b = new byte[1024];
      ByteBuffer bb = ByteBuffer.wrap(b);
c. 内存映射,即调用FileChannel的map()方法。

缓冲区基本属性
这几个属性是每个缓冲区都有的并且是常用的操作。
a. 容量(capacity),缓冲区大小
b. 限制(limit),第一个不应被读取或写入的字节的索引,总是小于容量。
c. 位置(position),下一个被读取或写入的字节的索引,总是小于限制。
d. clear()方法:设置limit为capacity,position为0。
e. filp()方法:设置limit为当前position,然后设置position为0。
f. rewind()方法:保持limit不变,设置position为0。

缓冲区数据操作
操作包括了读取和写入数据两种。
读取数据使用get()及其系列方法,除boolean外,每一种类型包括了对应的get()方法,如getInt(),getChar()等,get()方法用来读取字节,支持相对和绝对索引两种方式。
写入数据使用put()及其系列方法,和get()方法是对应的。

package nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class BufferDemo ...{

    
    public static void main(String[] args) throws Exception...{
        //分配一个非直接缓冲区
        ByteBuffer bb = ByteBuffer.allocate(100);
        //向缓冲区写入0到100的字节制
        for(int i = 0; i <100; i++)...{
            byte b = (byte) (Math.random() * 100);
            bb.put(b);
        }
        
        System.out.println("写入文件前的缓冲区数据");
        bb.flip();
        while(bb.hasRemaining())
            System.out.print(bb.get() + " ");
        System.out.println();
        
        //获取一个关联到文件buffer.txt的信道
        FileChannel fc = new FileOutputStream("buffer.txt").getChannel();
        //将缓冲区数据写到文件中
        bb.flip();
        fc.write(bb);
        //防止缓存
        fc.force(true);
        //关闭信道
        fc.close();
        bb = null;
        fc = null;
        
        //下面从文件中读取数据
        fc = new FileInputStream("buffer.txt").getChannel();
        ByteBuffer bb2 = ByteBuffer.allocate((int) fc.size());
        fc.read(bb2);
        System.out.println("从文件读取的缓冲区数据");
        bb2.flip();
        while(bb2.hasRemaining())
            System.out.print(bb2.get() + " ");
        System.out.println();
        fc.close();
        bb2 = null;
        fc = null;
        

    }

}

内存映射文件

第 8 种 Buffer 类型 MappedByteBuffer 只是一种特殊的 ByteBuffer 。 MappedByteBuffer 将文件所在区域直接映射到内存。通常,该区域包含整个文件,但也可以只映射部分文件。所以,必须指定要映射文件的哪部分。而且,与其它 Buffer 对象一样,这里没有构造函数;必须让 java.nio.channels.FileChannel 的 map() 方法来获取 MappedByteBuffer 。此外,无需过多涉及通道就可以用 getChannel() 方法从 FileInputStream 或 FileOutputStream 获取 FileChannel 。通过从命令行传入文件名来读取文本文件的内容,清单 4 显示了 MappedByteBuffer :


清单 4. 读取内存映射文本文件
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class ReadFileBuff {
  public static void main(String args[]) throws IOException {
     if (args.length != 0) {
      String filename = args[0];
      FileInputStream fis = new FileInputStream(filename);
      FileChannel channel = fis.getChannel();
      int length = (int)channel.size();
      MappedByteBuffer byteBuffer =
        channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
      Charset charset = Charset.forName("ISO-8859-1");
      CharsetDecoder decoder = charset.newDecoder();
      CharBuffer charBuffer = decoder.decode(byteBuffer);
      for (int i=0, n=charBuffer.length(); i<n; i++) {
        System.out.print(charBuffer.get());
      }
    }
  }
}

posted @ 2007-08-01 11:13 比特鼠 阅读(3813) | 评论 (0)编辑 收藏

  1. 左移操作: x << n
    x可以是byte, short, char, int, long基本类型, n(位移量)只能是int型

    编译器的执行步骤:
    1) 如果x是byte, short, char类型, 则将x提升为int;
    2) 如果x是byte, short, char, int类型, 则n被重新赋值(过程是:取n的补码的低5位再转成十进制的int值,相当对n取32模: n=n%32);
       如果x是long型, 则n被重新赋值(过程是:取n的补码的低6位再转成十进制的int值,相当对n取64模: n=n%64);
       (因为int类型为4个字节,即32位,移动32位将没有任何意义.对于long则是模64)
    3) 对x左移n个位数, 整个表达式产生一个新值(x的值不变);
  2. <<是左移符号,列x<<1,就是x的内容左移一位(x的内容并不改变)
  3. >>是带符号位的右移符号,x>>1就是x的内容右移一位,如果开头是1则补1,是0责补0,(x的内容并不改变).
  4. >>>是不带符号位的右移,x>>>1就是x的内容右移一位,开头补0(x的内容并不改变)
posted @ 2007-08-01 10:12 比特鼠 阅读(2795) | 评论 (0)编辑 收藏

原文地址 http://www.programbbs.com/doc/2433.htm
为什么会排队等待?

下面的这个简单的 Java 程序完成四项不相关的任务。这样的程序有单个控制线程,控制在这四个任务之间线性地移动。此外,因为所需的资源 — 打印机、磁盘、数据库和显示屏 -- 由于硬件和软件的限制都有内在的潜伏时间,所以每项任务都包含明显的等待时间。因此,程序在访问数据库之前必须等待打印机完成打印文件的任务,等等。如果您正在等待程序的完成,则这是对计算资源和您的时间的一种拙劣使用。改进此程序的一种方法是使它成为多线程的。
 
四项不相关的任务
 

class myclass {
    static public void main(String args[]) {
        print_a_file();
        manipulate_another_file();
        access_database();
        draw_picture_on_screen();
    }
}

在本例中,每项任务在开始之前必须等待前一项任务完成,即使所涉及的任务毫不相关也是这样。但是,在现实生活中,我们经常使用多线程模型。我们在处理某些任务的同时也可以让孩子、配偶和父母完成别的任务。例如,我在写信的同时可能打发我的儿子去邮局买邮票。用软件术语来说,这称为多个控制(或执行)线程。

可以用两种不同的方法来获得多个控制线程:

多个进程
在大多数操作系统中都可以创建多个进程。当一个程序启动时,它可以为即将开始的每项任务创建一个进程,并允许它们同时运行。当一个程序因等待网络访问或用户输入而被阻塞时,另一个程序还可以运行,这样就增加了资源利用率。但是,按照这种方式创建每个进程要付出一定的代价:设置一个进程要占用相当一部分处理器时间和内存资源。而且,大多数操作系统不允许进程访问其他进程的内存空间。因此,进程间的通信很不方便,并且也不会将它自己提供给容易的编程模型。


线程
线程也称为轻型进程 (LWP)。因为线程只能在单个进程的作用域内活动,所以创建线程比创建进程要廉价得多。这样,因为线程允许协作和数据交换,并且在计算资源方面非常廉价,所以线程比进程更可取。线程需要操作系统的支持,因此不是所有的机器都提供线程。Java 编程语言,作为相当新的一种语言,已将线程支持与语言本身合为一体,这样就对线程提供了强健的支持。


使用 Java 编程语言实现线程
Java 编程语言使多线程如此简单有效,以致于某些程序员说它实际上是自然的。尽管在 Java 中使用线程比在其他语言中要容易得多,仍然有一些概念需要掌握。要记住的一件重要的事情是 main() 函数也是一个线程,并可用来做有用的工作。程序员只有在需要多个线程时才需要创建新的线程。

Thread 类
Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建一个线程,程序员必须创建一个从 Thread 类导出的新类。程序员必须覆盖 Thread 的 run() 函数来完成有用的工作。用户并不直接调用此函数;而是必须调用 Thread 的 start() 函数,该函数再调用 run()。下面的代码说明了它的用法:

创建两个新线程

import java.util.*;

class TimePrinter extends Thread {
    int pauseTime;

    String name;

    public TimePrinter(int x, String n) {
        pauseTime = x;
        name = n;
    }

    public void run() {
        while (true) {
            try {
                System.out.println(name + ":"
                        + new Date(System.currentTimeMillis()));
                Thread.sleep(pauseTime);
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }

    static public void main(String args[]) {
        TimePrinter tp1 = new TimePrinter(1000, "Fast Guy");
        tp1.start();
        TimePrinter tp2 = new TimePrinter(3000, "Slow Guy");
        tp2.start();

    }
}

在本例中,我们可以看到一个简单的程序,它按两个不同的时间间隔(1 秒和 3 秒)在屏幕上显示当前时间。这是通过创建两个新线程来完成的,包括 main() 共三个线程。但是,因为有时要作为线程运行的类可能已经是某个类层次的一部分,所以就不能再按这种机制创建线程。虽然在同一个类中可以实现任意数量的接口,但 Java 编程语言只允许一个类有一个父类。同时,某些程序员避免从 Thread 类导出,因为它强加了类层次。对于这种情况,就要 runnable 接口。

Runnable 接口
此接口只有一个函数,run(),此函数必须由实现了此接口的类实现。但是,就运行这个类而论,其语义与前一个示例稍有不同。我们可以用 runnable 接口改写前一个示例。(不同的部分用黑体表示。)

创建两个新线程而不强加类层次

import java.util.*;

class TimePrinter implements Runnable {
    int pauseTime;

    String name;

    public TimePrinter(int x, String n) {
        pauseTime = x;
        name = n;
    }

    public void run() {
        while (true) {
            try {
                System.out.println(name + ":"
                        + new Date(System.currentTimeMillis()));
                Thread.sleep(pauseTime);
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }

    static public void main(String args[]) {
        Thread t1 = new Thread(new TimePrinter(1000, "Fast Guy"));
        t1.start();
        Thread t2 = new Thread(new TimePrinter(3000, "Slow Guy"));
        t2.start();

    }
}

请注意,当使用 runnable 接口时,您不能直接创建所需类的对象并运行它;必须从 Thread 类的一个实例内部运行它。许多程序员更喜欢 runnable 接口,因为从 Thread 类继承会强加类层次。

synchronized 关键字
到目前为止,我们看到的示例都只是以非常简单的方式来利用线程。只有最小的数据流,而且不会出现两个线程访问同一个对象的情况。但是,在大多数有用的程序中,线程之间通常有信息流。试考虑一个金融应用程序,它有一个 Account 对象,如下例中所示:

一个银行中的多项活动

public class Account {
    String holderName;

    float amount;

    public Account(String name, float amt) {
        holderName = name;
        amount = amt;
    }

    public void deposit(float amt) {
        amount += amt;
    }

    public void withdraw(float amt) {
        amount -= amt;
    }

    public float checkBalance() {
        return amount;
    }
}

在此代码样例中潜伏着一个错误。如果此类用于单线程应用程序,不会有任何问题。但是,在多线程应用程序的情况中,不同的线程就有可能同时访问同一个 Account 对象,比如说一个联合帐户的所有者在不同的 ATM 上同时进行访问。在这种情况下,存入和支出就可能以这样的方式发生:一个事务被另一个事务覆盖。这种情况将是灾难性的。但是,Java 编程语言提供了一种简单的机制来防止发生这种覆盖。每个对象在运行时都有一个关联的锁。这个锁可通过为方法添加关键字 synchronized 来获得。这样,修订过的 Account 对象(如下所示)将不会遭受像数据损坏这样的错误:

对一个银行中的多项活动进行同步处理

public class Account {
    String holderName;

    float amount;

    public Account(String name, float amt) {
        holderName = name;
        amount = amt;
    }

    public synchronized void deposit(float amt) {
        amount += amt;
    }

    public synchronized void withdraw(float amt) {
        amount -= amt;
    }

    public float checkBalance() {
        return amount;
    }
}

deposit() 和 withdraw() 函数都需要这个锁来进行操作,所以当一个函数运行时,另一个函数就被阻塞。请注意,checkBalance() 未作更改,它严格是一个读函数。因为 checkBalance() 未作同步处理,所以任何其他方法都不会阻塞它,它也不会阻塞任何其他方法,不管那些方法是否进行了同步处理。

Java 编程语言中的高级多线程支持

线程组
线程是被个别创建的,但可以将它们归类到线程组中,以便于调试和监视。只能在创建线程的同时将它与一个线程组相关联。在使用大量线程的程序中,使用线程组组织线程可能很有帮助。可以将它们看作是计算机上的目录和文件结构。

线程间发信
当线程在继续执行前需要等待一个条件时,仅有 synchronized 关键字是不够的。虽然 synchronized 关键字阻止并发更新一个对象,但它没有实现线程间发信。Object 类为此提供了三个函数:wait()、notify() 和 notifyAll()。以全球气候预测程序为例。这些程序通过将地球分为许多单元,在每个循环中,每个单元的计算都是隔离进行的,直到这些值趋于稳定,然后相邻单元之间就会交换一些数据。所以,从本质上讲,在每个循环中各个线程都必须等待所有线程完成各自的任务以后才能进入下一个循环。这个模型称为屏蔽同步,下例说明了这个模型:

屏蔽同步

public class BSync {
    int totalThreads;

    int currentThreads;

    public BSync(int x) {
        totalThreads = x;
        currentThreads = 0;
    }

    public synchronized void waitForAll() {
        currentThreads++;
        if (currentThreads < totalThreads) {
            try {
                wait();
            } catch (Exception e) {
            }
        } else {
            currentThreads = 0;
            notifyAll();
        }
    }
}

当对一个线程调用 wait() 时,该线程就被有效阻塞,只到另一个线程对同一个对象调用 notify() 或 notifyAll() 为止。因此,在前一个示例中,不同的线程在完成它们的工作以后将调用 waitForAll() 函数,最后一个线程将触发 notifyAll() 函数,该函数将释放所有的线程。第三个函数 notify() 只通知一个正在等待的线程,当对每次只能由一个线程使用的资源进行访问限制时,这个函数很有用。但是,不可能预知哪个线程会获得这个通知,因为这取决于 Java 虚拟机 (JVM) 调度算法。

将 CPU 让给另一个线程
当线程放弃某个稀有的资源(如数据库连接或网络端口)时,它可能调用 yield() 函数临时降低自己的优先级,以便某个其他线程能够运行。

守护线程
有两类线程:用户线程和守护线程。用户线程是那些完成有用工作的线程。守护线程是那些仅提供辅助功能的线程。Thread 类提供了 setDaemon() 函数。Java 程序将运行到所有用户线程终止,然后它将破坏所有的守护线程。在 Java 虚拟机 (JVM) 中,即使在 main 结束以后,如果另一个用户线程仍在运行,则程序仍然可以继续运行。

避免不提倡使用的方法
不提倡使用的方法是为支持向后兼容性而保留的那些方法,它们在以后的版本中可能出现,也可能不出现。Java 多线程支持在版本 1.1 和版本 1.2 中做了重大修订,stop()、suspend() 和 resume() 函数已不提倡使用。这些函数在 JVM 中可能引入微妙的错误。虽然函数名可能听起来很诱人,但请抵制诱惑不要使用它们。

调试线程化的程序
在线程化的程序中,可能发生的某些常见而讨厌的情况是死锁、活锁、内存损坏和资源耗尽。

死锁
死锁可能是多线程程序最常见的问题。当一个线程需要一个资源而另一个线程持有该资源的锁时,就会发生死锁。这种情况通常很难检测。但是,解决方案却相当好:在所有的线程中按相同的次序获取所有资源锁。例如,如果有四个资源 —A、B、C 和 D — 并且一个线程可能要获取四个资源中任何一个资源的锁,则请确保在获取对 B 的锁之前首先获取对 A 的锁,依此类推。如果“线程 1”希望获取对 B 和 C 的锁,而“线程 2”获取了 A、C 和 D 的锁,则这一技术可能导致阻塞,但它永远不会在这四个锁上造成死锁。

活锁
当一个线程忙于接受新任务以致它永远没有机会完成任何任务时,就会发生活锁。这个线程最终将超出缓冲区并导致程序崩溃。试想一个秘书需要录入一封信,但她一直在忙于接电话,所以这封信永远不会被录入。

内存损坏
如果明智地使用 synchronized 关键字,则完全可以避免内存错误这种气死人的问题。

资源耗尽
某些系统资源是有限的,如文件描述符。多线程程序可能耗尽资源,因为每个线程都可能希望有一个这样的资源。如果线程数相当大,或者某个资源的侯选线程数远远超过了可用的资源数,则最好使用资源池。一个最好的示例是数据库连接池。只要线程需要使用一个数据库连接,它就从池中取出一个,使用以后再将它返回池中。资源池也称为资源库。

调试大量的线程
有时一个程序因为有大量的线程在运行而极难调试。在这种情况下,下面的这个类可能会派上用场:

public class Probe extends Thread {
    public Probe() {
    }

    public void run() {

        while (true) {
            Thread[] x = new Thread[100];
            Thread.enumerate(x);

            for (int i = 0; i < 100; i++) {
                Thread t = x[i];
                if (t == null)
                    break;
                else
                    System.out.println(t.getName() + "\t" + t.getPriority()
                            + "\t" + t.isAlive() + "\t" + t.isDaemon());
            }
        }
    }
}

限制线程优先级和调度
Java 线程模型涉及可以动态更改的线程优先级。本质上,线程的优先级是从 1 到 10 之间的一个数字,数字越大表明任务越紧急。JVM 标准首先调用优先级较高的线程,然后才调用优先级较低的线程。但是,该标准对具有相同优先级的线程的处理是随机的。如何处理这些线程取决于基层的操作系统策略。在某些情况下,优先级相同的线程分时运行;在另一些情况下,线程将一直运行到结束。请记住,Java 支持 10 个优先级,基层操作系统支持的优先级可能要少得多,这样会造成一些混乱。因此,只能将优先级作为一种很粗略的工具使用。最后的控制可以通过明智地使用 yield() 函数来完成。通常情况下,请不要依靠线程优先级来控制线程的状态。

小结
本文说明了在 Java 程序中如何使用线程。像是否应该使用线程这样的更重要的问题在很大程序上取决于手头的应用程序。决定是否在应用程序中使用多线程的一种方法是,估计可以并行运行的代码量。并记住以下几点:

使用多线程不会增加 CPU 的能力。但是如果使用 JVM 的本地线程实现,则不同的线程可以在不同的处理器上同时运行(在多 CPU 的机器中),从而使多 CPU 机器得到充分利用。
如果应用程序是计算密集型的,并受 CPU 功能的制约,则只有多 CPU 机器能够从更多的线程中受益。
当应用程序必须等待缓慢的资源(如网络连接或数据库连接)时,或者当应用程序是非交互式的时,多线程通常是有利的。
基于 Internet 的软件有必要是多线程的;否则,用户将感觉应用程序反映迟钝。例如,当开发要支持大量客户机的服务器时,多线程可以使编程较为容易。在这种情况下,每个线程可以为不同的客户或客户组服务,从而缩短了响应时间。

某些程序员可能在 C 和其他语言中使用过线程,在那些语言中对线程没有语言支持。这些程序员可能通常都被搞得对线程失去了信心。

posted @ 2007-07-30 09:36 比特鼠 阅读(199) | 评论 (0)编辑 收藏

众所周知, Java在从XML文件中装载内容到内存过程中,不论用何种方式,IO操作的开销都无可避免。本文尝试比较dom4j中的XPP3和SAX两种方式装载XML文件的性能,以便将IO操作的开销降到最小!

package gz.lwm;

import java.io.File;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XPP3Reader;

public class TestDom4j {
 private static final Logger log = Logger.getLogger(TestDom4j.class);
 private static long bt; 
 
 public static void main(String[] args) {
  Document doc = DocumentHelper.createDocument();   
  //先运行getXmlSAX()
  bt = System.currentTimeMillis();
  String strXml = getXmlSAX("xml/test.xml");
  if(log.isDebugEnabled()){
   log.debug("\ngetXmlSAX() use time: " + (System.currentTimeMillis() - bt) + " millis\n");
  }

  //再运行getXmlXPP3()
  bt = System.currentTimeMillis();
  String s1 =getXmlXPP3("xml/test.xml");
  if(log.isDebugEnabled()){
   log.debug("\ngetXmlXPP3() use time: " + (System.currentTimeMillis() - bt) + " millis\n");
  }
  
  
 }
 
 public static String getXmlSAX(String xmlFile){
  String result = "";
  try {
   SAXReader reader = new SAXReader();
   Document document = reader.read(new File(xmlFile));
   result = document.asXML();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }
 
 public static String getXmlXPP3(String xmlFile){
  String result = "";
  try {
   XPP3Reader reader = new XPP3Reader();
   Document document = reader.read(new File(xmlFile));
   result = document.asXML();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }


 
}

有没有这一句"Document doc = DocumentHelper.createDocument()",对性能的影响很大,特别是对大xml文件(尽管并没有使用doc)

另外, getXmlXSAX()和getXmlXPP3()运行的先后次序对性能的影响也很大!

测试:
    在我的机器上,对一个100k左右的XML文件进行多次测试后的均值结果为:

    getXmlXPP3() use time: 265 millis
    ...
    getXmlXSAX() use time: 359 millis
    ...

结论:
    通过比较,在读取XML文件上,XPP3略为优于SAX!


注意:

要运行例子,classpath需包含:
dom4j-1.6.1.jar
jaxen-1.1-beta-10.jar
log4j-1.2.9.jar
pull-parser-2.1.10.jar
xpp3-1.1.4c.jar


参考:
dom4j :  http://www.dom4j.org/
XPP   :  http://www.extreme.indiana.edu/xgws/xsoap/xpp/

posted @ 2007-05-19 00:39 比特鼠 阅读(2498) | 评论 (0)编辑 收藏

   Namespace namespace ...

   //第一种方法
   Document doc = DocumentHelper.createDocument();
   Element root = doc.addElement("Root", namespace.getURI());
   Element eResultMessage = root.addElement("ResultMessage");

   结果为:
   <Root xmlns="http://aaaaaa"><ResultMessage>...</ResultMessage></Root>



   //第二种方法
   Document doc = DocumentHelper.createDocument();
   Element root = doc.addElement(("Root");
   root.add(namespace);
   Element eResultMessage = root.addElement("ResultMessage");
   
   结果为:
   <Root xmlns="
http://aaaaaa"><ResultMessage xmlns="">...</ResultMessage></Root>

posted @ 2007-05-17 21:08 比特鼠 阅读(2642) | 评论 (0)编辑 收藏

正则表达式语法 
 

正则表达式是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”)。模式描述在搜索文本时要匹配的一个或多个字符串。

正则表达式示例
 
表达式  匹配 
/^\s*$/
 匹配空行。
 
/\d{2}-\d{5}/
 验证由两位数字、一个连字符再加 5 位数字组成的 ID 号。
 
/<\s*(\S+)(\s[^>]*)?>[\s\S]*<\s*\/\1\s*>/
 匹配 HTML 标记。
 

下表包含了元字符的完整列表以及它们在正则表达式上下文中的行为:

 
字符  说明 
\
 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,“n”匹配字符“n”。“\n”匹配换行符。序列“\\”匹配“\”,“\(”匹配“(”。
 
^
 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与“\n”或“\r”之后的位置匹配。
 
$
 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与“\n”或“\r”之前的位置匹配。
 
*
 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配“z”和“zoo”。* 等效于 {0,}。
 
+
 一次或多次匹配前面的字符或子表达式。例如,“zo+”与“zo”和“zoo”匹配,但与“z”不匹配。+ 等效于 {1,}。
 
?
 零次或一次匹配前面的字符或子表达式。例如,“do(es)?”匹配“do”或“does”中的“do”。? 等效于 {0,1}。
 
{n}
 n 是非负整数。正好匹配 n 次。例如,“o{2}”与“Bob”中的“o”不匹配,但与“food”中的两个“o”匹配。
 
{n,}
 n 是非负整数。至少匹配 n 次。例如,“o{2,}”不匹配“Bob”中的“o”,而匹配“foooood”中的所有 o。“o{1,}”等效于“o+”。“o{0,}”等效于“o*”。
 
{n,m}
 M 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,“o{1,3}”匹配“fooooood”中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。
 
?
 当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是“非贪心的”。“非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的“贪心的”模式匹配搜索到的、尽可能长的字符串。例如,在字符串“oooo”中,“o+?”只匹配单个“o”,而“o+”匹配所有“o”。
 
.
 匹配除“\n”之外的任何单个字符。若要匹配包括“\n”在内的任意字符,请使用诸如“[\s\S]”之类的模式。
 
(pattern)
 匹配 pattern 并捕获该匹配的子表达式。可以使用 $0…$9 属性从结果“匹配”集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用“\(”或者“\)”。
 
(?:pattern)
 匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用“or”字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经济的表达式。
 
(?=pattern)
 执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配“Windows 2000”中的“Windows”,但不匹配“Windows 3.1”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。
 
(?!pattern)
 执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配“Windows 3.1”中的 “Windows”,但不匹配“Windows 2000”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。
 
x|y
 匹配 x 或 y。例如,'z|food' 匹配“z”或“food”。'(z|f)ood' 匹配“zood”或“food”。
 
[xyz]
 字符集。匹配包含的任一字符。例如,“[abc]”匹配“plain”中的“a”。
 
[^xyz]
 反向字符集。匹配未包含的任何字符。例如,“[^abc]”匹配“plain”中的“p”。
 
[a-z]
 字符范围。匹配指定范围内的任何字符。例如,“[a-z]”匹配“a”到“z”范围内的任何小写字母。
 
[^a-z]
 反向范围字符。匹配不在指定的范围内的任何字符。例如,“[^a-z]”匹配任何不在“a”到“z”范围内的任何字符。
 
\b
 匹配一个字边界,即字与空格间的位置。例如,“er\b”匹配“never”中的“er”,但不匹配“verb”中的“er”。
 
\B
 非字边界匹配。“er\B”匹配“verb”中的“er”,但不匹配“never”中的“er”。
 
\cx
 匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回车符。x 的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是“c”字符本身。
 
\d
 数字字符匹配。等效于 [0-9]。
 
\D
 非数字字符匹配。等效于 [^0-9]。
 
\f
 换页符匹配。等效于 \x0c 和 \cL。
 
\n
 换行符匹配。等效于 \x0a 和 \cJ。
 
\r
 匹配一个回车符。等效于 \x0d 和 \cM。
 
\s
 匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。
 
\S
 匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。
 
\t
 制表符匹配。与 \x09 和 \cI 等效。
 
\v
 垂直制表符匹配。与 \x0b 和 \cK 等效。
 
\w
 匹配任何字类字符,包括下划线。与“[A-Za-z0-9_]”等效。
 
\W
 与任何非单词字符匹配。与“[^A-Za-z0-9_]”等效。
 
\xn
 匹配 n,此处的 n 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,“\x41”匹配“A”。“\x041”与“\x04”&“1”等效。允许在正则表达式中使用 ASCII 代码。
 
\num
 匹配 num,此处的 num 是一个正整数。到捕获匹配的反向引用。例如,“(.)\1”匹配两个连续的相同字符。
 
\n
 标识一个八进制转义码或反向引用。如果 \n 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八进制数 (0-7),那么 n 是八进制转义码。
 
\nm
 标识一个八进制转义码或反向引用。如果 \nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用。如果 \nm 前面至少有 n 个捕获,则 n 是反向引用,后面跟有字符 m。如果两种前面的情况都不存在,则 \nm 匹配八进制值 nm,其中 n 和 m 是八进制数字 (0-7)。
 
\nml
 当 n 是八进制数 (0-3),m 和 l 是八进制数 (0-7) 时,匹配八进制转义码 nml。
 
\un
 匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (?)。
 

posted @ 2007-05-17 10:48 比特鼠 阅读(7515) | 评论 (0)编辑 收藏

package com.sunrise.ocs.webservice.unicom.test;

import java.io.File;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public class TestDom4j {
 private static final Logger log = Logger.getLogger(TestDom4j.class);

 private static long bt;

 public static void main(String[] args) {
  String strXml = "";
  int b = 0;
  String file1 = "xml/CreateUserRequest.xml";
  String file2 = "xml/CancelUserRequest.xml";
  if(b==0){
   bt = System.currentTimeMillis();
   strXml = xmlFile2String(file1);
   if (log.isDebugEnabled()) {
    log.debug("\nxmlFile2String() use time: "
      + (System.currentTimeMillis() - bt) + " millis\n");
   }
  }else{
   bt = System.currentTimeMillis();
   strXml = xmlFile2String2(file1);
   if (log.isDebugEnabled()) {
    log.debug("\nxmlFile2String2() use time: "
      + (System.currentTimeMillis() - bt) + " millis\n");
   }
  }

  if(b==0){
   bt = System.currentTimeMillis();
   findElement4XPath1(strXml);
   if (log.isDebugEnabled()) {
    log.debug("\nfindElement4XPath1() use time: "
      + (System.currentTimeMillis() - bt) + " millis\n");
   }
  }else{
   bt = System.currentTimeMillis();
   findElement4XPath2(strXml);
   if (log.isDebugEnabled()) {
    log.debug("\nfindElement4XPath2() use time: "
      + (System.currentTimeMillis() - bt) + " millis\n");
   } 
   
  }
 }

 public static void findElement4XPath1(String xml) {
  try {
   String str = delNamespace4Pattern(xml);
   Document doc = DocumentHelper.parseText(str);
   Element e = (Element) doc.selectSingleNode("//CreateUserRequest/RequestMessage/MessageHeader");
   if (e != null) {
    Iterator iter = e.elementIterator();
    while (iter.hasNext()) {
     Element sub = (Element) iter.next();
     log.debug("\n" + sub.getText() + "\n");
    }
   }
   
   /* 读取属性的例子
   List childNodes = doc.selectNodes("//Config/Child/ChildNode");
         for(Object obj:childNodes) {
             Node childNode = (Node)obj;
             String name = childNode.valueOf("@name"); //读取属性
             String text = childNode.getText();
         }
         */

   
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 public static void findElement4XPath2(String xml) {
  try {
   Document doc = DocumentHelper.parseText(xml);
   Element root = doc.getRootElement();
   
   HashMap map = new HashMap();
   map.put("tns", "http://bme.sunrise.com/unicom/gd");
   XPath x = doc.createXPath("//tns:CreateUserRequest/tns:RequestMessage/tns:MessageHeader");
   x.setNamespaceURIs(map);
   
   Element e = (Element) x.selectSingleNode(doc);
   if (e != null) {
    Iterator iter = e.elementIterator();
    while (iter.hasNext()) {
     Element sub = (Element) iter.next();
     if (log.isDebugEnabled()) {
      log.debug("\n" + sub.getText() + "\n");
     }
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 public static Document xml2Document(String xml) {
  try {
   return DocumentHelper.parseText(xml);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }

 public static String xmlFile2String(String xmlFile) {
  try {
   return new SAXReader().read(new File(xmlFile)).asXML();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }
 
 //读取xml文件为xml串
 public static String xmlFile2String2(String xmlFile) {
  try {
   org.w3c.dom.Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile); 
   OutputFormat format = new OutputFormat(document);
   //format.setEncoding("UTF-8");
   StringWriter stringOut = new StringWriter();
   XMLSerializer serial = new XMLSerializer(stringOut, format);
   serial.asDOMSerializer();
   serial.serialize(document.getDocumentElement());
   return stringOut.toString();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return "";
 }
 
 
 public static String delNamespace4Pattern(String xml){
  String result = "";
  try {
   result = xml.replaceFirst("xmlns([^ ]*)=([^ ]*)http([^>^\"]*)\"", "");
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
  
 }

 

}

posted @ 2007-05-17 10:46 比特鼠 阅读(198) | 评论 (0)编辑 收藏

package com.sunrise.ocs.webservice.unicom.test;

import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public class TestDom {
 
 //将xml串转换为document
 public static Document xml2Document(String xml) {
  Document doc = null;
  try {
   DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
   doc = builder.parse(new InputSource(new StringReader(xml)));
  } catch (Exception e) {
   e.printStackTrace();
  }
  return doc;
 }
 
 //将xml文件串转换为document
 public static Document xmlFile2Document(String xmlFile) {
  Document doc = null;
  try {
   DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
   doc = builder.parse(new File(xmlFile));
  } catch (Exception e) {
   e.printStackTrace();
  }
  return doc;
 }
 
 //删除命名空间: xmlns="..."
 public static String delNamespace(String xml) {
  String result = xml;
  try {
   Document doc = xml2Document(xml);
   Element root = doc.getDocumentElement();
   root.removeAttribute("xmlns");
   result = asXml(doc);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }
 
 //将doc转换为xml串
 public static String asXml(Document doc) {
  String strxml = "";
  try {
   OutputFormat format = new OutputFormat(doc);
   // format.setEncoding("UTF-8");
   StringWriter stringOut = new StringWriter();
   XMLSerializer serial = new XMLSerializer(stringOut, format);
   serial.asDOMSerializer();
   serial.serialize(doc.getDocumentElement());
   strxml = stringOut.toString();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return strxml;
 }
 
 //将node转换为xml串
 public static String asXml(Node node, Document doc) {
  String strxml = "";
  try {
   OutputFormat format = new OutputFormat(doc);
   // format.setEncoding("UTF-8");
   StringWriter stringOut = new StringWriter();
   XMLSerializer serial = new XMLSerializer(stringOut, format);
   serial.asDOMSerializer();
   serial.serialize((Element)node);
   strxml = stringOut.toString();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return strxml;
 }
}

posted @ 2007-05-17 10:10 比特鼠 阅读(271) | 评论 (0)编辑 收藏

vi 是visual edit 的缩写
文本编辑器是所有计算机系统中最常用的一种工具。UNIX下的编辑器有ex,sed和vi等,其中,使用最为广泛的是vi,而vi命令繁多,论坛里好像这方面的总结不多,以下稍做总结,以资共享!渴望更正和补充!

进入vi的命令
vi filename :打开或新建文件,并将光标置于第一行首
vi +n filename :打开文件,并将光标置于第n行首
vi + filename :打开文件,并将光标置于最后一行首
vi +/pattern filename:打开文件,并将光标置于第一个与pattern匹配的串处
vi -r filename :在上次正用vi编辑时发生系统崩溃,恢复filename
vi filename....filename :打开多个文件,依次进行编辑

移动光标类命令
h :光标左移一个字符
l :光标右移一个字符
space:光标右移一个字符
Backspace:光标左移一个字符
k或Ctrl+p:光标上移一行
j或Ctrl+n :光标下移一行
Enter :光标下移一行
w或W :光标右移一个字至字首
b或B :光标左移一个字至字首
e或E :光标右移一个字至字尾
) :光标移至句尾
( :光标移至句首
}:光标移至段落开头
{ :光标移至段落结尾
nG:光标移至第n行首
n+:光标下移n行
n-:光标上移n行
n$:光标移至第n行尾
H :光标移至屏幕顶行
M :光标移至屏幕中间行
L :光标移至屏幕最后行
0:(注意是数字零)光标移至当前行首
$:光标移至当前行尾

屏幕翻滚类命令
Ctrl+u:向文件首翻半屏
Ctrl+d:向文件尾翻半屏
Ctrl+f:向文件尾翻一屏
Ctrl+b;向文件首翻一屏
nz:将第n行滚至屏幕顶部,不指定n时将当前行滚至屏幕顶部。

插入文本类命令
i :在光标前
I :在当前行首
a:光标后
A:在当前行尾
o:在当前行之下新开一行
O:在当前行之上新开一行
r:替换当前字符
R:替换当前字符及其后的字符,直至按ESC键
s:从当前光标位置处开始,以输入的文本替代指定数目的字符
S:删除指定数目的行,并以所输入文本代替之
ncw或nCW:修改指定数目的字
nCC:修改指定数目的行

删除命令
ndw或ndW:删除光标处开始及其后的n-1个字
do:删至行首
d$:删至行尾
ndd:删除当前行及其后n-1行
x或X:删除一个字符,x删除光标后的,而X删除光标前的
Ctrl+u:删除输入方式下所输入的文本

搜索及替换命令
/pattern:从光标开始处向文件尾搜索pattern
?pattern:从光标开始处向文件首搜索pattern
n:在同一方向重复上一次搜索命令
N:在反方向上重复上一次搜索命令
:s/p1/p2/g:将当前行中所有p1均用p2替代
:n1,n2s/p1/p2/g:将第n1至n2行中所有p1均用p2替代
:g/p1/s//p2/g:将文件中所有p1均用p2替换

选项设置
all:列出所有选项设置情况
term:设置终端类型
ignorance:在搜索中忽略大小写
list:显示制表位(Ctrl+I)和行尾标志($)
number:显示行号
report:显示由面向行的命令修改过的数目
terse:显示简短的警告信息
warn:在转到别的文件时若没保存当前文件则显示NO write信息
nomagic:允许在搜索模式中,使用前面不带“\”的特殊字符
nowrapscan:禁止vi在搜索到达文件两端时,又从另一端开始
mesg:允许vi显示其他用户用write写到自己终端上的信息

最后行方式命令
:n1,n2 co n3:将n1行到n2行之间的内容拷贝到第n3行下
:n1,n2 m n3:将n1行到n2行之间的内容移至到第n3行下
:n1,n2 d :将n1行到n2行之间的内容删除
:w :保存当前文件
:e filename:打开文件filename进行编辑
:x:保存当前文件并退出
:q:退出vi
:q!:不保存文件并退出vi
:!command:执行shell命令command
:n1,n2 w!command:将文件中n1行至n2行的内容作为command的输入并执行之,若不指定n1,n2,则表示将整个文件内容作为command的输入
:r!command:将命令command的输出结果放到当前行

寄存器操作
"?nyy:将当前行及其下n行的内容保存到寄存器?中,其中?为一个字母,n为一个数字
"?nyw:将当前行及其下n个字保存到寄存器?中,其中?为一个字母,n为一个数字
"?nyl:将当前行及其下n个字符保存到寄存器?中,其中?为一个字母,n为一个数字
"?p:取出寄存器?中的内容并将其放到光标位置处。这里?可以是一个字母,也可以是一个数字
ndd:将当前行及其下共n行文本删除,并将所删内容放到1号删除寄存器中。


进入vi
vi test
离开vi
:q! 离开vi,并放弃刚在缓冲区内编辑的内容。
   :wq 将缓冲区内的资料写入磁盘中,并离开vi。
   :ZZ 同wq
同wq
:w 将缓冲区内的资料写入磁盘中,但并不离开vi。
  :q 离开vi,若文件被修改过,则会被要求确认是否放弃修改的内容,此指令可与: w 配合使用。
Vi 的操作模式
Vi 提供两种操作模式:
输入模式(insert mode)
指令模式(command mode)
当使用者进入vi后,既处于指令模式下,此刻键入任何字元皆被视为指令。

输入模式:a(append) 游标之后加入资料。
A 该行之末加入资料
i (insert) 游标之前加入资料
I 该行之首加入资料
o (open) 新增一行与该行之下供输入资料
O 新增一行与该行之上供输入资料

指令模式:B      移至该行第一个字符,若光标在该行第一字符则光标移至上一行第一字符。
   b    由游标所在位置之前一个字串的第一个字元
     cc 删除整行,修改整行的内容。
     D      以行为单位,删除游标在内后面的所有字符。
db 删除该行光标前字符
     dd 删除该行
     de 删除自光标开始后面的字符
     d加字符   删除光标所在位置至字符之间的单
     E      移至该行最后字符,若光标在该行最后字符则光标移至下一行最后字符
 e      由游标所在位置至该字串的最后一个字元
     G 移至该档案的最后一行 
     h 向前移一个字元
j 向下移一个字元
k 向上移一个字元
0 移至该行之首
M 移至视窗的中间那行
L 移至视窗的最后一行
     l 向后移一个字符
0 由游标所在位置该行的第一个字元
nG 移至该档案的第n行
n+ 自游标所在位置向后移n行至该行的第一字符
n- 自游标所在位置向前移n行至该行的第一字符
R 进入取代状态,直到《ESC》为止
s 删除游标所在字元,并进入取代模式直到《ESC》
S 删除游标所在之该行资料,并进入输入模式直到《ESC》
w 由游标所在位置之下一个字串的第一个字元
x 删除游标所在该字元。
X 删除游标所在之前一字元。
r 用接于此指令之后的字元取代(replace)游标所在字元
yy yank整行,使游标所在该行复制到记忆体缓冲区
显示该行之行号、档案名称、档案中最末之行号、游标所在行号占
总行号之百分比
$ 由游标所在位置至该行的最后一个字元。
) 由游标所在位置至下一个句子的第一个字元。
( 由游标所在位置至该句子的第一个字元。
{  由游标所在位置至该段落的最后一个字元。
} 由游标所在位置至该段落的第一个字元

yank和delete可将指定的资料复制到记忆体缓冲区,而藉有put指令可将缓冲区内的资料复制到荧幕上
例如:搬移一行 :在该行执行dd
游标移至目的地
执行p
复制一行 :在该行执行yy
游标移至目的地
执行p
视窗移动:
视窗往下卷一页
视窗往上卷一页
视窗往下卷半页
视窗往上卷半页
视窗往下卷一行
视窗往上卷一行
删除、复制及修改指令介绍:
d(delete)、c(change)和y(yank)这一类的指令在vi 中的指令格式为:
operation+scope=command
(运算子)(范围)
运算子:
d 删除指令。删除资料,但会将删除资料复制到记忆体缓冲区。
y 将资料(字组、行列、句子或段落)复制到缓冲区。
p 放置(put)指令,与d和y配合使用。可将最后delete或yank的资料放置于游标所在位置之行列下。
c 修改(change)指令,类似delete于insert的组合。删除一个字组、句子等资料,并插入新键入的




posted @ 2007-05-15 11:33 比特鼠 阅读(229) | 评论 (0)编辑 收藏

1、使用JdbcTemplate的execute()方法执行SQL语句

代码

2、如果是UPDATE或INSERT,可以用update()方法。
代码

3、带参数的更新
代码

代码

4、使用JdbcTemplate进行查询时,使用queryForXXX()等方法
代码

代码

代码

代码

JdbcTemplate将我们使用的JDBC的流程封装起来,包括了异常的捕捉、SQL的执行、查询结果的转换等等。spring大量使用Template Method模式来封装固定流程的动作,XXXTemplate等类别都是基于这种方式的实现。
除了大量使用Template Method来封装一些底层的操作细节,spring也大量使用callback方式类回调相关类别的方法以提供JDBC相关类别的功能,使传统的JDBC的使用者也能清楚了解spring所提供的相关封装类别方法的使用。

JDBC的PreparedStatement

代码

代码

代码

在getUser(id)里面使用UserRowMapper

代码

网上收集
org.springframework.jdbc.core.PreparedStatementCreator 返回预编译SQL 不能于Object[]一起用

代码

1.增删改
org.springframework.jdbc.core.JdbcTemplate 类(必须指定数据源dataSource)
代码


代码

org.springframework.jdbc.core.PreparedStatementSetter 接口 处理预编译SQL
代码

2.查询JdbcTemplate.query(String,[Object[]/PreparedStatementSetter],RowMapper/RowCallbackHandler)
org.springframework.jdbc.core.RowMapper 记录映射接口 处理结果集
代码

org.springframework.jdbc.core.RowCallbackHandler 记录回调管理器接口 处理结果集
代码

posted @ 2007-04-11 18:48 比特鼠 阅读(502) | 评论 (0)编辑 收藏

制作可执行的JAR文件包及jar命令详解


常常在网上看到有人询问:如何把 java 程序编译成 .exe 文件。通常回答只有两种,一种是制作一个可执行的 JAR 文件包,然后就可以像.chm 文档一样双击运行了;而另一种是使用 JET 来进行 编译。但是 JET 是要用钱买的,而且据说 JET 也不是能把所有的 Java 程序都编译成执行文件,性能也要打些折扣。所以,使用制作可执行 JAR 文件包的方法就是最佳选择了,何况它还能保持 Java 的跨平台特性。 

下面就来看看什么是 JAR 文件包吧: 

1. JAR 文件包 

JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式。JAR 文件非常类似 ZIP 文件——准确的说,它就是 ZIP 文件,所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,包含了一个 META-INF/MANIFEST.MF 文件,这个文件是在生成 JAR 文件的时候自动创建的。举个例子,如果我们具有如下目录结构的一些文件: 

  == 

  `-- test 

    `-- Test.class 

把它压缩成 ZIP 文件 test.zip,则这个 ZIP 文件的内部目录结构为: 

  test.zip 

  `-- test 

    `-- Test.class 

如果我们使用 JDK 的 jar 命令把它打成 JAR 文件包 test.jar,则这个 JAR 文件的内部目录结构为: 

  test.jar 

  |-- META-INF 

  |  `-- MANIFEST.MF 

  `-- test 

    `--Test.class 

2. 创建可执行的 JAR 文件包 

制作一个可执行的 JAR 文件包来发布你的程序是 JAR 文件包最典型的用法。 

Java 程序是由若干个 .class 文件组成的。这些 .class 文件必须根据它们所属的包不同而分级分目录存放;运行前需要把所有用到的包的根目录指定给 CLASSPATH 环境变量或者 java 命令的 -cp 参数;运行时还要到控制台下去使用 java 命令来运行,如果需要直接双击运行必须写 Windows 的批处理文件 (.bat) 或者 Linux 的 Shell 程序。因此,许多人说,Java 是一种方便开发者苦了用户的程序设计语言。 

其实不然,如果开发者能够制作一个可执行的 JAR 文件包交给用户,那么用户使用起来就方便了。在 Windows 下安装 JRE (Java Runtime Environment) 的时候,安装文件会将 .jar 文件映射给 javaw.exe 打开。那么,对于一个可执行的 JAR 文件包,用户只需要双击它就可以运行程序了,和阅读 .chm 文档一样方便 (.chm 文档默认是由 hh.exe 打开的)。那么,现在的关键,就是如何来创建这个可执行的 JAR 文件包。 

创建可执行的 JAR 文件包,需要使用带 cvfm 参数的 jar 命令,同样以上述 test 目录为例,命令如下: 

jar cvfm test.jar manifest.mf test 

这里 test.jar 和 manifest.mf 两个文件,分别是对应的参数 f 和 m,其重头戏在 manifest.mf。因为要创建可执行的 JAR 文件包,光靠指定一个 manifest.mf 文件是不够的,因为 MANIFEST 是 JAR 文件包的特征,可执行的 JAR 文件包和不可执行的 JAR 文件包都包含 MANIFEST。关键在于可执行 JAR 文件包的 MANIFEST,其内容包含了 Main-Class 一项。这在 MANIFEST 中书写格式如下: 

Main-Class: 可执行主类全名(包含包名) 

例如,假设上例中的 Test.class 是属于 test 包的,而且是可执行的类 (定义了 public static void main(String[]) 方法),那么这个 manifest.mf 可以编辑如下: 

Main-Class: test.Test <回车>; 

这个 manifest.mf 可以放在任何位置,也可以是其它的文件名,只需要有 Main-Class: test.Test 一行,且该行以一个回车符结束即可。创建了 manifest.mf 文件之后,我们的目录结构变为: 

  == 

  |-- test 

  |  `-- Test.class 

  `-- manifest.mf 

这时候,需要到 test 目录的上级目录中去使用 jar 命令来创建 JAR 文件包。也就是在目录树中使用“==”表示的那个目录中,使用如下命令: 

jar cvfm test.jar manifest.mf test 

之后在“==”目录中创建了 test.jar,这个 test.jar 就是执行的 JAR 文件包。运行时只需要使用 java -jar test.jar 命令即可。 

需要注意的是,创建的 JAR 文件包中需要包含完整的、与 Java 程序的包结构对应的目录结构,就像上例一样。而 Main-Class 指定的类,也必须是完整的、包含包路径的类名,如上例的 test.Test;而且在没有打成 JAR 文件包之前可以使用 java <类名>; 来运行这个类,即在上例中 java test.Test 是可以正确运行的 (当然要在 CLASSPATH 正确的情况下)。 

3. jar 命令详解 

jar 是随 JDK 安装的,在 JDK 安装目录下的 bin 目录中,Windows 下文件名为 jar.exe,Linux 下文件名为 jar。它的运行需要用到 JDK 安装目录下 lib 目录中的 tools.jar 文件。不过我们除了安装 JDK 什么也不需要做,因为 SUN 已经帮我们做好了。我们甚至不需要将 tools.jar 放到 CLASSPATH 中。 

使用不带任何的 jar 命令我们可以看到 jar 命令的用法如下: 

jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ... 

其中 {ctxu} 是 jar 命令的子命令,每次 jar 命令只能包含 ctxu 中的一个,它们分别表示: 

-c 创建新的 JAR 文件包 

-t 列出 JAR 文件包的内容列表 

-x 展开 JAR 文件包的指定文件或者所有文件 

-u 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中) 

[vfm0M] 中的选项可以任选,也可以不选,它们是 jar 命令的选项参数 

-v 生成详细报告并打印到标准输出 

-f 指定 JAR 文件名,通常这个参数是必须的 

-m 指定需要包含的 MANIFEST 清单文件 

-0 只存储,不压缩,这样产生的 JAR 文件包会比不用该参数产生的体积大,但速度更快 

-M 不产生所有项的清单(MANIFEST〕文件,此参数会忽略 -m 参数 

[jar-文件] 即需要生成、查看、更新或者解开的 JAR 文件包,它是 -f 参数的附属参数 

[manifest-文件] 即 MANIFEST 清单文件,它是 -m 参数的附属参数 

[-C 目录] 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令,它只能在创建和更新 JAR 文件包的时候可用。   

文件名 ... 指定一个文件/目录列表,这些文件/目录就是要添加到 JAR 文件包中的文件/目录。如果指定了目录,那么 jar 命令打包的时候会自动把该目录中的所有文件和子目录打入包中。 

下面举一些例子来说明 jar 命令的用法: 

1) jar cf test.jar test 

该命令没有执行过程的显示,执行结果是在当前目录生成了 test.jar 文件。如果当前目录已经存在 test.jar,那么该文件将被覆盖。 

2) jar cvf test.jar test 

该命令与上例中的结果相同,但是由于 v 参数的作用,显示出了打包过程,如下: 

标明清单(manifest) 

增加:test/(读入= 0) (写出= 0)(存储了 0%) 

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%) 

3) jar cvfM test.jar test 

该命令与 2) 结果类似,但在生成的 test.jar 中没有包含 META-INF/MANIFEST 文件,打包过程的信息也略有差别: 

增加:test/(读入= 0) (写出= 0)(存储了 0%) 

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%) 

4) jar cvfm test.jar manifest.mf test 

运行结果与 2) 相似,显示信息也相同,只是生成 JAR 包中的 META-INF/MANIFEST 内容不同,是包含了 manifest.mf 的内容 

5) jar tf test.jar 

在 test.jar 已经存在的情况下,可以查看 test.jar 中的内容,如对于 2) 和 3) 生成的 test.jar 分别应该此命令,结果如下; 

对于 2) 

META-INF/ 

META-INF/MANIFEST.MF 

test/ 

test/Test.class 

对于 3) 

test/ 

test/Test.class 

6) jar tvf test.jar 

除显示 5) 中显示的内容外,还包括包内文件的详细信息,如: 

0 Wed Jun 19 15:39:06 GMT 2002 META-INF/ 

86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF 

0 Wed Jun 19 15:33:04 GMT 2002 test/ 

7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class 

7) jar xf test.jar 

解开 test.jar 到当前目录,不显示任何信息,对于 2) 生成的 test.jar,解开后的目录结构如下: 

  == 

  |-- META-INF 

  |  `-- MANIFEST 

  `-- test 

    `--Test.class 

8) jar xvf test.jar 

运行结果与 7) 相同,对于解压过程有详细信息显示,如: 

创建:META-INF/ 

展开:META-INF/MANIFEST.MF 

创建:test/ 

展开:test/Test.class 

9) jar uf test.jar manifest.mf 

在 test.jar 中添加了文件 manifest.mf,此使用 jar tf 来查看 test.jar 可以发现 test.jar 中比原来多了一个 manifest。这里顺便提一下,如果使用 -m 参数并指定 manifest.mf 文件,那么 manifest.mf 是作为清单文件 MANIFEST 来使用的,它的内容会被添加到 MANIFEST 中;但是,如果作为一般文件添加到 JAR 文件包中,它跟一般文件无异。 

10) jar uvf test.jar manifest.mf 

与 9) 结果相同,同时有详细信息显示,如: 

增加:manifest.mf(读入= 17) (写出= 19)(压缩了 -11%) 

4. 关于 JAR 文件包的一些技巧 

1) 使用 unzip 来解压 JAR 文件 

在介绍 JAR 文件的时候就已经说过了,JAR 文件实际上就是 ZIP 文件,所以可以使用常见的一些解压 ZIP 文件的工具来解压 JAR 文件,如 Windows 下的 WinZip、WinRAR 等和 Linux 下的 unzip 等。使用 WinZip 和 WinRAR 等来解压是因为它们解压比较直观,方便。而使用 unzip,则是因为它解压时可以使用 -d 参数指定目标目录。 

在解压一个 JAR 文件的时候是不能使用 jar 的 -C 参数来指定解压的目标的,因为 -C 参数只在创建或者更新包的时候可用。那么需要将文件解压到某个指定目录下的时候就需要先将这具 JAR 文件拷贝到目标目录下,再进行解压,比较麻烦。如果使用 unzip,就不需要这么麻烦了,只需要指定一个 -d 参数即可。如: 

unzip test.jar -d dest/ 

2) 使用 WinZip 或者 WinRAR 等工具创建 JAR 文件 

上面提到 JAR 文件就是包含了 META-INF/MANIFEST 的 ZIP 文件,所以,只需要使用 WinZip、WinRAR 等工具创建所需要 ZIP 压缩包,再往这个 ZIP 压缩包中添加一个包含 MANIFEST 文件的 META-INF 目录即可。对于使用 jar 命令的 -m 参数指定清单文件的情况,只需要将这个 MANIFEST 按需要修改即可。 

3) 使用 jar 命令创建 ZIP 文件 

有些 Linux 下提供了 unzip 命令,但没有 zip 命令,所以需要可以对 ZIP 文件进行解压,即不能创建 ZIP 文件。如要创建一个 ZIP 文件,使用带 -M 参数的 jar 命令即可,因为 -M 参数表示制作 JAR 包的时候不添加 MANIFEST 清单,那么只需要在指定目标 JAR 文件的地方将 .jar 扩展名改为 .zip 扩展名,创建的就是一个不折不扣的 ZIP 文件了,如将上一节的第 3) 个例子略作改动: 

jar cvfM test.zip test
posted @ 2007-04-11 18:19 比特鼠 阅读(314) | 评论 (1)编辑 收藏

 

import java.io.File;

public class Test {
    
public static void main(String[] args) {
         System.out.println(
"1:"+Thread.currentThread().getContextClassLoader().getResource(""));     

          System.out.println(
"2:"+Test.class.getClassLoader().getResource(""));        

          System.out.println(
"3:"+ClassLoader.getSystemResource(""));        
          System.out.println(
"4:"+Test.class.getResource(""));        
          System.out.println(
"5:"+Test.class.getResource("/")); //Class文件所在路径  
          System.out.println("6:"+new File("").getAbsolutePath());        
          System.out.println(
"7:"+System.getProperty("user.dir"));  
          
          String s 
= ClassLoader.getSystemResource("").getPath();
          System.out.println(s.substring(
1));
          System.out.println(s.substring(
1).substring(0, s.lastIndexOf("/classes")));

    }

}
posted @ 2007-04-04 16:49 比特鼠 阅读(160) | 评论 (0)编辑 收藏

dom4j 直接往Element中加入符合格式的xml串!

下面的代码直接往root元素插入符合格式的xml串:

String strXml = "<aaa><bbb></bbb><ccc></ccc></aaa>";
Document doc = DocumentHelper.createDocument();
Element  root = doc.getRootElement();
root.add(DocumentHelper.parseText(strXml).getRootElement());
posted @ 2007-03-27 16:20 比特鼠 阅读(469) | 评论 (1)编辑 收藏

 public long algo(){
    int a = 0;
    String b = null;
    long st = System.currentTimeMillis();
    for (int i = 0; i < 2000000; i++){
      b = Integer.toString(a);
    }
    long et = System.currentTimeMillis();
    return (et - st);
  }
posted @ 2007-03-27 15:11 比特鼠 阅读(1406) | 评论 (1)编辑 收藏

玩转 XPath 和缺省命名空间(Default Namespaces)

原文出自:http://www.edankert.com/defaultnamespaces.html
翻译文出自:http://wakan.blog.51cto.com/blog/59583/7220



诸如“为什么用 XPath 的表达式进行查询,却没有返回所期望的结果?”的问题通常都与命名空间(NameSpace)有关,而且绝大多数是与缺省命名空间(Default Namespace)有关。本文试图解释这个问题并针对三种流行的 XPath 实现给出解决方法:Jaxen、JAXP XPPathFactory 以及 XSLT。
内容列表
问题描述
“前缀-命名空间”映射
Jaxen 和 Dom4J
Jaxen 和 XOM
Jaxen 和 JDOM
JAXP XPathFactory
XSLT
结束语
资源


问题描述
看下述 XML:
<catalog>
  <cd>
    <artist>Sufjan Stevens</artist>
    <title>Illinois</title>
    <src>http://www.sufjan.com/</src>
  </cd>
  <cd>
    <artist>Stoat</artist>
    <title>Future come and get me</title>
    <src>http://www.stoatmusic.com/</src>
  </cd>
  <cd>
    <artist>The White Stripes</artist>
    <title>Get behind me satan</title>
    <src>http://www.whitestripes.com/</src>
  </cd>
</catalog>

    你可以使用“//cd”来得到没有在任何命名空间中定义的“cd”节点。 


    现在让我们来改造这个 XML,让它的所有元素都属于 'http://www.edankert.com/examples/' 命名空间中。
 
    为了避免在每个不同的元素前都要加个前缀,我们在根元素上定义通常所说的缺省命名空间。改造后的 XML 如下:
 
<catalog xmlns="
http://www.edankert.com/examples/ ">
  <cd>
    <artist>Sufjan Stevens</artist>
    <title>Illinois</title>
    <src>http://www.sufjan.com/</src>
  </cd>
  <cd>
    <artist>Stoat</artist>
    <title>Future come and get me</title>
    <src>http://www.stoatmusic.com/</src>
  </cd>
  <cd>
    <artist>The White Stripes</artist>
    <title>Get behind me satan</title>
    <src>http://www.whitestripes.com/</src>
  </cd>
</catalog>

    当我们使用与上文相同的 XPath “//cd”,将得不到任何元素。这是因为指定的 XPath 返回的是所有不属于任何命名空间的“cd”节点,而本例中,所有的“cd”元素都属于缺省的命名空间“http://www.edankert.com/examples/”。


“前缀-命名空间”映射
    为了取出命名空间“http://www.edankert.com/examples/”中的所有“cd”元素,我们需要对 XPath 表达式做一些额外的工作。
 
    为了解决这个问题,XPath 规范允许我们使用 QName 来指定元素或者属性。QName 可以是元素的直接名称(形如“element”),或者包含一个前缀(形如“pre:element”)。这个前缀需要映射到一个命名空间的 URI 上。例如,如果把“pre”前缀映射到“http://www.edankert.com/test”上,则通过“pre:element”可以查找出属于命名空间“http://www.edankert.com/test”的所有 “element”元素。
 
    在本例中,我们把“edx”映射到“'http://www.edankert.com/examples/”命名空间上。通过 XPath“//edx:cd”就可以查找出属于“'http://www.edankert.com/examples/”命名空间的所有“cd”元素。 
 
    XPath 处理器允许设置“前缀-命名空间”的映射,但是,如何去映射,却要依赖于具体的实现。下文举例说明 Jaxen (JDOM/dom4j/XOM)、JAXP 以及 XSLT 中是如何进行“前缀-命名空间”的映射的。

Jaxen 和 Dom4J
    下述代码从文件系统读入一个 XML 文件到 org.dom4j.Document 对象中,并且在 Document 中查找属于“http://www.edankert.com/examples/”命名空间的所有“cd”元素。
 
try {
  SAXReader reader = new SAXReader();
  Document document = reader.read( "file:catalog.xml");
 
  HashMap map = new HashMap();
  map.put( "edx", "
http://www.edankert.com/examples/ ");
 
  XPath xpath = new Dom4jXPath( "//edx:cd");
  xpath.setNamespaceContext( new SimpleNamespaceContext( map));
 
  List nodes = xpath.selectNodes( document);
 
  ...
 
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( DocumentException e) {
  // the document is not well-formed.
  ...
}
    第一步,创建一个 SAXReader,用来从文件系统中读取“catalog.xml”并创建一个特定于 Dom4j 的 Document 对象。

    第二步,对于所有 Jaxen 实现都一样,就是创建一个 HashMap 对象,用于保存“前缀-命名空间的 URI”的映射。
 
    为了能通过 Dom4j 使用 Jaxen 的 XPath 功能,需要创建一个与 Dom4j 相关的 XPath 对象:Dom4jXPath。创建方法是把 XPath 的表达式(即“//edx:cd”)传给 Dom4jXPath 的构造方法。
 
    现在,我们已经创建了 XPath 对象,接下来可以把“前缀-命名空间”的映射表传递给 XPath 引擎:把这个 HashMap 映射表用 SimpleNamespaceContext 包装起来。SimpleNamespaceContext 是 Jaxen 的 NamespaceContext 接口的默认实现类。
 
    最后一步就是调用 XPath 对象的 selectNodes() 方法进行查找。并把完整的 Dom4j  Document 对象作为参数传递进去。实际上,Document 中的任何结点都可以作为参数。

Jaxen 和 XOM
    XOM 是基于简单的 Java DOM APIs 之上的最新工具,它的设计初衷是提供简单和易学易用的接口。
 
try {
  Builder builder = new Builder();
  Document document = builder.build( "file:catalog.xml");
 
  HashMap map = new HashMap();
  map.put( "edx", "
http://www.edankert.com/examples/ ");
 
  XPath xpath = new XOMXPath( "//edx:cd");
  xpath.setNamespaceContext( new SimpleNamespaceContext( map));
 
  List nodes = xpath.selectNodes( document);
 
  ...
 
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
  // An error occurred opening the document
  ...
} catch ( ParsingException e) {
  // An error occurred parsing the document
  ...
}

 
    我们需要创建一个 Builder 对象,从文件系统中读取“catalog.xml”文件,并创建出与 XOM 相关的 Document 对象。
 
    下一步创建出包含了“前缀-命名空间”映射关系的 HashMap 对象。
 
    我们需要创建一个特定于 XOM 的 XPath 对象:XOMXPath。创建方法是把 XPath 表达式传递给构造方法,然后就可以通过 XOM 使用 Jaxen 的 XPath 功能了。
 
    创建完 XPath 对象后,同样,我们把“前缀-命名空间”的映射表用 SimpleNamespaceContext 对象封装后,传递给 XPath 引擎。
 
    最后调用 XPath 对象的“selectNodes()”方法进行查找,把 XOM Document 对象作为本方法的参数。
Jaxen 和 JDOM
    JDOM 是第一个提供简单的 XML 访问 API 的工具。
 
try {
  SAXBuilder builder = new SAXBuilder();
  Document document = builder.build( "file:catalog.xml");
 
  HashMap map = new HashMap();
  map.put( "edx", "
http://www.edankert.com/examples/ ");
 
  XPath xpath = new JDOMXPath( "//edx:cd");
  xpath.setNamespaceContext( new SimpleNamespaceContext( map));
 
  List nodes = xpath.selectNodes( document);
 
  ...
 
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
  // An error occurred opening the document
  ...
} catch ( JDOMException e) {
  // An error occurred parsing the document
  ...
}

 
    首先,通过 SAXBuilder 创建了一个特定于 JDom 的 Document 对象。
 
    接着创建一个特定于 JDOM 的 XPath 对象:JDOMXPath。
 
    然后,把“前缀-命名空间”的映射表(HashMap)用 SimpleNamespaceContext 对象封装起来,传递给 XPath 引擎。
 
    最后调用 XPath 对象的“selectNodes()”方法来进行查找,并把 JDOM 的 Document 对象作为本方法的输入参数。
JAXP XPathFactory
    从 1.3 版起, JAXP 还提供了一种在 XML Object Models 上进行查询的通用机制。
 
try {
 DocumentBuilderFactory domFactory =DocumentBuilderFactory.newInstance();
  domFactory.setNamespaceAware( true);
 
 DocumentBuilder builder = domFactory.newDocumentBuilder();Document document = builder.parse( new InputSource( "file:catalog.xml"));
 
 XPathFactory factory =XPathFactory.newInstance();
 XPath xpath = factory.newXPath();
  xpath.setNamespaceContext( new NamespaceContext() {
    public String getNamespaceURI(String prefix) {
      if ( prefix.equals( "edx")) {
        return "
http://www.edankert.com/examples/ ";
      } else if ...
        ...
      }
     
      return XPathConstants.NULL_NS_URI;
    }
 
    public String getPrefix(String namespaceURI) {
      if ( namespaceURI.equals( "
http://www.edankert.com/examples/ ")) {
        return "edx";
      } else if ...
        ...
      } 
   
      return null;
    }
 
    public Iterator getPrefixes(String namespaceURI) {
     ArrayList list = new ArrayList();
   
      if ( namespaceURI.equals( "
http://www.edankert.com/examples/ ")) {
        list.add( "edx");
      } else if ...
        ...
      }
   
      return list.iterator();
    }
  });
 
 Object nodes = xpath.evaluate( "//edx:cd", document.getDocumentElement(),
                                XPathConstants.NODESET);
 
  ...
 
} catch (ParserConfigurationException e) {
  ...
} catch (XPathExpressionException e) {
  ...
} catch (SAXException e) {
  ...
} catch (IOException e) {
  ...
}

    首先用 JAXP 的 DocumentBuilderFactory 创建一个org.w3c.dom.Document 对象,确保启用了 namespace 处理功能。
 
    现在可以通过 XPathFactory 来创建 XPath 对象,并通过 XPath 对象对文档进行查询。
 
    为了创建“前缀-命名空间”映射并传递给 XPath 引擎,我们需要实现 NamespaceContext 接口,该接口目前还没有默认实现类。这就意味着要实现 getNamespaceURI、getPrefix 和getPrefixes 方法,并确保这些方法能返回正确的值,包括“xmlns”和“xml”前缀所对应的命名空间的 URI 值。
 
    把我们自己实现的 NamespaceContext 对象传递给 XPath 引擎后,就可以通过 evaluate 方法来查询 XPath 表达式所对应的元素:使用上文中提到的 XPath 表达式,并使用 Document 的根节点作为输入入参数,并接收一个 NodeList 对象作为返回结果。
XSLT
    XPath 设计的初衷是用于 XSLT。这也许能解释“为什么在 XSLT 中定义命名空间的前缀是一件很平常的事”(也许因为 XSLT 也是一个 XML 名词的缘故吧)。
 
<xsl:stylesheet version="1.1" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform ">
  <xsl:template match="//edx:cd" xmlns:edx="
http://www.edankert.com/examples/ ">
    <xsl:apply-templates/>
  </xsl:template>
</xsl:stylesheet>
 
    只需要使用 XML 本身的机制,简单地为 edx 前缀赋予一个命名空间的 URI 值。
 
    通过与我们的 XPath 表达式“//edx:cd”相匹配的 xsl:template,能得到与上文其他例子相同的输出结果。

结束语
    为了在(缺省)命名空间上使用 XPath 表达式,我们需要指定一个“前缀-命名空间”映射。正如我们所看到的,具体使用什么样的前缀名称,是无关紧要的。
 
    同样的方法,也可以用于查询那些用其他前缀修饰的元素。这意味着上面的例子对下述 XML 也有效。下述 XML 没有使用缺省命名空间,而是使用了 examples 作命名空间的前缀:
 
<examples:catalog xmlns:examples="
http://www.edankert.com/examples/ ">
  <examples:cd>
    <examples:artist>Sufjan Stevens</examples:artist>
    <examples:title>Illinois</examples:title>
    <examples:src>http://www.sufjan.com/</examples:src>
  </examples:cd>
  <examples:cd>
    <examples:artist>Stoat</examples:artist>
    <examples:title>Future come and get me</examples:title>
    <examples:src>http://www.stoatmusic.com/</examples:src>
  </examples:cd>
  <examples:cd>
    <examples:artist>The White Stripes</examples:artist>
    <examples:title>Get behind me satan</examples:title>
    <examples:src>http://www.whitestripes.com/</examples:src>
  </examples:cd>
</examples:catalog>

 
    使用“//edx:cd”作为 XPath 表达式,使用与前文例子相同的“前缀-命名空间”映射,在这个 XML 上同样能查询出属于“http://www.edankert.com/examples/”命名空间的所有“cd”元素。
资源
Extensible Markup Language (XML) 1.0 (Third Edition)
http://www.w3.org/TR/REC-xml/
Namespaces in XML
http://www.w3.org/TR/REC-xml-names/
XML Path Language (XPath) Version 1.0
http://www.w3.org/TR/xpath
XSL Transformations (XSLT) Version 1.0
http://www.w3.org/TR/xslt
dom4j
http://www.dom4j.org/
XOM
http://www.xom.nu/
JDOM
http://www.jdom.org/
Jaxen
http://www.jaxen.org/
Java 5.0
http://java.sun.com/j2se/1.5.0/

posted @ 2007-03-21 10:13 比特鼠 阅读(1707) | 评论 (1)编辑 收藏

spring事务探索

原文出处:
http://www.javaeye.com/topic/11190

spring自建事务管理模块。而且这个事务管理是一个抽象设计,可以应用到很多场合,包括普通的DataSource,jta,jms和hibernate上。

要正确使用spring的事务,首先需要了解spring在事务设计上的一些概念
统观spring事务,围绕着两个核心PlatformTransactionManager和TransactionStatus

PlatformTransactionManager直译过来就是平台相关事务,这里的平台指的是“事务源”,包括刚才我说的DataSource,jta等等。这些无一不是一个事务源。广义的说,凡是可以完成事务性操作的对象,都可以设计出相对应的PlatformTransactionManager,只要这个事务源支持commit,rollback和getTransaction语意。

查看spring代码,可以发现这些manager实现事务,就是调用事务源的事务操作方法

比如

HibernateTransactionManager

代码

 

  1. protected   void  doCommit(DefaultTransactionStatus status) {   
  2.         HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();   
  3.          if  (status.isDebug()) {   
  4.             logger.debug( "Committing Hibernate transaction on session ["  +   
  5.                     txObject.getSessionHolder().getSession() +  "]" );   
  6.         }   
  7.          try  {   
  8.             txObject.getSessionHolder().getTransaction().commit();   
  9.         }   
  10. ...   
  11.   
  12.     }  

jdbc 的DataSourceTransactionManager

代码

 

  1. protected   void  doCommit(DefaultTransactionStatus status) {   
  2.         DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();   
  3.         Connection con = txObject.getConnectionHolder().getConnection();   
  4.          if  (status.isDebug()) {   
  5.             logger.debug( "Committing JDBC transaction on connection ["  + con +  "]" );   
  6.         }   
  7.          try  {   
  8.             con.commit();   
  9.         }   
  10.         ...   
  11.     }  

那么PlatformTransactionManager以什么依据处理事务呢?
是TransactionStatus
查看api发现这个接口有三个方法
isNewTransaction() ,isRollbackOnly(),setRollbackOnly()
PlatformTransactionManager就是根据前两个方法决定是否要创建一个新事务,是要递交还是回滚。至于第三个方法是改变事务当前状态的,很多地方都要用到,偏偏PlatformTransactionManager自身好像不怎么用,毕竟事务状态的改变是由程序员代码决定的,不需要一个manager多管闲事。

总结上面所说的,spring的事务由PlatformTransactionManager管理,manager最后调用事务源的方法来实现一个事务过程。而manager通过TransactionStatus 来决定如何实现。

接下去说spring事务中的TransactionTemplate和TransactionInterceptor

TransactionTemplate其实和spring中其他的template的作用类似,起到化简代码的作用,不要被它那么长的名字吓倒了,事实上这个template并不是什么非常核心的对象。如果比较学究派的,可以去看看template设计模式,在此就不再对此赘述了。
为什么要有TransactionTemplate?先来看看如果没有TransactionTemplate,我们的代码该怎么写

先来看看spring reference中的一段代码

代码

 

  1. DefaultTransactionDefinition def =  new  DefaultTransactionDefinition()   
  2. def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);   
  3.   
  4. TransactionStatus status = transactionManager.getTransaction(def);   
  5.   
  6. try  {   
  7.      // execute your business logic here   
  8. catch  (MyException ex) {   
  9.     transactionManager.rollback(status);   
  10.      throw  ex;   
  11. }   
  12. transactionManager.commit(status);  


这是直接使用transactionManager的例子,可以看到真正执行business logic 的地方是在try当中那段,前后的代码都是为了完成事务管理的。如果每个business logic都要写上那么一段,我肯定是疯了。我们翻出TransactionTemplate的代码看看他怎么化简了我们的代码

代码

 

  1. public  Object execute(TransactionCallback action)  throws  TransactionException {   
  2.         TransactionStatus status =  this .transactionManager.getTransaction( this );   
  3.         Object result =  null ;   
  4.          try  {   
  5.             result = action.doInTransaction(status);   
  6.         }   
  7.          catch  (RuntimeException ex) {   
  8.              // transactional code threw application exception -> rollback   
  9.             rollbackOnException(status, ex);   
  10.              throw  ex;   
  11.         }   
  12.          catch  (Error err) {   
  13.              // transactional code threw error -> rollback   
  14.             rollbackOnException(status, err);   
  15.              throw  err;   
  16.         }   
  17.          this .transactionManager.commit(status);   
  18.          return  result;   
  19.     }  

同上面的代码如出一辙,前后是事务处理代码,当中那段result = action.doInTransaction(status);是我们的应用代码。至于action是什么,全看各位的需要了。但是有一点要主要,如果利用TransactionTemplate,那么他不管你扔出什么异常都会回滚事务,但是回滚的是哪个事务呢?继续挖代码

代码

 

  1. private   void  rollbackOnException(TransactionStatus status, Throwable ex)  throws  TransactionException {   
  2.          if  (logger.isDebugEnabled()) {   
  3.             logger.debug( "Initiating transaction rollback on application exception" , ex);   
  4.         }   
  5.          try  {   
  6.              this .transactionManager.rollback(status);   
  7.         }   
  8.          catch  (RuntimeException ex2) {   
  9.             logger.error( "Application exception overridden by rollback exception" , ex);   
  10.              throw  ex2;   
  11.         }   
  12.          catch  (Error err) {   
  13.             logger.error( "Application exception overridden by rollback error" , ex);   
  14.              throw  err;   
  15.         }   
  16.     }  


真相大白,是对template所持有的某个transactionManager进行回滚。所以如果你的应用代码用的是事务源a的一些资源,比如到服务器a的一个datasource,但是你的transactionManager管理的是另一些资源,比如服务器b的一个datasource,代码铁定不会正常运行

特别是在一些多事务源的程序里,这点千万不能搞错。如果多个事务源之间要完成全局事务,还是老老实实用分布式事务管理服务吧(jta)

那么TransactionInterceptor是干什么的?这个是spring 的声明式事务的支持方式。因为用TransactionTemplate要硬编码,而且调整事务策略很麻烦(不是说不能调。举个例子原来程序抛出异常A需要回滚,现在不需要要,我就可以把a catch吃掉。这时候template就不会回滚了。但是每次调整都要重写编码。)而用TransactionInterceptor就可以将这些调整写在配置中。我们再来挖TransactionInterceptor的代码

代码

 

  1. public  Object invoke(MethodInvocation invocation)  throws  Throwable {   
  2.          // Work out the target class: may be null.   
  3.          // The TransactionAttributeSource should be passed the target class   
  4.          // as well as the method, which may be from an interface   
  5.         Class targetClass = (invocation.getThis() !=  null ) ? invocation.getThis().getClass() :  null ;   
  6.            
  7.          // Create transaction if necessary   
  8.         TransactionInfo txInfo = createTransactionIfNecessary(invocation.getMethod(), targetClass);   
  9.   
  10.         Object retVal =  null ;   
  11.          try  {   
  12.              // This is an around advice.   
  13.              // Invoke the next interceptor in the chain.   
  14.              // This will normally result in a target object being invoked.   
  15.             retVal = invocation.proceed();   
  16.         }   
  17.          catch  (Throwable ex) {   
  18.              // target invocation exception   
  19.             doCloseTransactionAfterThrowing(txInfo, ex);   
  20.              throw  ex;   
  21.         }   
  22.          finally  {   
  23.             doFinally(txInfo);   
  24.         }   
  25.         doCommitTransactionAfterReturning(txInfo);   
  26.   
  27.          return  retVal;   
  28.     }  


万变不离其宗。

所以使用spring的事务管理需要作这些事
1,设置好事务源,比如DataSource,hibernate的session。如果有多个事务源要考虑他们之间是否有全局事务,如果有,老老实实用jta,否则就需要自己写一个manager了
2,设置manager,根据你的事务源选择对应的PlatformTransactionManager
3,选择实现事物的方式,用template还是interceptor。用template代码直观点,但是template所管辖的manager和你应用代码所用的事务源要一致。如果用interceptor千万注意,一定要调用interceptor那个bean,而不是原始的那个target。在坛子上我已经看到至少有两个朋友说spring事物不起作用,从配置和代码上看都正确,这时要好好查查,调用的bean是哪一个。
4,这个是设计问题了,推荐事务处于一个较高层次,比如service上的某个函数,而底层的dao可以不考虑事务,否则可能会出现事务嵌套,增加程序复杂度。

posted @ 2007-03-05 14:45 比特鼠 阅读(234) | 评论 (0)编辑 收藏

2007年已经过去一个月了, 还没有制定今年的规划, 现在是时候了!

争取掌握以下技术:

1. Ruby On Rails 
进一步学习ruby的语法和语义,最好能深入到解释器一层看看有些特性是怎么实现的。

2、Spring AOP 及 AspectJ
个人觉得Spring 2.0和AspectJ的结合,有很多潜力,值得深入挖掘。

3. Web Service

4. Java JPA

5. Role-Based Access Control , Acegi
buaawhl推荐的,
http://csrc.nist.gov/rbac/
http://www.amazon.com/Role-Based-Access-Control-David-Ferraiolo/dp/1580533701/sr=1-1/qid=1161828835/ref=pd_bbs_1/002-1138304-7372032?ie=UTF8&s=books

下一个项目的权限管理会比较复杂,以前没学过这方面的知识。从Acegi开始吧,如果够用最好。

6. JBoss JBoss的Cache,AppServer的cluster方面是我比较感兴趣的地方,想尝试一下JBoss应用服务器

7. 满足一定条件(比如: 并发达1000个请求)框架Web Application群集部署

posted @ 2007-02-02 15:28 比特鼠 阅读(227) | 评论 (0)编辑 收藏

关于Spring属性编辑器详解

原文出处:
http://stamen.javaeye.com/blog/24660

最近刚在研究Spring的编辑器,发现很有意思,刚好galaxystar起了一个这样贴,我想对PropertyEditor作一个详细的整理会对大家有益,特定启了这个新帖。

所谓的PropertyEditor,顾名思义,就是属性编辑器。由于Bean属性通过配置文档以字符串了方式为属性赋值,所以必须有一个“东东”负责将这个字符串转换为属性的直接对象,如属性的类型为int,那么编辑器要做的工作就是int i = Integer.parseInt("1");
Spring为一般的属性类型提供了默认的编辑器,BeanWrapperImpl是Spring框架中重要的类,它负责对注入的Bean进行包装化的管理,常见属性类型对应的编辑器即在该类中通过以下代码定义:

代码

但是,并非Bean的属性都是这些常见的类型,如果你的Bean需要注入一个自定义类型的属性,而又想享受IoC的好处,那么就只得自己开干,提供一个自定义的PropertyEditor了。
下面,分几个步骤来说明,定义一个自定义PropertyEditor的过程。
1)首先,碰到的问题即是,要如何编辑自己的PropertyEditor,其实需要了解一点java.beans包的知识,在该包中,有一个java.beans.PropertyEditor的接口,它定义了一套接口方法(12个),即通过这些方法如何将一个String变成内部的一个对象,这两个方法是比较重要的:
a)setValue(Object value) 直接设置一个对象,一般不直接用该方法设置属性对象
b)setAsText(String text) 通过一个字符串来构造对象,一般在此方法中解析字符串,将构造一个
类对象,调用setValue(Object)来完成属性对象设置操作。

2)实现所有的接口方法是麻烦的,java.beans.PropertyEditorSupport 适时登场,一般情况下,我们通过扩展这个方便类即可。

3)编写完后,就是在Spring配置文件中注册该属性类型编辑器的问题,Spring提供了专门的注册工具类
org.springframework.beans.factory.config.CustomEditorConfigurer,它负责将属性类型和
属性编辑器关联起来。到时BeanFactory注入Bean的属性时,即会在注册表中查找属性类型对应的编辑器。

下面给出一个小例子,例子先作一个简单描述:
1)Person 需要进行属性注入的Bean,有两个属性 一个是name,一个是address Address是一个类
2)Address Person的属性类型,本身有3个属性。
3)AddressPropertyEditor Address类型对应的属性编辑器。

开工:
1.Person.java

代码

2.Address.java
代码


AddressPropertyEditor.java
代码

打开Spring配置文件,添上这两个配置项:

代码

 

下面是我自己写的日期转换类:

我的配置:
posted @ 2007-01-20 21:21 比特鼠 阅读(395) | 评论 (0)编辑 收藏

Error convertoring HTML to XHTML: System.ArgumentException: Cannot have ']]>' inside an XML CDATA block. at System.Xml.XmlTextWriter.WriteCData(String text) at System.Xml.XmlWriter.WriteNode(XmlReader reader, Boolean defattr) at FreeTextBoxControls.Support.Formatter.HtmlToXhtml(String input)
posted @ 2007-01-19 16:51 比特鼠 阅读(203) | 评论 (0)编辑 收藏

原文出自: http://www.blogjava.net/waterye/archive/2005/08/23/10836.aspx

OpenSessionInView模式

Spring+Hibernate中,  集合映射如果使用lazy="true", 当PO传到View层时, 出现未初始化session已关闭的错误,只能在dao先初始化
parent.getChilds().size();

Spring提供Open Session In View来解决这个问题, 有两种方式
1. Interceptor
    <!--  =========== OpenSession In View pattern ============== -->
    
< bean  id ="openSessionInViewInterceptor"
          class
="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor" >
        
< property  name ="sessionFactory"  ref ="sessionFactory" />
    
</ bean >

    
< bean  id ="urlMapping"  class ="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
        
< property  name ="interceptors"  ref ="openSessionInViewInterceptor" />
        
< property  name ="mappings" >
            
< props >
            ......
            
</ props >

        
</ property >
    
</ bean >
2. Filter
< web-app >
 
< filter >
< filter-name > hibernateFilter </ filter-name >
< filter-class >
org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</ filter-class >
</ filter >
 
< filter-mapping >
< filter-name > hibernateFilter </ filter-name >
< url-pattern > *.do </ url-pattern >
</ filter-mapping >

</ web-app >

说明: 个人更喜欢用Interceptor方式, filter是在web.xml中定义
有了OpenSessionInView,并不是一切就ok了。简单的crud可以,但对于复杂业务逻辑就要想点法子。

OSIV默认是request per session的, 所以即使没有显式update(po),Hibernate也会帮你保存的,dao的业务判断就无效,还好有evict()可用。
update code
// Controller
public
 ModelAndView update() {
    p 
=
 dao.getVo(id);
    bindObject(request);
    dao.update(p);
}

// Dao
public
 Object getVo(id) {
    p 
=
 getHibernateTemplate().get(clazz, id);
    p.getChilds().size();
    getHibernateTemplate().evict(p);
    
return
 p;
}

public   void
 update(p) {
    oldP 
=
 getVo(id);
    
//  ++--(oldP);

    getHibernateTemplate().update(p);
    
//  ++--(p);

}
posted @ 2007-01-19 15:33 比特鼠 阅读(296) | 评论 (0)编辑 收藏

例如:
<class name="User" table="user">
<id name="id" column="id" type="java.lang.Long">
<generator class="native"/>
</id>

generator标识符生成器 描述

increment      适用于代理主键。由Hibernate自动以递增方式生成
identity      适用于代理主键。由底层数据库生成标识符
sequence      适用于代理主键。Hibernate根据底层数据库的序列生成标识符,这要求底层数据库支持序列
hilo         适用于代理主键。Hibernate分局high/low算法生成标识符
seqhilo       适用于代理主键。使用一个高/低位算法来高效的生成long,short或者int类型的标识符。
native        适用于代理主键。根据底层数据库对自动生成标识符的方式,自动选择identity、sequence或hilo
uuid.hex       适用于代理主键。Hibernate基于128 位UUID 算法生成16 进制数值(编码后以长度32 的字符串表示)
uuid.string      适用于代理主键。与uuid.hex 类似,只是生成的主键未进行编码(以长度16 的字符串表示),不能应用在 PostgreSQL 数据库中
assigned       适用于自然主键。由Java应用程序负责生成标识符
foreign        适用于代理主键。使用另外一个相关联的对象的标识符

以下举例:
1、指定参数的情况:
    <id name="id" unsaved-value="0">
      <generator class="sequence">
        <param name="sequence">SEQ_CHILD</param>
      </generator>
</id>
使用的是sequence,适合oracle数据库;


2、对于sql server2000中的数据库子增字段,在配置文件使用下列方法实现:
<id name="id" type="long" unsaved-value="0">
     <column name="id" sql-type="numeric" not-null="true" />
     <generator class="identity" />
</id>
这里主要是:identity:代表由sql server2000数据库自己提供子增字段.如果要hibernate自己提供,则用increment关键字来实现


3、如果表中的主键用字符串类型:可以用hibernate自己提供的方法实现主键唯一:
  <id name="id" type="string" unsaved-value="null">
      <column name="cid" sql-type="char(32)" not-null="true" />
      <generator class="uuid.hex" />
  </id>
使用的是uuid.hex: 采用128位的算法来生成一个32位字符串。最通用的一种方式。适用于所有数据库。


重要的知识点:
1. 如果有部门表,有员工表,员工表中有dep_id,则表部门类和员工类是one-to-many的关系:
   可以使用:  ( 在部门类department中使用下列)
   Department类: 
     /**  部门的所有员工   */
    private Set staffs = new TreeSet();
   
    xml的文件:
      <set name="staffs" >
          <key column="dep_id"/>
          <one-to-many class="hbp.sys.data.Staff"/>
      </set>
      如果是list,需要用索引<index> </index>,具体其中的含义,不是很明白.待以后研究
     
 2. 如果部门要有一个负责人,即部门表(tb_department)中有一个字段:staff_id.
     那么表示部门和负责人之间的关系是many-to-one的关系
     Department类:
      /** 部门负责人id */
    private Staff staff;
   
    xml 文件
     <many-to-one name="staff" class="hbp.sys.data.Staff"  column="staff_id"/> 
 
 3. 多对多关系,一般我们是做一个中间关联表.我用角色和权限做了个例子,
      Right(id,name)     Role(id,name)   中间表:tb_role_right(role_id,right_id)
      Right类中有一个Role的集合:private Set roles=new TreeSet();
      Role类中也有一个Right的集合:private Set rights=new TreeSet();
      则两者是明显的多对多关系.使用many-to-many来实现;
      xml文件中
      right.hbm.xml:如下:
        <set name="roles" table="tb_role_right" cascade="all">
           <key column="right_id"/>
           <many-to-many column="role_id" class="hbp.sys.data.Role"/>
        </set>
      role.hbm.xml文件中类似:
        <set name="rights" table="tb_role_right" cascade="all">
          <key column="role_id"/>
          <many-to-many column="right_id" class="hbp.sys.data.Right"/>
        </set>


4. 几个值得注意的问题:
        a)在xml?映射文件中,写类的名字时一定用类的全名:即:包+类名如:(hbp.sys.data.Staff),这个错误使我费了半天劲.:(
        b)我在写实现基本DAO操作时,写了
             session.delete("from Right as right where right.id="+id); 
             程序死活报错,我折腾了半天,跟踪到底,才恍然大悟,hibernate在解析sql语句的时候把
             其中的right,当成了数据库中的右连接("保留字"),唉,这种关键字,不能随便用啊,:)


5. hibernate中HQL语言的查询根据你的sql的不同而返回不同的对象类型.
         如果你使用session.find(String hql)
         一般会返回一个List,如:from Staff staff;返回的是包含所有的员工对象的集合
         如你的hql为:select count(*) from Staff staff;则返回的是一个Integer对象
         如果你使用的hql为:select count(distinct staff.name),count(*) from Staff staff;则返回的是一个Object
         即Object[],需要先把他转换成Object[],然后在取[0],[1].
         这种设计我不知道hibernate是如何处理的,感觉既好也不好.好的是可以使用一个find获得任意查询
         不好在于根据hql来处理返回结果,容易出错.

posted @ 2007-01-18 10:44 比特鼠 阅读(2072) | 评论 (0)编辑 收藏

<标识符>在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线,如:stdio.h

#ifndef _STDIO_H_           //如果没有定义_STDIO_H_
#define _STDIO_H_  1        //则定义_STDIO_H_, 值为 1


1. 如果没有定义<标识符>, 则定义<标识符>
#ifndef <标识符>
#ifdefine <标识符> <标识符值>
..
..
#endif



2. 如果没有定义<标识符>, 则预编译程序段1, 否则编译程序段2

#ifndef <标识符> 
    
程序段1
 
#else

    
程序段2
 
#endif

3. 如果表达式的值不为0, 则编译程序段1, 否则编译程序段2

#if 表达式
    
程序段1
 
#else

    
程序段2
 
#endif


posted @ 2007-01-10 10:46 比特鼠 阅读(292) | 评论 (0)编辑 收藏

     摘要: JS的正则表达式 //校验是否全由数字组成 代码 function isDigit(s)    ...  阅读全文
posted @ 2006-12-20 21:49 比特鼠 阅读(208) | 评论 (0)编辑 收藏



正则表达式测试(V0.1)

表达式:
测试值:
输出值:
标  识: 全局  忽略  多行   方  法:
        


posted @ 2006-12-20 21:40 比特鼠 阅读(606) | 评论 (0)编辑 收藏

正则表达式用于字符串处理,表单验证等场合,实用高效,但用到时总是不太把握,以致往往要上网查一番。我将一些常用的表达式收藏在这里,作备忘之用。本贴随时会更新。

匹配中文字符的正则表达式: [\u4e00-\u9fa5]

匹配双字节字符(包括汉字在内):[^\x00-\xff]

应用:计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)

String.prototype.len=function(){return this.replace([^\x00-\xff]/g,"aa").length;}

匹配空行的正则表达式:\n[\s| ]*\r

匹配HTML标记的正则表达式:/<(.*)>.*<\/\1>|<(.*) \/>/

匹配首尾空格的正则表达式:(^\s*)|(\s*$)

应用:javascript中没有像vbscript那样的trim函数,我们就可以利用这个表达式来实现,如下:

String.prototype.trim = function()
{
    return this.replace(/(^\s*)|(\s*$)/g, "");
}

利用正则表达式分解和转换IP地址:

下面是利用正则表达式匹配IP地址,并将IP地址转换成对应数值的javascript程序:

function IP2V(ip)
{
 re=/(\d+)\.(\d+)\.(\d+)\.(\d+)/g  //匹配IP地址的正则表达式
if(re.test(ip))
{
return RegExp.$1*Math.pow(255,3))+RegExp.$2*Math.pow(255,2))+RegExp.$3*255+RegExp.$4*1
}
else
{
 throw new Error("Not a valid IP address!")
}
}

不过上面的程序如果不用正则表达式,而直接用split函数来分解可能更简单,程序如下:

var ip="10.100.20.168"
ip=ip.split(".")
alert("IP值是:"+(ip[0]*255*255*255+ip[1]*255*255+ip[2]*255+ip[3]*1))

匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

匹配网址URL的正则表达式:http://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?

利用正则表达式去除字串中重复的字符的算法程序:[注:此程序不正确,原因见本贴回复]

var s="abacabefgeeii"
var s1=s.replace(/(.).*\1/g,"$1")
var re=new RegExp("["+s1+"]","g")
var s2=s.replace(re,"")
alert(s1+s2)  //结果为:abcefgi

我原来在CSDN上发贴寻求一个表达式来实现去除重复字符的方法,最终没有找到,这是我能想到的最简单的实现方法。思路是使用后向引用取出包括重复的字符,再以重复的字符建立第二个表达式,取到不重复的字符,两者串连。这个方法对于字符顺序有要求的字符串可能不适用。

得用正则表达式从URL地址中提取文件名的javascript程序,如下结果为page1

s="http://www.9499.net/page1.htm"
s=s.replace(/(.*\/){0,}([^\.]+).*/ig,"$2")
alert(s)

利用正则表达式限制网页表单里的文本框输入内容:

用正则表达式限制只能输入中文:onkeyup="value=value.replace(/[^\u4E00-\u9FA5]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\u4E00-\u9FA5]/g,''))"

用正则表达式限制只能输入全角字符: onkeyup="value=value.replace(/[^\uFF00-\uFFFF]/g,'')" onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\uFF00-\uFFFF]/g,''))"

用正则表达式限制只能输入数字:onkeyup="value=value.replace(/[^\d]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"

用正则表达式限制只能输入数字和英文:onkeyup="value=value.replace(/[\W]/g,'') "onbeforepaste="clipboardData.setData('text',clipboardData.getData('text').replace(/[^\d]/g,''))"

posted @ 2006-12-20 21:25 比特鼠 阅读(261) | 评论 (0)编辑 收藏

JavaScript--正则表达式

  正则表达式(regular expression)对象包含一个正则表达式模式(pattern)。它具有用正则表达式模式去匹

配或代替一个串(string)中特定字符(或字符集合)的属性(properties)和方法(methods)。

正则表达式构造函数: new RegExp("pattern"[,"flags"]);
参数说明:
pattern -- 一个正则表达式文本
flags -- 如果存在,将是以下值:
g: 全局匹配
i: 忽略大小写
gi: 以上组合

在构造函数中,一些特殊字符需要进行转意(在特殊字符前加"\")。正则表达式中的特殊字符:
字符  含意 
\ 转意,即通常在"\"后面的字符不按原来意义解释,如/b/匹配字符"b",当b前面加了反斜杆后/\b/,转意为

匹配一个单词的边界。
-或-
对正则表达式功能字符的还原,如"*"匹配它前面元字符0次或多次,/a*/将匹配a,aa,aaa,加了"\"后,/a\*/

将只匹配"a*"。 
^  匹配一个输入或一行的开头,/^a/匹配"an A",而不匹配"An a" 
$  匹配一个输入或一行的结尾,/a$/匹配"An a",而不匹配"an A" 
*  匹配前面元字符0次或多次,/ba*/将匹配b,ba,baa,baaa 
+  匹配前面元字符1次或多次,/ba*/将匹配ba,baa,baaa 
?  匹配前面元字符0次或1次,/ba*/将匹配b,ba 
(x)  匹配x保存x在名为$1...$9的变量中 
x|y  匹配x或y 
{n}  精确匹配n次 
{n,}  匹配n次以上 
{n,m}  匹配n-m次 
[xyz]  字符集(character set),匹配这个集合中的任一一个字符(或元字符) 
[^xyz]  不匹配这个集合中的任何一个字符 
[\b]  匹配一个退格符
\b  匹配一个单词的边界 
\B  匹配一个单词的非边界
\cX  这儿,X是一个控制符,/\cM/匹配Ctrl-M 
\d  匹配一个字数字符,/\d/ = /[0-9]/ 
\D  匹配一个非字数字符,/\D/ = /[^0-9]/ 
\n  匹配一个换行符 
\r  匹配一个回车符 
\s  匹配一个空白字符,包括\n,\r,\f,\t,\v等 
\S  匹配一个非空白字符,等于/[^\n\f\r\t\v]/ 
\t  匹配一个制表符 
\v  匹配一个重直制表符 
\w  匹配一个可以组成单词的字符(alphanumeric,这是我的意译,含数字),包括下划线,如[\w]匹配"$5.98" 中的5,等于[a-zA-Z0-9_] 
\W  匹配一个不可以组成单词的字符,如[\W]匹配"$5.98"中的$,等于[^a-zA-Z0-9]。


说了这么多了,我们来看一些正则表达式的实际应用的例子:
HTML代码的屏蔽
 function mask_HTMLCode(strInput) {
   var myReg = /<(\w+)>/;
   return strInput.replace(myReg, "&lt;$1&gt;");
 }
E-mail地址验证:
 function test_email(strEmail) {
  var myReg = /^[_a-z0-9]+@([_a-z0-9]+\.)+[a-z0-9]{2,3}$/;
  if(myReg.test(strEmail)) return true;
  return false;
 }

 

正则表达式对象的属性及方法:
  预定义的正则表达式拥有有以下静态属性:input, multiline, lastMatch, lastParen, leftContext,

rightContext和$1到$9。其中input和multiline可以预设置。其他属性的值在执行过exec或test方法后被根据

不同条件赋以不同的值。许多属性同时拥有长和短(perl风格)的两个名字,并且,这两个名字指向同一个值。( JavaScript模拟perl的正则表达式)

正则表达式对象的属性:
属性 含义
$1...$9 如果它(们)存在,是匹配到的子串 
$_ 参见input 
$* 参见multiline 
$& 参见lastMatch 
$+ 参见lastParen 
$` 参见leftContext 
$''          参见rightContext 
constructor     创建一个对象的一个特殊的函数原型 
global        是否在整个串中匹配(bool型) 
ignoreCase     匹配时是否忽略大小写(bool型) 
input        被匹配的串 
lastIndex      最后一次匹配的索引 
lastParen      最后一个括号括起来的子串 
leftContext     最近一次匹配以左的子串 
multiline      是否进行多行匹配(bool型) 
prototype      允许附加属性给对象 
rightContext    最近一次匹配以右的子串 
source        正则表达式模式 
lastIndex      最后一次匹配的索引 

正则表达式对象的方法:
方法 含义
compile       正则表达式比较 
exec        执行查找 
test        进行匹配 
toSource      返回特定对象的定义(literal

representing),其值可用来创建一个新的对象。重载Object.toSource方法得到的。 
toString      返回特定对象的串。重载Object.toString方法得到的。 
valueOf       返回特定对象的原始值。重载Object.valueOf方法得到
 

例子:
<script language = "JavaScript">
var myReg = /(w+)s(w+)/;
var str  = "John Smith";
var newstr = str.replace(myReg, "$2, $1");
document.write(newstr);
</script>
将输出"Smith, John"

posted @ 2006-12-20 21:23 比特鼠 阅读(541) | 评论 (3)编辑 收藏

Expression Description
\t       Tab字符
\n       新行
.        匹配任意字符
|        匹配"左边和右边"的字符串, 例如: "ac|ad" 匹配 "ac" 和 "ad"
[]       匹配所有在 符号"[]"闭合区间内 的任意字符, 例如: "[ab]" matches "a" and "b". "[0-9]" matches any digit.
[^]      匹配所有不在  符号"[]"闭合区间内  的任意字符, 例如: "[^ab]" 匹配所有除了"a"和"b"之外的字符, "[^0-9]" 匹配所有非数字字符
*        匹配符号"*"的左边字符(0次或多次) 例如:"be*" 匹配 "b", "be" , "bee"
+        匹配符号"+"的左边字符(1次或多次) 例如:"be+" 匹配 "be" and "bee", 但不匹配 "b"
?        匹配符号"?"的左边字符(0次或1次) 例如:be?" 匹配 "b" 和 "be", 但不匹配 "bee"
^        匹配符号"^"的右边字符串, 并且该字符串必须在行首, 例如: ^AB 匹配字符串"AB",并且该"AB"必须在行首
$        匹配符号"$"的左边字符串, 并且该字符串必须在行尾, 例如: AB$ 匹配字符串"AB",并且该"AB"必须在行尾
()       Affects evaluation order of expression and also used for tagged expression.
\        Escape character. If you want to use character "\" itself, you should use "\\".


技巧:
1. 查找空行 : "^[ \t]*\n"
2. 查找行首序号: "^[0-9. ]+"
   例如: 
   "
      1. aaaa
      2. bbbb
      :
      100. abcd
      :
   "
3. 查找"["和"]"之间的内容: "\[.+\]"
4. 匹配<b></b>之间的内容(包括<b></b>):"^\<b\>.+\</b\>"
5. 匹配<b>...<br/>之间没有"/"字符的内容:"^\<b\>[^/]+\<br/\>"
   例如:
      ...
      <b>appeal</b> - v. to take to <br/>
      <b>somebody for help <br/>
      <b>appoint</b> - v. to name;<br/>
      <b>statues <br/>
      ...
      
      使用:^(\<b\>)([^/]+\<br/\>) 和替换串"\2", 将使得以上的第2行和第4行变成:
      ...
      <b>appeal</b> - v. to take to <br/>
      somebody for help <br/>
      <b>appoint</b> - v. to name;<br/>
      statues <br/>
      ...

The tagged expression is enclosed by (). Tagged expressions can be referenced by \0, \1, \2, \3, etc. \0 indicates a tagged expression representing the entire substring that was matched. \1 indicates the first tagged expression, \2 is the second, etc. See following examples.

Original Search  Replace  Result 
abc      (ab)(c) \0-\1-\2 abc-ab-c
abc      a(b)(c) \0-\1-\2 abc-b-c
abc      (a)b(c) \0-\1-\2 abc-a-c

posted @ 2006-12-20 18:18 比特鼠 阅读(467) | 评论 (0)编辑 收藏

Eclipse中常用的快捷键(最经典的快捷键,就不用多说了)

Ctrl+D: 删除当前行    

Ctrl+Alt+↓ 复制当前行到下一行(复制增加)

Ctrl+Alt+↑ 复制当前行到上一行(复制增加)

Alt+↓ 当前行和下面一行交互位置(特别实用,可以省去先剪切,再粘贴了)

Alt+↑   当前行和上面一行交互位置(同上)

Alt+← 前一个编辑的页面

Alt+→ 下一个编辑的页面(当然是针对上面那条来说了)

Alt+Enter 显示当前选择资源(工程,or 文件 or文件)的属性

Shift+Enter 在当前行的下一行插入空行(这时鼠标可以在当前行的任一位置,不一定是最后)

Shift+Ctrl+Enter 在当前行插入空行(原理同上条)

Ctrl+Q   定位到最后编辑的地方

Ctrl+L 定位在某行 (对于程序超过100的人就有福音了)

Ctrl+M 最大化当前的Edit或View (再按则反之)

Ctrl+/   注释当前行,再按则取消注释

Ctrl+O   快速显示 OutLine

Ctrl+T   快速显示当前类的继承结构

Ctrl+W 关闭当前Editer

Ctrl+K   参照选中的Word快速定位到下一个

Ctrl+E 快速显示当前Editer的下拉列表(如果当前页面没有显示的用黑体表示)

Ctrl+/(小键盘) 折叠当前类中的所有代码

Ctrl+×(小键盘) 展开当前类中的所有代码

Ctrl+Space 代码助手完成一些代码的插入(但一般和输入法有冲突,可以修改输入法的热键,也可以暂用Alt+/来代替)

Ctrl+Shift+E 显示管理当前打开的所有的View的管理器(可以选择关闭,激活等操作)

Ctrl+J 正向增量查找(按下Ctrl+J后,你所输入的每个字母编辑器都提供快速匹配定位到某个单词,如果没有,则在stutes line中显示没有找到了,查一个单词时,特别实用,这个功能Idea两年前就有了)

Ctrl+Shift+J 反向增量查找(和上条相同,只不过是从后往前查)

Ctrl+Shift+F4 关闭所有打开的Editer

Ctrl+Shift+X   把当前选中的文本全部变味小写

Ctrl+Shift+Y   把当前选中的文本全部变为小写

Ctrl+Shift+F 格式化当前代码

Ctrl+Shift+P 定位到对于的匹配符(譬如{}) (从前面定位后面时,光标要在匹配符里面,后面到前面,则反之)

下面的快捷键是重构里面常用的,本人就自己喜欢且常用的整理一下(注:一般重构的快捷键都是Alt+Shift开头的了)

Alt+Shift+R 重命名 (是我自己最爱用的一个了,尤其是变量和类的Rename,比手工方法能节省很多劳动力)

Alt+Shift+M 抽取方法 (这是重构里面最常用的方法之一了,尤其是对一大堆泥团代码有用)

Alt+Shift+C 修改函数结构(比较实用,有N个函数调用了这个方法,修改一次搞定)

Alt+Shift+L 抽取本地变量( 可以直接把一些魔法数字和字符串抽取成一个变量,尤其是多处调用的时候)

Alt+Shift+F 把Class中的local变量变为field变量 (比较实用的功能)

Alt+Shift+I 合并变量(可能这样说有点不妥Inline)

Alt+Shift+V 移动函数和变量(不怎么常用)

Alt+Shift+Z 重构的后悔药(Undo)

Ctrl+Shift+U 选择选中的文字后非常类似于UE的列表查询


Eclipse启动参数大全 - -
                         
Eclipse 运行命令行参数大全
包括英文版本和中文版本两种的说明, 特别需要值得一提的是那个 -nl 参数, 可以指定程序启动时所使用的语言. 例如:
eclipse -nl en_US
将启动英文语言, 这个特性在安装了国际化语言包以后特别有用, 可以方便的切换各个语言的版本. 注意 IBM WSAD v5.1 也支持这个功能.

运行 Eclipse
将 Eclipse 驱动程序安装(解压缩)到某个目录(例如,c:\eclipse)中之后,通过运行顶级安装目录中的 Eclipse 可执行文件来启动"工作台"。在 Windows 系统上,该可执行文件称为 eclipse.exe,而在 Linux 系统上称为 eclipse。注意:下列讨论描述 Windows 系统上的设置。Linux 上的设置是相似的。

如果您没有另行指定,则平台将缺省工作区目录创建为可执行文件的兄弟目录(例如 c:\eclipse\workspace)。此工作区目录用作项目的缺省内容区,还用于保存任何必需的元数据。要进行共享安装或多工作区安装,应明确指出工作区的位置而不是使用缺省值。有两种控制工作区位置的方法:使用当前工作目录或使用 -data 命令行自变量。

将工作区位置设置为在当前工作目录内
在此方案中,工作区位置将是当前工作目录中称为 workspace 的目录。

实现此目的最容易的方法可能是使用下列步骤来创建快捷方式:

导航到 Windows 资源管理器中的 eclipse.exe 并使用右键拖动来创建 eclipse.exe 的快捷方式。
编辑快捷方式的属性,以使启动位置:字段标识工作区位置的父目录(例如,c:\users\robert)。
关闭属性对话框并双击快捷方式(如果提供的目录为 c:\users\robert,则工作区位置将为 c:\users\robert\workspace)。
当然,您也可以使用命令提示符(通过将目录切换为工作区父目录然后运行 eclipse.exe)来获得同样的效果。

使用 -data 设置工作区的特定位置
要使用 -data 命令行自变量,只要将 -data your_workspace_location(例如,-data c:\users\robert\myworkspace)添加至快捷方式属性中的目标字段或显式地将它包括在命令行上。

使用 -vm 设置 java VM
建议显式指定在运行 Eclipse 时要使用哪个 Java VM。使用 -vm 命令行自变量(例如,-vm c:\jre\bin\javaw.exe)可以实现此目的。如果不使用 -vm,则 Eclipse 将使用在 O/S 路径上找到的一个 Java VM。当安装其它产品时,它们可更改您的路径,导致在下一次启动 Eclipse 时使用另一 Java VM。

运行 Eclipse 中的高级主题
Eclipse 可执行文件及平台本身提供了人们感兴趣的开发或调试 Eclipse 各部件的许多执行选项。运行 Eclipse 可执行文件的一般格式是:

eclipse [platform options] [-vmargs [Java VM arguments]]
Eclipse 启动参数 命令 描述 原因
-arch architecture
定义 Eclipse 平台在其上运行的处理器体系结构。Eclipse 平台通常使用 Java os.arch 属性的常用值来计算最佳设置。如果在此处指定该项,则这是 Eclipse 平台使用的值。此处指定的值可作为 BootLoader.getOSArch() 用于插件。示例值有:"x86"、"sparc"、"PA-RISC"和"ppc"。 2.0
-application applicationId
要运行的应用程序。应用程序由向 org.eclipse.core.runtime.applications 扩展点提供扩展的插件来声明。通常不需要此自变量。如果指定了此项,则该值会覆盖配置提供的值。如果不指定此项,则会运行"Eclipse 工作台"。 1.0
-boot bootJarURL
(建议不使用;用 -configuration 代替;支持 1.0 兼容)。Eclipse 平台的引导插件代码(boot.jar)的位置,表示为 URL。如果指定此项,则会用它来为装入 Eclipse 平台引导程序类装入器的类装入器设置类路径。仅当更改 startup.jar 和 boot.jar 的相对位置时才需要它。注意,不允许使用相对 URL。 *1.0
-classloaderproperties [file]
如果指定的话,则使用给定位置处的类装入器属性文件来激活平台类类装入器增强。文件自变量可以是文件路径或 URL。注意,不允许使用相对 URL。单击此处以获得更多详细信息。 2.0.2
-configuration configurationFileURL
Eclipse 平台配置文件的位置,表示为 URL。配置文件确定 Eclipse 平台、可用插件集和主要功能部件的位置。注意,不允许使用相对 URL。当安装或更新 Eclipse 平台时配置文件被写至此位置。 2.0
-consolelog
将 Eclipse 平台的错误日志镜像到用来运行 Eclipse 的控制台。与 -debug 组合时很方便使用。 1.0
-data workspacePath
要运行 Eclipse 平台的工作区的路径。工作区位置也是项目的缺省位置。相对于从中启动 eclipse 的目录来解释相对路径。 1.0
-debug [optionsFile]
将平台置于调试方式,并从给定位置处的文件装入调试选项(如果指定的话)。此文件指示哪些调试点可用于插件以及是否已启用它们。如果未给出文件位置,则平台在启动 eclipse 的目录中查找称为".options"的文件。URL 和文件系统路径都可作为文件位置。 1.0
-dev [classpathEntries]
将平台置于开发方式。将可选类路径条目(用逗号分隔的列表)添加至每个插件的运行时类路径。例如,当工作区包含要开发的插件时,指定 -dev bin 会为每个插件项目的名为 bin 的目录添加类路径条目,允许在其中存储最新生成的类文件。除去了冗余或不存在的类路径条目。 1.0
-endsplash params
用于在 Eclipse 平台启动并运行时关闭闪屏的内部选项。此选项在闪屏处理链中不同的位置有不同的语法和语义。 2.0
-feature featureId
主要功能部件的标识。主要功能部件为 Eclipse 的已启动实例提供了产品个性,并确定使用的产品定制信息。 2.0
-keyring keyringFilePath
磁盘上授权数据库(或"密钥环"文件)的位置。此自变量必须与 -password 选项配合使用。相对于从中启动 eclipse 的目录来解释相对路径。 1.0
-nl locale
定义 Eclipse 平台在其上运行的语言环境的名称。Eclipse 平台通常自动计算最佳设置。如果在此处指定该项,则这是 Eclipse 平台使用的值。此处指定的值可作为 BootLoader.getNL() 用于插件。示例值有:"en_US"和"fr_FR_EURO"。 2.0
-nolazyregistrycacheloading
取消激活装入优化的平台插件注册表高速缓存。缺省情况下,仅当需要时才从注册表高速缓存(可用时)中装入扩展的配置元素,以减少内存占用。此选项将在启动时强制完全装入注册表高速缓存。 2.1
-noregistrycache
绕过读写内部插件注册表高速缓存文件。 2.0
-nosplash
运行平台而不显示闪屏。 1.0
-os operatingSystem
定义 Eclipse 平台在其上运行的操作系统。Eclipse 平台通常使用 Java os.name 属性的常用值来计算最佳设置。如果在此处指定该项,则这是 Eclipse 平台使用的值。此处指定的值可作为 BootLoader.getOS() 用于插件,并用于解析插件清单文件中提及的路径中 $os$ 变量的出现。示例值有:"win32"、"linux"、"hpux"、"solaris"和"aix"。 1.0
-password password
授权数据库的密码。与 -keyring 选项配合使用。 1.0
-perspective perspectiveId
启动时要在活动工作台窗口中打开的透视图。如果没有指定该参数,则将打开关闭时活动的透视图。 1.0
-plugincustomization   propertiesFile
包含插件首选项缺省设置的属性文件的位置。这些缺省设置覆盖在主要功能部件中指定的缺省设置。相对于从中启动 eclipse 的目录来解释相对路径。 2.0
-plugins pluginsFileURL
(建议不使用;用 -configuration 代替;支持 1.0 兼容)。 指定 Eclipse 平台查找插件的文件的位置,表示为 URL。该文件为属性文件格式,其中键是任意用户定义名称,值是指向 plugin.xml 文件的显式路径或指向包含插件的目录的路径的用逗号分隔的列表。注意,不允许使用相对 URL。如果指定此项,则此选项会导致创建适当的临时配置。 *1.0
-refresh
启动时执行工作区的全局刷新的选项。这将使从上次平台运行以来在文件系统中所做的任何更改一致。 1.0
-showlocation
用于在窗口标题栏中显示工作区的位置的选项。在发行版 2.0 中,此选项仅与 -data 命令行自变量一起使用。 2.0
-showsplash params
用于显示闪屏(由可执行的 Eclipse 平台启动器执行)的内部选项。此选项在闪屏处理链中不同的位置有不同的语法和语义。 2.0
-vm vmPath
要用来运行 Eclipse 平台的"Java 运行时环境"(JRE)的位置。如果不指定此项,则 JRE 位于 jre(它是 Eclipse 可执行文件的兄弟目录)。相对于从中启动 eclipse 的目录来解释相对路径。 1.0
-ws windowSystem
定义 Eclipse 平台在其上运行的 Windows 系统。Eclipse 平台通常使用 Java os.name 属性的常用值来计算最佳设置。如果在此处指定该项,则这是 Eclipse 平台使用的值。此处指定的值可作为 BootLoader.getWS() 用于插件、用于配置 SWT 以及用于解析插件清单文件中提及的路径中 $ws$ 变量的出现。示例值有:"win32"、"motif"和"gtk"。 1.0

将 -vmargs 条目后面的所有自变量(但不包括 -vmargs)作为虚拟机自变量(即,在要运行的类的前面)直接传递到所指示的 Java VM。注意:如果 Eclipse 启动在 Java vm 自变量(-vmargs)之后提供的自变量(例如,-data),则 Eclipse 将不会启动并且您将接收到"JVM 已终止。出口代码为 1"的错误。

在不同的 VM 上运行
在 J9 上运行 Eclipse
当在 J9 版本 1.5 上运行 Eclipse 时,建议使用以下 VM 选项:

eclipse.exe [eclipse arguments] -vm path_to_j9w.exe         -vmargs -ms:32 -mm:2048 -mo:32768 -moi:32768 -mca:32 -mco:128 -mx:2000000
当在 J9 版本 2.0 上运行 Eclipse 时,J9W 选择的缺省自变量应为合适的选项。但是,要覆盖 Eclipse 可执行文件以内部方式自动设置的参数,必须指定 -vmargs 不带任何参数,如下所示:

eclipse.exe [eclipse arguments] -vm path_to_j9w.exe -vmargs
有关进一步信息,参考 J9 VM 文档和帮助。

在 IBM Developer Kit, Java(TM) Technology Edition VM 上运行 Eclipse
IBM Developer Kit, Java(TM) Technology Edition 1.3 Linux 的缺省 VM 设置适合进行初期研究工作,但在进行大型开发时是不够的。对于大型开发,应修改 VM 自变量以使有更多的堆可用。例如,下列设置将允许 Java 堆增大为 256MB
posted @ 2006-12-19 12:18 比特鼠 阅读(230) | 评论 (0)编辑 收藏

RubyGems是一个方便而强大的Ruby程序包管理器( package manager),类似RedHat的RPM.它将一个Ruby应用程序打包到一个gem里,作为一个安装单元。无需安装,最新的Ruby版本已经包含RubyGems了。


特点:
能远程安装包
包之间依赖关系的管理
简单可靠的卸载(uninstallation)
查询机制,能查询本地和远程服务器的包信息
能保持一个包的不同版本
基于Web的查看接口,能查看你安装的gem的信息。


使用:
1. gem query --remote        # shortcut: gem q -R
#列出所有包
2. gem query --remote --name-matches doom   # shortcut: gem q -R -n doom
#列出所有名字匹配的包
3. gem install --remote progressbar  # shortcut: gem i -r progressbar
#安装指定名字的包。
gem ins -r progressbar --version '> 0.0.1' #这里可以匹配版本
4. gem specification  progressbar  # shortcut: gem spec progressbar
#查看安装过的指定包详细信息
5.gem uninstall progressbar
#卸载包
6. gem query --local # shortcut: 'gem q -L'
#列出所有本地安装过的包
7. gem ins rake
#在本地安装,如果没有远程安装
8. gem list -b ^C
#列出所有以C开头的包
9. gem_server
开启rdoc服务。可以查看安装包的rdoc

posted @ 2006-12-15 09:53 比特鼠 阅读(2314) | 评论 (0)编辑 收藏

Tip 1. 默认情况下,Rails认为类名是单数,表明是复数形式。
如果你不喜欢这样的行为可以在配置文件(config/environment.rb)中设置一个全局标识来禁用它。
ActiveRecord::Base.pluralize_table_names = false

Tip 2.默认情况下,ActiveRecord会使用推导表明的算法,如果你不希望使用此算法可以在指定的model中使用 "set_table_name" 方法,例如:
class Sheep < ActiveRecord::Base
 set_table_name "sheep"
end

Tip 3.SQL 类型和Ruby 类型之间的对应关系可能会在 decimal 类型的精度上出现问题。SQL的 demimal,numeric,float,double 都对应 Ruby 的 Float。可以使用聚合对象 composed_of 对字段进行处理。                                                                                                                                                                                         

Tip 4.ActiveRecord会尽量的把从数据库中获取的值转换为Ruby的类型(譬如说,假设数据库字段是 timestamp 类型的,就会返回 Time 对象)。如果你希望得到一个属性的原始值,可以在属性名称后面加上 _before_type_cast.

Tip 5.Ruby 认为:除了 nil 和 false 之外的所有值都被解释为 true。所以,如果要查询一个 boolean 型字段的状态,就必须在字段名的后面加上一个问号:
user = User.find_by_name("Dave")
if user.superuser? # 不可以使用 user.superuser,否则不论数据库中存储的是数字 0、字符串 f 还是德语的 N,此条件都将成立。
 grant_privileges
end

posted @ 2006-12-15 09:30 比特鼠 阅读(278) | 评论 (0)编辑 收藏

最近一直在看Programming Ruby - 2nd, 先记录下一些不熟悉的Ruby的基本语法和操作(熟悉的就不再记啦...)

Ruby的命名:

      局部变量         全局变量      实例变量      类变量             常量和类名
==============================================================================
      name            $debug       @name        @@total           PI
      fish_and_chips  $CUSTOMER    @point_1     @@symtab          FeetPerMile
      x_axis          $_           @X           @@N               String      
      thx1138         $plan9       @_           @@x_pos           MyClass
      _26             $Global      @plan9       @@SINGLE          JazzSong
==============================================================================


数组:

数组下标从0开始, 下标可以为负整数, 此时代表数组从最末端开始, 最末端索引从-1开始
数组的元素可以是任何类型的对象实例,包括nil实例

数组的初始化:
a = [ 'ant', 'bee', 'cat', 'dog', 'elk' ]
或者
a = %w{ ant bee cat dog elk }

访问:
a[0]    #返回"ant"
a[1]    #返回"bee" 
a[-1]   #返回"elk"
a[-2]   #返回"dog"

赋值:
a = [ 1, 3, 5, 7, 9 ]
a[1] = 'bat'       #a = [1, "bat", 5, 7, 9]
a[3]= 'cat'        #a = [1, "bat", "cat", 7, 9]
a[3] = [ 9, 8 ]    #a = [1, "bat", "cat", [9, 8], 9]
a[6] = 99          #a = [1, "bat", "cat", [9, 8], 9, nil, 99]

a = [ 1, 3, 5, 7, 9 ]
a[2, 2] = 'cat'           #a = [1, 3, "cat", 9]
a[2, 0] = 'dog'           #a = [1, 3, "dog", "cat", 9]
a[1, 1] = [ 9, 8, 7 ]     #a = [1, 9, 8, 7, "dog", "cat", 9]
a[0..3] = [ ]             #a = ["dog", "cat", 9]
a[5..6] = 99, 98          #a = ["dog", "cat", 9, nil, nil, 99, 98]


哈希表:
相当于Java中的Map
具有"键值对"特性, key和value都可以是任何类型的实例

哈希的初始化:
h = Hash["a", 100, "b", 200]     # {"a"=>100, "b"=>200}
h = Hash["a" => 100, "b" => 200] # {"a"=>100, "b"=>200}
h = { "a" => 100, "b" => 200 }   # {"a"=>100, "b"=>200}

访问:
h.length = 2

赋值:
h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
h.length    # 3
h['dog']    # "canine"
h['cow']    # 'bovine'
h[12]       # 'dodecine'
h['cat'] = 99
h           # {"cow"=>"bovine", "cat"=>99, 12=>"dodecine","donkey"=>"asinine", "dog"=>"canine"}


代码块:
代码块在ruby 中是以{} 和do end 来包围起来的。通常如果是单行使用{} ,如果是多行使用do..end
代码块可以接受参数, 参数用两个竖线"|"符号来包含, 参数间用逗号","来分隔
代码块被作为一个对象传入method体中执行(如果代码块对象有参数, 则method体在执行代码块中的代码前,需要对代码块对象的参数赋值)

例如:

def test(max)
   i1, i2 = 1, 1          # parallel assignment (i1 = 1 and i2 = 1)
   while i1 <= max
      yield i1            # 也可以写成yield(i1), yield指令代表了代码块执行的位置, i1是method体中对代码块f进行了赋值
      i1, i2 = i2, i1+i2
   end
end

test(1000) {|f| print f, " " }

输出: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987



又例如:
[1,3,5,7].inject(0) {|sum, element| sum+element} # 输出:16



Method的声明:

一般格式是:
   def my_new_method(arg1, arg2, arg3)    # 3 arguments
      # Code for the method would go here
   end

方法名initialize是特殊的method, 它在类的new操作时被执行, 用于对类实例进行初始化操作:
   def initialize(name, &block)
      @name, @block = name, block
   end

如果需要方法返回一个值, 可以显式使用return关键字, 也可以使用"默认最后一行返回值"形式:
   def meth_one
      "one"    # 相当于 return "one"
   end

如果method的声明中最后的参数带有"&"前缀(例如下例的&action), 则表示该参数接受的是代码块, 并且该代码块将被转换为Proc对象, 最后被赋值给一个类实例变量,如:
   class JukeboxButton < Button
      def initialize(label, &action)
         super(label)
         @action = action #代码块对象被赋值给一个类实例变量(即@action)
      end
      def button_pressed
         @action.call(self)
      end
   end
   start_button = JukeboxButton.new("Start") { songlist.start }

如果method的声明中最后的参数带有"*"前缀, 表示该参数接受的是0个或多个参数(即:可变长参数), 例如:
   def varargs(arg1, *rest)
      "Got #{arg1} and #{rest.join(', ')}"
   end
   varargs("one")                # "Got one and "
   varargs("one", "two")         # "Got one and two"
   varargs "one", "two", "three" # "Got one and two, three"

如果method的方法名最后为字符"=", 表示该方法可为类实例变量赋值, 相当于Java中的setXXXX方法
   class Song
      def duration=(new_duration)   # 整个方法名为"duration="
         @duration = new_duration   # @duration为类实例变量
      end
   end
   song = Song.new("Bicylops", "Fleck", 260)
   song.duration       # 260
   song.duration = 257 # set attribute with updated value
   song.duration       # 257


   上例中的"duration="方法 相当于下面的: attr_writer :duration
   class Song
      attr_writer :duration
   end
   song = Song.new("Bicylops", "Fleck", 260)
   song.duration = 257


区间:

两个点创建一个"包含"区间
1..10    #包含1和10
'a'..'z'

三个点创建一个除最后一个元素外的区间
my_array = [ 1, 2, 3 ]
0...my_array.length  # 相当于0..2

区间可以通过to_a函数来转换为数组
a = (1..3).to_a   # a = [1, 2, 3]


区间的一些方法:
digits = 0..9
digits.include?(5)                   # true
digits === 5                         # true
digits.min                           # 0
digits.max                           # 9
digits.reject {|i| i < 5 }           # [5, 6, 7, 8, 9]
digits.each {|digit| dial(digit)}    #  0..9


待续...


posted @ 2006-11-22 11:26 比特鼠 阅读(402) | 评论 (0)编辑 收藏

<?xml version="1.0" encoding="UTF-8"?>
<project name="test" default="compile" basedir=".">
  <!-- define the properties for target reference -->
  <property name="dir.src" value="src"/>
  <property name="dir.etc" value="etc"/>  
  <property name="dir.web" value="web"/>
  <property name="dir.meta" value="meta"/>  
  <property name="dir.lib" value="${dir.web}/WEB-INF/lib"/>
  <property name="dir.build" value="${dir.web}/WEB-INF"/>
  <property name="dir.classes" value="${dir.web}/WEB-INF/classes"/>
  <property name="app.name" value="ocs"/>

  <!-- data base -->  
  <property file="${dir.web}/WEB-INF/config/jdbc.properties"/>  
  <property name="database.driver"   value="${jdbc.driverClassName}"/>
  <property name="database.url"      value="${jdbc.url}"/>
  <property name="database.username" value="${jdbc.username}"/>
  <property name="database.password" value="${jdbc.password}"/>

  <!-- the classpth of ant -->
  <path id="classpath">
    <fileset dir="${dir.lib}" includes="**/*.jar" />
    <pathelement path="${dir.classes}"/>
  </path>


  <!-- compile the src and test source code -->
  <target name="compile" description="Compile main source and test tree java files">
   <delete dir="${dir.classes}"/>   
   <mkdir dir="${dir.classes}"/>
    <javac srcdir="${dir.src}" destdir="${dir.classes}" debug="on">
      <classpath refid="classpath"/>
    </javac>
    <copy todir="${dir.classes}">
      <fileset dir="${dir.src}" includes="**/*.xml,**/*.properties">
          <!--exclude name="**/*.hbm.xml" /-->
      </fileset>
      <fileset dir="${dir.etc}" includes="**/*.properties"/>
    </copy>
  <native2ascii dest="${dir.classes}" src="${dir.etc}" encoding="UTF-8" includes="**/*.src" ext=".properties" />
  </target>

 
  <!-- setup the database and initialize the data -->
  <target name="setup-db">
    <echo message="=========================================================="/>
    <echo message="database driver:${database.driver}"/>
    <echo message="database
url:${database.url}"/ >
    <echo message="database username:${database.username}"/>
    <echo message="database password:${database.password}"/>
    <echo message="=========================================================="/>  
   
    <antcall target="runSql" inheritAll="false">
      <param name="script.sql" value="${dir.meta}/database-setup.sql"/>
      <param name="database.driver" value="${database.driver}"/>
      <param name="database.url" value="${database.url}"/>
      <param name="database.username" value="${database.username}"/>
      <param name="database.password" value="${database.password}"/>
    </antcall>
    <echo message="initialize Sample data..."/>
    <antcall target="runSql" inheritAll="false">
      <param name="script.sql" value="${dir.meta}/database-init.sql"/>
      <param name="database.driver" value="${database.driver}"/>
      <param name="database.url" value="${database.url}"/>
      <param name="database.username" value="${database.username}"/>
      <param name="database.password" value="${database.password}"/>
    </antcall>
  </target>
  
 
  <!-- run the sql to setup database -->
  <target name="runSql">
    <sql src="${script.sql}"
         driver="${database.driver}"
         url="${database.url}"
         userid="${database.username}"
         password="${database.password}"
         classpathref="classpath"
         onerror="continue"/>
  </target>

  <target name="clean" description="Clean output directories">
    <delete dir="${dir.classes}"/>
  </target>
 
   
  <target name="deploy" description="deploy......" depends="clean,compile" >
  </target>
</project>

posted @ 2006-11-08 09:47 比特鼠| 编辑 收藏

1. window -- Preferences
      1.1 Content Types 
          设置相关的文件为"UTF-8", 如果在列表中没有文件后缀, 则增加相应的文件后缀.

      1.2 Workspace
          设置"Text file encoding" 为 "UTF-8".


2. Validation
      将Validator中, 除了Application client validator外的所有validator的"Manual"和"Build", 全部置空(即取消打勾)
posted @ 2006-11-08 09:44 比特鼠 阅读(259) | 评论 (0)编辑 收藏

1. 标准用法
c = Object.const_get("Array")
s = c.new

2. 使用Ruby Extension
c = Class.by_name("Array")
参见: http://extensions.rubyforge.org/rdoc/index.html

3. 通过eval 
c = eval("Array").new
posted @ 2006-10-24 12:35 比特鼠 阅读(429) | 评论 (0)编辑 收藏

Ruby on Rails是一个很不错的web开发框架,不过由于目前其本身对国际化的支持较差,所以需要一些辅助的东西来解决国际化问题。

我最先使用的是一个叫localization的插件,这个插件的使用说明在Ruby on Rails官方的wiki上面有,这个插件使用比较简单,不过功能比较弱。

目前使用的是Ruby-GetText,这个插件功能强大,基于GNU的gettext,详细的使用说明在 http://manuals.rubyonrails.com/read/chapter/105 上面有写,我大概提一下要点:

首先要是Ruby on Rails支持UTF-8,通过将如下代码添加到environment.rb中:

$KCODE = ‘u’
require ‘jcode’

接着是安装Ruby-GetText,运行gem install gettext就可以了,如果是Windows平台就选2,否则选1。接着就是在项目根目录下面创建po目录,里面添加你需要Locale的名字作为子目录,比如en, zh_CN等。需要国际化的字符串都以_(”")来书写,比如想国际化“编辑”这个标签,就可以在视图的rhtml中使用_(”Edit”),这样在显示的时候会自动根据Locale来替换成相应的值。

接着在项目的rakefile里面如下内容:

desc “Create mo-files for L10n”
task :makemo do
  GetText.create_mofiles(true, “po”, “locale”)
end

需要注意的是在上面给出的链接的那篇文章里面,还需要添加updatepo这样一个目标,不过这个目标在我这不起作用,总是出错,我的办法是下载那篇文章附带的代码: http://www.digitale-wertschoepfung.de/artikel/gettext/using-gettext-with-rails.tgz 。在里面的script目录下面有一个localize文件,把它拷贝到你的项目的script目录下面,然后运行ruby ./script/localize {文本域的名字}就可以从controllers, views中更新po数据,用Unicode编辑器(比如UltraEdit)修改各个po文件,然后运行rake makemo就可以了。关于如果修改application.rb及其他controller的代码,查看给出的链接。此时重新启动server,就可以查看效果了。

posted @ 2006-10-24 09:53 比特鼠 阅读(1150) | 评论 (2)编辑 收藏

原文出处: http://www.rubychina.org/viewthread.php?tid=124&extra=page%3D1

    元编程并不是一个很新的概念,通常元编程被认为是通过程序来生成程序,如果从这种意义上来考虑,那么lex和yacc以及JavaCC应该都可以算是具有了元编程的概念,在Java中,元编程得到了广泛的应用。但在Ruby中,元编程的使用变得相当的简单和容易实现,使用Ruby语言本身来产生Ruby代码,不需要借助外部的工具,著名的RoR框架就是建立的Ruby元编程的基础上的。可能你你对元编程还没什么概念,但是Ruby已经内建了元编程这种机制,所以很有可能,你在不知不觉中就已经使用了Ruby元编程技术为你带来的方便之处。如下面这段代码:

class Person
   attr_reader :name
end
你肯定知道 :name 是和 @name 相关联的,但是你不一定清楚它到底是怎么实现的,其实attr_reader方法的实现就是采用了元编程技术,如下面的这段代码:

class Module
    def attr_reader(*syms)
        syms.each do |sym|
            class_eval %{def #{sym}   
                             @#{sym}
                         end
        end   
    end
end

    看了这段代码,你应该大概了解元编程的机制了吧,如果你现在还不了解,那么我建议你先认真的学习一下Ruby的反射机制,然后再接下去看这篇帖子,因为下面介绍的内容并不是一杯婴儿奶粉。

    在Ruby On Rails中,有一个OR映射层,就是动态的从一张关系表映射到一个对象,这主要由ActiveRecord类来实现。在OR映射模型中,将关系数据库中的关系表表映射到对象模型时,将关系表的表名映射到类名,表中的每一个元组映射到对应于这个类的一个对象,元组的一个字段对应于对象的一个属性。

假如我们有一个保存职员基本信息的文件,文件的格式是这样的:第一行是文件内容的每个字段的名称,从第二行开始,则是每个职员的基本信息。现在我们有一个文件名为“employee.txt”的文件,其内容如下所示:


name,age,gender

"John", 23, "male"

"Linclon", 25, "male"


假设我们就要从这个文本文件中读取数据,并进行一定的处理。如果是使用C++编程,你首先一定会想到应该定义一个Employee类,然后这个类中有name, age, gender这些成员变量。但是是采用这种方法的话,可以发现,如果想在职员信息中加入一个字段,比如部门(department),就不得不修改Employee类的代码,在Employee类中增加一个“department”成员变量,所以我们的代码是高度依赖于文件的具体格式,这当然不是一个好的现象。我们希望有一种更简单和优雅的方案,还有,Ruby动态性提高给我们一个解决方案,但是,我们应该从和下手呢,这就需要Ruby的元编程能力。


    首先,我们想应该有一个职员类,在Rails中,每个关系表的名称会成为类的名称,在这里,采用类似的方法,将文本文件的名称作为类的名称,在Ruby中,类名同时也是一个常量名,所以第一个字母必须为大写,我们使用如下的代码来生成类名。


class_name = File.basename(file_name, ".txt").capitalize
# "employee.txt" => "Employee"
klass = Object.const_set(class_name, Class.new)



Class.new生成一个新的类,这个类的名称是匿名的,所以采用const_set操作来绑定一个类名,变量klass是新类型的引用。


生成了这个类以后,需要想这个类添加姓名,年龄和性别这些属性,这些属性的名称是在文本文件的的第一行中给出的。


data = File.new(file_name)
header = data.gets.chomp

data.close

names = header.split(",")


下面的代码给出了如何生成这些属性,以及初始化这些属性值。


klass.class_eval do
    attr_accessor *names
            
    define_method(:initialize) do |*values|
        names.each_with_index do |name, i|
            instance_variable_set("@" + name, values )
        end
    end
    #...   
end   

现在,有了一系列的访问子(可读和可写),通过instance_variable_set方法,又给每个属性做了初始化。

变量names是在块外部定义的,由于块的闭合性,所以变量names在块中也是有效的。当然,为了程序的演示,又定义的了一个to_s方法,代码如下所示:


    define_method(:to_s) do
        str = "<#{self.class}: "
        names.each {|name| str << "#{name}=#{self.send(name)} "}
        str + ">"
    end
    alias_method :inspect, :to_s


    完成了这些以后,对于类的构造已经基本结束了,现在就需要真正的从文本文件中读取数据了。从文本文件读数据应该是一个类方法,而不是一个实例的方法,其实现代码如下:


    def klass.read
        array = []
        data = File.new(self.to_s.downcase + ".txt")
        data.gets #throw away header
        data.each do |line|
            line.chomp!
            values = eval("[#{line}]")
            array << self.new(*values)
        end
        data.close
        return array
    end


在这个方法中,使用字的类名来匹配相关的文件,比如将Employee类映射到“employee。txt”。

然后,从文件中读取职员信息,由于第一行是字段定义,所以要舍弃第一行数据。从第二行开始读取数据,每读取一行数据,则构造一个Employee实例。通过上面这个简单的例子,我们可以看出元编程的功能是相当之强大的,使用元编程技术,可以构造相当简单,优雅的解决方案。

posted @ 2006-10-24 09:52 比特鼠 阅读(471) | 评论 (0)编辑 收藏