10 十二月 2008

Linux中的硬连结和符号连结(个人理解)

在Linux中创建文档或者目录的快捷方式有两种(类似Windows中的快捷方式),硬连结和符号连结。两者的区别如下:
1、硬连结,用来创建文档的快捷方式。实际上是在文档所在目录的block区域添加了文档的关联数据inode。因此当创建一个硬连结指向一个文档时,这个硬连结实际上表示指向了一个文档的inode。因此对该连结进行访问,实际上是通过存储的inode信息来找到文档的数据域。所以如果删除该链接,删除的只是上层目录存储的文档inode信息,并不会影响原文档在原目录下的关系。如果删除原档案,实际上删除的是该文档上层目录从存储的关联数据inode,而原文档仍然可以通过其他关联的目录读取出来(即通过硬连结读取)。该方式不能跨分区创建连结。
2、符号连结,可以用来创建文档和目录的快捷方式。它与硬连结的不同在于,它实际起一个指向的作用指向档案是实际的位置,因此给一个文档做多少个符号链接,如果删除原文档,那么该文档的关联数据就会被删除 ,因此其它的符号链接是无法再次读取数据的。该方式可以跨分区创建连结。
这是通过相关资料得出的结果。一般来说很多人对于硬连结和符号链接都搞的不太清楚,主要是Linux的一些基本概念没有清楚的原因。所以如果要理解这上面的概念,首先必须理解Linux系统中的inode和block的概念。其次如果有一定的开发经验或者C方面的概念,那么理解起来更方便。
实际上硬连结就像给一个数据起了一个别名,符号链接就像一个指向了该数据的指针。因此如果数据有多个别名,那么删除任意一个都不会影响原数据,其它的别名照样可以访问。如果是符号链接,访问数据的时候就像通过C语言的*来访问指针指向的数据,如果删除了该指针指向的内容,那么原数据肯定会被删除。其它指向该数据的指针也无法访问该数据。

29 十一月 2008

Ubuntu确实很好用

对于我这个用了N年的windows用户来说,转为Linux是很麻烦的。最大的麻烦不是装系统,而是使用。还记得大二的时候下载了红帽子自己从硬盘安装,结果不小心把自己收藏的40G的歌曲全部格式化,心里那个悔啊。心一横,装了再说,大不了到时候再下载。结果用了之后才傻眼了,首先不乎设置网络,其次很多平常应用,比如听歌,看电影,都没办法,必须自己手动安装。而那个时候红帽子下面安装软件,自己得一条一条的终端下些命令。对于我这个菜鸟来说,简直犹如地狱。所以我的第一次Linux尝试以失败告终。
转眼很多年过去了。当初的菜鸟也变成了别人眼中的编程高手。对于windows的应用,已经熟的有种厌恶的感觉。天天折腾windows还如不自己敲代码,部署开发环境有意思。来到上海以后,才发现,自己与别人之间的差距有多大。在这里,对于开发人员,Linux就像家常便饭一样普通。在这种压力和朋友的鼓励下,又重新拿起了Linux,当然,也不会像以前那样茫然和盲目。使用虚拟机安装了Ubuntu,同时手头还找了一本比较好的Linux学习手册《鸟哥的Linux私房菜》,刚开始一直用的命令行模式跟着鸟哥的书学习。无意中进入了窗体环境,抱着玩一玩的态度使用起来ubuntu。结果出乎意料,首先内置了很多常用的软件,比如office,gimp,聊天工具pidgin,其次我常用的QQ也出了官方的Linux版本,在加上Ubuntu中非常方便的新立得软件管理器,很容易的就设置好了中文输入法。对于我来说,还有很多的开发环境是非常必要的。然后在Ubuntu中,新立得软件管理器里有很多开发环境的支持,只需要鼠标一点,你需要的环境全部能够下载下来,C语言,Java,Apache,MySQL……。一切行云流水,非常方便。Ubuntu确实可以称为最好的桌面Linux版本,因为对于用户来说,他们承认一个操作系统好不好,就在于好不好用。现在,Ubuntu做到了这一点。相信,用不了几个月,我就会放下虚拟机,彻底投入Linux的怀抱。当然,不是说以后就不用Windows,因为我的很多好游戏还必须在Windows下玩,但是其他的很多应用包括开发都可以在Linux下进行了。

普通的应用,Ubunut已经够了。但是对于软件开发而言,shell必须要学,所以shell是不会丢下的。

22 十月 2008

微软黑屏解除工具V1.0 绿色版

软件语言: 简体中文
软件类型:国产软件 - 系统工具 - 升级补丁
授权方式:免费版
软件大小:102 KB


08年10月21日零点,微软WGA(Windows 正版增值计划)开始执行,该计划将限制为国内非正版系统用户,提供安全更新服务。非正版用户如果开启自动更新,可能会遇到“黑色桌面”的麻烦。而如果用户 关闭自动更新,那么系统内漏洞将不能被修复,可能导致遭受病毒感染或黑客攻击。可见,停止安全更新引发的威胁远远高过“黑色桌面”的视觉问题。
我们强烈推荐您开启自动更新,接受微软Windows正版增值计划,非正版用户请购买正版微软系统。对于因多种因素无力即时购买正版系统软件的用户。“微软黑屏解决工具”将为您提供完美的解决方案,避免因黑客攻击造成的财产损失。
微软黑屏解决工具特点:

1、针对那些已经黑屏的用户:
一键卸载微软的“黑屏验证”补丁,解除系统“黑屏”。
2、针对那些没有“黑屏”,但已关闭自动更新的用户:



微软黑屏解除工具V1.0 绿色版 下载页面
http://www.520win.cn/soft/sort01/sort069/down-885.html

20 十月 2008

郭德纲的话 笑死人不偿命

1.要不是打不过你,我早跟你翻脸了

2.不想当厨子的裁缝不是好司机。

3.龙虾,海蟹,!我就爱吃带壳的!服务员,给他来盘瓜子

4.牛肉面大排面便饭一曰本人过来,吃点什么?这个,曰本人用手一指 :牛大便!

5.我终究没能飙得过那辆宝马,只能眼看着它在夕阳中绝尘而去,不是我的引擎不好,而是我脚蹬子坏了!

6.901次航班,由打西直门开往中关村,票价5元,请您登机。你说这多有意思。乘务员站那儿得喊,快上快上有大座儿,有大座儿。

7. 接了一活儿,完事能挣三十万……拿过图纸一看,盖一40米的烟囱,等盖好了,人家来一看,把我打了一通,图纸看倒了,人家让挖口井。

8.流氓会武术,谁也挡不住……科学家会武术,流氓也挡不住!

9.一些女演员为了上戏和男导演 ,太无耻了!女导演太少,让我们这些个男演员怎么办?

10. 你拿的什么?我妈给带的鸡蛋。给我吃。不给……你猜,猜有几个。我猜出来你给我一个。你要猜出来我把这两个都给你。5个?

11.曾经有一个赚钱的机会摆在我的面前,但是我没有珍惜。机会过去了,我追悔莫及。假如上天再给我一个机会的话,我希望跟那个村长说:我愿意去。假如非让我在那工资前面加一个限额的话,我希望是:400元~

12.住的房子千疮百孔,一下雨算要了亲命了:外边小雨屋里中雨,外边大雨屋里暴雨,有时候雨实在太大了,全家人都上街上避雨去了。

13.英语、日语、韩语、南斯拉夫语、北斯拉夫语、西斯拉夫语...会7、8国外国话,和八国联军对着骂街都不带重样的~

14.你无耻的样子,颇有我年轻时候的神韵。

15.谁要不认识他,谁就没吃过猪肉。

16.有困难要帮,没有困难制造困难也要帮。

17.上次喝多了,拿筷子当鸡爪子,吃了一根半。

18.小妞,给大爷笑一个,不笑,那大爷给你笑一个 ~

19.那锁拿根面条一捅就开,一包方便面能开一小区。

20.你叫什么名字?你别说出来啊!你说出来就是骂街!

21.我扔铅球扔得可远了,教练说了,人出去了不算啊!

22.今天说的这故事,离现在不远,家里有老人的可以回去问问,在春秋战国时期啊 ……

23.遛累了,他坐这,狗坐这,一边高,谁过来都纳闷:这是谁家双胞胎啊?

24.我买50辆好车--奥拓、奥拓、奥拓……!用铁丝镖起来,开起来跟火车一样!

25.小学上了十年,中学十二年,我被评为全校最熟悉的面孔,新老师来了都跟我打听学校内幕……

26.我要了份鱼翅炒饭,用三双筷子楞没找着鱼翅,你能告诉我鱼翅在哪吗?厨师说,我叫鱼翅。

27. 你们千万别打他,也别骂他,不就是要钱么,咱好商量。不过丑话说头里,要是超过100块钱,你们就撕票得了。

28.中国北京有一军事奇才,叫郭德纲,文能提笔安天下,武能上马定乾坤;上炕认识娘儿们,下炕认识鞋。

29.跳楼死多可乐,我研究过,二十层和二层效果不一样,二层是啪、啊!二十层是啊~~~~~啪

26 九月 2008

GET和POST提交方式在web容器中的编码区别

当然,最主要的区别大家都知道

1、在发送请求时,安全性有区别

2、在提交数据时,数据量的大小有区别

但是两者在提交数据时,编码是不同的。GET在提交的时候不进行编码,提交的数据作为查询字符串附加到URL的末尾,然后发送到服务器。POST方式在提交时,会将数据进行编码(WEB容器默认的编码是ISO-8859-1)。所以要输出表单提交的数据,此时应该注意提交的方式以及编码的影响,否则如果存在中文,会出现乱码。

对于POST方式,如果需要得到表单提交的中文数据,不能直接通过request.getParameter()获取,否则得到的是以ISO-8859-1编码返回的字符串(中文会产生乱码);应该在读取数据前调用request.setCharacterEncoding()方法指定中文编码方式(GBK,GB2312)。在浏览器显示中文数据前,也应该调用respongse.setContentType()来指定字符编码方式为中文编码。

对于get方式,则必须拿到数据后自己指定编码重新构造字符串。如果用post那样的方式会发现毫无作用。为什么会这样?查了一下相关资料,貌似setCharacterEncoding方法是服务器支持的,所以有局限性;但是自己编码确实通用的。

21 七月 2008

一个面试题

用1、2、2、3、4、5这六数,打印出所有不同的排列,要求:"4"不能在第三位,"3"与"5"不能相连。

要求用Java做,我试着用C语言做了下。

#include <stdio.h>
int checkNum(int num);//检查是否是122345五个数字构成
int checkCondition1(int num);//先判断除了2可以重复两次以外,其它各位不能存在重复的情况
int checkCondition2(int num);//判断4是否在第三位
int checkCondition3(int num);//判断3与5是否相连
int checkIsMyNum(int num);

void main()
{
int num=122345;
int i=0;
int count=0;

for(;num<=543221;num++)
{
if(checkNum(num))
{
if(checkCondition1(num)&&checkCondition2(num)&&checkCondition3(num))
{
printf("%d ",num);
i++;
count++;
if(i==10)
{
i=0;
printf("\n");
}
}
}
}

printf("\n%d",count);
}

int checkCondition3(int num)//判断35相连
{
int temp1=num%10;
int temp2=num%10;
while(1)
{
num=num/10;
temp1=temp2;
temp2=num%10;
if((temp1==3&&temp2==5)||(temp1==5&&temp2==3))
{
return 0;
}
if(num/10==0)
{
break;
}
}

return 1;
}

int checkCondition2(int num)
{
int i=0;
while(1)
{
int temp=num%10;
i++;
if(i==3)//如果是第三位
{
if(temp==4)
{
return 0;
}
else
{
return 1;
}
}
if(num/10==0)
{
break;
}
num=num/10;
}
}

int checkIsMyNum(int num)
{
if(num==1||num==2||num==3||num==4||num==5)
{
return 1;
}
else
{
return 0;
}
}

int checkNum(int num)
{
int temp;
do
{
temp=num%10;
if(!checkIsMyNum(temp))
{
return 0;
}

if(num/10==0)
{
break;
}
num=num/10;
}while(1);

return 1;
}

int checkCondition1(int num)
{
int criterion[6]={1,2,2,3,4,5};
int i=0;


//先判断除了2可以重复两次以外,其它各位不能存在重复的情况
while(1)
{
int temp=num%10;


for(i=0;i<6;i++)
{
if(criterion[i]==temp)
{
criterion[i]=0;
break;
}
}

if(num/10==0)
{
break;
}

num=num/10;
}


for(i=0;i<6;i++)
{
if(criterion[i]>0)
{
return 0;
}
}

return 1;
}

因为限时做的,所以有点乱,没有考虑流程的优化和效率问题。最后答案是198种。

18 七月 2008

AutoHotkey——小巧强大的脚本软件

AHK 是一款叫做 AutoHotKey 的脚本软件的缩写,它是改造 Windows 必备的软件!用了它你可以替换掉很多软件。(俺是程序员,不是文学家,所以这句话照抄别人的,但是软件确实是好软件)

AHK可以做什么?从应用的角度来说,可以编写脚本实现屏幕取色,编写脚本实现时间定时……(反正功能太强了),对于我来说有什么好处?

平常使用MyEclipse编写JavaEE的东西,经常需要web服务器的重新部署,通过AHK来录制宏命令,这样每次需要类似的重新部署的时候,运行宏,全部自动搞定o(∩_∩)o...(当然ant也可以,但是东西小的时候ant就有点大材小用了)。

这里有很多AHK的具体应用实例:http://www.appinn.com/tag/ahk/

AHK官方网站:www.autohotkey.com

11 七月 2008

SwingUtilities.invokeLater()和SwingUtilities.invokeAndWait()介绍

今天看了下MP3在线搜索器的源代码,发现里面出现了SwingUtilities.invokeLater()和SwingUtilities.invokeAndWait()这两个方法。以前做Swing事件响应的时候从来没有用过,所以网上查了下,大概明白他们的作用。

举个例子,比如单击按钮实现某些操作,我们一般使用ActionListener监听器来调用actionPerformed方法来完成按钮的功能。这样的做法一般来说没有问题,但是某些情况下会出现问题。比如单击按钮要执行很费时的操作,此时你的按钮就会始终处于按下的状态,而且导致界面其他的组件无法正常使用,好像界面死了一样。这是由于Swing不是线程安全的,它是单线程的设计,因此如果它处于某种费时的操作,由于单线程设计的缘故,会导致其他操作在当前功能未完毕之前无法启动。如何解决这类问题,就可以考虑使用SwingUtilities.invokeLater()和SwingUtilities.invokeAndWait()这两个方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块,即当前费时的操作不会影响Swing中的其他操作。如果你希望部件能够立即显示或重画,而处理代码在后台运行,那么你就可以使用invokeLater。invokeAndWait指的是当前线程阻塞,一直等待invokeAndWait中指定的代码执行完毕之后,才接着运行,这种方式用的不多。

27 六月 2008

单词Mp3随身宝源码分享

前两天联系了令狐虫,收藏了他的单词MP3随身宝源码(名字太长了,实际上就像一个Java版的金山词霸)。自己学习研究了下,并且把相应的注释补全(汗一个!令狐同志不写注释害苦了我这个Java初学者)。感兴趣的朋友可以联系令狐虫,或者从我这里直接下载(因为有词库数据和语音数据,所以比较大,我正在上传到网盘)。

单词MP3

纳米盘下载

23 六月 2008

JSP中的pageEncoding和contentType属性区别



转:http://www.webjx.com/htmldata/2007-09-27/1190856301.html

关于JSP页面中的pageEncoding和contentType两种属性的区别:

pageEncoding是jsp文件本身的编码

contentType的charset是指服务器发送给客户端时的内容编码

JSP要经过两次的“编码”,第一阶段会用pageEncoding,第二阶段会用utf-8至utf-8,第三阶段就是由Tomcat出来的网页, 用的是contentType。

第一阶段是jsp编译成.java,它会根据pageEncoding的设定读取jsp,结果是由指定的编码方案翻译成统一的UTF-8 JAVA源码(即.java),如果pageEncoding设定错了,或没有设定,出来的就是中文乱码。

第二阶段是由JAVAC的JAVA源码至java byteCode的编译,不论JSP编写时候用的是什么编码方案,经过这个阶段的结果全部是UTF-8的encoding的java源码。

JAVAC用UTF-8的encoding读取java源码,编译成UTF-8 encoding的二进制码(即.class),这是JVM对常数字串在二进制码(java encoding)内表达的规范。

第三阶段是Tomcat(或其的application container)载入和执行阶段二的来的JAVA二进制码,输出的结果,也就是在客户端见到的,这时隐藏在阶段一和阶段二的参数contentType就发挥了功效

contentType的設定.

pageEncoding 和contentType的预设都是 ISO8859-1. 而随便设定了其中一个, 另一个就跟着一样了(TOMCAT4.1.27是如此). 但这不是绝对的, 这要看各自JSPC的处理方式. 而pageEncoding不等于contentType, 更有利亚洲区的文字 CJKV系JSP网页的开发和展示, (例pageEncoding=GB2312 不等于 contentType=utf-8)。

jsp文件不像.java,.java在被编译器读入的时候默认采用的是操作系统所设定的locale所对应的编码,比如中国大陆就是GBK,台湾 就是BIG5或者MS950。而一般我们不管是在记事本还是在ue中写代码,如果没有经过特别转码的话,写出来的都是本地编码格式的内容。所以编译器采用 的方法刚好可以让虚拟机得到正确的资料。

但是jsp文件不是这样,它没有这个默认转码过程,但是指定了pageEncoding就可以实现正确转码了。

举个例子:

<%@ page contentType="text/html;charset=utf-8" %>

大都会打印出乱码,因为输入的“你好”是gbk的,但是服务器是否正确抓到“你好”不得而知。

但是如果更改为

<%@ page contentType="text/html;charset=utf-8" pageEncoding="GBK"%>

这样就服务器一定会是正确抓到“你好”了。

22 六月 2008

(转的)使用Eclipse打JAR包

To create a new JAR file in the workbench:



  1. In the Package Explorer, you can optionally pre-select one or more Java elements to export. (These will be automatically selected in the JAR Package Specification wizard page, described in Step 4.)


  2. From the menu bar, select File > Export.


  3. Select JAR file, then click Next.


  4. In the Jar Package Specification page, select the resources that you want to export in the Select the resources to export field.


  5. Select the appropriate check box to specify whether you want to Export generated class files and resources or Export Java source files and resources.


    Note: Selected resources are exported in both cases.



  6. In the Select the export destination field, either type or click Browse to select a location for the JAR file.


  7. Select or clear the Compress the contents of the JAR file check box.


  8. Select or clear the Overwrite existing files without warning check box. If you clear thischeck box, then you will be prompted to confirm the replacement of each file that will be overwritten.


    Note: The overwrite option is applied when writing the JAR file, the JAR description, and the manifest file.



  9. You have two options:



    • Click Finish to create the JAR file immediately.


    • Click Next to use the JAR Packaging Options page to set advanced options, create a JAR description, or change the default manifest.


    (还是NetBeans方便,没这么多麻烦事。当然,高人直接使用jar命令手动打包了。)

19 六月 2008

使用Eclipse平台共享代码

原文地址:http://www.ibm.com/developerworks/cn/linux/opensource/os-ecshare/index.html
本文概述了 Eclipse 平台如何支持软件项目中的源代码版本控制。首先,我们将简要讨论一下团队代码开发的思想,然后研究 Eclipse 如何使用 CVS 代码资源库。我们还将研究一些源代码管理软件工具,可以通过 Eclipse 插件扩展来支持这些工具。



在团队项目中共享源代码

现今的大多数应用程序是由多人组成的团队开发的。即使只涉及几个开发人员的小项目,也需要对源代码的更改进行严格控制。这就是源代码管理软件的任务。 源代码版本控制软件必须支持两个核心功能:

  • 提供一种方法,能够协调对源代码的更改,并能集成这些更改

  • 团队所提交工作的历史记录


当 团队成员完成新的工作时,通过将这些更改提交到资源库来共享他们的工作。类似地,当他们希望获得最新可用的工作成果时,就可以根据资源库中的更改,更新自 己的本地工作空间。这意味着项目资源库会因团队成员提交新工作成果而经常发生更改。换句话说,资源库应该表示项目的当前状态。任何时候,团队成员都要能够 根据资源库更新自己的工作空间,并确信它们是最新的。

维护历史记录也很重要,那样就可以将当前工作与先前版本进行比较,如有必要,还可以回复到先前版本。协调团队的工作,以便只存在唯一的当前项目状态定义,以及包含团队已集成的工作,这些对于管理版本控制也是十分必要的。这种协调有可能是最难实现的目标。

最 理想的模型是:团队的任何成员都可以对自己有权访问的任何资源进行更改。因为两个团队成员可以提交对同一资源的更改,所以有可能发生冲突,必须解决这种冲 突。这种模型假定冲突具有唯一性。但遗憾的是,没有任何源代码是孤立地存在的;通常它包含与其它资源隐式或显式的相关性。源代码引用了在其它源代码资源中 描述的构件。但源代码管理软件的工作就到此为止了,因为它并不能取代项目管理。项目管理者必须履行其职责:协调其它成员的工作以及负责进度、项目阶段和发 布日期。此外,源代码管理也不能替代开发人员之间的交流。




















Eclipse 平台如何支持代码管理

Eclipse 平台提供了作为团队在软件项目中共享代码和工作的能力。Eclipse 广泛地支持各种代码管理解决方案,这要归功于它的插件体系结构(不过,现已推出了对 CVS 的支持)。Eclipse 平台体系结构的重点在于 工作空间。工作空间维护构建和测试软件项目所需的一切。它包含对象(源代码和资源)。 它还保存了用于项目、IDE 和插件的配置设置。工作空间是在开发人员的机器上本地进行维护的,而团队通过外部资源库进行协作,不同开发人员的代码在资源库进行汇集。可以经由因特网通过“客户机-服务器”体系结构访问资源库。

Eclipse 平台提供了对于直接从工作空间进行团队开发操作的支持。这种支持允许开发人员并发地与几个独立的资源库以及不同版本的代码或项目进行交互。工作空间中的资 源允许团队支持组件处理版本和配置管理问题。当然,单个工作空间可以同时访问不同类型的资源库。Eclipse 平台并没有提供它自己的代码管理解决方案;它总是依靠外部系统。Eclipse 平台只对一个(但也是最流行的一个)源代码管理系统提供内置支持:并发版本控制系统(Concurrent Versions System,CVS)。 对第三方代码管理应用程序的支持一节中描述了使用第三方插件支持其它资源库。




















CVS 是什么?

CVS 诞生于 1986 年,当时作为一组 shell 脚本而出现,但它现在已经发展成了最流行的针对软件开发人员的源代码版本管理解决方案。CVS 是用于代码版本管理的开放源码的客户机/服务器解决方案,它可用于各种平台,包括 Linux 和 Windows NT/2000/XP。请参阅本文末尾的 参考资料,其中有 CVS 客户机、服务器和源代码的下载链接。

通常,CVS 的主要功能是记录源文件的历史。当一组开发人员从事同一个项目时,CVS 将他们彼此隔离开来。每个开发人员都在他/她自己的目录中独立工作,然后使用 CVS 资源库(不时地)合并工作结果。

Eclipse 拥有与 Eclipse 平台 IDE 紧密集成的内置 CVS 客户机,它是作为一个单独透视图(CVS Repository Exploring 透视图)而实现的,用于与 CVS 的交互。用于 CVS 的通用 Eclipse 设置(General Eclipse settings for CVS)位于 Window -> Preferences window -> Team下。在切换到 CVS Repository Exploring 透视图之后,就可以使用所有 CVS 操作了(转至 Window -> Open Perspective -> Other -> CVS Repository Exploring菜单 — 请参阅 图 1图 2)。
图 1. 切换到 CVS Repository Exploring 透视图
CVS 资源库

首先设置资源库的位置,它将定义用于选定 CVS 服务器/资源库的连接参数。请确保使用 SSH 隧道( extssh )。
图 2. 浏览 CVS Repository Exploring 透视图中的 CVS 资源库
浏览 CVS 资源库



















回页首


Eclipse/CVS 的源代码工作流

在 CVS 团队协作模型中,团队成员彼此独立地在他们各自的工作台上完成自己的所有工作。最后,他们希望共享其工作。他们通过 CVS 资源库实现这一点。CVS 使用分支(branch)模型来支持彼此独立而又高度相互依赖的多个工作流程(course of work)。这些分支是开发团队用来共享和集成正在进行中的工作的地方。可以认为 分支是一个共享的工作台,当团队成员对源代码进行更改时就更新这个工作台。这个模型允许从事 CVS 团队项目的每个开发人员在进行更改时与其他成员共享其工作,以及在项目进展期间访问其他成员的工作。

一个称为 HEAD的特殊分支用来表示资源库中的主要工作流程(HEAD 通常被称为 主干)。当团队成员将资源提交给该分支时,会影响这些相关性。 确保相关性的完整性是很重要的,因为该分支表示了当前项目的状态。当然,任何时候,团队成员都可以使用该分支的内容作为新工作的基础。

那些规则不仅适用于 CVS:无论使用哪种版本控制软件,团队项目中都有一些用于源代码管理的常见步骤。下面是一个使用 Eclipse 内置的 CVS 支持的示例工作流:

1. 启动新的团队项目
每个新的空 Eclipse 项目都可以通过 CVS(或受支持的任何其它源代码管理系统)进行共享。开发人员也可以通过将其现有的代码迁移到资源库来共享它。要进行共享,单击项目主文件夹,在显示的上下文菜单中使用 Team -> Share Project选项,如 图 3所示。
图 3. 使用 CVS 资源库共享本地项目
共享项目

另一个选项是通过从选定的 CVS 资源库分支导入代码来创建新的工作台项目。只要选择适当分支(或 HEAD),然后选择从 CVS Repository Exploring 透视图中的上下文菜单中选择“Checkout As Project”选项,如 图 4所示。
图 4. 从现有的 CVS 资源库创建新项目
新建项目

2. 使用代码并进行更改
开发人员通过 Eclipse 工作台在本地使用代码,包括的工作有创建新资源、修改现有资源、编写注释,并在他们使用后在本地保存这些内容。

3. 使本地更改与 CVS 资源库同步
如果一个项目开发人员准备提交他/她的工作,那么首先要执行更新操作。这会针对引入的更改核对资源库,并将这些更改添加到该开发人员的本地工作台。这样确保了开发人员知道这些更改可能会影响他/她将要提交的工作的完整性。使用项目上下文菜单中的 Compare With...选项将本地版本与资源库中存储的代码进行比较(请参阅 图 5)。

图 5. 比较本地版本与资源库中的版本

本地版本

下一步是解决最后出现的任何冲突,并设法再次编译代码。如果一切正常,那么从项目上下文菜单使用 Team -> Commit...选项执行提交操作,如 图 6所示。这会使所有更改都集成到资源库中。

图 6. 将更改提交到远程资源库
提交更改

4. 管理资源库
CVS 允许开发人员将更改隔离在开发的某些独立路径之内,这些路径称为分支。当一个开发人员更改某个分支上的文件时,这种更改不会出现在主干或其它分支上。 那些分支被命名为 子版本(subversion)代码分叉(code fork)。稍后,由合并操作将更改从一个分支迁移到另一个分支(或主干)。然后提交这些修订。这样就有效地将更改复制到了另一个分支上。使用项目上下文菜单的 Team -> Branch...选项,Eclipse 使开发分支之间的迁移变得容易。

当然,当开发团队维护大型资源库时,有必要控制项目内的提交和合并操作。Eclipse/CVS 集成提供了一种特殊的视图:CVS Repository History(请参阅 图 7)。它给出了关于团队成员在资源库中所执行更改的快速预览。

图 7. 在 CVS Resource History 窗口中查看带注释的修订历史记录
修订历史记录

Eclipse 平台提供了几个支持代码管理的实用程序。最有用的是补丁功能。它将出自两个来源(譬如本地工作台和资源库)的代码进行比较,然后创建一个包含代码差异的类似 UNIX 的补丁文件(请参阅 图 8)。可以将该文件发送给开发人员以将源代码升级到最新版本。

图 8. 创建用于源代码分发的补丁
源代码分发

5. 断开项目与 CVS 的连接
当项目开发已经结束,并且团队希望 冻结源代码时,可以从 HEAD 资源库删除该项目的最终版本。断开项目与 CVS 的连接将在该项目及其资源上禁用资源库操作,并删除与该项目相关联的 CVS 信息(这一操作是可选的)。

可以通过项目上下文菜单中的 Team -> Disconnect选 项执行断开连接操作。通过选择这个选项,会打开 Confirm Disconnect from CVS 对话框。在将该项目与资源库的连接断开之后,该团队必须确定如何处理 CVS 信息。第一个选项是“Delete the CVS meta information”;它将禁用 CVS 团队菜单操作并从文件系统中删除 CVS 文件夹及其内容。第二个选项是“Do not delete the CVS meta information”;它将禁用 CVS 团队菜单操作,但保留 CVS 元信息。



















对第三方代码管理应用程序的支持

CVS 有几个重要的限制:它不能确定单个文件或整个文件集范围内同时进行的更改,它也不能检测文件之间的逻辑冲突。其冲突概念纯粹是文本意义上的,当对于同一基 本文件的两个更改时间上非常非常接近,从而使合并命令受到干扰时,就会发生冲突。CVS 也不能提供任何类似于消息传递这样的交互式协作工具。幸运的是,CVS 并不是 Eclipse 平台所支持的唯一的源代码管理软件。 开发人员可以通过插件扩展 Eclipse 平台的功能,而且目前(到 2003 年 3 月 4 日为止)已有 16 个可用于团队开发软件的插件。所有插件都是由 Eclipse 社区或商业软件供应商创建的。这些插件中的大多数添加了对第三方、商业源代码管理系统的支持。最有价值的插件是那些支持流行的企业代码管理系统(如 Merant PVCS 和 Rational ClearCase)的插件。例如,CVS-SSH2 插件允许通过 SSH2 会话访问 CVS,而 Microsoft Visual SourceSafe(VSS)团队提供程序插件添加了对 MS VSS 产品的支持(也可以在诸如 Linux 这样的非 Windows 平台上使用)。

但是,我本人所偏爱的插件是 Koi(请参阅 参考资料以 获取链接)。尽管它并非严格用于源代码控制,但这个创新的工具给协作开发注入了许多新的活力。其当前版本支持工作台到工作台的消息传递、共享标记、冲突更 改通知、共享日历和事件通知。Koi 将 XML-RPC 用作其客户机-服务器体系结构中的通信模型。客户机是与“协作服务器”通信的单个 Eclipse 平台实例,而协作服务器也是一个 Eclipse 插件。Koi 使用以 JDBC 访问的关系数据库作为数据存储。可在 参考资料中找到指向完整的、经过分类的 Eclipse 插件注册表的链接。
参考资料

关于作者










Pawel Leszek 是 Studio B 的一位作家, 他是一位专长于 Linux/Win/Mac OS 系统体系结构和管理的独立软件顾问和作家。他具有许多操作系统、编程语言和网络协议方面的经验, 尤其是 Lotus Domino 和 DB2 方面的经验。Pawel 还是 LinuxWorld上一系列文章的作者,以及 PC World波兰版的 Linux 专栏作家。Pawel 和他的妻子以及可爱的小女儿住在华沙。欢迎提问并提出意见;您可以直接给作者发电子邮件( pawel.leszek@ipgate.pl)。

13 六月 2008

Java程序打包为EXE可执行文件并且打包安装文件

首先需要下载exe4j和Inno Setup,前者负责将Java程序打包为可执行文件,后者负责制作安装程序。 >
pic_20080613_3.png

>
pic_20080613_4.png


首先将你的Java程序打包为jar文件(最简单的方式是使用NetBeans环境,自动生成包含Main Class 的jar文件)。


pic_20080613_1.png

其中conf是当前程序用来存储配置文件的目录,lib是需要的库文件,IPConfig.jar是我们的主程序。
首先将Java程序打包为可执行文件:

(视频速度较慢,请耐心等待)

演示教程
最后生成可执行文件


pic_20080613_6.png

下一步将可执行文件打包为安装文件


最后生成安装程序


pic_20080613_7.png







11 六月 2008

IP切换软件

这几天背着本本公司家里来回跑,每次都要配置IP地址。闲麻烦,所以自己做了一个切换IP的小工具。


pic_20080611_2.png



IP切换工具 畅易网盘

IP切换工具 纳米盘

26 五月 2008

Java内存分配

原文地址:http://www.blogjava.net/Jack2007/archive/2008/05/21/202018.html

今天看了一下java的内存分配,分享一下:
基础数据类型直接在栈空间分配, 方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。 引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配 对象的类变量 。 方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。 方法调用时传入的 literal 参数,先在栈空间分配,在方法调用完成后从栈空间分配。字符串常量在 DATA 区域分配 ,this 在堆空间分配 。数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小!
哦 对了,补充一下static在DATA区域分配。
其实是有规律的,只要你理解了这些个基本的原理:
堆空间的话: 操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删 除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样代码中的delete语句才能正确的释放 本内存空间。另外由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。是由new分配的内存,一般速度比较 慢,而且容易产生内存碎片,不过用起来最方便,另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直 接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来 存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵 活,也比较大。

栈空间的话:在Windows下, 栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是固定 的(是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。只要栈的剩余空间大于所申请 空间,系统将为程序提供内存,否则将报异常提示栈溢出。 由系统自动分配,速度较快。但程序员是无法控制的。

ok,头会不会有点小晕,不会的话继续吧:
JVM中的堆和栈
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的 Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译 原理中的活动纪录的概念是差不多的.
从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。
每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程 共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也 就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。

AWT的语义事件和低级事件

AWT事件分为语义事件(高级事件)和低级事件。语义事件是指表达用户行为的事件,例如,按钮单击,下拉列表的选择;所以经常使用的ActionEvent是语义事件。(最简单的区分语义事件和低级事件的方法是语义事件的监听器接口只需要实现一个方法即可)。低级事件是指形成事件的事件。例如,单击按钮的时候,必须按下鼠标。此时鼠标事件就是一个低级事件。

24 五月 2008

魔法泡泡-puyo-第三次修正(基本功能已实现)

更新:

1、添加了工具栏按钮的控制
2、添加了游戏帮助说明

待解决的问题:很多,很多,很多。比如界面美化,一些bug的测试,计分规则,代码的优化,内存效率问题的解决,环境的配置的优化(最好用配置文件进行管理),等等……

>
2008-05-24_110310.png



源码下载: 纳米盘 畅易网盘



上海gameloft的面试题上周日就做完了,大概花了10天时间。实际上主要功能一周内就全部实现,剩下的时间主要是一些bug的解决和代码的优化。接下来就等那边的电话了。

13 五月 2008

puyo-魔法泡泡-第二次修改版

更新:

1、解决了临界条件的处理,使得泡泡能够进行堆叠。
2、当泡泡停止下落时,产生新的泡泡进行控制。
3、产生随机的泡泡对位置。
3、解决部分bug。

需要解决的问题:

1、扫描消除算法的实现

2、计分规则的实现

3、代码结构优化

以下是程序界面:

puyo

源码下载:puyo2.7z
puyo2.7z

10 五月 2008

puyo-魔法泡泡-部分源代码下载

收到上海优酷的面试题,实现魔法泡泡的游戏。以下是试题内容:

Purpose:
----------

The goal of this exercise is to develop a JAVA or C/C++ version of Puyo-Puyo, a variation of the Tetris game.

We are interested in seeing your code writing skills, style and logic.
Don't hesitate to comment on your code in order to make it as clear as possible.

Please do not browse the web (or any other source of information) to find the code of this program already developed.

anyway feel free to browse the web to find a playable version of the game for reference.

The purpose of this exercise is purely a skill assessment, and gameplay implementation ability.

Rules of Game:
---------------

* Spheres come down from the top of the game board in pairs. A player can rotate them on their way down.

* Spheres come in 4 different colors: red, blue, green, and yellow

* Linking four spheres of the same color (horizontally, vertically or both(as in tetris)) removes them from the game board, allowing any sphere remaining to drop and fill the vacated space.
This may lead to several possible "chain combos" if additional colors match.

* The game board can be any size you want (we recommend 6x12)

* Note that the pair of spheres are not linked to each other. If one sphere is blocked because there is something underneath, the other one will continue falling (with no player control over it) until it reaches something.

The Zip Files Contain:
* 4 pictures of the spheres
* A screenshot of the game as it should look

as stated before, feel free to browse the web to find a playable version of the game for reference if some rules are unclear.

To Submit:
------------

Please submit a java or C/C++ console application that will open a window and start the game.
You may attach a separate text file that documents player controls and any other comments (as gameplay improvements, bugs not corrected, any comment that will help evaluate your submission...)

important:
- use Eclipse or Visual C++ as your IDE if you need one, DO NONT use JBuilder or C++ Builder
- java or C/C++ console application MUST be available
- it will be better to attach your project files and source code files
- ALL COMMENTS IN ENGLISH

这是我实现的界面:

puyo

因为白天上班,还得备课,所以挤时间,现在基本实现了随机产生一对泡泡,通过多线程键盘控制移动和变形以及自动下落,泡泡的移动全部用二维数组存储,并且每一次移动,下落或者变形都会产生对应的二维数组元素的变换。全部界面通过代码实现,没有借助其他设计工具。
接下来要解决的问题:
1、通过线程实现泡泡对的连续产生,临界条件的判断(即泡泡下落时,什么时候终止下落)
2、因为已经将泡泡和二维数组对应,所以当泡泡达到临界条件式,调用扫描算法进行泡泡的清除(这个过程需要扫描二维数组)。
3、一旦扫描完成,需要从二维数组重新读取图像信息,然后显示到窗体面板中。
4、一些计分规则的实现。
5、代码和工程结构的优化。

这是部分源代码: puyo.7z

puyo.zip

23 四月 2008

Tomcat的安装及目录结构介绍

安装Tomcat之前首先安装JDK环境。Tomcat可以从Apache官方网站下载
(http://jakarta.apache.org/site/binindex.cgi)上下载。对于Windows操作系统,提供了两种安装文件,一种是可安装文件(和我们平常安装软件一下),还有一种是zip压缩包(解压后就可以用,不需要安装)。推荐后者,这样每次重新做系统,就不需要再次安装了。使用解压缩软件将tomcat压缩包解压到相应的本地目录。安装后目录层次如下所示。



各目录的用途如下:

bin:存放启动和关闭Tomcat的脚本文件;
common/lib:存放Tomcat服务器及所有Web应用程序都可以访问的库;
conf:存放Tomcat服务器的各种配置文件,其中包括server.xml、tomcat-user.xml和web.xml等配置文件;
logs:存放Tomcat的日志文件;
server/lib:存放Tomcat服务器运行所需的各种库文件;
server/webapps:存放Tomcat的两个Web应用程序:admin应用程序和manager应用程序;
shared/lib:存放所有Web应用程序都能够访问的库文件;
temp:存放Tomcat运行时产生的临时文件;
webapps:存放你的Web应用程序的目录和文件;
work:Tomcat将JSP生成的Servlet源文件和字节码文件存放到这个目录

注意:common/lib目录、server/lib和shared/lib目录下都可以存放库文件,区别:server/lib目录下的库只能被Tomcat服务器访问;shared/lib目录下的库可以被所有的Web应用程序访问,但不能被Tomcat服务器访问;common/lib目录下的库可以被Tomcat服务器和所有的Web应用程序访问。

此外,对于Web应用程序,在它的WEB-INF目录下,也可以建立lib子目录,在其中存放需要的各种库,这些库只能被当前Web应用程序所访问。

14 四月 2008

JSP页面编码问题处理

今天使用Filter做过滤器,实现的功能如下:对客户端所有请求进行过滤器编码为GB2312,这样就不用每一个页面添加编码设置,简化开发。出现一个小问题:客户端输入用户名和密码,在服务器验证时发现验证无法通过。



表单如下:

<form action="LoginChecker" method="POST">
<table border="1">

<tr>
<td>用户名称:</td>
<td><input type="text" name="userId"></td>
</tr>
<tr>
<td>用户密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td><input type="hidden" name="originalURI" value="${requestScope[originalURI]}"></td>
<td><input type="submit" value="登陆" name="submit"></td>
</tr>
</table>
</form>


过滤器如下:

package com.p268.google;

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
/**
* <p>Example filter that sets the character encoding to be used in parsing the
* incoming request, either unconditionally or only if the client did not
* specify a character encoding. Configuration of this filter is based on
* the following initialization parameters:</p>
* <ul>
* <li><strong>encoding</strong> - The character encoding to be configured
* for this request, either conditionally or unconditionally based on
* the <code>ignore</code> initialization parameter. This parameter
* is required, so there is no default.</li>
* <li><strong>ignore</strong> - If set to "true", any character encoding
* specified by the client is ignored, and the value returned by the
* <code>selectEncoding()</code> method is set. If set to "false,
* <code>selectEncoding()</code> is called <strong>only</strong> if the
* client has not already specified an encoding. By default, this
* parameter is set to "true".</li>
* </ul>
*
* <p>Although this filter can be used unchanged, it is also easy to
* subclass it and make the <code>selectEncoding()</code> method more
* intelligent about what encoding to choose, based on characteristics of
* the incoming request (such as the values of the <code>Accept-Language</code>
* and <code>User-Agent</code> headers, or a value stashed in the current
* user's session.</p>
*
* @author Craig McClanahan
* @version $Revision: 500674 $ $Date: 2007-01-28 00:15:00 +0100 (dim., 28 janv. 2007) $
*/


public class SetCharacterEncodingFilter implements Filter {

// ----------------------------------------------------- Instance Variables
/**
* The default character encoding to set for requests that pass through
* this filter.
*/
protected String encoding = null;
/**
* The filter configuration object we are associated with. If this value
* is null, this filter instance is not currently configured.
*/

protected FilterConfig filterConfig = null;
/**
* Should a character encoding specified by the client be ignored?
*/

protected boolean ignore = true;

// --------------------------------------------------------- Public Methods
/**
* Take this filter out of service.
*/
public void destroy() {

this.encoding = null;
this.filterConfig = null;

}
/**
* Select and set (if specified) the character encoding to be used to
* interpret request parameters for this request.
*
* @param request The servlet request we are processing
* @param result The servlet response we are creating
* @param chain The filter chain we are processing
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet error occurs
*/

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException {

// Conditionally select and set the character encoding to be used
// System.out.println("ignore="+ignore+",encoding="+this.encoding);

if (ignore || (request.getCharacterEncoding() == null)) {
String encoding2 = selectEncoding(request);
System.out.println("encoding="+encoding2);
if (encoding2 != null)
request.setCharacterEncoding(encoding2);
}

// Pass control on to the next filter
chain.doFilter(request, response);

}
/**
* Place this filter into service.
*
* @param filterConfig The filter configuration object
*/

public void init(FilterConfig filterConfig) throws ServletException {

this.filterConfig = filterConfig;
this.encoding = filterConfig.getInitParameter("encoding");
String value = filterConfig.getInitParameter("ignore");
if (value == null)
this.ignore = true;
else if (value.equalsIgnoreCase("true"))
this.ignore = true;
else if (value.equalsIgnoreCase("yes"))
this.ignore = true;
else
this.ignore = false;

}

// ------------------------------------------------------ Protected Methods
/**
* Select an appropriate character encoding to be used, based on the
* characteristics of the current request and/or filter initialization
* parameters. If no character encoding should be set, return
* <code>null</code>.
* <p>
* The default implementation unconditionally returns the value configured
* by the <strong>encoding</strong> initialization parameter for this
* filter.
*
* @param request The servlet request we are processing
*/

protected String selectEncoding(ServletRequest request) {
return (this.encoding);
}
}

用户验证Servlet如下:

/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.p268.google;



import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


/**
*
* @author lonetown
*/
public class LoginChecker extends HttpServlet {

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String userId = request.getParameter("userId");
String password = request.getParameter("password");
String targetURI=request.getParameter("originalURI");

if ((!userId.equals("张三")) || (!password.equals("1234"))) {
throw new ServletException("认证失败!");
}

HttpSession session = request.getSession();
session.setAttribute("passed", "true");
session.setAttribute("userId", userId);
response.sendRedirect(targetURI);
}
}

部署文件配置如下:

<filter>
<filter-name>setCharacterEncoding</filter-name>
<filter-class>com.p268.google.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GB2312</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>setCharacterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


结果出现问题:

snapshot2.png
解决方案:

1、先考虑服务器端是否拿到用户数据。

在Servlet中添加两行代码,分别得到用户名和密码:
System.out.println("userId="+userId);
System.out.println("password="+password);
结果如下:出现同样的500错误;但是在命令行下输出
userId=寮??
password=1234

服务器端能够拿到数据。但是发现userId不是张三,而变成乱码。因此乱码和"张三"比较,肯定不相同。所以问题出在过滤器编码上。

2、检测编码:

在Servlet中,在验证数据前添加

……

String password = request.getParameter("password");
String targetURI=request.getParameter("originalURI");
System.out.println("encoding="+request.getCharacterEncoding());
if ((!userId.equals("张三")) || (!password.equals("1234"))) {
……

运行后,命令行输出

encoding=GB2312
所以编码没有问题,所有的请求都被过滤器过滤,编码为GB2312。

但是为何拿出的数据会变成乱码?

我们知道,当一个用户通过JSP页面发送请求的时候,会经历如下过程:

首先用户访问登陆页面,出现登陆的表单。此时用户会发送请求,而服务器会根据用户的请求找到相应的资源,返回给客户端。也就是说,用户所访问的页面,其内容是由服务器端发送的。当用户提交表单时,此时会再次访问服务器,由于过滤器的配置,服务器对任何请求和响应都会进行过滤编码,所以服务器端将表单内容编码为GB2312。回到登陆页面观察,发现登陆页面的编码格式为:UTF-8。此时问题找到,前台JSP页面编码为UTF-8,而到后台服务器端以GB2312解码,自然会是乱码。所以将前台页面改为GB2312,或者将后台过滤器编码设置为UTF-8试试。结果发现正常,能够正确编码。

总结原因如下:

在服务器端存储的用户数据并不是过滤器所设置的编码格式。过滤器设置的编码格式只在数据进行传递时进行编码。前台页面的编码格式为UTF-8,当用户请求该页面时,客户浏览器以UTF-8形式解码,自然看到正确的登陆页面。当用户提交表单数据时,表单数据是以ISO-8859-1的编码方式进行传递,并不是以UTF-8进行传递。JSP页面所设置的编码,仅仅告诉浏览器以什么方式读取,并不是说明所有数据是以UTF-8编码进行传递的,数据传递时有一个默认的编码:ISO-8859-1。所以无论服务器端发送数据,还是客户端发送数据,默认的编码格式都是ISO-8859-1,然后根据本地编码设置,将该编码以本地编码进行解析。但是有一个前提,就是服务器端编码,客户端解码或者服务器端解码,客户端编码,都必须保证两者编码格式相同。即客户端发送数据时以GB2312编码,然后转换为ISO-8859-1进行传递,当服务器端接收到数据时,此时接受的数据为ISO-8859-1,那么必须以GB2312解码,而不能以UTF-8解码。否则仍然乱码。当然,这个过程仅仅针对数据的发送和响应而言的。但是对于服务器端存储的数据而言,它的编码格式会根据服务器的平台为准,只有当数据需要发送给用户时,才会以相应的编码方式编码。

这样,我的问题就找到了。之所以出现我的问题,就在于客户端和服务器端以及过滤器编码设置不一致导致的。我的前台是UTF-8编码,过滤器编码是GB2312。两者编码格式不一样,但是传送的数据都按照各自的方式读取,自然出现问题。所以我将过滤器编码设置为UTF-8,这样,无论客户是发送数据还是读取数据,服务器和客户端编码都是一致的。

Javascript开发的超级玛丽

http://www.nihilogic.dk/

Java虚拟机垃圾回收机制

原文:http://www.blogjava.net/Jack2007/archive/2008/04/11/192288.html

引言
Java的堆是一个运行时数据区,类的实例(对象)从中分配空间。Java虚拟机(JVM)的堆中储存着正在运行的应用程序所建立的所有对象,这些对 象通过new、newarray、anewarray和multianewarray等指令建立,但是它们不需要程序代码来显式地释放。一般来说,堆的是 由垃圾回收 来负责的,尽管JVM规范并不要求特殊的垃圾回收技术,甚至根本就不需要垃圾回收,但是由于内存的有限性,JVM在实现的时候都有一个由垃圾回收所管理的 堆。垃圾回收是一种动态存储管理技术,它自动地释放不再被程序引用的对象,按照特定的垃圾收集算法来实现资源自动回收的功能。

垃圾收集的意义

在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象;而在Java中,当没有对象引用指向原先分配给某个对 象的内存时,该内存便成为垃圾。JVM的一个系统级线程会自动释放该内存块。垃圾收集意味着程序不再需要的对象是"无用信息",这些信息将被丢弃。当一个 对象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。事实上,除了释放没用的对象,垃圾收集也可以清除内存记录碎片。由于创建对象 和垃圾收集器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,JVM将 整理出的内存分配给新的对象。

垃圾收集能自动释放内存空间,减轻编程的负担。这使Java 虚拟机具有一些优点。首先,它能使编程效率提高。在没有垃圾收集机制的时候,可能要花许多时间来解决一个难懂的存储器问题。在用Java语言编程的时候, 靠垃圾收集机制可大大缩短时间。其次是它保护程序的完整性, 垃圾收集是Java语言安全性策略的一个重要部份。

垃圾收集的一个潜在的缺点是它的开销影响程序性能。Java虚拟机必须追踪运行程序中有用的对象, 而且最终释放没用的对象。这一个过程需要花费处理器的时间。其次垃圾收集算法的不完备性,早先采用的某些垃圾收集算法就不能保证100%收集到所有的废弃 内存。当然随着垃圾收集算法的不断改进以及软硬件运行效率的不断提升,这些问题都可以迎刃而解。

垃圾收集的算法分析

Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾收集算法一般要做2件基本的事情:(1)发现无用信息对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用。

大多数垃圾回收算法使用了根集(root set)这个概念;所谓根集就量正在执行的Java程序可以访问的引用变量的集合(包括局部变量、参数、类变量),程序可以使用引用变量访问对象的属性和 调用对象的方法。垃圾收集首选需要确定从根开始哪些是可达的和哪些是不可达的,从根集可达的对象都是活动对象,它们不能作为垃圾被回收,这也包括从根集间 接可达的对象。而根集通过任意路径不可达的对象符合垃圾收集的条件,应该被回收。下面介绍几个常用的算法。

1、 引用计数法(Reference Counting Collector)

引用计数法是唯一没有使用根集的垃圾回收的法,该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。 当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用),引用 计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。

基于引用计数器的垃圾收集器运行较快,不会长时间中断程序执行,适宜地必须 实时运行的程序。但引用计数器增加了程序执行的开销,因为每次对象赋给新的变量,计数器加1,而每次现有对象出了作用域生,计数器减1。

2、tracing算法(Tracing Collector)

tracing算法是为了解决引用计数法的问题而提出,它使用了根集的概念。基于tracing算法的垃圾收集器从根集开始扫描,识别出哪些对象可 达,哪些对象不可达,并用某种方式标记可达对象,例如对每个可达对象设置一个或多个位。在扫描识别过程中,基于tracing算法的垃圾收集也称为标记和 清除(mark-and-sweep)垃圾收集器.

3、compacting算法(Compacting Collector)

为了解决堆碎片问题,基于tracing的垃圾回收吸收了Compacting算法的思想,在清除的过程中,算法将所有的对象移到堆的一端,堆的另一 端就变成了一个相邻的空闲内存区,收集器会对它移动的所有对象的所有引用进行更新,使得这些引用在新的位置能识别原来 的对象。在基于Compacting算法的收集器的实现中,一般增加句柄和句柄表。
4、copying算法(Coping Collector)

该算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它开始时把堆分成 一个对象 面和多个空闲面, 程序从对象面为对象分配空间,当对象满了,基于coping算法的垃圾 收集就从根集中扫描活动对象,并将每个 活动对象复制到空闲面(使得活动对象所占的内存之间没有空闲洞),这样空闲面变成了对象面,原来的对象面变成了空闲面,程序会在新的对象面中分配内存。

一种典型的基于coping算法的垃圾回收是stop-and-copy算法,它将堆分成对象面和空闲区域面,在对象面与空闲区域面的切换过程中,程序暂停执行。

5、generation算法(Generational Collector)

stop-and-copy垃圾收集器的一个缺陷是收集器必须复制所有的活动对象,这增加了程序等待时间,这是coping算法低效的原因。在程序设计中 有这样的规律:多数对象存在的时间比较短,少数的存在时间比较长。因此,generation算法将堆分成两个或多个,每个子堆作为对象的一代 (generation)。由于多数对象存在的时间比较短,随着程序丢弃不使用的对象,垃圾收集器将从最年轻的子堆中收集这些对象。在分代式的垃圾收集器 运行后,上次运行存活下来的对象移到下一最高代的子堆中,由于老一代的子堆不会经常被回收,因而节省了时间。

6、adaptive算法(Adaptive Collector)

在特定的情况下,一些垃圾收集算法会优于其它算法。基于Adaptive算法的垃圾收集器就是监控当前堆的使用情况,并将选择适当算法的垃圾收集器。
透视Java垃圾回收

1、命令行参数透视垃圾收集器的运行

2、使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java的垃圾回收。在命令行中有一个参数-verbosegc可以查看Java使用的堆内存的情况,它的格式如下:

java -verbosegc classfile

可以看个例子:

class TestGC
{
public static void main(String[] args)
{
new TestGC();
System.gc();
System.runFinalization();
}
}

在这个例子中,一个新的对象被创建,由于它没有使用,所以该对象迅速地变为可达,程序编译后,执行命令: java -verbosegc TestGC 后结果为:

[Full GC 168K->97K(1984K), 0.0253873 secs]

机器的环境为,Windows 2000 + JDK1.3.1,箭头前后的数据168K和97K分别表示垃圾收集GC前后所有存活对象使用的内存容量,说明有168K-97K=71K的对象容量被回 收,括号内的数据1984K为堆内存的总容量,收集所需要的时间是0.0253873秒(这个时间在每次执行的时候会有所不同)。

2、finalize方法透视垃圾收集器的运行

在JVM垃圾收集器收集一个对象之前 ,一般要求程序调用适当的方法释放资源,但在没有明确释放资源的情况下,Java提供了缺省机制来终止化该对象心释放资源,这个方法就是finalize()。它的原型为:

protected void finalize() throws Throwable

在finalize()方法返回之后,对象消失,垃圾收集开始执行。原型中的throws Throwable表示它可以抛出任何类型的异常。

之所以要使用finalize(),是由于有时需要采取与Java的普通方法不同的一种方法,通过分配内存来做一些具有C风格的事情。这主要可以通过 "固有方法"来进行,它是从Java里调用非Java方法的一种方式。C和C++是目前唯一获得固有方法支持的语言。但由于它们能调用通过其他语言编写的 子程序,所以能够有效地调用任何东西。在非Java代码内部,也许能调用C的malloc()系列函数,用它分配存储空间。而且除非调用了free(), 否则存储空间不会得到释放,从而造成内存"漏洞"的出现。当然,free()是一个C和C++函数,所以我们需要在finalize()内部的一个固有方 法中调用它。也就是说我们不能过多地使用finalize(),它并不是进行普通清除工作的理想场所。

在普通的清除工作中,为清除一个对象,那个对象的用户必须在希望进行清除的地点调用一个清除方法。这与C++"破坏器"的概念稍有抵触。在C++中, 所有对象都会破坏(清除)。或者换句话说,所有对象都"应该"破坏。若将C++对象创建成一个本地对象,比如在堆栈中创建(在Java中是不可能的),那 么清除或破坏工作就会在"结束花括号"所代表的、创建这个对象的作用域的末尾进行。若对象是用new创建的(类似于Java),那么当程序员调用C++的 delete命令时(Java没有这个命令),就会调用相应的破坏器。若程序员忘记了,那么永远不会调用破坏器,我们最终得到的将是一个内存"漏洞",另 外还包括对象的其他部分永远不会得到清除。

相反,Java不允许我们创建本地(局部)对象--无论如何都要使用new。但在Java中,没有"delete"命令来释放对象,因为垃圾收集器会 帮助我们自动释放存储空间。所以如果站在比较简化的立场,我们可以说正是由于存在垃圾收集机制,所以Java没有破坏器。然而,随着以后学习的深入,就会 知道垃圾收集器的存在并不能完全消除对破坏器的需要,或者说不能消除对破坏器代表的那种机制的需要(而且绝对不能直接调用finalize(),所以应尽 量避免用它)。若希望执行除释放存储空间之外的其他某种形式的清除工作,仍然必须调用Java中的一个方法。它等价于C++的破坏器,只是没后者方便。

下面这个例子向大家展示了垃圾收集所经历的过程,并对前面的陈述进行了总结。

class Chair {
static boolean gcrun = false;
static boolean f = false;
static int created = 0;
static int finalized = 0;
int i;
Chair() {
i = ++created;
if(created == 47)
System.out.println("Created 47");
}
protected void finalize() {
if(!gcrun) {
gcrun = true;
System.out.println("Beginning to finalize after " + created + " Chairs have been created");
}
if(i == 47) {
System.out.println("Finalizing Chair #47, " +"Setting flag to stop Chair creation");
f = true;
}
finalized++;
if(finalized >= created)
System.out.println("All " + finalized + " finalized");
}
}


public class Garbage {
public static void main(String[] args) {
if(args.length == 0) {
System.err.println("Usage: \n" + "java Garbage before\n or:\n" + "java Garbage after");
return;
}
while(!Chair.f) {
new Chair();
new String("To take up space");
}
System.out.println("After all Chairs have been created:\n" + "total created = " + Chair.created +
", total finalized = " + Chair.finalized);
if(args[0].equals("before")) {
System.out.println("gc():");
System.gc();
System.out.println("runFinalization():");
System.runFinalization();
}
System.out.println("bye!");
if(args[0].equals("after"))
System.runFinalizersOnExit(true);
}
}

上面这个程序创建了许多Chair对象,而且在垃圾收集器开始运行后的某些时候,程序会停止创建Chair。由于垃圾收集器可能在任何时间运行,所以 我们不能准确知道它在何时启动。因此,程序用一个名为gcrun的标记来指出垃圾收集器是否已经开始运行。利用第二个标记f,Chair可告诉main ()它应停止对象的生成。这两个标记都是在finalize()内部设置的,它调用于垃圾收集期间。另两个static变量--created以及 finalized--分别用于跟踪已创建的对象数量以及垃圾收集器已进行完收尾工作的对象数量。最后,每个Chair都有它自己的(非static) int i,所以能跟踪了解它具体的编号是多少。编号为47的Chair进行完收尾工作后,标记会设为true,最终结束Chair对象的创建过程。

关于垃圾收集的几点补充

经过上述的说明,可以发现垃圾回收有以下的几个特点:

(1)垃圾收集发生的不可预知性:由于实现了不同的垃圾收集算法和采用了不同的收集机制,所以它有可能是定时发生,有可能是当出现系统空闲CPU资源时发生,也有可能是和原始的垃圾收集一样,等到内存消耗出现极限时发生,这与垃圾收集器的选择和具体的设置都有关系。

(2)垃圾收集的精确性:主要包括2 个方面:(a)垃圾收集器能够精确标记活着的对象;(b)垃圾收集器能够精确地定位对象之间的引用关系。前者是完全地回收所有废弃对象的前提,否则就可能 造成内存泄漏。而后者则是实现归并和复制等算法的必要条件。所有不可达对象都能够可靠地得到回收,所有对象都能够重新分配,允许对象的复制和对象内存的缩 并,这样就有效地防止内存的支离破碎。

(3)现在有许多种不同的垃圾收集器,每种有其算法且其表现各异,既有当垃圾收集开始时就停止应用程序的运行,又有当垃圾收集开始时也允许应用程序的线程运行,还有在同一时间垃圾收集多线程运行。

(4)垃圾收集的实现和具体的JVM 以及JVM的内存模型有非常紧密的关系。不同的JVM 可能采用不同的垃圾收集,而JVM 的内存模型决定着该JVM可以采用哪些类型垃圾收集。现在,HotSpot 系列JVM中的内存系统都采用先进的面向对象的框架设计,这使得该系列JVM都可以采用最先进的垃圾收集。

(5)随着技术的发展,现代垃圾收集技术提供许多可选的垃圾收集器,而且在配置每种收集器的时候又可以设置不同的参数,这就使得根据不同的应用环境获得最优的应用性能成为可能。

针对以上特点,我们在使用的时候要注意:

(1)不要试图去假定垃圾收集发生的时间,这一切都是未知的。比如,方法中的一个临时对象在方法调用完毕后就变成了无用对象,这个时候它的内存就可以被释放。

(2)Java中提供了一些和垃圾收集打交道的类,而且提供了一种强行执行垃圾收集的方法--调用System.gc(),但这同样是个不确定的方 法。Java 中并不保证每次调用该方法就一定能够启动垃圾收集,它只不过会向JVM发出这样一个申请,到底是否真正执行垃圾收集,一切都是个未知数。

(3)挑选适合自己的垃圾收集器。一般来说,如果系统没有特殊和苛刻的性能要求,可以采用JVM的缺省选项。否则可以考虑使用有针对性的垃圾收集器, 比如增量收集器就比较适合实时性要求较高的系统之中。系统具有较高的配置,有比较多的闲置资源,可以考虑使用并行标记/清除收集器。

(4)关键的也是难把握的问题是内存泄漏。良好的编程习惯和严谨的编程态度永远是最重要的,不要让自己的一个小错误导致内存出现大漏洞。

(5)尽早释放无用对象的引用。大多数程序员在使用临时变量的时候,都是让引用变量在退出活动域(scope)后,自动设置为null,暗示垃圾收集器来收集该对象,还必须注意该引用的对象是否被监听,如果有,则要去掉监听器,然后再赋空值。

结束语

一般来说,Java开发人员可以不重视JVM中堆内存的分配和垃圾处理收集,但是,充分理解Java的这一特性可以让我们更有效地利用资源。同时要注 意finalize()方法是Java的缺省机制,有时为确保对象资源的明确释放,可以编写自己的finalize方法。

25 三月 2008

I18N格式标签库总结

I18N标签库主要进行国际格式化支持,此功能可以对一个特定的语言请求作出合适的响应。如果要在JSP页面中使用I18N标签库,如下:

<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>

I18N标签库主要包含:国际化、消息和数字日期格式化。

一、国际化(<fmt:setLocale>和<fmt:requestEncoding>)

  1. <fmt:setLocale>:用来设定用户的语言区域。
  2. <fmt:requestEncoding>:用来设定字符串编码,功能和ServletRequest.setCharacterEncoding()相同。

二、消息(<fmt:message>、<fmt:param>、<fmt:bundle>和<fmt:setBundle>)

  1. <fmt:message>:从指定的资源中把特定关键字中的值抓取出来。
  2. <fmt:param>:从数据源抓出内容时,进行动态参数的设定。
  3. <fmt:bundle>:用来设定本体内容的数据来源。
  4. <fmt:setBundle>:用来设定默认的数据来源,也可以将其设定到属性范围中。

三、数字、日期格式化(<fmt:formatNumber>、<fmt:parseNumber>、<fmt:formatDate>、
<fmt:parseDate>、<fmt:setTimeZone>和<fmt:timeZone>)

  1. <fmt:formatNumber>:根据设定的区域将数字改为适当的形式。
  2. <fmt:parseNumber>:将字符串类型的数字、货币或百分比,转化为梳子类型。
  3. <fmt:formatDate>:用来格式化日期和时间。
  4. <fmt:parseDate>:将字符串类型的时间或日期都转换为日期时间类型。
  5. <fmt:setTimeZone>:用来设定默认时区或是将时区存储至属性范围中。
  6. <fmt:timeZone>:设定暂时时区。

24 三月 2008

pageContext、request、session和application的区别

我们经常会将pageContext、request、session和application混为一谈,因为它们都可以通过setAttribute()和getAttribute()来设定或读取属性。但它们之间是有别的,它们最大的区别在于使用范围。

pageContext对象的范围只适用于当前页面范围,即超过这个页面就不能够使用了。所以使用pageContext对象向其它页面传递参数是不可能的。

request对象的范围是指在一JSP网页发出请求到另一个JSP网页之间,随后这个属性就失效。

session的作用范围为一段用户持续和服务器所连接的时间,但与服务器断线后,这个属性就无效。比如断网或者关闭浏览器。

application的范围在服务器一开始执行服务,到服务器关闭为止。它的范围最大,生存周期最长。

22 三月 2008

SQL标签库总结

SQL标签库提供了访问关系数据库的能力,提供了增、删、改、查以及事务处理功能。在JSP页面中使用SQL标签库,如下:
<%@ taglib uri="http://java.sun.com/jsp/jstl/sql" prefix="sql" %>

SQL标签库包含<sql:setDataSource>、<sql:query>、<sql:param>、<sql:dateParam>、<sql:update>、<sql:transaction>

  1. <sql:setDataSource>用于设置数据源。

  2. <sql:query>用于对数据库进行查询。

  3. <sql:param>用于设置SQL语句中标记为(?)的参数值,类似于PreparedStatement中的setXXX()方法的作用。

  4. <sql:dateParam>用java.util.Date类型的值设置SQL语句中标签为(?)的参数。

  5. <sql:update>用于执行增、删、改操作,还可以执行DDL语句。

  6. <sql:transaction>用于为<sql:query>和<sql:update>子标签建立事务处理上下文。

20 三月 2008

Core标签库总结

Core标签库作用:主要包含了一般用途的标签、条件标签、迭代标签和URL相关的标签。在JSP中使用Core标签库,使用taglib指令如下:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

一、表达式操作(<c:out>、<c:set>、<c:remove>和<c:catch>)

  1. <c:out>:用于计算一个表达式并将结果输出到JspWriter对象。它的功能类似于JSP中的表达式<%=expression%>,或者EL表达式${el-expression}。
  2. <c:set>:用来将变量存储至JSP范围中或是JavaBean的属性中。
  3. <c:remove>:用来移除变量。
  4. <c:catch>:用来处理错误异常,并且将异常信息存储起来。

二、流程控制(<c:if>、<c:chooise>、<c:when>和<c:otherwise>)

  1. <c:if>:功能类似于程序语言中的if语句。
  2. <c:choose>:只用作<c:when>和<c:otherwise>的父标签。
  3. <c:when>:类似于程序语言中的when。
  4. <c:otherwise>:当所有<c:when>的条件没有成立时,则执行这个标签的本体内容。

三、迭代操作<c:forEach>和<c:forTokens>

  1. <c:forEach>:循环控制,可以将集合Collection中的成员循序访问一遍。当条件符合时,会持续重复执行本体内容。支持Java中的集合框架。
  2. <c:forTokens>:用来浏览字符串中所有成员,其成员有自定义符合所分割。

四、URL操作<c:import>、<c:redirect>和<c:url>

  1. <c:import>:用来导入其它文件或者JSP页面。和<jsp:include>的区别在于后者只能包含和自己同一个web应用程序下的文件;前者可以包含不同web应用程序下的文件。
  2. <c:redirect>:将客户的请求从一个JSP网页导向其它文件。
  3. <c:url>用来产生一个URL。

Powered by Zoundry Raven

16 三月 2008

类和接口

从网上看到的

把接口比喻成寄生虫。寄生虫不能独自存活。接口也是,必须要靠类来实现它。

如果再把类看成人。每个人只能有一个父亲(后爹不提),但是身上可以有多个寄生虫。

interface InterfaceA{}
interface InterfaceB{}
interface InterfaceC{}

class ClassA{}
class ClassB{}
class ClassC{}

//有一个父亲,身上没有寄生虫。是正确的
class SubClassA extends ClassA {}

//有一个父亲,寄生多个寄生虫。正确的
class SubClassA extends ClassA implements InterfaceA,InterfaceB,InterfaceC{}

//有一个以上的父亲,身上没有寄生虫。错误的
class SubClassA extends ClassA,ClassB,ClassC{}

//有一个以上的父亲,身上有很多寄生虫。错误的
class SubClassA extends ClassA,ClassB,ClassC implements InterfaceA,InterfaceB,InterfaceC{}

09 三月 2008

实现SimpleTag接口的标签处理器的生命周期

 


SimpleTag


1、容器在创建标签处理器实例后,调用setJspContext()方法设置JspContext。如果该标签没有被嵌套,则不会调用setParent()方法;如果被嵌套,则调用setParent()方法设置它的父标签。这和传统标签处理过程不一样,传统标签处理中,无论标签是否被嵌套,setParent()方法都会被调用。


2、调用标签处理器setXXX()方法,设置标签的属性。如果没有定义属性,则没有该步骤。


3、如果存在标签体,容器则调用setJspBody()方法,设置标签体。如果标签体不存在,则没有该步骤。


4、容器调用doTag()方法,这个方法中,完成标签处理器的主要逻辑。


简单标签的标签处理器实例不会被缓存而重复使用,每当遇到标签时,容器就会创建一个新的标签处理器的实例。

Microsoft SQL Server 2005 安装失败处理

Microsoft SQL Server 2005 安装失败处理


作者:郑佐

日期:2007-5-29

前段时间笔者在Microsoft Virtual PC 2007和一台笔记本电脑上安装MSDN订阅下载的


SQL Server 2005 企业版时出现问题。


安装环境为windows Server 2003 企业版 + sp1,另外一个系统是带sp2的,


安装用户使用超级管理员(Administrator)。


在安装"Integration Services"步骤时出现安装错误,提示"错误: -2146233087"。


看上去是安装问题,不过解决的过程还是值得一番思考。


通过本文笔者认为可为碰到类似问题的朋友提供一种解决思路,提高查找问题技巧,


故记录到 BLOG


安装过程详细错误记录如下:


标题: Microsoft SQL Server 2005 安装程序


------------------------------


无法在 COM+ 目录中安装和配置程序集


C:\Program Files\Microsoft SQL Server\90\DTS\Tasks\Microsoft.SqlServer.MSMQTask.dll。


错误: -2146233087


错误消息: Unknown error 0x80131501


错误说明: 要执行此任务,您必须具有管理凭据。请与您的系统管理员联系以获得帮助。


有关帮助信息, 请单击


通过帮助信息的超链接页面,提示"需要为MS DTC服务配置 Network Service帐户运行",


不过我的MS DTC本来就是在Network Service帐户下运行。


通过百度和Google搜索了一下,有不少朋友碰到类似错误消息,不过没有得到好的解决方式,


只好自己寻找线索进行尝试。


可以肯定的是问题应该出现在访问权限,而这方面常见的问题一般都是对文件系统目录和系统


注册表的访问。



在微软的 客户帮助和支持网站 搜索SQL Server的相关安装信息。有一篇文章提到设置"C:\WINDOWS\Registration"的权限,确认该目录以及目录下的clb和 crmlog文件
有完全的读写权限,

再次安装SQL Server 2005在"Integration Services"步骤还是提示失败。在安装过程中
选择服务帐户时使用内置系统帐户或使用域用户帐户都一样。


看来问题没有这么简单,笔者在另外一个系统中安装时启用RegMon进行注册表操作监视,
过滤后只监视"msiexec.exe:484"进程,在报以上面提到的错误后保存监视日志到文件。


图(1)



通过监控日志查看失败的情况。由于没有仔细查找,在后来通过微软SQL Server组的技术支持
工程师才想到问题早应该解决。


以下是我事后再次查看RegMon Trace发现的问题故障点,通过下面的日志看出使用本机
Administrator访问注册表HKCR\ Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.
DataFileMessage项时被拒绝。

11305 32.35072327 msiexec.exe:484 OpenKey HKCR\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.DataFileMessage


ACCESS DENIED NBCN-ZZ\Administrator


11306 32.36096954 msiexec.exe:484 QueryKey HKCU\Software\Classes SUCCESS


Name: \REGISTRY\USER\S-1-5-21-730333180-4162487032-3015738926-500_CLASSES



11307 32.36102295 msiexec.exe:484 OpenKey HKCU\Software\Classes\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.
DataFileMessage

NOT FOUND


11308 32.36108017 msiexec.exe:484 OpenKey HKCR SUCCESS


11309 32.36190796 msiexec.exe:484 CreateKey HKCR\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.DataFileMessage


ACCESS DENIED NBCN-ZZ\Administrator


11310 32.36222076 msiexec.exe:484 CreateKey HKCR\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.DataFileMessage


ACCESS DENIED NBCN-ZZ\Administrator


由于粗心结果又多做了很多工作,查看系统的"组件服务"配置是否有问题,但是没有发现什么异常。



在再次出现安装问题后,这次就直接点击"忽略"按钮继续安
装SQLServer的其他部分,安装结束后,提示安装成功完成。


运行"dcomcnfg",打开系统组件服务。展开控制台根目录->组件服务->计算机->我的电脑
->COM+应用程序节点,没有发现Microsoft.SqlServer.MSMQTask节点,可见该COM+应用
程序没有安装上。


再次运行SQL Server 2005安装程序进行添加删除安装,提示安装成功,在"组件服务"中看到"Microsoft.SqlServer.MSMQTask"。看来问题解决了,但是那时还不知道问题出在哪里。
卸载SQL Server 2005,发现"Microsoft.SqlServer.MSMQTask"并没有卸载,测试只安装
SQL Server 2005的Integration Services服务。结果安装失败,选择"中止"取消安装,再次
确认存在"Microsoft.SqlServer.MSMQTask"服务。


接下去笔者把安装过程中出现的错误发送给微软,请SQL Server 组的技术支持工程师帮助解决。
在工程师查找问题过程中,虽然许多检查点我都做过,不过他查找分析解决问题的思路清晰,
以下我列了大概几个步骤。

一.MSDTC运行帐户问题



确认MSDTC (Microsoft Distributed Transaction Coordinator)服务正在运行,并且其启动帐
户是NT AUTHORITY\Network Service"。按照以下步骤来检查:

1. 点击"开始"->"运行"->services.msc


2. 在服务列表中找到Distributed Transaction Coordinator,双击以编辑其属性



3. 在属性窗口切换至登录选项卡,确认其启动帐号为"NT AUTHORITY\Network Service"
(密码为空即可)

4. 启动DTC服务再尝试安装SQL Server 2005


结果:这个步骤我已经尝试过,不是这个问题。


二.分析SQL Server 2005安装日志



将C:\Program Files\Microsoft SQL Server\90\Setup Bootstrap\LOG\目录下所有的文件
(包括Summary.txt和Files子目录下的所有文件)发送给微软技术支持工程师进行分析。

结果:这个步骤我没有做过,不过微软工程师好像也没有发现线索。


三.COPY安装程序到硬盘安装尝试



建立 D:\SQLServer2005 文件夹, 创建以下两个子文件夹:Servers和Tools。Serviers
文件夹拷贝SQL Server2005的第一张安装盘,包含所有要安装主 SQL Server 2005 组件。
Tools文件夹拷贝SQL Server 2005的第二张盘,包含为 SQL Server 2005 联机丛书和工
具组件。运行D:\SQLServer2005\Servers 文件夹下的Setup.exe,启动 SQL Server 2005
安装程序。

结果:以上方式安装过程中还是碰到同样的问题。


四.重新安装 COM+应用程序


1. 从控制面板的"添加或删除程序"中,单击"添加/删除 Windows 组件"。



2 .在"Windows 组件向导"中,不对选择做任何更改,单击"下一步"。一直单击以完成向导,
然后再次运行 SQL Server 2005 安装程序。

结果:问题没有解决。


通过以上分析微软的工程师总结如下:



目前还不确定引起这个错误的原因,并且在安装日志中也没有找到更多的有用信息。可能引起
此类错误的原因有以下几种:

1. COM+ 的配置问题。


2. 帐号权限问题。



3. 有一些安装失败的Microsoft.SqlServer.MSMQTask.dll情况出现在测试版中,或者是由于未
完全删除的测试版组件而导致安装失败。

4. 除此之外还有一些类似的问题都是与64位操作系统或者Cluster环境有关。



我认为以上的一些原因可以排除,看来又是Live Meeting的时候了,和微软工程师约定时间联
机查找问题。

五.手工注册Microsoft.SqlServer.MSMQTask.dll


使用 regsvcs.exe 应用程序工具注册.NET服务


使用cmd执行过程如下:


C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>


regsvcs.exe "C:\Program Files\Microsoft SQL Server\90\DTS\


Tasks\Microsoft.SqlServer.MSMQTask.dll"


Microsoft(R) .NET Framework 服务安装实用工具版本 2.0.50727.42


Copyright (c) Microsoft Corporation. All rights reserved.


出现下列安装错误:


1: 要执行此任务,您必须具有管理凭据。请与您的系统管理员联系以获得帮助。


2: 注册程序集"Microsoft.SqlServer.MSMQTask, Version=9.0.242.0, Culture=neutral,


PublicKeyToken=89845dcd8080cc91 "失败。


3: 对注册表项"HKEY_CLASSES_ROOT\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.


DataFileMessage "的访问被拒绝。



分析:这看起来是由于当前Windows用户在该注册表键值上没有足够的权限造成的。经过研究类似案例,
在某些情况下,即使是Administrators组的用户也可能出现对这些键值没有Full Control的情况。需要确
认一下在这些注册表值上,Administrators组用户是否有Full Control。在注册表中右键点击注册表值->
选择权限来查看及改变该值上的权限设置。如果权限设置都正确的话,可以试着抓一个RegMon trace来
看到底是不是注册表键值的权限问题导致这个错误。

看来问题终于找到了。


六.监控注册表



1. 运行 RegMon 工具 ,通过菜单Options->Filter中设置Include list,把Regsvcs.exe加入
Include list中。

2. 执行regsvcs.exe "C:\Program Files\Microsoft SQL Server\90\DTS\Tasks\Microsoft.SqlServer.MSMQTask.dll"


3. 在Regsvcs.exe执行失败停止后,保存RegMon生成的log。


分析RegMon Trace。定位问题如下:


5484 17.98829079 RegSvcs.exe:3404 OpenKey HKCR\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.MessageQueueTask


ACCESS DENIED NBCN-ZZ\Administrator


5488 17.98927116 RegSvcs.exe:3404 CreateKey HKCR\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.MessageQueueTask


ACCESS DENIED NBCN-ZZ\Administrator


5489 17.98955727 RegSvcs.exe:3404 CreateKey HKCR\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.MessageQueueTask


ACCESS DENIED NBCN-ZZ\Administrator


6431 19.89454460 RegSvcs.exe:3404 OpenKey HKCR\Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.MessageQueueTask


ACCESS DENIED NBCN-ZZ\Administrator


以上其实就是我一开始使用RegMon 时应该发现的问题,结果到现在才发现。


从这几条记录来看,问题是对注册表值HKCR\ Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.MessageQueueTask的权限问题。



而 RegSvcs进程在遇到这些权限问题之后,并没有马上终止,还有一些别的注册表活动。
我查看了注册表

HKEY_CLASSES_ROOT\ Microsoft.SqlServer.Dts.Tasks.MessageQueueTask.



DataFileMessage 项的权限,Administrators组没有"完全控制"和"读取"权限,对其进行授权。
重新运行注册,结果又报另外的注册表项没有权限,对其进行添加,直到注册成功,以下是输出结果。

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>


regsvcs.exe "C:\Program Files\Microsoft SQL Server\90\DTS\Tasks\


Microsoft.SqlServer.MSMQTask.dll"


Microsoft(R) .NET Framework 服务安装实用工具版本 2.0.50727.42


Copyright (c) Microsoft Corporation. All rights reserved.


正在自动将"Microsoft.SqlServer.ManagedDTS, Version=9.0.242.0, Culture=neutral,


PublicKeyToken=89845dcd8080cc91 "导出为"C:\WINDOWS\assembly\GAC_MSIL\


Microsoft. SqlServer.ManagedDTS\9.0.242.0__89845dcd8080cc91\


Microsoft.SqlServer.ManagedDTS. tlb "。


警告: 程序集未声明 ApplicationAccessControl 属性。默认情况下启用应用程序安全性。


已安装的程序集:


程序集: C:\Program Files\Microsoft SQL Server\90\DTS\Tasks\Microsoft.Sql


Server.MSMQTask.dll


应用程序: Microsoft.SqlServer.MSMQTask


TypeLib: C:\Program Files\Microsoft SQL Server\90\DTS\Tasks\Microsoft.Sq


lServer.MSMQTask.tlb


C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>


修改授权的相关注册表项如下:


HKEY_CLASSES_ROOT\Microsoft.SqlServer.Dts.Tasks.


MessageQueueTask.MessageQueueTask


HKEY_CLASSES_ROOT\Microsoft.SqlServer.Dts.Tasks.


MessageQueueTask.EncryptionAlgorithmWrapper


HKEY_CLASSES_ROOT\Microsoft.SqlServer.Dts.Tasks.


MessageQueueTask.ServCompMQTask


HKEY_CLASSES_ROOT\Microsoft.SqlServer.Dts.Tasks.


MessageQueueTask.Formatter



再次运行"dcomcnfg",打开系统组件服务。展开控制台根目录->组件服务->计算机->我的电脑
->COM+应用程序节点,发现Microsoft.SqlServer.MSMQTask节点,可见该COM+服务已经安装上。

由于处理该问题不急,笔者和微软技术支持工程师断断续续就该问题差不多花了一个多星期。


Del.icio.us : ,

06 三月 2008

50多种网页设计技巧

1. oncontextmenu="window.event.returnvalue=false" 将彻底屏蔽鼠标右键
<table border oncontextmenu=return(false)><td>no</table>
可用于table
2. <body onselectstart="return false">
取消选取、防止复制
3. onpaste="return false"
不准粘贴
4. oncopy="return false;" oncut="return false;"
防止复制
5. <link rel="shortcut icon" href="favicon.ico"> ie
地址栏前换成自己的图标
6. <link rel="bookmark" href="favicon.ico">
可以在收藏夹中显示出你的图标
7. <input style="ime-mode:disabled">
关闭输入法
8.
永远都会带着框架
<script language="javascript"><!--
if (window == top)top.location.href="frames.htm"; //frames.htm
为框架网页
// --></script>
9.
防止被人frame
<script language=javascript><!--
if (top.location != self.location)top.location=self.location;
// --></script>
10.
网页将不能被另存为
<noscript><iframe src=*.html></iframe></noscript>
11. <input type=button value=
查看网页源代码
onclick="window.location = "view-source:"+ "http://www.pconline.com.cn"">

网上收集的。更多的可以从这里下载:下载资料1
下载资料2

23 二月 2008

实现Tag接口的标签处理器的生命周期

Tag

1、容器在创建标签处理器的实例后,调用setPageContext()方法设置标签的页面上下文,然后调用setParent()方法设置这个标签的父标签,如果没有父标签,则设置为null。

2、调用标签处理器的setXXX()方法,设置标签的属性。如果没有定义属性,则没有这个步骤。

3、调用doStartTag()方法,该方法可以返回EVAL_BODY_INCLUDE或者SKIP_BODY。如果返回EVAL_BODY_INCLUDE,则将标签体输出到当前的输出流中;如果返回SKIP_BODY ,则忽略标签体。

4、调用doEndTag()方法,该方法可以返回EVAL_PAGE或者SKIP_PAGE。如果返回前者,则执行JSP页面的余下部分;如果返回后者,则忽略JSP页面的余下部分。

5、容器会缓存标签处理器的实例,一旦遇到同样的标签,则重复使用缓存的标签处理器实例。

6、当需要释放标签处理器实例时,release()方法才被调用。

实现IterationTag接口的标签处理器的生命周期

IterationTag

1、容器在创建标签处理器的实例后,调用setPageContext()方法设置标签的页面上下文,然后调用setParent()方法设置该标签的父标签,如果没有父标签,则设置为null。

2、调用标签处理器的setXXX()方法,设置标签的属性。如果没有定义属性,则没有该步骤。

3、调用doStartTag()方法,该方法可以返回EVAL_BODY_INCLUDE或者SKIP_BODY。如果返回EVAL_BODY_INCLUDE,则执行标签体;如果返回SKIP_BODY,则忽略标签体。

4、执行完标签体之后,doAfterBody()方法被调用,该方法可以返回EVAL_BODY_AGAIN或者SKIP_BODY。如果返回EVAL_BODY_AGAIN ,则重复执行标签体。如果返回 SKIP_BODY,则不再执行标签体。注意,在调用该方法之前,标签体已经被执行了一遍,如果要忽略标签体,需要在doStartTag()方法中返回SKIP_BODY

5、调用doEndTag()方法,该方法返回EVAL_PAGE 或者 SKIP_PAGE。如果返回EVAL_PAGE,则执行JSP页面的余下部分;如果返回SKIP_PAGE,则忽略JSP页面的剩余部分。

6、 容器会缓存标签处理器实例,一旦遇到同样的标签,则重复使用缓存的标签处理器实例。

7、当需要释放标签处理器实例时,release()方法才被调用。

实现Tag接口的标签处理器的生命周期

Tag

1、容器在创建标签处理器的实例后,调用setPageContext()方法设置标签的页面上下文,然后调用setParent()方法设置这个标签的父标签,如果没有父标签,则设置为null。

2、调用标签处理器的setXXX()方法,设置标签的属性。如果没有定义属性,则没有这个步骤。

3、调用doStartTag()方法,该方法可以返回EVAL_BODY_INCLUDE或者SKIP_BODY。如果返回EVAL_BODY_INCLUDE,则将标签体输出到当前的输出流中;如果返回SKIP_BODY ,则忽略标签体。

4、调用doEndTag()方法,该方法可以返回EVAL_PAGE或者SKIP_PAGE。如果返回前者,则执行JSP页面的余下部分;如果返回后者,则忽略JSP页面的余下部分。

5、容器会缓存标签处理器的实例,一旦遇到同样的标签,则重复使用缓存的标签处理器实例。

6、当需要释放标签处理器实例时,release()方法才被调用。

09 二月 2008

动作元素include和include指令的区别

初学JSP,我们可能对<jsp:include>元素和include指令有所混淆,认为两者是一样的。虽然从表象上来看,两者的效果差不多,但是实质上是有区别的。





  1. include指令所包含的对象是静态对象,<jsp:include>动作包含的对象是动态对象。所谓的静态对象和动态对象并不是指静态html页面和动态页面。所谓的静态和动态指的是:include指令将JSP页面作为静态对象,将页面的内容(可以是文本或者代码)在include指令的位置处包含进来,这个过程发生在JSP容器管理JSP页面生命周期的转换阶段。而<jsp:include>动作包含的JSP页面作为动态对象,在请求处理期间,将请求发送给改对象,然后将最后处理的结果重新包含在当前页面的请求响应中,这个过程发生在JSP页面生命周期的执行阶段。




  2. include指令的file属性所给出的路径是相对于当前文件的,而<jsp:include>动作的page属性所指的路径是相对于当前页面的。




当使用include指令包含某个资源时,资源路径的查找是在JSP页面的转换期间发生的,一旦找到该资源(文本、代码或者JSP页面),它的内容就会在include指令的位置处被包含进来,成为一个整体,然后转换为Servlet源文件。而采用<jsp:include>动作包含资源时,相对路径的查找是在处理阶段完成的,当前页面和被包含的资源是相互独立的个体,当前页面将请求发送给被包含的资源,被包含的资源对请求处理的结果作为当前页面对请求响应的一部分发送给客户端。





  1. 两者发生的时间不同。include指令发生在转换阶段,后者发生在处理阶段。



Tomcat数据库连接池简单配置

一般情况下,我们要建立数据库连接,首先需要调用Class.forName()加载数据库驱动,然后调用驱动管理器建立数据库连接对象(即Connection对象)。


但是这种方式的数据库连接存在一个很大的问题。当访问量增多时,每一次不同用户的连接都需要经过一系列繁琐的步骤,最后访问结束,还需要关闭结果集,语句对象,连接对象。比较麻烦,而且这样的访问浪费很多时间。


在大型Web应用中,Servlet容器可能同时会接收到多个客户端的上千个请求,如何快速的对每个请求进行响应和服务十分重要。如果采用一个请求一个请求的处理,会导致响应时间非常迟钝。一种较好的解决方案就是采用多线程并发处理每个请求。


Servlet容器维护了一个线程池来服务请求。线程池实际上是等待执行代码的一组线程,这些线程我们称之为工作者线程。当Servlet容器接收到一个访问请求时,Servlet容器从线程池中选取一个工作者线程,将请求传递给该线程,然后由这个线程执行Servletservice()方法。如果当这个线程正在执行,而此时容器又接收到另一个请求,那么容器将会在线程池中选择另一个线程来对新的请求进行服务。这就是数据库连接池的最基本的作用。


显然,通过数据库连接池,能够很大程度上提高web容器的效率。


下面,我们就简单实现一个tomcat连接池的应用。


环境:jdk6.0tomcat5.5MySQL5.0 myeclipse6.0 Windows XP Pro


第一步:基本工作。


建立数据库test,建立表stu,两个字段,一个是学号(整型),一个是姓名(字符串),在myeclipse建立一个web工程test(为了省事,所以都起名为test),使用tomcat5.5作为web容器。



第二步:编写数据库访问代码(读取表stu中的记录)

代码

javax.sql包中,定义了DataSource接口,给我们提供了通过连接池建立数据库连接的方式。DataSource接口由驱动程序供应商来实现。利用DataSource建立数据库连接,不需要在客户程序中加载JDBC驱动。在程序中,通过向一个JNDIJava Naming and Directory)服务器查询来得到DataSource对象,然后调用DataSource对象的getConnection()方法来建立数据库的连接。以上代码就是描述的这个过程。


当访问完数据库之后,就可以调用close()方法结束数据连接。在普通的数据库访问程序中,客户程序得到的连接对象是物理连接,调用连接对象的close()方法将关闭连接,而采用线程池技术,客户程序得到的连接对象是连接池中物理连接的一个句柄,调用连接对象的close()方法之后,物理连接并没有关闭,只是删除了客户程序中连接对象和连接池中连接对象之间的联系。


第三步:在Tomcat中配置JDBC数据源。


JDBC数据源配置放到当前Web应用程序运行目录的配置文件中,如果当前你的工程名为“test”,你可以将你的Web应用程序配置文件起名为“test.xml”,编辑%CATALINA%/conf/Catalina/localhost/test.xml。内容如下:


 


部署描述符配置

在配置文件中Resource元素中,指定的JNDI名称为test。这个名称不一定非要和Context元素的名字一样,可以任起。


第四步:工程部署,运行。


结果如下:


运行结果

我们成功的以数据源的方式读取了数据库中的数据。

Session和Cookie的学习

Session属于服务器端技术,在服务器端创建,其实质通过散列表来存储信息。例如HashMap对象存储属性名和属性值。


CookieNetscape公司发明的,它也用来跟踪用户会话。但是Cookies是由服务器端发送给客户端的信息片段,存储在客户端浏览器的内存或者客户端硬盘上,在客户请求中发回。


SessionCookie的最大区别在于:前者在服务器端保存,而后者在客户端保存。


为了跟踪用户会话,服务器端在创建Session后,需要将Session ID交给客户端,在客户端下次请求时,将这个ID随请求一起发送回来。可以采用Cookie(这种保存Session IDCookie我们称之为会话Cookie)URL重写的方式,将Session ID发送给客户端。


Servlet规范中,会话跟踪的Cookie的名字必须是JSESSIONID,它通常保存在浏览器的内存中。浏览器内存中的会话Cookie在关闭浏览器的时候会自动删除,不能在多个浏览器进程之间共享。而如果我们自己创建一个Cookie实例,则可以保存Cookie信息,在浏览器关闭后,再次打开,仍然存在,而且可以在多个浏览器进程间共享。


但是要注意,虽然每次打开一个新的浏览器进程,会发现开始一个新的会话,但是并不是意味着上次的Session消失了。主要是因为会话Cookie保存在浏览器内存中,当浏览器关闭时,会话Cookie被删除了,从而导致下一次浏览器请求中,没有相应的Session ID,从而导致服务器创建新的会话。

JDBC介绍

JDBC介绍


数据库现在作为各种应用和系统的主要数据源之一,它的作用已经越来越重要。为了访问数据库中的数据,在Windows平台下,提供了统一的数据库访问方式,微软推出了ODBC(开放式数据库连接),并提供了ODBC API,使用者在程序中只要调用ODBC API ,由ODBC驱动程序将调用请求转换为对特定数据库的调用请求。为了在Java语言中提供对数据库访问的支持,Sun公司于96年推出了JDBCJava数据库连接)。


JDBC描述了一组访问关系数据库的标准Java类库。我们通过使用这些类库,连接到关系数据库,通过SQL语句,对数据进行处理。JDBC不但提供了访问关系数据库的标准API,还为数据库厂商提供了一套标准的规范,数据库厂商只要遵循这套规范,就可以很灵活的为自己的数据库产品提供JDBC驱动支持,这些驱动程序可以是Java程序访问数据库的效率大大提高。


JDBC的驱动程序可以分为四类,分别是:





  • JDBC-ODBC




  • 部分本地API,部分Java驱动程序




  • JDBC网络纯Java驱动程序




  • 本地协议Java纯驱动程序




 





  1. JDBCODBC





因为微软推出的ODBCJDBC时间要早,因此绝大部分数据库都可以通过ODBC来访问,因此Sun公司推出JDBC的时候,为了支持更多的数据库,提供了JDBC-ODBC桥。JDBC-ODBC本身就是一个驱动,只不过,我们利用这个驱动,表面上使用的JDBCAPI,而实质上是通过内部调用ODBC区访问数据库。这种桥机制实际上是把JDBC调用转换成ODBC调用,并通过ODBC库把它们发送给ODBC数据源。


通过桥式连接访问数据库,需要经过多层调用,因此利用桥式连接访问数据库的效率比较低。当数据库么有提供JDBC驱动,只有ODBC驱动的情况下,此时才考虑桥式连接。


这种访问要求客户的机器上安装JDBC-ODBC桥驱动、ODBC驱动程序和相应数据库 的本地API。在JDK中,已经提供了JDBC-ODBC桥的实现类。





  1. 部分本地API Java驱动程序


    大部分数据库厂商都提供了与他们的数据库产品进行通信所需要的API,这些API往往用C语言或类似的语言编写,依赖于具体的平台。这一类型的JDBC驱动程序使用Java编写,它调用数据库厂商提供的本地API。当我们在程序中利用JDBC API访问数据库时,JDBC驱动程序将调用请求转换为厂商提供的本地API调用,数据库处理完请求将结果通过这些API返回,进而返回给JDBC驱动程序,JDBC驱动程序将结果转化为JDBC标准形式,再返回给客户程序。





通过这种类型的JDBC驱动程序访问数据库减少了ODBC的调用环节,提高了数据库访问的效率,并且能够充分利用厂商提供的本地API的功能。


这种访问,要求客户的机器上安装本地JDBC驱动程序和特定厂商的本地API





  1. JDBC网络纯Java驱动程序





这种驱动利用作为中间件的应用服务器来访问数据库。应用服务器作为一个到多个数据库的网关,客户端通过它可以连接到不同的数据库服务器。应用服务器通常都有自己的网络协议,Java客户程序通过JDBC驱动程序将JDBC调用发送给应用服务器,应用服务器使用本地驱动程序访问数据库,从而完成请求。





  1. 本地协议的纯Java驱动程序




这种类型的JDBC驱动程序完全用Java编写,通过与数据库建立直接的套接字连接,采用具体于厂商的网络协议把JDBC API调用转换为直接的网络调用。
这种类型的驱动是四种类型驱动中访问数据库效率最高的。不过,因为每个数据库厂商都有自己的协议,因此,访问不同厂商的数据库,需要不同的JDBC驱动。目前,主流的数据库厂商(OracleMicrosoftSybaseMySQL等)都为他们各自的数据库产品提供了这种类型的驱动。