1、源代码包的安装
gzip -d apache_1.3.20.tar.gz (解压)
tar xvf apache_1.3.20.tar (解包)
cd apache_1.3.20
./configure (配置) ----./configure --help(查看configure选项)
make (编译)
make install (安装)
make clean (卸载)
注:典型的源代码包可以这样安装,但不都是这样,如webmin
要执行其目录下./setup.sh进入交互式配置安装
卸载用uninstall程序
具体如果不清楚看要安装的包下的README文件
2、RPM包的安装
RPM软件包的一个例子:
foo-1.0-1.i386.rpm
其中包括软件包的名称(foo),版本号(1.0),发行号(1),和硬件平台(i386)。
(1)安装
# rpm -ivh foo-1.0-1.i386.rpm
foo
#######################
A. 软件包已被安装
# rpm -ivh foo-1.0-1.i386.rpm
foo package foo-1.0-1 is already installed
error: foo-1.0-1.i386.rpm cannot be installed
如果你仍旧要安装该软件包,你可以在命令行上使用--replacepkgs 选项,这将忽略该错误信息。
B. 文件冲突
如果要安装的软件包中有一个文件已在安装其它软件包时安装,会出现以下错误信息:
# rpm -ivh foo-1.0-1.i386.rpm
foo /usr/bin/foo conflicts with file from bar-1.0-1
error: foo-1.0-1.i386.rpm cannot be installed
要想让RPM 忽略该错误信息, 请使用--replacefiles 命令行选项
C.未解决依赖关系
RPM软件包可能依赖于其它软件包, 在安装了特定的软件包之后才能安装该软件包。
# rpm -ivh bar-1.0-1.i386.rpm
failed dependencies:
foo is needed by bar-1.0-1
你必须安装完所依赖的软件包,才能解决这个问题。如果想强制安装 (但是,这样安装后的软件包未必能正常运行),请使用-nodeps 命令行选项。
(2) 卸载
# rpm -e foo
注意:这里使用软件包的名字foo,而不是软件包文件的名字“foo-1.0-1.i386.rpm”.
如果其它软件包依赖于你要卸载的软件包,卸载时则会产生错误信息。如:
# rpm -e foo
removing these packages would break dependencies:
foo is needed by bar-1.0-1
要想RPM忽略该错误信息继续卸载的话 (但是,依赖于该软件包的程序可能无法运行), 请使用-nodeps 命令行选项。
(3)升级
# rpm -Uvh foo-2.0-1.i386.rpm
foo
###############################
当使用旧版本的软件包来升级新版本的软件时,会产生以下错误信息:
# rpm -Uvh foo-1.0-1.i386.rpm
foo package foo-2.0-1 (which is newer) is already installed
error: foo-1.0-1.i386.rpm cannot be installed
要使RPM 坚持这样“升级”,可使用--oldpackage 命令行参数。
(4)查询
# rpm -q foo
foo-2.0-1
软件包指定选项:
-a 查询所有已安装的软件包.
-f <file> 将查询包含有文件<file>的软件包
-p <packagefile>
查询软件包文件名为<packagefile>的软件包
信息选择选项:
-i 显示软件包信息,如描述, 发行号, 尺寸, 构建日期, 安装日期, 平台, 以及其它各类信息。
-l 显示软件包中的文件列表。
-s 显示软件包中所有文件的状态。
-d 显示被标注为文档的文件列表(man 手册, info 手册, README's, etc).
-c 显示被标注为配置文件的文件列表。这些是要在安装完毕以后加以定制的文件(sendmail.cf, passwd, inittab, etc)。
对于那些要显示文件列表的文件,可以增加-v 命令行选项以获得如同 ls -l 格式的输出。
(5)验证
验证软件包是通过比较软件包中安装的文件和软件包中的原始文件信息来进行的。除了其它一些东西,验证主要是比较文件的尺寸, MD5 校验码, 文件权限, 类型, 属主和用户组等。
rpm -V命令用来验证一个软件包,如
rpm -V foo
验证包含特定文件的软件包:
rpm -Vf /bin/vi
验证所有已安装的软件包:
rpm -Va
根据一个RPM来验证某个软件包:
rpm -Vp foo-1.0-1.i386.rpm
如果你担心RPM数据库已被破坏,就可以使用这种方式。如果一切校验均正常将不会产生任何输出。如果有不一致的地方,就会显示出来。
输出格式是8位长字符串,c 用以指配置文件,接着是文件名. 8位字符的每一个用以表示文件与RPM数据库中一种属性的比较结果。“.” (点)表示测试通过。
以下字符表示某种测试的失败:
5 MD5 校验码
S 文件尺寸
L 符号连接
T 文件修改日期
D 设备
U 用户
G 用户组
M 模式e (包括权限和文件类型)
如果有信息输出,应当认真加以考虑,是删除,重新安装,还是修正出现的问题。
(6)RPM应用的几个例子
A. 如你误删了一些文件, 但是不能肯定到底删除了哪些文件。如果你想验证一下整个系统看看都丢失了哪些文件的话,可以键入:
rpm –Va
B.若是一些文件丢失了或已被损坏, 就可以重新安装或先卸载再安装该软件包。如果碰到了一个自己不认识的文件,要想查处它属于哪个软件包,可以输入以下命令:
rpm -qf /usr/X11R6/bin/xjewel
而输出的结果会是:
xjewel-1.6-1
C. 如果发生综合以上两个例子的情况,如文/usr/bin/paste出了问题。你想验证一下拥有该文件的软件包,可又不知道软件包的名字,这时可以简单的键入:
rpm -Vf /usr/bin/paste
这样相应的软件包就会被验证。
D. 如果你想了解一个正在使用的程序的详细信息,可以键入如下命令来获得拥有该程序的软件包中的文档信息:
rpm -qdf /usr/bin/ispell
输出结果为:
/usr/man/man4/ispell.4
/usr/man/man4/english.4
…………………………………………………………
E. 如果你发现了一个新的koules RPM,但是不知道它是什么东西,可以键入如下命令:
rpm -qip koules-1.2-2.i386.rpm
F. 现在你想了解koules RPM 所安装的文件。可以键入:
rpm -qlp koules-1.2-2.i386.rpm
图形管理工具:kpackage
3、shell或java脚本安装
基于图形界面的安装,一般基于SHELL或Java语言编写,主要应用于一些办公软件和制图软件及安装程序,如staroffice、oracle的安装,很容易,和windows一样
LOAD DATA [LOW_PRIORITY] [LOCAL] INFILE 'file_name.txt' [REPLACE | IGNORE]
INTO TABLE tbl_name
[FIELDS
[TERMINATED BY '\t']
[OPTIONALLY] ENCLOSED BY '']
[ESCAPED BY '\\' ]]
[LINES TERMINATED BY '\n']
[IGNORE number LINES]
[(col_name,...)]
LOAD DATA INFILE语句从一个文本文件中以很高的速度读入一个表中。如果指定LOCAL关键词,从客户主机读文件。如果LOCAL没指定,文件必须位于服务器上。(LOCAL在MySQL3.22.6或以后版本中可用。)
为了安全原因,当读取位于服务器上的文本文件时,文件必须处于数据库目录或可被所有人读取。另外,为了对服务器上文件使用LOAD DATA INFILE,在服务器主机上你必须有file的权限。见6.5 由MySQL提供的权限。
如果你指定关键词LOW_PRIORITY,LOAD DATA语句的执行被推迟到没有其他客户读取表后。
使用LOCAL将比让服务器直接存取文件慢些,因为文件的内容必须从客户主机传送到服务器主机。在另一方面,你不需要file权限装载本地文件。
你也可以使用mysqlimport实用程序装载数据文件;它由发送一个LOAD DATA INFILE命令到服务器来运作。 --local选项使得mysqlimport从客户主机上读取数据。如果客户和服务器支持压缩协议,你能指定--compress在较慢的网络上获得更好的性能。
当在服务器主机上寻找文件时,服务器使用下列规则:
如果给出一个绝对路径名,服务器使用该路径名。
如果给出一个有一个或多个前置部件的相对路径名,服务器相对服务器的数据目录搜索文件。
如果给出一个没有前置部件的一个文件名,服务器在当前数据库的数据库目录寻找文件。
注意这些规则意味着一个像“./myfile.txt”给出的文件是从服务器的数据目录读取,而作为“myfile.txt”给出的一个文件是从当前数据库的数据库目录下读取。也要注意,对于下列哪些语句,对db1文件从数据库目录读取,而不是db2:
mysql> USE db1;
mysql> LOAD DATA INFILE "./data.txt" INTO TABLE db2.my_table;
REPLACE和IGNORE关键词控制对现有的唯一键记录的重复的处理。如果你指定REPLACE,新行将代替有相同的唯一键值的现有行。如果你指定IGNORE,跳过有唯一键的现有行的重复行的输入。如果你不指定任何一个选项,当找到重复键键时,出现一个错误,并且文本文件的余下部分被忽略时。
如果你使用LOCAL关键词从一个本地文件装载数据,服务器没有办法在操作的当中停止文件的传输,因此缺省的行为好像IGNORE被指定一样。
LOAD DATA INFILE是SELECT ... INTO OUTFILE的逆操作,
SELECT句法。为了将一个数据库的数据写入一个文件,使用SELECT ... INTO OUTFILE,为了将文件读回数据库,使用LOAD DATA INFILE。两个命令的FIELDS和LINES子句的语法是相同的。两个子句是可选的,但是如果指定两个,FIELDS必须在LINES之前。
如果你指定一个FIELDS子句,它的每一个子句(TERMINATED BY, [OPTIONALLY] ENCLOSED BY和ESCAPED BY)也是可选的,除了你必须至少指定他们之一。
如果你不指定一个FIELDS子句,缺省值与如果你这样写的相同:
FIELDS TERMINATED BY '\t' ENCLOSED BY '' ESCAPED BY '\\'
如果你不指定一个LINES子句,缺省值与如果你这样写的相同:
LINES TERMINATED BY '\n'
换句话说,缺省值导致读取输入时,LOAD DATA INFILE表现如下:
在换行符处寻找行边界
在定位符处将行分进字段
不要期望字段由任何引号字符封装
将由“\”开头的定位符、换行符或“\”解释是字段值的部分字面字符
相反,缺省值导致在写入输出时,SELECT ... INTO OUTFILE表现如下:
在字段之间写定位符
不用任何引号字符封装字段
使用“\”转义出现在字段中的定位符、换行符或“\”字符
在行尾处写换行符
注意,为了写入FIELDS ESCAPED BY '\\',对作为一条单个的反斜线被读取的值,你必须指定2条反斜线值。
IGNORE number LINES选项可被用来忽略在文件开始的一个列名字的头:
mysql> LOAD DATA INFILE "/tmp/file_name" into table test IGNORE 1 LINES;
当你与LOAD DATA INFILE一起使用SELECT ... INTO OUTFILE将一个数据库的数据写进一个文件并且随后马上将文件读回数据库时,两个命令的字段和处理选项必须匹配,否则,LOAD DATA INFILE将不能正确解释文件的内容。假定你使用SELECT ... INTO OUTFILE将由逗号分隔的字段写入一个文件:
mysql> SELECT * FROM table1 INTO OUTFILE 'data.txt'
FIELDS TERMINATED BY ','
FROM ...
为了将由逗号分隔的文件读回来,正确的语句将是:
mysql> LOAD DATA INFILE 'data.txt' INTO TABLE table2
FIELDS TERMINATED BY ',';
相反,如果你试图用下面显示的语句读取文件,它不会工作,因为它命令LOAD DATA INFILE在字段之间寻找定位符:
mysql> LOAD DATA INFILE 'data.txt' INTO TABLE table2
FIELDS TERMINATED BY '\t';
可能的结果是每个输入行将被解释为单个的字段。
LOAD DATA INFILE能被用来读取从外部来源获得的文件。例如,以dBASE格式的文件将有由逗号分隔并用双引号包围的字段。如果文件中的行由换行符终止,下面显示的命令说明你将用来装载文件的字段和行处理选项:
mysql> LOAD DATA INFILE 'data.txt' INTO TABLE tbl_name
FIELDS TERMINATED BY ',' ENCLOSED BY '"'
LINES TERMINATED BY '\n';
任何字段或行处理选项可以指定一个空字符串('')。如果不是空,FIELDS [OPTIONALLY] ENCLOSED BY和FIELDS ESCAPED BY值必须是一个单个字符。FIELDS TERMINATED BY和LINES TERMINATED BY值可以是超过一个字符。例如,写入由回车换行符对(CR+LF)终止的行,或读取包含这样行的一个文件,指定一个LINES TERMINATED BY '\r\n'子句。
FIELDS [OPTIONALLY] ENCLOSED BY控制字段的包围字符。对于输出(SELECT ... INTO OUTFILE),如果你省略OPTIONALLY,所有的字段由ENCLOSED BY字符包围。对于这样的输出的一个例子(使用一个逗号作为字段分隔符)显示在下面:
"1","a string","100.20"
"2","a string containing a , comma","102.20"
"3","a string containing a \" quote","102.20"
"4","a string containing a \", quote and comma","102.20"
如果你指定OPTIONALLY,ENCLOSED BY字符仅被用于包围CHAR和VARCHAR字段:
1,"a string",100.20
2,"a string containing a , comma",102.20
3,"a string containing a \" quote",102.20
4,"a string containing a \", quote and comma",102.20
注意,一个字段值中的ENCLOSED BY字符的出现通过用ESCAPED BY字符作为其前缀来转义。也要注意,如果你指定一个空ESCAPED BY值,可能产生不能被LOAD DATA INFILE正确读出的输出。例如,如果转义字符为空,上面显示的输出显示如下。注意到在第四行的第二个字段包含跟随引号的一个逗号,它(错误地)好象要终止字段:
1,"a string",100.20
2,"a string containing a , comma",102.20
3,"a string containing a " quote",102.20
4,"a string containing a ", quote and comma",102.20
对于输入,ENCLOSED BY字符如果存在,它从字段值的尾部被剥去。(不管是否指定OPTIONALLY都是这样;OPTIONALLY对于输入解释不起作用)由ENCLOSED BY字符领先的ESCAPED BY字符出现被解释为当前字段值的一部分。另外,出现在字段中重复的ENCLOSED BY被解释为单个ENCLOSED BY字符,如果字段本身以该字符开始。例如,如果ENCLOSED BY '"'被指定,引号如下处理:
"The ""BIG"" boss" -> The "BIG" boss
The "BIG" boss -> The "BIG" boss
The ""BIG"" boss -> The ""BIG"" boss
FIELDS ESCAPED BY控制如何写入或读出特殊字符。如果FIELDS ESCAPED BY字符不是空的,它被用于前缀在输出上的下列字符:
FIELDS ESCAPED BY字符
FIELDS [OPTIONALLY] ENCLOSED BY字符
FIELDS TERMINATED BY和LINES TERMINATED BY值的第一个字符
ASCII 0(实际上将后续转义字符写成 ASCII'0',而不是一个零值字节)
如果FIELDS ESCAPED BY字符是空的,没有字符被转义。指定一个空转义字符可能不是一个好主意,特别是如果在你数据中的字段值包含刚才给出的表中的任何字符。
对于输入,如果FIELDS ESCAPED BY字符不是空的,该字符的出现被剥去并且后续字符在字面上作为字段值的一个部分。例外是一个转义的“0”或“N”(即,\0或\N,如果转义字符是“\”)。这些序列被解释为ASCII 0(一个零值字节)和NULL。见下面关于NULL处理的规则。
对于更多关于“\”- 转义句法的信息,在某些情况下,字段和行处理选项相互作用:
如果LINES TERMINATED BY是一个空字符串并且FIELDS TERMINATED BY是非空的,行也用FIELDS TERMINATED BY终止。
如果FIELDS TERMINATED BY和FIELDS ENCLOSED BY值都是空的(''),一个固定行(非限定的)格式被使用。用固定行格式,在字段之间不使用分隔符。相反,列值只用列的“显示”宽度被写入和读出。例如,如果列被声明为INT(7),列的值使用7个字符的字段被写入。对于输入,列值通过读取7个字符获得。固定行格式也影响NULL值的处理;见下面。注意如果你正在使用一个多字节字符集,固定长度格式将不工作。
NULL值的处理有多种,取决于你使用的FIELDS和LINES选项:
对于缺省FIELDS和LINES值,对输出,NULL被写成\N,对输入,\N被作为NULL读入(假定ESCAPED BY字符是“\”)。
如果FIELDS ENCLOSED BY不是空的,包含以文字词的NULL作为它的值的字段作为一个NULL值被读入(这不同于包围在FIELDS ENCLOSED BY字符中的字NULL,它作为字符串'NULL'读入)。
如果FIELDS ESCAPED BY是空的,NULL作为字NULL被写入。
用固定行格式(它发生在FIELDS TERMINATED BY和FIELDS ENCLOSED BY都是空的时候),NULL作为一个空字符串被写入。注意,在写入文件时,这导致NULL和空字符串在表中不能区分,因为他们都作为空字符串被写入。如果在读回文件时需要能区分这两者,你应该不使用固定行格式。
一些不被LOAD DATA INFILE支持的情况:
固定长度的行(FIELDS TERMINATED BY和FIELDS ENCLOSED BY都为空)和BLOB或TEXT列。
如果你指定一个分隔符与另一个相同,或是另一个的前缀,LOAD DATA INFILE不能正确地解释输入。例如,下列FIELDS子句将导致问题:
FIELDS TERMINATED BY '"' ENCLOSED BY '"'
如果FIELDS ESCAPED BY是空的,一个包含跟随FIELDS TERMINATED BY值之后的FIELDS ENCLOSED BY或LINES TERMINATED BY的字段值将使得LOAD DATA INFILE过早地终止读取一个字段或行。这是因为LOAD DATA INFILE不能正确地决定字段或行值在哪儿结束。
下列例子装载所有persondata表的行:
mysql> LOAD DATA INFILE 'persondata.txt' INTO TABLE persondata;
没有指定字段表,所以LOAD DATA INFILE期望输入行对每个表列包含一个字段。使用缺省FIELDS和LINES值。
如果你希望仅仅装载一张表的某些列,指定一个字段表:
mysql> LOAD DATA INFILE 'persondata.txt'
INTO TABLE persondata (col1,col2,...);
如果在输入文件中的字段顺序不同于表中列的顺序,你也必须指定一个字段表。否则,MySQL不能知道如何匹配输入字段和表中的列。
如果一个行有很少的字段,对于不存在输入字段的列被设置为缺省值。
如果字段值缺省,空字段值有不同的解释:
对于字符串类型,列被设置为空字符串。
对于数字类型,列被设置为0。
对于日期和时间类型,列被设置为该类型的适当“零”值。
如果列有一个NULL,或(只对第一个TIMESTAMP列)在指定一个字段表时,如果TIMESTAMP列从字段表省掉,TIMESTAMP列只被设置为当前的日期和时间。
如果输入行有太多的字段,多余的字段被忽略并且警告数字加1。
LOAD DATA INFILE认为所有的输入是字符串,因此你不能像你能用INSERT语句的ENUM或SET列的方式使用数字值。所有的ENUM和SET值必须作为字符串被指定!
如果你正在使用C API,当LOAD DATA INFILE查询完成时,你可通过调用API函数mysql_info()得到有关查询的信息。信息字符串的格式显示在下面:
Records: 1 Deleted: 0 Skipped: 0 Warnings: 0
当值通过INSERT语句插入时,在某些情况下出现警告,除了在输入行中有太少或太多的字段时,LOAD DATA INFILE也产生警告。警告没被存储在任何地方;警告数字仅能用于表明一切是否顺利。如果你得到警告并且想要确切知道你为什么得到他们,一个方法是使用SELECT ... INTO OUTFILE到另外一个文件并且把它与你的原版输入文件比较。
1. 我和超人的唯一区别是:我把内裤穿在里面.
2. 我不是随便的人, 但随便了起来就不是人了.
3. 我身在江湖, 江湖却没有我的传说.
4. 走别人的路, 让别人无路可走.
5. 穿别人的鞋, 走自己的路, 让他们找去吧.
6. 走NB的路, 让SB说去吧.
7. 听说女人如衣服, 兄弟如手足. 回想起来, 我竟然七手八脚的裸奔了19年.
8. 宁愿相信世间有鬼, 也不相信男人那张嘴.
9. 水至清则无鱼, 人至贱则无敌.
10. 树不要皮, 必死无疑. 人不要脸, 天下无敌.
11. 一山不能容二虎, 除非一公一母.
12. 对流血一周仍然不死的动物千万不能大意.
13. 我, 一个大学生的人生目标:农妇, 山泉, 有点田.
14. 女人谨记:一定要吃好玩好睡好喝好. 一旦累死了, 就别的女人花咱的钱, 住咱的房子, 睡咱的老公, 泡咱的男朋友, 还打咱的娃.
15. 有一天, 我梦见自己的钱花光了, 醒来一看, 口袋真的空空的.
16. 我减肥已取得了很大的成功, 你看, 我三个下巴都尖了.
17. 千万别等到人人都说你丑的时候才发现自己真的丑.
18. 如果朋友可以出卖, 每个值五元的话, 我也能发笔小财了.
19. 肚子大不可怕, 可怕的是大而无料.
20. 女人先表现自己大方, 男人就不敢小气了.
21. 人, 生在床上, 死在床上, 欲生欲死, 也在床上.
22. 巫师, 请转告公主, 老子还在披荆斩刺的路上, 还有雪山未翻, 大河未过, 巨龙未杀, 美女未泡. 叫她继续死睡吧.
23. 无所为而无所谓, 无所谓而无所不为.
24. 铁饭碗的真正含义不是在一个地方有饭吃, 而是一辈子到哪儿都有饭吃.
25. 骚归骚, 骚有骚的贞操. 贱归贱, 贱有贱的尊严.
26. 人生的成功不在于拿到一副好牌, 而是怎样将坏牌打好.
27. 0岁出场亮相, 10岁天天向上, 20岁远大理想, 30岁发奋图墙, 40岁基本定向, 50岁处处吃香, 60岁打打麻将, 70岁处处闲逛, 80岁拉拉家常, 90岁挂在墙上.
28. 再过几十年, 我们都来相会, 送到火葬场, 全部烧成灰, 你一堆, 我一堆, 谁也不认识谁, 全部送到农村做化肥.
29. 自从我变成了狗屎, 就再也没有人踩在我头上了.
30. 路边的野花不要, 踩.
31. 也许似乎大概是, 然而未必不见得.
32. 说过的话可以不算, 喜欢的人天天要换.
33. 本人中科院高级潜水院院士, 诺贝尔长期掉线奖, 奥斯卡终身隐身奖.
34. 你放心, 看到你我连食欲都没有了, 还谈什么性欲.
35. 生活真他妈好玩, 因为生活老他妈玩我.
36. 佛曰:"前世的500次回眸才换来今生的一次擦肩而过." 我宁愿用来世的一次擦肩而过换得今生的500次回眸.
37. 黑夜给了我一双黑色的眼睛, 可我却用它来翻白眼.
38. 我想早恋, 但是已经晚了.
39. 天啊, 我的衣服又瘦了.
40. 这个世界上我只相信两个人, 一个是我, 一个不是你.
41. 思想有多远, 你就给我滚多远.
42. 流氓不可怕, 就怕流氓有文化.
43. 客官请自重, 小女子只卖身不卖艺.
44. 男人的谎言可以骗女人一夜, 女人的谎言可以骗男人一辈子.
45. 如果不能给你的女人幸福, 请停下你解开她衣扣的手.
46. 如果不能给你的女人穿上嫁衣, 那么千万别停下你解开她衣扣的手.
47. 水能载舟, 亦能煮粥.
48. 开车无难事, 只怕有新人.
49. 一见钟情, 再而衰, 三而竭.
50. 生, 容易. 活, 容易. 生活不容易.
51. 如果我能看到我的背影, 我想它一定很忧伤, 因为我把快乐都留在了前面.
52. 出问题先从自己身上找原因, 别一便秘就怪地球引力不够.
53. 给我织一条围巾, 我愿以一生关怀相报. 否则, 你就用围巾勒死我吧.
54. 男人在不懂的时候装懂, 女人则恰好相反.
55. 春色满园关不住, 我拉红杏出墙来.
56. 你以为我会眼睁睁看着你去送死吗? 我会闭上眼睛的. -_-
57. 我以为我很颓废, 今天我才知道, 原来我早报废了.
58. 我视金钱如粪土, 我爸视我为粪池.
59. 我喝酒想把痛苦溺死, 但这该死的痛苦却学会了游泳.
60. 我是你的风筝, 线在你手上, 可陪伴我的只有风.
61. 别人都在假装正经, 那我就只有假装不正经了.
本文将介绍在JSP页面中包含一些JavaScript这样一个简单而有效的方法来结合AJAX和Struts。在此我们介绍如何重新使用已经存在的Struts actions,但此技术同样可用于你选择的其他Java-Web 框架。本方法同样可平移到Struts或者JSF的下一个版本。
AJAX是“异步的JavaScript和XML”的缩写。这是一项技术,而不是一个如Struts一样的框架。为什么在AJAX周围会有这么多的关注呢?这是因为AJAX使web页面看起来并不像一个平面的文档,而更像用户所期望的如桌面应用的动态GUI应用程序。AJAX技术能在很多的浏览器上使用(包括IE和Netscape/Mozilla)。它已经为Microsoft(用于Outlook的web客户端)和Google(用户Google Maps和Gmail)所使用。
未使用AJAX之前
目前大多数的Struts应用都是标准的“如同一个平面文档的web页面”的结构。如果你想模仿一些桌面应用程序(比如那些使用Java Swing,Visual Basic,或者Delphi建立的应用程序),那么你有两个选择:你可以发送所有的可能作为页面的一部分被请求的信息,使用大量的JavaScript来操作其动态的显示(一个很慢并且非企业级Java的方法);或者你可以不改变形式地提交到后台服务器(一种有效的方法) 。AJAX提高给你了融合前面的最佳解决方案:动态的页面,但是大多数的应用是在你的web服务器的Java程序来处理的。
AJAX 101
AJAX和现有的动态HTML技术非常相似,并在其上增加了一个发送到“后台”服务器的请求来获取需要的新的或者更新的信息。AJAX的机制在其他地方已经有详细的说明――请查看本文后的Resources来获取更多。但是你至少需要知道:
- 1. XMLHttpRequest (如果你使用的是IE浏览器的话,则是Microsoft.XMLHTTP的ActiveX的对象)。这些对象,你可以在web页面中使用JavaScript调用。他们允许你作为后台的调用方式来请求web服务器的内容(例如,在表单提交后,屏幕并不像平时一样显示“空白”)。
- 2. XMLHttpRequest 和Microsoft.XMLHTTP 返回的内容可以作为XML或者文本来处理。JavaScript(在你页面上的)可以使用请求的新内容来更新页面。
- 3. 整个处理过程可以由普通的JavaScript事件来触发:onclick,onchange,onblur,等。
在你的Struts应用中使用AJAX
你阅读了本文,然后你会对使用AJAX来创建动态的web页面感兴趣,并且想知道如何将它加入到你的Struts应用中。这只是选择之一。那么你会如何选择呢?
- · 等待,知道Struts的下一个版本融合了AJAX技术。如果Struts开发者准备开发一个新的应用,这或许会是一个好的选择。再后面,可能会是要求JSF也这样做――对其本身并不一件坏事,但是对于已经存在的系统这将会带来最根本的改变。
- · 你也可以直接采用新的方法,比如Direct Web Remoting (DWR) 和Ruby on Rails,这些都是专为建立AJAX应用的。如果你考虑不使用Struts来开发web的话,那么这些都是很又用的框架,并且值得一试。但是,这也同时意味着你必须要重写你的应用。
- · 在你已经存在的Struts应用中增加AJAX。既然AJAX只是一项技术而并非框架,那么它就很容易融入到Struts中。作为现有的系统,其稳定性(如,保持现有的库文件)是相当重要的。所以这个方法被推荐,并且我们将在后面详细介绍。
我们选择在Struts应用中增加AJAX的优势是:
- 1. 它并不需要任何新的库文件或者服务器代码;只需要使用现有的Struts库文件和action。
- 2. 解决方案中所有部分――JavaScript,XML,Java和Struts――早已为广泛所知。
- 3. 此应用可以一块一块地移植到AJAX;我们可以确定哪些部分对用户有益,并首先将它们更新到动态AJAX显示。
实现方案
我们如何真正的贯彻我们的选择呢?我们首先应该注意一个“标准的”(没有AJAX)Struts应用是如何工作的。在此应用中,一个一般的事件流程如下:
- 1. 使用点击超链接或者表单的提交按钮,用户发送请求。
- 2. web服务器运行处理请求的Struts Action来生成一个web页面。
- 3. 浏览器显示web页面。
- 4. 当用户点击保存的时候,信息由Struts框架中一个ActionForm类来转换并发送到服务器。
- 5. 然后,Struts框架调用Struts Action来出来请求(如,保存数据到数据库中)。
- 6. 页面再一次回传,处理流程继续。
现有的Struts应用
一个演示事件流程的简单Struts应用可以在以下地址下载: struts-non-ajax.zip。此基于Struts的应用,是基于用户的输入显示或者隐藏蓝色和绿色的表格。图1显示了载入初始页面的画面。图2显示了用户输入值并点击了提交后的画面。虽然简单,但它已经足以表示一个Struts的工作流程。

图 1. 没有AJAX的例子:初始屏幕

图 2. 没有AJAX的例子:输入值并点击了提交
服务器端的代码是:一个Struts Action使用struts-config.xml 中定义的值转发到(相同的)JSP。这个例子代码中一些需要注意的地方是:
- ·struts-config.xml文件将所有的请求重定向到http://localhost:8080/struts-non-ajax/(或者和你自己的服务器相同)的index.jsp。
- · index.jsp 包含了一个两个文本框的Struts form(showBlue和showGreen)。该页面同样包含了标签,但是如同两个文本框被初始化为空,标签之间的内容并不显示。
- · 用户输入值(true或者false)并点击提交按钮,处理控制(经过Struts框架,读取struts-config.xml)提交到SampleAction类中。
- ·SampleAction记录下值,然后转发到index.jsp。一个成熟的Struts应用可能会处理更多的事情,不如保存或者查询数据库等。
- · index.jsp 现在重新处理请求;如果ShowBlue或者ShowGreen的值是true,这些表格就显示出来。
该应用并没有任何“错误”。类似的Struts项目好多年都是这样做的。但是,我们如何在不添加复杂的JavaScript或者频繁的表单提交的前提下,为此应用增加动态的元素呢?
我们的第一个Struts AJAX应用
观察下下面的图3和图4。第一眼看上去,它们和前面的例子没有说明区别。它们的不同之处在于,页面载入后(图3)然后文本框中的值改变了,窗体自动提交而不显示空白的,然后在图4中显示结果。普通的提交按钮仍然在,你也可以选择使用它。

图 3. 页面载入后的AJAX例子

图 4. AJAX调用后的AJAX例子
添加AJAX是出奇的容易。服务器端的代码和前面的例子是一样的: 一个Struts的ActionForm来后去数据,一个Struts的Action来执行需要的任务(例如,存储数据库)然后转发到适当的JSP页面来显示结果。
继续
如果你希望就此停止阅读(跳过这个例子的工作说明),但是这里的是和你需要转换你的Struts应用到一个Struts-AJAX应用同样的风格:
- 1. 在你的web页面中引入一个Ajax.js (该文件是struts-ajax.zip 例文件中的一部分)。Ajax.js 包含了所有需要发送和接收AJAX调用的JavaScript方法。
- 2. 确保你希望在AJAX调用中更新的web页面的部分包含在标签中,并且给每个标签一个id。
- 3. 当一些事件触发的时候就更新页面(例如,文本框的the onchange()方法),调用retrieveURL()方法,通过URL传递到需要执行服务器端处理的Struts Action。
- 4. 为了页面的显示/更新,最简单的方法是Struts Action转发回同样的页面。在本例中,showGreen/showBlue 文本框中的onchange()方法来触发AJAX调用。
JavaScript方法retrieveURL()调用服务器的Struts(通过URL),获取JSP响应,然后更新显示页面中的 标签中的部分。就是这么简单!
AJAX解决方案的细节
我们将例子变为AJAX-Struts应用的时候,需要三个变化:
- 1. 增加一个JavaScript方法来完成到服务器的“背后的”AJAX调用。
- 2. 增加JavaScript代码来接收服务器的响应并更新页面。
- 3. 在JSP页面增加标签标签,这个标签中内容将在AJAX调用中更新。
我们将详细的说明上面的每一步。
发送AJAX请求到服务器
有两个方法(在下面列出)用于发送请求到服务器。
· retrieveURL()方法获得服务器的URL和Struts form。URL用于使用AJAX,form的值用于传递到服务器。
· getFormAsString()方法用于将retrieveURL()中form命名的值组装成查询字符串,并发送到服务器。
使用方法很简单,使用onclick()/onChange()事件来触发retrieveURL()更新显示。
在这两个方法中有一些有趣的东西。
在retrieveURL()方法中,req.onreadystatechange = processStateChange (注意,没有括号)这一行来告诉浏览器在服务器响应到达的时候调用processStateChange()方法(该方法将在后面介绍)。retrieveURL()方法中(现在已经是AJAX的标准了)同样决定是使用IE浏览器(ActiveX)还是使用Netscape/Mozilla (XmlHttpRequest) 来实现跨浏览器兼容。
getFormAsString()方法将HTML form转换成字符串连接在URL后面(这样就允许我们发送HTTP GET请求)。这个字符串是经过转换的(比如,空格转换成%20等),并且是一个Struts能将其组装成ActionForm的格式(并不需要Struts清楚的明白这个是来之AJAX的请求)。注意,在本例中我们使用HTTP GET,使用HTTP POST的方法也是类似的。
function retrieveURL(url,nameOfFormToPost) {
//将url转换成字符串
url=url+getFormAsString(nameOfFormToPost);
//调用AJAX
if (window.XMLHttpRequest) {
// 非IE浏览器
req = new XMLHttpRequest();
req.onreadystatechange = processStateChange;
try {
req.open("GET", url, true);
} catch (e) {
alert("Server Communication Problem\n"+e);
}
req.send(null);
} else if (window.ActiveXObject) {
// IE
req = new ActiveXObject("Microsoft.XMLHTTP");
if (req) {
req.onreadystatechange=processStateChange;
req.open("GET", url, true);
req.send();
}
}
}
getFormAsString() 是一个“私有” 方法,在retrieveURL()中使用。
function getFormAsString(formName){
//设置返回字符串
returnString ="";
//取得表单的值
formElements=document.forms[formName].elements;
//循环数组,组装url
//像'/strutsaction.do&name=value'这样的格式
for(var i=formElements.length-1;i>=0; --i ){
//转化每一个值
returnString+="&"
+escape(formElements[i].name)+"="
+escape(formElements[i].value);
}
//返回字符串
return returnString;
}
根据AJAX的响应更新web页面
到现在为止,我们学习过了使用JavaScript来完成AJAX调用(前面列出),Struts Action,ActionForm以及JSP(基本没有变化,只是增加了标签)。为了完善我们对Struts-AJAX项目的了解,我们需要了解三个用于根据服务器返回的结果而更新页面的JavaScript方法。
- · processStateChange(): 该方法在AJAX调用前设定。它在服务器响应到达后由XMLHttpRequest/Microsoft.XMLHTTP 对象调用。
- ·splitTextIntoSpan(): 根据响应,循环取出数组中的元素组装成NewContent。
- ·replaceExistingWithNewHtml(): 根据span元素数组,循环搜索,将里面的元素调换掉页面中和它的'someName'相同的中的内容。注意,我们使用的是req.responseText 方法来获得返回的内容(它允许我们操作任何文本的响应)。与此相对于的是req.responseXml (它的作用更大,但是要求服务器返回是XHTML或者XML)。
function processStateChange() {
if (req.readyState == 4) { // 完成
if (req.status == 200) { // 响应正常
//将响应的文本分割成Span元素
spanElements =
splitTextIntoSpan(req.responseText);
//使用这些Span元素更新页面
replaceExistingWithNewHtml(spanElements);
} else {
alert("Problem with server response:\n "
+ req.statusText);
}
}
}
replaceExistingWithNewHtml() 是为processStateChange()使用的“私有”方法。
function replaceExistingWithNewHtml
(newTextElements){
//循环newTextElements
for(var i=newTextElements.length-1;i>=0;--i){
//判断是否以 if(newTextElements[i].
indexOf("-1){
//获得span的名字- 设置在第一和第二个引号之间
//确认span元素是以下的格式
//NewContent
startNamePos=newTextElements[i].
indexOf('"')+1;
endNamePos=newTextElements[i].
indexOf('"',startNamePos);
name=newTextElements[i].
substring(startNamePos,endNamePos);
//获得内容-在第一个>标记后的所有内容
startContentPos=newTextElements[i].
indexOf('>')+1;
content=newTextElements[i].
substring(startContentPos);
//现在更新现有的Document中的元素,
// 确保文档存在该元素
if(document.getElementById(name)){
document.getElementById(name).
innerHTML = content;
}
}
}
splitTextIntoSpan() 是为processStateChange() 使用的“私有”方法。
function splitTextIntoSpan(textToSplit){
//分割文档
returnElements=textToSplit.
split("")
//处理每个元素
for(var i=returnElements.length-1;i>=0;--i){
//删除掉第一个span后面的元素
spanPos = returnElements[i].
indexOf("
//如果找到匹配的,获得span前的内容
if(spanPos>0){
subString=returnElements[i].
substring(spanPos);
returnElements[i]=subString;
}
}
return returnElements;
}
新的控制流
添加以下的JavaScript代码到我们的应用中,以下的步骤将在服务器和浏览器中执行。
- 1. 如同一个普通Struts应用装载页面。
- 2. 用户改变文本框的值,触发一个onChange() 事件,调用retrieveURL() 方法。
- 3. 该JavaScript方法通过发送Struts明白的表单变量(后台)请求到服务器的Struts Action。
- 4. 该JavaScript方法同样设定了第二个JavaScript方法的名字,此方法将到服务器响应完毕后调用。本例子中,设定为processStateChange() 方法。
- 5. 如我们所预期的,服务器响应完毕,调用processStateChange() 方法。
- 6. JavaScript在(新的)服务器响应中循环取出所有元素。将页面上存在与获得元素名字相同的 中的元素替换掉。
在你的应用中设计AJAX
以上描述的JavaScript方法能在大多数的应用中使用,包括比我们的例子复杂得多的。但是,在使用之前,你需要注意以下几点:
· 避免复制代码,最好在初始化请求(如,显示完整的页面)和AJAX(更新部分页面)请求中使用相同的Struts Action和JSP。
·在公共的Action(控制器)中,决定JSP页面(所有的JSP页面或者其中的一部分)中的一个区域需要传送到浏览器。通过在web服务器的session或者ActionForm中设定标记来让JSP页面知道哪些部分需要提交。
· 在JSP中,使用Struts 或者JSTL标签来决定提交的HTML区域。
使用AJAX的本例子,可以在以下下载: struts-Ajax.zip
结语
AJAX技术允许我们在创建和使用web应用的时候完全的改变。本文介绍了一个简单的技术,在现有的Struts应用中增加Struts的处理。它允许我们利用我们已有的东西,不仅仅是代码,还包括了开发的技能。作为一个好的产品,它同样允许我们写出更清晰,更具移植性的Java Struts应用。
版权声明:Techtarget获Matrix授权发布,如需转载请联系Matrix
作者:作者:Paul Browne;pawenwen(作者的blog:http://blog.matrix.org.cn/page/pawenwen)
译文:点击
随着企业intranet和国际internet的迅速发展,越来越多的工作流程,商务交易,教育、培训、会议和讲座,以及个人消费娱乐都被转移到所谓的万维网(World Wide Web,以下简称WEB)上来了。与此相对应的是交互操作的复杂性越来越高。
随着Browser/Server模式的日渐流行,很多操作都是在浏览器环境下的网页上完成的,并不是只有失效的链接和意外的出错才会使操作者感到烦恼,即便是一次完整的成功操作过程,也可能因为操作的繁复性过高或者使用上的不方便而给操作者带来不愉快的体验。
本文试图阐述WEB交互页面设计的一些指导性原则,这些原则有利于避免发生不愉快的操作体验。这些原则是用户友好性的,是在完成同一种操作要求下,使用户最感到轻松、简单、舒适的WEB交互界面设计原则。我们假定我们讨论的WEB页面都是功能正常的,符合美学观点的。需要说明我们讨论的原则可能会和设计上的美学观点以及既有的功能设计有所冲突。如果发生这种情况,基于“实用的就是美的”观点,我们会建议您酌情放弃原先的美学观点与功能设计。
1. 输入控件的自动聚焦和可用键盘切换输入焦点
使用JavaScript实现页面加载完成后立即自动聚焦(focus)到第一个输入控件。可用TAB键(IE缺省实现)或方向键切换聚焦到下一个输入控件。
输入控件指WEB页面表单(<form>)中显式的,需要用户进行修改、编辑操作的表单元素。对于这些控件,如果没有自动聚焦操作,不可避免的出现一次用户鼠标定位操作(如果用户此前处于键盘输入操作状态或鼠标定位后需要进行键盘输入操作,实际上是键盘鼠标切换操作)。如果鼠标定位后需要进行键盘输入操作,如果不能键盘切换输入焦点,那么不可避免的在切换输入焦点时需要反复的键盘鼠标切换操作,这是很繁琐的。
如果实现了页面加载完成即自动聚焦到第一个输入控件,并且可以键盘切换输入焦点标定位操作,那么对于用户来说整个页面的输入操作可能都不需要鼠标操作,或次数较少,这是一种便利。毕竟频繁的键盘鼠标切换操作是比较累人的。
对于有输入栏的对话框或网页,在不干预的情况下就应将当前控制焦点定位在待输入的输入栏上;如果输入栏在一般情况下不需要更改其中的内容,则应直接将焦点定在“确定”按钮上;在几个输入栏之间应支持tab,shift+tab切换操作,“确定”和“取消”应该是切换操作的终点,与具体所在位置无关。
2. 可用Enter(或Ctrl+Enter)键提交,确保和点击提交按钮的效果是相同的
不要在提交按钮上加入onClick=”…”这样的JavaScript代码。
用Enter键提交页面是原则1的自然延伸,而且这也是浏览器所缺省支持的。只所以单独列出来是因为实际上有些设计者设计的页面不能达到这种效果,结果导致使用Enter键提交和点击“确定”按钮提交带来的效果不一样。大部分情况下是设计者在“确定”按钮上加入了onClik=”…”这样的代码,通过点击“确定”按钮后,会执行一段JavaScript代码,比如对某些hidden类型的input元素设值。而使用Enter键提交时就不会执行这段代码。
正确的做法是把这段代码移到表单标签<form>中,以onSubmit=”…”属性引入。
对于<textarea>表单元素,它会消耗Enter键,因此会使得Enter键提交失效。可以引入JavaScript代码捕捉Ctrl+Enter复合键,一旦捕捉到即执行表单的submit()方法。对于需要频繁提交的场合,比如BBS上,这种代码是很有必要的。
3. 鼠标动作提示和回应
对用户的鼠标定位操作,当移动到可响应的位置上时,应给予视觉或听觉的提示。
动作回应的最简单形式就是鼠标ICON变成手状。浏览器只对具有href属性的HTML标签会自动进行这种变换ICON的行为。对于没有href属性(或没有设置href属性)的标签,可以通过JavaScript设置style属性的cursor为hand。
目标区域发生变化是更为主动的响应形式。当鼠标指针移到目标区域,此时指针图形改变或文字颜色发生改变均能较大的减轻用户搜索定位目标区域的注意力负担。在按钮上增添直观的图形,尽可能的增大按钮面积;按钮间保持适当的距离,太近增加了用户区别它们之间界限以防误操作的负担,太远增加了用户搜索定位按钮的负担。
4.尽可能早的在客户端完成输入数据合法性验证
输入数据的合法性检验应该在客户端使用JavaScript进行验证。除非验证只能在服务器端完成,否则验证工作应在最早能完成的情况下进行。
在客户端完成数据合法性验证,可以避免一次服务器请求和回复通讯,这种通讯是需要用户等待的,如果用户等待很长时间后从服务器返回的结果提示出现的错误是在输入时即可发现的,那么这种设计就是不友好的。诸如密码长度限制,用户名允许字符限制等等,显然应该在客户端提交前就应该进行验证。
5. 根据应用场景决定在表单页面和提交后返回页面间是否使用中间过渡页面
根据应用场景,决定是否显示接收表单页面(表单页面和提交后返回页面间的中间过渡页面),以及使用何种方式显示接收表单页面。
表单页面和接收表单页面是大部分WEB交互操作赖以实现的配合模式。关于表单页面和接收表单页面的相互关系的设计,要做如下几个方面的考虑。
一,对于需要频繁操作的场合,从操作便利和快捷性出发,尽可能的减少服务器和客户端交互次数,应该避免使用中间过渡页面。提交完毕直接返回原来的表单页面或默认页面。在这种情况下要考虑到数据安全和可恢复性。
如果因为用户输入的数据不合格,需要重新输入,那么,去除中间页面,把错误信息直接显示在原表单页面上的设计方式,将是最简洁的处理方式。用户只需要根据错误提示进行更正即可。当然这样做稍微增加了编程负担。在表单接收页面上需要包含原表单页面的内容,而且输入数据项都必须用服务器端代码或客户端JavaScript设置成用户输入的值。为了开发快捷,可以这样做:表单页面和接收表单页面用同一个服务器端脚本页面实现。这个页面按如下流程完成原来两个页面的工作:
页面脚本初始化
┃
检查“提交”变量是否设置
┠已设置,做数据验证
┃ ┠验证通过->业务逻辑处理->使用包含页面方式或重定向方式返回到特定页面
┃ ┗验证不通过->保存用户输入的数据->退出表单提交处理到表单页面流程中
┗未设置,做表单页面流程,如有来自提交流程中产生的用户输入数据,则显示出来
其中,使用包含页面方式返回到特定页面可以避免一次客户端重定向过程,比客户端重定向过程还要快捷和稳定一些。但是有些情况下因为代码变量冲突或其他原因,使用包含页面方式可能并不方便,这时候可以使用服务器端重定向技术,在ASP里是Server.Transfer方法,在Java Servlet里是RequestDispatcher.forward()方法。不要使用Response.Redirect或者HttpServletResponse.sendRedirect()这种客户端HTTP重定向方法。不使用中间过渡页面也就意味着用户不能后退浏览原先已经填好的表单页面,因为使用的是同一个URL。所以在验证不通过情况下保存用户输入的数据就是必不可少的。
不使用中间过渡页面带来的另一个问题就是使用包含页面方式或服务器端重定向方式返回会使得URL和页面内容不能一一对应。对于用户可能会直接用这个URL(会收藏这个URL)访问返回页面的情况,他会发现实际上到达的是表单页面,不是他想要的那个返回结果页面。所以,去除中间过渡页面,确实会带来URL和内容含混不清的情况,因而不适合需要URL和页面内容一一对应的场合。
二,从技术角度考虑,使用中间过渡页面能保证URL和页面内容一一对应,简化页面开发工作。
为了保证页面内容总是和固定的URL联系起来,必须使用客户端重定向:
提交 业务逻辑处理 (中间过渡页面)
表单页面――――->接收表单页面―――――――――>显示处理结果―――>客户端重定向到特定页面
客户端重定向分几种情况:1,使用HTTP Header重定向,Location:http://www.netall.com.cn,这种定向是最快的,在窗口一片空白的情况下就迅速访问(GET)另一个页面。这种方式实际上不能显示处理结果,只能说是向第一种快速重定向方式的一种折衷处理;2,HTML标签刷新,<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://www.netall.com.cn">,这种定向比较友好,在这个页面加载完毕后访问另一个页面。很多设计者把这个作为一个技巧使用,在载入一个大页面前放置一个缓冲页面以避免用户乏味的等待;3,JavaScript重定向。由于是用代码控制重定向,可以做的更灵活。比如根据用户习惯,控制操作完毕后的转向流程。4,被动式的重定向。在页面上放置按钮或链接,由用户手动决定返回到特定页面。这种情况适合于处理结果的显示页面包含相当多的信息,需要用户仔细浏览,而决定下一步的操作。
在使用中间过渡页面的情况下,不能再使用页面过期失效了。否则一旦出现错误,需要用户重新输入表单数据,用户就不能用后退按钮恢复此前填写的表单数据了。除非设计者有意禁止这种恢复。
6. 防止表单重复提交处理
对提交按钮点击后做变灰处理避免在网络响应较慢情况下用户重复提交同一个表单。使用页面过期失效避免用户后退浏览重复提交表单。
有些复杂的应用会导致需要较长时间的等待才会返回处理结果。而在较慢的网络环境中,这种情况更是频繁发生。焦急等待的用户往往会重复点击提交按钮。这种情况是设计者所不希望看到的。
使用JavaScript在点击提交按钮后使按钮失效变灰是一个最直接的办法(根据原则2这段代码应该放在<form>标签里onSubmit=”…”做)。此外,在表单页面上,用服务器端脚本设置HTTP Header的Expires为立即过期可以保证用户没办法使用后退浏览恢复表单页面。注意这样做的代价可能是用户辛辛苦苦填写很长的内容,结果一旦操作失误就没法恢复。所以应该避免在包含<textarea>表单元素的页面上使用页面过期失效。
应该说,更严格的方法是,服务器端脚本就应该具备抵抗重复提交的能力。例如,为这个表单分配一个唯一ID或一个使用一次即失效的验证码。此外,这个表单处理还应具有事务性质,如果表单不被接受,所做的改变还是能恢复的。在金融应用场合,重复提交同一笔交易是肯定不被允许的。能在重复提交中获利的一方总是会想办法绕过浏览器的限制,所以不能依赖于客户端的技术。
7. 页面链接是打开新窗口、使用原窗口还是弹出窗口的原则
一般而言,首页上链接可以使用target=”_blank”属性打开新窗口,而其他页面上的链接都应使用原窗口或弹出窗口。如果链接页面内容相对原页面来说不重要,是附属性质的,可以使用弹出窗口方式。
一般情况下应该使用原窗口,把是否保留原窗口内容的权利留给用户。除非设计者相信原页面是如此重要,在用户发出点击指令后还有使用上的价值,以至于不能被随便更新或覆盖。一般来说,只有首页才会处于这样一个地位,用户在首页上打开一个链接后,一般还会在这个首页上去打开另一个链接。比如首页包含极多链接的门户网站,或者搜索引擎的搜索结果页面。Google.com以前的搜索结果页面上的链接是使用原窗口的,后来他们意识到用户会反复使用这个页面,而改成打开新窗口了。一般的网站如果首页链接不多,就不必使用新窗口,这是用户友好的设计原则。
上述情形的一个极端情况就是新页面内容比起原页面内容的重要性差很多,以至于都未必需要打开一个新页面。这时候使用弹出窗口比较合适。用JavaScript弹出窗口有好几种:一个是window.open()函数。这里有个技巧。应该使用window.open()先打开一个空白窗口,再使用location.replace()用目标页面替换。这样做可以避免在打开新页面的过程中导致原页面失去响应。Window.open()将打开一个新的浏览器窗口进程,因此资源消耗比较大。另一个是由微软DynamicHTML规范中扩充的方法createPopup()。createPopup()可以创建无边框的弹出窗口,消耗系统资源较小。还有一个就是用页面中隐藏的层<div>来模拟一个弹出页面。后两种可以使用JavaScript代码填充弹出窗口内容。如果需要下载网页作为其内容的话,需要微软DynamicHTML规范中的<download>标签。
8. 尽可能少的排列可选项,尽可能少的安排操作步骤
根据用户操作习惯安排尽可能少的操作菜单选项,同时要保证尽可能少的操作步骤。
在不降低功能多样性的前提下减少菜单项和操作步骤是用户友好的设计。要做到这一点很不容易。要从用户出发考虑他们最频繁的操作是什么。正常情况下一个用户需要的操作总可以归类为5个以下的种类,如果出现更多的种类,那一定是没有针对用户兴趣去区分主次。一个用户同时有5个以上的强烈兴趣中心是难以想像的,走马观花似的随意点击浏览的用户,是不大可能在某个种类上进行深入的交互操作的。在这5个种类中,每个种类都可能有若干个可操作的二级种类。如果这些二级操作项是不可见的,那么意味着要做两次选择才能进入可操作页面。这就违背了“尽可能少的安排操作步骤”这一原则。如果使用JavaScript制作二级菜单,避免请求服务器,会好一些。如果二级菜单项总共不超过20个左右,不妨将二级菜单直接显示出来,比如放在左列一字向下排开,这样只需要一次选择到可操作项,更加明了方便。
9. 操作逻辑无漏洞,保证数据是操作安全的
多个页面间的操作和同个页面上的多个操作间的逻辑关系在设计上是安全和严谨的。保证不会出现不被允许的用户操作组合,至少不会因为用户的不适当的操作导致出错。
这最典型的表现则是在页面上广泛采用的所谓联动下拉框设计。一个下拉框中允许的选项受另一个下拉框中的选择而变。另外一个例子是根据选择使表单元素有效或者失效。如果在多个页面间也要维持某种合法性逻辑,那么就需要服务器端脚本的参与。这样会使表单设计跟操作有关,应该说这不是一个好的设计。可以通过变更操作步骤顺序、组合方式来尽可能避免这种情况出现。
操作逻辑的设计既要保证用户任意的输入不会导致错误,也要保证是用户输入的数据能购被安全处理。在Session控制下的表单中输入大幅文字可能会导致超时出错,这时候往往还伴随重定向过程,导致用户的长篇输入荡然无存。用JavaScript提醒用户已超时,请保存输入后重新提交,是一个好办法。某些表单元素如<input type=”text”>接受ESC键清除数据,并且无法撤销,这也是很危险的。在中文输入法中常常使用ESC键清楚输入的码位,一旦不小心多按一下ESC就会使得输入数据消失。因此有必要用JavaScript禁用<input>和<textarea>的ESC键处理过程。
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=413028
1.下载
FCKeditor2.3 (FCKeditot for java)
FCKeditor2.4 (FCKeditor基本文件)
以下是下载地址:
http://www.fckeditor.net/download/default.html
2.建立项目:
建立项目tomcat/webapps/TestFCKeditor.
3.将FCKeditor2.4解压缩
将FCKeditor2.4解压缩,将整个目录FCKeditor复制到项目的根目录下,并将解压缩出来的文件夹fckeditor重命名为FCKeditor
目录结构为:tomcat/webapps/TestFCKeditor/FCKeditor
然后将FCKeditor-2.3.zip(java)压缩包中\web\WEB-INF\lib\目录下的两个jar文件拷到项目的\WEB-INF\ lib\目录下。把其中的src目录下的FCKeditor.tld文件copy到TestFCKedit/WEB-INF/下
4.合并web.xml:
将FCKeditor-2.3.zip压缩包中\web\WEB-INF\目录下的web.xml文件合并到项目的\WEB-INF\目录下的web.xml文件中。
5. 修改合并后的web.xml文件
修改合并后的web.xml文件,将名为SimpleUploader的Servlet的enabled参数值改为true,
以允许上传功能,Connector Servlet的baseDir参数值用于设置上传文件存放的位置。
在web.xml最后添加标签定义:
<taglib>
<taglib-uri>/TestFCKeditor</taglib-uri>
<taglib-location>/WEB-INF/FCKeditor.tld</taglib-location>
</taglib>
现在的web.xml文件没有<taglib>标签了,应该直接在jsp文件中使用:<%@ taglib uri="http://fckeditor.net/tags-fckeditor" prefix="FCK" %>
6. 映射:
上面文件中两个servlet的映射分别为:/editor/filemanager/browser/default/connectors/jsp/connector
和/editor/filemanager/upload/simpleuploader,需要在两个映射前面加上/FCKeditor,
即改为/FCKeditor/editor/filemanager/browser/default/connectors/jsp/connector和
/FCKeditor/editor/filemanager/upload/simpleuploader。
这两个名字根据你放在工程中的FCKeditor文件夹名称而定。。
7.修改skin文件夹
进入skin文件夹,如果你想使用fckeditor默认的这种奶黄色,
那就把除了default文件夹外的另两个文件夹直接删除.(建议不删除,以后要用到其中的一个文件夹)
8.删除无用文件
删除/FCKeditor/目录下除fckconfig.js, fckeditor.js, fckstyles.xml, fcktemplates.xml四个文件以外的所有文件,保留文件夹editor
删除目录/editor/_source,
删除/editor/filemanager/browser/default/connectors/下的所有文件
删除/editor/filemanager/upload/下的所有文件
删除/editor/lang/下的除了fcklanguagemanager.js(我下载的没有这个文件), en.js, zh.js, zh-cn.js四个文件的所有文件
9.修改配置:
打开/FCKeditor/fckconfig.js
修改 FCKConfig.DefaultLanguage = 'zh-cn' ;
把FCKConfig.LinkBrowserURL等的值替换成以下内容:
FCKConfig.LinkBrowserURL
= FCKConfig.BasePath + "filemanager/browser/default/browser.html?Connector=connectors/jsp/connector" ;
FCKConfig.ImageBrowserURL
= FCKConfig.BasePath + "filemanager/browser/default/browser.html?Type=Image&Connector=connectors/jsp/connector" ;
FCKConfig.FlashBrowserURL
= FCKConfig.BasePath + "filemanager/browser/default/browser.html?Type=Flash&Connector=connectors/jsp/connector" ;
FCKConfig.LinkUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=File' ;
FCKConfig.FlashUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=Flash' ;
FCKConfig.ImageUploadURL = FCKConfig.BasePath + 'filemanager/upload/simpleuploader?Type=Image' ;
10.其它
fckconfig.js总配置文件,可用记录本打开,修改后将文件存为utf-8 编码格式。找到:
FCKConfig.TabSpaces = 0 ; 改为FCKConfig.TabSpaces = 1 ; 即在编辑器域内可以使用Tab键。
如果你的编辑器还用在网站前台的话,比如说用于留言本或是日记回复时,那就不得不考虑安全了,
在前台千万不要使用Default的toolbar,要么自定义一下功能,要么就用系统已经定义好的Basic,
也就是基本的toolbar,找到:
FCKConfig.ToolbarSets["Basic"] = [
['Bold','Italic','-','OrderedList','UnorderedList','-',/*'Link',*/'Unlink','-','Style','FontSize','TextColor','BGColor','-',
'Smiley','SpecialChar','Replace','Preview'] ] ;
这是改过的Basic,把图像功能去掉,把添加链接功能去掉,因为图像和链接和flash和图像按钮添加功能都能让前台
页直接访问和上传文件, fckeditor还支持编辑域内的鼠标右键功能。
FCKConfig.ContextMenu = ['Generic',/*'Link',*/'Anchor',/*'Image',*/'Flash','Select','Textarea','Checkbox','Radio','TextField','HiddenField',
/*'ImageButton',*/'Button','BulletedList','NumberedList','TableCell','Table','Form'] ;
这也是改过的把鼠标右键的“链接、图像,FLASH,图像按钮”功能都去掉。
找到: FCKConfig.FontNames = 'Arial;Comic Sans MS;Courier New;Tahoma;Times New Roman;Verdana' ;
加上几种我们常用的字体
FCKConfig.FontNames
= '宋体;黑体;隶书;楷体_GB2312;Arial;Comic Sans MS;Courier New;Tahoma;Times New Roman;Verdana' ;
11.添加文件
添加文件 /TestFCKeditor/test.jsp:
<%@ page language="java" import="com.fredck.FCKeditor.*" %>
<%@ taglib uri="/TestFCKeditor" prefix="FCK" %>
<script type="text/javascript" src="/TestFCKeditor/FCKeditor/fckeditor.js"></script>
<%--
三种方法调用FCKeditor
1.FCKeditor自定义标签 (必须加头文件 <%@ taglib uri="/TestFCKeditor" prefix="FCK" %> )
2.script脚本语言调用 (必须引用 脚本文件 <script type="text/javascript" src="/TestFCKeditor/FCKeditor/fckeditor.js"></script> )
3.FCKeditor API 调用 (必须加头文件 <%@ page language="java" import="com.fredck.FCKeditor.*" %> )
--%>
//标签调用方式
<%--
<form action="show.jsp" method="post" target="_blank">
<FCK:editor id="content" basePath="/TestFCKeditor/FCKeditor/"
width="700"
height="500"
skinPath="/TestFCKeditor/FCKeditor/editor/skins/silver/"
toolbarSet = "Default"
>
input
</FCK:editor>
<input type="submit" value="Submit">
</form>
--%>
//JS调用方式
<form action="show.jsp" method="post" target="_blank">
<table border="0" width="700"><tr><td>
<textarea id="content" name="content" style="WIDTH: 100%; HEIGHT: 400px">input</textarea>
<script type="text/javascript">
var oFCKeditor = new FCKeditor('content') ;
oFCKeditor.BasePath = "/TestFCKeditor/FCKeditor/" ;
oFCKeditor.Height = 400;
oFCKeditor.ToolbarSet = "Default" ;
oFCKeditor.ReplaceTextarea();
</script>
<input type="submit" value="Submit">
</td></tr></table>
</form>
//FCKeditor API 调用
<%--
<form action="show.jsp" method="post" target="_blank">
<%
FCKeditor oFCKeditor ;
oFCKeditor = new FCKeditor( request, "content" ) ;
oFCKeditor.setBasePath( "/TestFCKeditor/FCKeditor/" ) ;
oFCKeditor.setValue( "input" );
out.println( oFCKeditor.create() ) ;
%>
<br>
<input type="submit" value="Submit">
</form>
--%>
添加文件/TestFCKeditor/show.jsp:
<%
String content = request.getParameter("content");
out.print(content);
%>
12.测试
浏览http://localhost:8080/TestFCKeditor/test.jsp
最后注意。。不同的版本 变量名称可能不一样。请参考你使用的API文档
配置选项:
AutoDetectLanguage=true/false 自动检测语言
BaseHref="" 相对链接的基地址
ContentLangDirection="ltr/rtl" 默认文字方向
ContextMenu=字符串数组,右键菜单的内容
CustomConfigurationsPath="" 自定义配置文件路径和名称
Debug=true/false 是否开启调试功能,这样,当调用FCKDebug.Output()时,会在调试窗中输出内容
DefaultLanguage="" 缺省语言
EditorAreaCss="" 编辑区的样式表文件
EnableSourceXHTML=true/false 为TRUE时,当由可视化界面切换到代码页时,把HTML处理成XHTML
EnableXHTML=true/false 是否允许使用XHTML取代HTML
FillEmptyBlocks=true/false 使用这个功能,可以将空的块级元素用空格来替代
FontColors="" 设置显示颜色拾取器时文字颜色列表
FontFormats="" 设置显示在文字格式列表中的命名
FontNames="" 字体列表中的字体名
FontSizes="" 字体大小中的字号列表
ForcePasteAsPlainText=true/false 强制粘贴为纯文本
ForceSimpleAmpersand=true/false 是否不把&符号转换为XML实体
FormatIndentator="" 当在源码格式下缩进代码使用的字符
FormatOutput=true/false 当输出内容时是否自动格式化代码
FormatSource=true/false 在切换到代码视图时是否自动格式化代码
FullPage=true/false 是否允许编辑整个HTML文件,还是仅允许编辑BODY间的内容
GeckoUseSPAN=true/false 是否允许SPAN标记代替B,I,U标记
IeSpellDownloadUrl=""下载拼写检查器的网址
ImageBrowser=true/false 是否允许浏览服务器功能
ImageBrowserURL="" 浏览服务器时运行的URL
ImageBrowserWindowHeight="" 图像浏览器窗口高度
ImageBrowserWindowWidth="" 图像浏览器窗口宽度
LinkBrowser=true/false 是否允许在插入链接时浏览服务器
LinkBrowserURL="" 插入链接时浏览服务器的URL
LinkBrowserWindowHeight=""链接目标浏览器窗口高度
LinkBrowserWindowWidth=""链接目标浏览器窗口宽度
Plugins=object 注册插件
PluginsPath="" 插件文件夹
ShowBorders=true/false 合并边框
SkinPath="" 皮肤文件夹位置
SmileyColumns=12 图符窗列数
SmileyImages=字符数组 图符窗中图片文件名数组
SmileyPath="" 图符文件夹路径
SmileyWindowHeight 图符窗口高度
SmileyWindowWidth 图符窗口宽度
SpellChecker="ieSpell/Spellerpages" 设置拼写检查器
StartupFocus=true/false 开启时FOCUS到编辑器
StylesXmlPath="" 设置定义CSS样式列表的XML文件的位置
TabSpaces=4 TAB键产生的空格字符数
ToolBarCanCollapse=true/false 是否允许展开/折叠工具栏
ToolbarSets=object 允许使用TOOLBAR集合
ToolbarStartExpanded=true/false 开启是TOOLBAR是否展开
UseBROnCarriageReturn=true/false 当回车时是产生BR标记还是P或者DIV标记
解决上传乱码:
在SimpleUploaderServlet.java和ConnectorServlet.java两个文件里找到
DiskFileUpload upload = new DiskFileUpload();
分别在其后加入 upload.setHeaderEncoding("utf-8");
这样解决了文件上传的中文乱码问题.
但是在控制台显示的中文内容还是乱码,但是没关系,我们没必要去看控制台下的中文
----------------------------------------------------------------another
FCKeditor2.4.2 Java版使用说明
下载地址以及基本配置请参考:http://hi.baidu.com/wain19/blog/item/c33fb0fab74f24dfb48f312d.html
我的开发环境是ubuntu7.04, 系统默认编码是utf-8,
期间,本人遇到了下面这些问题:
问题一:XML request error: Internel Server Error(500)
出现错误的地方是在:点插入图片,点Browse按钮的时候:
XML request error: Internel Server Error(500)
找资料:http://lamono.javaeye.com/blog/49135
拷贝xalan.jar和serialize.jar到/WEB-INF/lib,问题解决。
FCKeditor-java没有很好的解决中文问题。需要我们修改它的源代码后重新编译打包。打包过程如下:
1。 新建一个web工程名字为FCKeditor-java-2.3,然后把FCKeditor-2.3-java.zip解压缩后的代码拷贝到工程目录下。
2。如果是用的Eclipse,使用快捷键Ctrl+Shift+R
在SimpleUploaderServlet.java和ConnectorServlet.java两个文件里找到
DiskFileUpload upload = new DiskFileUpload();
分别在其后加入 upload.setHeaderEncoding("utf-8");
现在如果直接运行ant任务,会报下面的错误:
taskdef class org.apache.catalina.ant.DeployTask cannot be found
3。 把tomcat安装目录下/server/lib中的catalina-ant.jar拷贝到/WEB-INF/lib目录 下。
4。 打开build.xml
找到
<property name="catalina.home"
修改成你自己的tomcat安装目录
<property name="catalina.home" value="/home/uniquejava/tool/tomcat5028/"/>
找到
<taskdef name="deploy"
修改成如下内容
<taskdef name="deploy" classname="org.apache.catalina.ant.DeployTask">
<classpath refid="compile.classpath">
</classpath>
</taskdef>
<taskdef name="list" classname="org.apache.catalina.ant.ListTask">
<classpath refid="compile.classpath">
</classpath>
</taskdef>
<taskdef name="reload" classname="org.apache.catalina.ant.ReloadTask">
<classpath refid="compile.classpath">
</classpath>
</taskdef>
<taskdef name="undeploy" classname="org.apache.catalina.ant.UndeployTask">
<classpath refid="compile.classpath">
</classpath>
</taskdef>
运行ant任务dist, 就可以生成新的FCKeditor-2.3.jar包
问题三: 上传时新建的中文目录全部乱码。 虽然上传到服务器上的文件名正常,但在JSP页面点下载链接时文件名乱码导致不能正常下载。
查找资料:TOMCAT 链接参数有中文时,乱码解决方法
http://hi.baidu.com/jadestone/blog/item/7564deefc9192d36acafd5be.html
修改tomcat-home/conf/server.xml
方法一:
在两处地方加上URIEncoding="utf-8":
<Connector port="8080"
maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
debug="0" connectionTimeout="20000"
disableUploadTimeout="true" URIEncoding="utf-8" />
<Connector port="8009"
enableLookups="false" redirectPort="8443" debug="0"
protocol="AJP/1.3" URIEncoding="utf-8" />
方法二:
使用useBodyEncodingForURI="true". 这个方法适合你的TOMCAT实例下需要跑多个不同Encoding的程序时。(有点怀疑?!)
<... maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
enableLookups="false" redirectPort="8443" acceptCount="100"
connectionTimeout="20000" disableUploadTimeout="true" useBodyEncodingForURI="true" />
enableLookups="false" redirectPort="8443" protocol="AJP/1.3" useBodyEncodingForURI="true" /
我只试了方法一,问题解决!
这样, FCKeditor终于可以正常使用了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
=================================================================
以下为个人原创 http://blog.csdn.net/nickshen3/
怎么将FCKeditor2.4使用在自己的web中。
1. 在webapps创建test文件夹。webapps\test
2. 将下载的FCKeditor2.3的Java包FCKeditor-2.3.zip解压缩,并将web文件夹下的两个文件夹_samples,WEB-INF拷贝到刚才建立的webapps\test下。再将src文件夹下的FCKeditor.tld拷贝到WEB-INF下。
3. 在webapps\test\下建立文件夹FCKeditor 。
4. 将刚才下载的 FCKeditor_2.4.3.zip 解压缩,然后将fckeditor文件夹下的 editor(文件夹),fckconfig.js,fckeditor.js,fckstyles.xml,fcktemplates.xml。四个文件拷贝到刚才建立的文件夹FCKeditor下。
5.修改test\_samples\jsp\sample02.jsp文件,将原来的
<FCK:editor id="EditorDefault" basePath="/FCKeditor/"
改为<FCK:editor id="EditorDefault" basePath="/test/FCKeditor/"。以后使用的时候只需将test换成项目的名称即可。
6.打开tomcat。测试Http://localhost:8080/test/_samples/jsp/sample02.jsp。
ok。
另外 : http://www.blogjava.net/youxia/archive/2007/03/15/104077.html
一、数组转成字符串:
1、 将数组中的字符转换为一个字符串
将数组中的字符转换为一个字符串
@param strToConv 要转换的字符串 ,默认以逗号分隔
@return 返回一个字符串
String[3] s={"a","b","c"}
StringUtil.convString(s)="a,b,c"
2、 static public String converString(String strToConv)
@param strToConv 要转换的字符串 ,
@param conv 分隔符,默认以逗号分隔
@return 同样返回一个字符串
String[3] s={"a","b","c"}
StringUtil.convString(s,"@")="a@b@c"
static public String converString(String strToConv, String conv)
二、空值检测:
3、
Checks if a String is empty ("") or null.
判断一个字符串是否为空,空格作非空处理。 StringUtils.isEmpty(null) = true StringUtils.isEmpty("") = true StringUtils.isEmpty(" ") = false StringUtils.isEmpty("bob") = false StringUtils.isEmpty(" bob ") = false
NOTE: This method changed in Lang version 2.0.
It no longer trims the String.
That functionality is available in isBlank().
@param str the String to check, may be null
@return true if the String is empty or null
public static boolean isEmpty(String str)
三、非空处理:
4、
Checks if a String is not empty ("") and not null.
判断一个字符串是否非空,空格作非空处理. StringUtils.isNotEmpty(null) = false StringUtils.isNotEmpty("") = false StringUtils.isNotEmpty(" ") = true StringUtils.isNotEmpty("bob") = true StringUtils.isNotEmpty(" bob ") = true
@param str the String to check, may be null
@return true if the String is not empty and not null
public static boolean isNotEmpty(String str)
5、
Checks if a String is not empty (""), not null and not whitespace only.
判断一个字符串是否非空,空格作空处理. StringUtils.isNotBlank(null) = false StringUtils.isNotBlank("") = false StringUtils.isNotBlank(" ") = false StringUtils.isNotBlank("bob") = true StringUtils.isNotBlank(" bob ") = true
@param str the String to check, may be null
@return true if the String is
not empty and not null and not whitespace
@since 2.0
public static boolean isNotBlank(String str)
四、 空格处理
6、
Removes control characters (char <= 32) from both
ends of this String, handling null by returning
null.
The String is trimmed using {@link String#trim()}.
Trim removes start and end characters <= 32.
To strip whitespace use {@link //strip(String)}.
To trim your choice of characters, use the
{@link //strip(String, String)} methods.
格式化一个字符串中的空格,有非空判断处理; StringUtils.trim(null) = null StringUtils.trim("") = "" StringUtils.trim(" ") = "" StringUtils.trim("abc") = "abc" StringUtils.trim(" abc ") = "abc"
@param str the String to be trimmed, may be null
@return the trimmed string, null if null String input
public static String trim(String str)
7、
Removes control characters (char <= 32) from both
ends of this String returning null if the String is
empty ("") after the trim or if it is null.
The String is trimmed using {@link String#trim()}.
Trim removes start and end characters <= 32.
To strip whitespace use {@link /stripToNull(String)}.
格式化一个字符串中的空格,有非空判断处理,如果为空返回null; StringUtils.trimToNull(null) = null StringUtils.trimToNull("") = null StringUtils.trimToNull(" ") = null StringUtils.trimToNull("abc") = "abc" StringUtils.trimToNull(" abc ") = "abc"
@param str the String to be trimmed, may be null
@return the trimmed String,
null if only chars <= 32, empty or null String input
@since 2.0
public static String trimToNull(String str)
8、
Removes control characters (char <= 32) from both
ends of this String returning an empty String ("") if the String
is empty ("") after the trim or if it is null.
The String is trimmed using {@link String#trim()}.
Trim removes start and end characters <= 32.
To strip whitespace use {@link /stripToEmpty(String)}.
格式化一个字符串中的空格,有非空判断处理,如果为空返回""; StringUtils.trimToEmpty(null) = "" StringUtils.trimToEmpty("") = "" StringUtils.trimToEmpty(" ") = "" StringUtils.trimToEmpty("abc") = "abc" StringUtils.trimToEmpty(" abc ") = "abc"
@param str the String to be trimmed, may be null
@return the trimmed String, or an empty String if null input
@since 2.0
public static String trimToEmpty(String str)
五、 字符串比较:
9、
Compares two Strings, returning true if they are equal.
nulls are handled without exceptions. Two null
references are considered to be equal. The comparison is case sensitive.
判断两个字符串是否相等,有非空处理。 StringUtils.equals(null, null) = true StringUtils.equals(null, "abc") = false StringUtils.equals("abc", null) = false StringUtils.equals("abc", "abc") = true StringUtils.equals("abc", "ABC") = false
@param str1 the first String, may be null
@param str2 the second String, may be null
@return true if the Strings are equal, case sensitive, or
both null
@see java.lang.String#equals(Object)
public static boolean equals(String str1, String str2)
10、
Compares two Strings, returning true if they are equal ignoring
the case.
nulls are handled without exceptions. Two null
references are considered equal. Comparison is case insensitive.
判断两个字符串是否相等,有非空处理。忽略大小写 StringUtils.equalsIgnoreCase(null, null) = true StringUtils.equalsIgnoreCase(null, "abc") = false StringUtils.equalsIgnoreCase("abc", null) = false StringUtils.equalsIgnoreCase("abc", "abc") = true StringUtils.equalsIgnoreCase("abc", "ABC") = true
@param str1 the first String, may be null
@param str2 the second String, may be null
@return true if the Strings are equal, case insensitive, or
both null
@see java.lang.String#equalsIgnoreCase(String)
public static boolean equalsIgnoreCase(String str1, String str2)
六、 IndexOf 处理
11、
Finds the first index within a String, handling null.
This method uses {@link String#indexOf(String)}.
A null String will return -1.
返回要查找的字符串所在位置,有非空处理 StringUtils.indexOf(null, *) = -1 StringUtils.indexOf(*, null) = -1 StringUtils.indexOf("", "") = 0 StringUtils.indexOf("aabaabaa", "a") = 0 StringUtils.indexOf("aabaabaa", "b") = 2 StringUtils.indexOf("aabaabaa", "ab") = 1 StringUtils.indexOf("aabaabaa", "") = 0
@param str the String to check, may be null
@param searchStr the String to find, may be null
@return the first index of the search String,
-1 if no match or null string input
@since 2.0
public static int indexOf(String str, String searchStr)
12、
Finds the first index within a String, handling null.
This method uses {@link String#indexOf(String, int)}.
A null String will return -1.
A negative start position is treated as zero.
An empty ("") search String always matches.
A start position greater than the string length only matches
an empty search String.
返回要由指定位置开始查找的字符串所在位置,有非空处理 StringUtils.indexOf(null, *, *) = -1 StringUtils.indexOf(*, null, *) = -1 StringUtils.indexOf("", "", 0) = 0 StringUtils.indexOf("aabaabaa", "a", 0) = 0 StringUtils.indexOf("aabaabaa", "b", 0) = 2 StringUtils.indexOf("aabaabaa", "ab", 0) = 1 StringUtils.indexOf("aabaabaa", "b", 3) = 5 StringUtils.indexOf("aabaabaa", "b", 9) = -1 StringUtils.indexOf("aabaabaa", "b", -1) = 2 StringUtils.indexOf("aabaabaa", "", 2) = 2 StringUtils.indexOf("abc", "", 9) = 3
@param str the String to check, may be null
@param searchStr the String to find, may be null
@param startPos the start position, negative treated as zero
@return the first index of the search String,
-1 if no match or null string input
@since 2.0
public static int indexOf(String str, String searchStr, int startPos)
七、 子字符串处理:
13、
Gets a substring from the specified String avoiding exceptions.
A negative start position can be used to start n
characters from the end of the String.
A null String will return null.
An empty ("") String will return "".
返回指定位置开始的字符串中的所有字符 StringUtils.substring(null, *) = null StringUtils.substring("", *) = "" StringUtils.substring("abc", 0) = "abc" StringUtils.substring("abc", 2) = "c" StringUtils.substring("abc", 4) = "" StringUtils.substring("abc", -2) = "bc" StringUtils.substring("abc", -4) = "abc"
@param str the String to get the substring from, may be null
@param start the position to start from, negative means
count back from the end of the String by this many characters
@return substring from start position, null if null String input
public static String substring(String str, int start)
14、
Gets a substring from the specified String avoiding exceptions.
A negative start position can be used to start/end n
characters from the end of the String.
The returned substring starts with the character in the start
position and ends before the end position. All postion counting is
zero-based -- i.e., to start at the beginning of the string use
start = 0. Negative start and end positions can be used to
specify offsets relative to the end of the String.
If start is not strictly to the left of end, ""
is returned.
返回由开始位置到结束位置之间的子字符串 StringUtils.substring(null, *, *) = null StringUtils.substring("", * , *) = ""; StringUtils.substring("abc", 0, 2) = "ab" StringUtils.substring("abc", 2, 0) = "" StringUtils.substring("abc", 2, 4) = "c" StringUtils.substring("abc", 4, 6) = "" StringUtils.substring("abc", 2, 2) = "" StringUtils.substring("abc", -2, -1) = "b" StringUtils.substring("abc", -4, 2) = "ab"
@param str the String to get the substring from, may be null
@param start the position to start from, negative means
count back from the end of the String by this many characters
@param end the position to end at (exclusive), negative means
count back from the end of the String by this many characters
@return substring from start position to end positon,
null if null String input
public static String substring(String str, int start, int end)
15、 SubStringAfter/SubStringBefore(前后子字符串处理:
Gets the substring before the first occurance of a separator.
The separator is not returned.
A null string input will return null.
An empty ("") string input will return the empty string.
A null separator will return the input string.
返回指定字符串之前的所有字符 StringUtils.substringBefore(null, *) = null StringUtils.substringBefore("", *) = "" StringUtils.substringBefore("abc", "a") = "" StringUtils.substringBefore("abcba", "b") = "a" StringUtils.substringBefore("abc", "c") = "ab" StringUtils.substringBefore("abc", "d") = "abc" StringUtils.substringBefore("abc", "") = "" StringUtils.substringBefore("abc", null) = "abc"
@param str the String to get a substring from, may be null
@param separator the String to search for, may be null
@return the substring before the first occurance of the separator,
null if null String input
@since 2.0
public static String substringBefore(String str, String separator)
16、
Gets the substring after the first occurance of a separator.
The separator is not returned.
A null string input will return null.
An empty ("") string input will return the empty string.
A null separator will return the empty string if the
input string is not null.
返回指定字符串之后的所有字符 StringUtils.substringAfter(null, *) = null StringUtils.substringAfter("", *) = "" StringUtils.substringAfter(*, null) = "" StringUtils.substringAfter("abc", "a") = "bc" StringUtils.substringAfter("abcba", "b") = "cba" StringUtils.substringAfter("abc", "c") = "" StringUtils.substringAfter("abc", "d") = "" StringUtils.substringAfter("abc", "") = "abc"
@param str the String to get a substring from, may be null
@param separator the String to search for, may be null
@return the substring after the first occurance of the separator,
null if null String input
@since 2.0
public static String substringAfter(String str, String separator)
17、
Gets the substring before the last occurance of a separator.
The separator is not returned.
A null string input will return null.
An empty ("") string input will return the empty string.
An empty or null separator will return the input string.
返回最后一个指定字符串之前的所有字符 StringUtils.substringBeforeLast(null, *) = null StringUtils.substringBeforeLast("", *) = "" StringUtils.substringBeforeLast("abcba", "b") = "abc" StringUtils.substringBeforeLast("abc", "c") = "ab" StringUtils.substringBeforeLast("a", "a") = "" StringUtils.substringBeforeLast("a", "z") = "a" StringUtils.substringBeforeLast("a", null) = "a" StringUtils.substringBeforeLast("a", "") = "a"
@param str the String to get a substring from, may be null
@param separator the String to search for, may be null
@return the substring before the last occurance of the separator,
null if null String input
@since 2.0
public static String substringBeforeLast(String str, String separator)
18、
Gets the substring after the last occurance of a separator.
The separator is not returned.
A null string input will return null.
An empty ("") string input will return the empty string.
An empty or null separator will return the empty string if
the input string is not null.
返回最后一个指定字符串之后的所有字符 StringUtils.substringAfterLast(null, *) = null StringUtils.substringAfterLast("", *) = "" StringUtils.substringAfterLast(*, "") = "" StringUtils.substringAfterLast(*, null) = "" StringUtils.substringAfterLast("abc", "a") = "bc" StringUtils.substringAfterLast("abcba", "b") = "a" StringUtils.substringAfterLast("abc", "c") = "" StringUtils.substringAfterLast("a", "a") = "" StringUtils.substringAfterLast("a", "z") = ""
@param str the String to get a substring from, may be null
@param separator the String to search for, may be null
@return the substring after the last occurance of the separator,
null if null String input
@since 2.0
public static String substringAfterLast(String str, String separator)
八、 Replacing(字符串替换)
19、
Replaces all occurances of a String within another String.
A null reference passed to this method is a no-op.
以指定字符串替换原来字符串的的指定字符串 StringUtils.replace(null, *, *) = null StringUtils.replace("", *, *) = "" StringUtils.replace("aba", null, null) = "aba" StringUtils.replace("aba", null, null) = "aba" StringUtils.replace("aba", "a", null) = "aba" StringUtils.replace("aba", "a", "") = "aba" StringUtils.replace("aba", "a", "z") = "zbz"
@param text text to search and replace in, may be null
@param repl the String to search for, may be null
@param with the String to replace with, may be null
@return the text with any replacements processed,
null if null String input
@see #replace(String text, String repl, String with, int max)
public static String replace(String text, String repl, String with)
20、
Replaces a String with another String inside a larger String,
for the first max values of the search String.
A null reference passed to this method is a no-op.
以指定字符串最大替换原来字符串的的指定字符串 StringUtils.replace(null, *, *, *) = null StringUtils.replace("", *, *, *) = "" StringUtils.replace("abaa", null, null, 1) = "abaa" StringUtils.replace("abaa", null, null, 1) = "abaa" StringUtils.replace("abaa", "a", null, 1) = "abaa" StringUtils.replace("abaa", "a", "", 1) = "abaa" StringUtils.replace("abaa", "a", "z", 0) = "abaa" StringUtils.replace("abaa", "a", "z", 1) = "zbaa" StringUtils.replace("abaa", "a", "z", 2) = "zbza" StringUtils.replace("abaa", "a", "z", -1) = "zbzz"
@param text text to search and replace in, may be null
@param repl the String to search for, may be null
@param with the String to replace with, may be null
@param max maximum number of values to replace, or -1 if no maximum
@return the text with any replacements processed,
null if null String input
public static String replace(String text, String repl, String with, int max)
九、 Case conversion(大小写转换)
21、
Converts a String to upper case as per {@link String#toUpperCase()}.
A null input String returns null.
将一个字符串变为大写 StringUtils.upperCase(null) = null StringUtils.upperCase("") = "" StringUtils.upperCase("aBc") = "ABC"
@param str the String to upper case, may be null
@return the upper cased String, null if null String input
public static String upperCase(String str) 22、
Converts a String to lower case as per {@link String#toLowerCase()}.
A null input String returns null.
将一个字符串转换为小写 StringUtils.lowerCase(null) = null StringUtils.lowerCase("") = "" StringUtils.lowerCase("aBc") = "abc"
@param str the String to lower case, may be null
@return the lower cased String, null if null String input
public static String lowerCase(String str) 23、
Capitalizes a String changing the first letter to title case as
per {@link Character#toTitleCase(char)}. No other letters are changed.
For a word based alorithm, see {@link /WordUtils#capitalize(String)}.
A null input String returns null.
StringUtils.capitalize(null) = null StringUtils.capitalize("") = "" StringUtils.capitalize("cat") = "Cat" StringUtils.capitalize("cAt") = "CAt"
@param str the String to capitalize, may be null
@return the capitalized String, null if null String input
@see /WordUtils#capitalize(String)
@see /uncapitalize(String)
@since 2.0
将字符串中的首字母大写
public static String capitalize(String str)
如何利用网页弹出各种形式的窗口,我想大家大多都是知道些的,但那种多种多样的弹出式窗口是怎么搞出来的,我们今天就来学习一下:
1.弹启一个全屏窗口
<html>
<body onload="window.open('http://www.baidu.com','example01','fullscreen');">;
<b>www.188sky.com</b>
</body>
</html>
2.弹启一个被F11化后的窗口
<html>
<body onload="window.open(''http://www.baidu.com','example02','channelmode');">;
<b>www.188sky.com</b>
</body>
</html>
3.弹启一个带有收藏链接工具栏的窗口
<html>
<body onload="window.open('www.baidu.com','example03','width=400,height=300,directories');">
<b>www.baidu.com</b>
</body>
</html>
4.网页对话框
<html>
<SCRIPT LANGUAGE="javascript">
<!--
showModalDialog('http://www.baidu.com,'example04','dialogWidth:400px;dialogHeight:300px;
dialogLeft:200px;dialogTop:150px;center:yes;help:yes;resizable:yes;status:yes')
//-->
</SCRIPT>
<b>www.188sky.com</b>
</body>
</html>
<html>
<SCRIPT LANGUAGE="javascript">
<!--
showModelessDialog('http://www.baidu.com,'example05','dialogWidth:400px;dialogHeight:300px;
dialogLeft:200px;dialogTop:150px;center:yes;help:yes;resizable:yes;status:yes')
//-->
</SCRIPT>
<b>http://www.baidu.com</b>
</body>
</html>
showModalDialog()或是showModelessDialog() 来调用网页对话框,至于showModalDialog()与showModelessDialog()的区别,在于showModalDialog()打开的窗口(简称模式窗口),置在父窗口上,必须关闭才能访问父窗口(建议尽量少用,以免招人反感);showModelessDialog()
dialogHeight: iHeight 设置对话框窗口的高度。
dialogWidth: iWidth 设置对话框窗口的宽度。
dialogLeft: iXPos 设置对话框窗口相对于桌面左上角的left位置。
dialogTop: iYPos 设置对话框窗口相对于桌面左上角的top位置。
center: {yes | no | 1 | 0 } 指定是否将对话框在桌面上居中,默认值是“yes”。
help: {yes | no | 1 | 0 } 指定对话框窗口中是否显示上下文敏感的帮助图标。默认值是“yes”。
resizable: {yes | no | 1 | 0 } 指定是否对话框窗口大小可变。默认值是“no”。
status: {yes | no | 1 | 0 } 指定对话框窗口是否显示状态栏。对于非模式对话框窗口,默认值是“yes”;对于模式对话框窗口,默认值是 “no”。
5、其他弹出窗口代码
经常上网的朋友可能到过这样一些网站,一进入首页立刻会弹出一个窗口,或者按一个链接或按钮弹出,通常在这个窗口里会显示一些注意事项、版权信息、警告、欢迎光顾之类的话或者作者想要特别提示的信息。其实制作这样的页面非常容易,只要往该页面的HTML里加入几段java script代码即可实现。下面我就带你剖析它的奥秘。
【最基本的弹出窗口代码】
其实代码非常简单:
<SCRIPT LANGUAGE="java script">
<!--
window.open (’page.html’)
-->
</SCRIPT>
因为这是一段java script代码,所以它们应该放在<SCRIPT LANGUAGE ="java script">标签和</script>之间。<!--和-->是对一些版本低的浏览器起作用,在这些老浏览器中如果不支持java script,不会将标签中的代码作为文本显示出来。
Window.open (’page.html’)用于控制弹出新的窗口page.html,如果page.html不与主窗口在同一路径下,前面应写明路径,绝对路径(http://)和相对路径(../)均可。
用单引号和双引号都可以,只是不要混用。
这一段代码可以加入HTML的任意位置,加入到<head>和</head>之间也可以,位置越靠前执行越早,尤其是页面代码较长时,又想使页面早点弹出就尽量往前放。
【经过设置后的弹出窗口】
下面再说一说弹出窗口外观的设置。只要再往上面的代码中加一点东西就可以了。
我们来定制这个弹出窗口的外观、尺寸大小、弹出位置以适应该页面的具体情况。
<SCRIPT LANGUAGE="java script:>
<!--
window.open (’page.html’,’newwindow’,’height=100,width=400,top=0,left=0,toolbar=no,menubar=no,scrollbars=no,resizable=no,
location=no,status=no’)
//写成一行
-->
</SCRIPT>
参数解释:
<SCRIPT LANGUAGE="java script"> js脚本开始;
window.open 弹出新窗口的命令;
page.html 弹出新窗口的文件名;
newwindow 弹出窗口的名字(不是文件名),可用空 ″代替;
height=100 窗口高度;
top=0 窗口距离屏幕上方的像素值;
left=0 窗口距离屏幕左侧的像素值;
toolbar=no 是否显示工具栏,yes为显示;
menubar,scrollbars 表示菜单栏和滚动栏;
resizable=no 是否允许改变窗口大小,yes为允许;
location=no 是否显示地址栏,yes为允许;
status=no 是否显示状态栏内的信息(通常是文件已经打开),yes为允许;
</SCRIPT> js脚本结束。
【用函数控制弹出窗口】
下面是一个完整的代码。
<html>
<head>
<script LANGUAGE="java script">
<!--
function openwin(){
window.open("page.html","newwindow","height=100,width=400,toolbar=no,menubar=no,scrollbars=no,resizable=no,
location=no,status=no";)
//写成一行
}
-->
</script>
</head>
<body onload="openwin()">
...任意的页面内容...
</body>
</html>
这里定义了一个函数openwin(),函数内容就是打开一个窗口。在调用它之前没有任何用途。怎么调用呢?
方法一:<body onload="openwen()"> 浏览器读页面时弹出窗口;
方法二:<body onunload="openwen()"> 浏览器离开页面时弹出窗口;
方法三:用一个连接调用:<a href="#" onclick="openwin()">打开一个窗口</a>
注意:使用的"#"是虚连接。
方法四:用一个按钮调用:<input type="button" onclick="openwin()" value="打开窗口">
【主窗口打开文件1.htm,同时弹出小窗口page.html】
将如下代码加入主窗口<head>区:
<script language="java script">
<!--
function openwin(){
window.open("page.html","","width=200,height=200" ;)
}
//-->
</script>
加入<body>区:<a href="1.htm" onclick="openwin()">open</a>即可。
【弹出的窗口之定时关闭控制】
下面我们再对弹出窗口进行一些控制,效果就更好了。如果我们再将一小段代码加入弹出的页面(注意是加入到page.html的HTML中,可不是主页面中,否则…),让它在10秒钟后自动关闭是不是更酷了?
首先,将如下代码加入page.html文件的<head>区:
<script language="java script">
function closeit() {
setTimeout("self.close()",10000) //毫秒
}
</script>
然后,再用<body onload="closeit()">这一句话代替page.html中原有的<BODY>这一句就可以了。(这一句话千万不要忘记写啊!这一句的作用是调用关闭窗口的代码,10秒钟后就自行关闭该窗口。)
【在弹出窗口中加上一个关闭按钮】
<form>
<INPUT TYPE=’BUTTON’ value=’关闭’ onClick=’window.close()’>
</form>
呵呵,现在更加完美了!
【内包含的弹出窗口——一个页面两个窗口】
上面的例子都包含两个窗口,一个是主窗口,另一个是弹出的小窗口。
通过下面的例子,你可以在一个页面内完成上面的效果。
<html>
<head>
<SCRIPT LANGUAGE="java script">
function openwin()
{
OpenWindow=window.open("","newwin","height=250,width=250,toolbar=no,scrollbars="+scroll+",menubar=no";);
//写成一行
OpenWindow.document.write("<TITLE>例子</TITLE>" ;)
OpenWindow.document.write("<BODY BGCOLOR=#FFFFFF>" ;)
OpenWindow.document.write("<H1>Hello!</h1>" ;)
OpenWindow.document.write("New window opened!" ;)
OpenWindow.document.write("</BODY >" ;)
OpenWindow.document.write("</HTML>" ;)
OpenWindow.document.close()
}
</script>
</head>
<body>
<a href="#" onclick="openwin()">打开一个窗口</a>
<input type="button" onclick="openwin()" value="打开窗口">
</body>
</html>
看看OpenWindow.document.write()里面的代码不就是标准的HTML吗?只要按照格式写更多的行即可。千万注意多一个标签或少一个标签都会出现错误。记住用OpenWindow.document.close()结束啊。
【终极应用——弹出窗口的Cookie控制】
回想一下,上面的弹出窗口虽然酷,但是有一点小毛病(你沉浸在喜悦之中,一定没有发现吧?)比如你将上面的脚本放在一个需要频繁经过的页面里(例如首页),那么每次刷新这个页面,窗口都会弹出一次,是不是非常烦人?有解决的办法吗?Yes!Follow me。我们使用Cookie来控制一下就可以了。
首先,将如下代码加入主页面HTML的<HEAD>区:
<script>
function openpopup(){
window.open("hello.htm","","width=300,height=300") //自己修改弹出窗口
}
function get_cookie(Name) {
var search = Name + "="
var returnvalue = "";
if (documents.cookie.length > 0) {
offset = documents.cookie.indexOf(search)
if (offset != -1) { // if cookie exists
offset += search.length
// set index of beginning of value
end = documents.cookie.indexOf(";", offset);
// set index of end of cookie value
if (end == -1)
end = documents.cookie.length;
returnvalue=unescape(documents.cookie.substring(offset, end))
}
}
return returnvalue;
}
function loadpopup(){
if (get_cookie('popped')==''){
openpopup()
documents.cookie="popped=yes"
}
}
</script>
</head>
将如下代码键入BODY区:
<body onunload="loadpopup()"> //pop when leave page
或者:
<body onload="loadpopup()"> //pop when enter page
你可以试着刷新一下这个页面或重新进入该页面,窗口再也不会弹出了。真正的Pop-Only-Once!
写到这里,弹出窗口的制作和应用技巧基本上算是讲完了,希望对正在制作网页的朋友有所帮助我就非常欣慰了。
需要注意的是,JS脚本中的大小写最好前后保持一致。
没有菜单、工具栏、地址栏的弹出窗口:
<script language="java script">
<!--
var gt = unescape(’%3e’);
var popup = null;
var over = "Launch Pop-up Navigator";
popup = window.open(’’, ’popupnav’, ’width=500,height=500,resizable=0,scrollbars=auto’); // width=500,height=500为窗口长和宽
if (popup != null) {
if (popup.opener == null) {
popup.opener = self; }
popup.location.href = ’要打开的文件名’;
}
// -->
</script>
方法二:Cookies应用:控制弹出窗口 当我们在一个页面中设置一个POP弹出窗口后,每次只要重新浏览该页面,POP窗口就会自动弹出来,造成不必要的麻烦。那么怎么解决这个问题呢? 我在这里用一个简单的例子讲解一下如何通过操作Cookies让弹出窗口只在第一次浏览该页面时弹出,以后就不再招人烦了!
<script> function openpopwindow() { window.open("hello.htm","","width=200,height=200" //自己修改弹出窗口 } function get_cookie(Name) { var search = Name + "="; var returnvalue = ""; if (documents.cookie.length > 0) { offset = documents.cookie.indexOf(search); if (offset != -1) { // 如果以前有cookies记录 offset += search.length; // 设置cookie的起始位置 end = documents.cookie.indexOf(";", offset); // 设置cookie的结束位置 if (end == -1) end = documents.cookie.length; returnvalue=unescape(documents.cookie.substring(offset, end)) } } return returnvalue; } function loadpopup() { if (get_cookie('popped')=='') { openpopwindow(); documents.cookie="popped=yes"; } } </script>
将上面的代码键入BODY区: <body onunload="loadpopup()"> //离开页面的时候弹出
或者: <body onload="loadpopup()"> //打开页面的时候弹出
【本文版权归作者与奥索网共同拥有,如需转载,请注明作者及出处】
离开页面时弹开窗口效果:
效果:别人关闭这个页面的时候,弹出一个窗口,你可以写一些祝福的话
核心代码:
<script LANGUAGE="javascript">
<!--Begin function leave(){
window.open
('1.htm',",'toolbar=no,menubar=no,location=no,height=225,width=235');
break
}
//END-->
</script>
1。最直接最简单的,方式是把文件地址直接放到html页面的一个链接中。这样做的缺点是把文件在服务器上的路径暴露了,并且还无法对文件下载进行其它的控制(如权限)。这个就不写示例了。
2。在服务器端把文件转换成输出流,写入到response,以response把文件带到浏览器,由浏览器来提示用户是否愿意保存文件到本地。(示例如下)
<%
response.setContentType(fileminitype);
response.setHeader("Location",filename);
response.setHeader("Cache-Control", "max-age=" + cacheTime);
response.setHeader("Content-Disposition", "attachment; filename=" + filename); //filename应该是编码后的(utf-8)
response.setContentLength(filelength);
OutputStream outputStream = response.getOutputStream();
InputStream inputStream = new FileInputStream(filepath);
byte[] buffer = new byte[1024];
int i = -1;
while ((i = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, i);
}
outputStream.flush();
outputStream.close();
inputStream.close();
outputStream = null;
%>
3。既然是JSP的话,还有一种方式就是用Applet来实现文件的下载。不过客户首先得信任你的这个Applet小程序,由这个程序来接受由servlet发送来的数据流,并写入到本地。
servlet端示例
public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType(" text/plain ");
OutputStream outputStream = null;
try {
outputStream = res.getOutputStream();
popFile(srcFile, outputStream)) ;//把文件路径为srcFile的文件写入到outputStream中。
} catch (IOException e) {
e.printStackTrace();
}
}
JApplet端示例
URLConnection con;
try {
con = url.openConnection();//url是被调用的SERVLET的网址 如http://localhost:8080/sendDateSevlet.do
con.setUseCaches(false);
con.setDoInput(true);
con.setDoOutput(true);
con.setRequestProperty("Content-Type",
"application/octet-stream");
InputStream in = con.getInputStream();
ProgressMonitorInputStream pmInputStream = new ProgressMonitorInputStream(
pane, "正在从服务器下载文件内容", in);
ProgressMonitor pMonitor = pmInputStream
.getProgressMonitor();
pMonitor.setMillisToDecideToPopup(3);
pMonitor.setMillisToPopup(3);
String localfilepath = localstr + filename ;//localfilepath本地路径,localstr文件文件夹,filename本地文件名
if(saveFilsaveFilee(localfilepath,pmInputStream)){ //方法saveFilsaveFilee是把输入流pmInputStream写到文件localfilepath中。
openLocalFile(localfilepath);
}
4。顺便把JApplet上传文件的代码也贴上来.
JApplet端示例
URLConnection con;
try {
con = url.openConnection();//url是被调用的SERVLET的网址 如http://localhost:8080/sendDateSevlet.do
con.setUseCaches(false);
con.setDoInput(true);
con.setDoOutput(true);
con.setRequestProperty("Content-Type",
"application/octet-stream");
OutputStream out = con.getOutputStream();
String localfilepath = localstr + filename; //localfilepath本地路径,localstr文件文件夹,filename本地文件名
getOutputStream(localfilepath,out);//文件getOutputStream是把文件localfilepath写到输出流out中。
InputStream in = con.getInputStream();
return true;
}catch (IOException e) {
System.out.println("文件上传出错!");
e.printStackTrace();
}
servlet端代码示例
public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
res.setContentType(" text/plain ");
InputStream inputStream = null;
try {
inputStream = res.getInputStream();
writefile(srcFile, inputStream);//把输入流inputStream保存到文件路径为srcFile的文件中
} catch (IOException e) {
e.printStackTrace();
}
} // end service
总结:在文件的传输中是流的形式存在的,在硬盘上是文件的形式存在的。我们要做的只是通过HttpServletRequest和HttpServletResponse,或者是response和request来发送流和读取流。以及把文件转换成流或把流转换成文件的操作。
怎样才能把这么多单词背下来呢?最重要的一点,就是:...如果想比别人成功,就一定要走捷径。不要期盼自己比别人幸运,也不要指望自己比别人更聪明或者更勤奋。从智力上说,从机遇上说,自己和别人都是差不了多少的,想超过和自己差不多的人,就一定要走捷径,捷径,捷径!
背单词捷径的第一条,就是:一定要每次都大量地背。因为自己不比别人聪明,所以背完单词,别人忘掉五分之一,自己决不会比别人忘得少。然而,别人每天背十个单词,自己却可以背一百个,忘掉五分之一,还剩八十个,是别人最聪明状态下的十倍。每天一百个是最低限。其实背到后来您会发现这个要求并不高,一个月后,您可能自然而然地就背到三百或者五百。 这一百个要分成四组来背,上午三十,中午十个,下午三十,晚上三十。第二天早晨复习以前没背下来的词。背的时候,要一目十词(注意,是十个而不是更多或更少),不要认认真真背,因为没有认认真真的时间。一边看一边读每个词的读音,默读也成。看完后回忆一遍,回忆不起来的再看。这次背的目的在于留下个大概印象,下次看见能知道这个词,所以背到大部分都能回忆得起来就成了,把剩下的词单独抄出来。
背单词捷径的第二条,就是:背字典!为什么要背字典呢?因为字典上每个词的解释比较全面,而且相同字母开头的单词都集中在一起。不是什么字典都可以拿来背的,一定要找只包含自己想背的词的字典。另外,最好有英文方式的解释和例句。而且,一定要有音标!如果是为了考TOFEL或者GRE,注意要选美音音标的字典。一般教材课文后面的词汇表都是为那些认认真真听课的好学生准备的,想走捷径就千万不要去背那些东西。 背字典的时候,按开头字母(Z,Y,X,Q,J,K,U)(V,W,N,O,L)(FG,IT,HM,BDE,R)(C,P,S,A)的顺序背,其中C,P,S,A每个都要分三部分背。这样背有几个好处:
(一)能增加成就感,提高兴趣。至于为什么,您翻翻字典就明白了。;)
(二)便于清楚地知道那些单词已经背过,那些还没背。(三)能先把最基本的词先掌握。 三万单词里,分为三个等级:三千到四千,八千到一万,两万二到三万。也就是说,您得分别准备三本字典。这几个等级之间各自有非常不同的特性,所以需要分别用不同方法背。俺当时没有认识到这一点,所以在从一万到三万之间走了一段弯路,浪费了一些时间,不然或许能突破到五万吧。;) 所以,背单词捷径的下面这条就有了三个分支:
背单词捷径的第三条,就是:和单词多见面。一个单词能不能记住,取决于和它在不同场合见面的频率,不在于每次看着它的时间长短(同样规律也适合于泡MM;))。一般想记住一个单词,每星期要和它在“不同场合”见三到四次面。俺在上文中提到大量背的时候,不要抠某一个字记住与否就是这个意思。因为是否一见钟情都是无所谓的,关键在于有更多不同类型的见面机会。不过,根据要背的单词的等级不同,增加见面机会的方式也有所不同。
第一个分支:瞎听!三千到四千这个等级,是非常常用的单词,而且几乎囊括了表达最基本思想所需要的一切词汇。每篇文章中百分之八十都是这些词汇,而且这些词都是最基本的语素(或称“词根”),就是分割到最小无法再分割,互相之间也没什么类似之处的东西。对付这些词的最好方法,就是进行大量的,不间断的,简单的初级听力练习。因为阅读材料中,还有百分之二十其他词汇,所以光凭这个等级的词还看不懂那些阅读材料。但是听力练习都是最基本的对话,而且发音一般很标准,多听能够增加单词的重复率,而且可以为以后背八千到一万那个等级的词打下语音基础。 听的时候,要分精听和泛听两部分。精听当然是指每个词都要弄懂,俺着重讲一下泛听(饭厅?;P)。泛听是最重要的,因为掌握语速和语调,以及总体印象都要靠泛听。而这些都是背八千到一万等级单词的基础?泛听能够让经常用到的词(也就是那些最必要掌握的词)把您的耳朵磨出茧子来,让您模模糊糊听到个音就能反应出它是什么意思。泛听中您听到的词,才是您真正应该记住的词,所以别害怕精听的时候什么都听不懂。到底什么是泛听呢?泛听,就是说您听的时候,精神要分散,要一边干着其他事(比如撮饭或和别人大声讨论撮饭;)~),一边有一搭没一搭地听着。泛听一定要见缝插针,一有机会就听着,最好耳机不离耳朵。;) 而精听的意义就在于找出您没听清的那些词。啊哈,那就是您背过但还不熟悉的词了。:)把这些词单独记在另外一个地方,别跟没背下来的词混了。泛听要听精听已经听过的内容。比如精听听到了第二盘磁带,那么泛听就听第一盘磁带,正好。提醒您一句,千万别拿英语广播当自己的听力教材!!! 顺便跟您推荐一套听力教材:武汉师范大学出的Step by Step.内容比较循序渐进,每一课开头的音乐也很好听。;) ...
第二个分支:狂看!八千到一万这个等级,基本包含了剩下的百分之二十。这些单词在听力教材里很难找到。但是,可不要停止听的练习呦,因为听能巩固您的语调感觉,而这是背这个等级单词的一个关键。不过,背这个等级的词,需要在听以外增加看的内容。 看,同样要分精读和泛读两种。就象听一样,也是泛读更重要一些。泛读要挑不太长,能有耐心看完的文章,而且看不懂的词不要太多,一篇文章有两三个不认识的就足够了。千万不要一上来就看英语报刊杂志小说,那些东西不但很难看懂,而且看懂了也对背单词没什么促进作用。:Q 泛读也需要大量练习,只要您有耐心,又有足够时间,就一直看下去吧!看的时候不要仔细阅读,扫一眼明白个大概意思就成了,然后把这一眼没看懂的词画上记号,别琢磨它是什么意思,继续扫描吧!全部看完之后,回头再看这些单词,有的可能已经想起来了,有的....还没想起来?那就查查字典,要是自己还没背过,就扔掉它,要是已经背过了,就单独抄下来吧,和听力练习中没听出来的词放在一起。 俺的经验是新概念第一二册,然后大学泛读课本前两册,然后是另外的一个泛读教程初级部分,然后新概念第三册,泛读课本第三册,某一种听力教材高级听力部分的教师用书,然后新概念第四册,泛读课本第四五册...这么个顺序进度比较合适。
第三个分支:乱说!这只适合背两万二到三万的词。因为其他的词不用说就已经背下来了。;)而这个等级的词在阅读材料里非常少,可能阅读十篇文章却一个这个等级的词也没有。所以靠阅读来增加见面机会已经不行了。这些词甚至有的老外一辈子都没见过,咋能指望他们能写出来呢?尤其是GRE词汇,什么"给马穿衣服","纽约的流氓","从非洲吹向南欧的风"....这些词都是一些精致的修辞,也就是说,如果您话里净是这些词,那您可以假冒一位学者了。;) 一般的老外都是听不懂您说的这些话的,所以不用这些词也能表达同样的含义。好了,您的机会来了,您可以跟老外讲话中带上这些单词,然后很自豪地假装谦虚地跟他们解释这些词的含义。;) 每次跟老外解释一两个词就足够了,老外会佩服您佩服得五体投地的。;)HL就向ws解释吧!要大胆而耐心,尤其是大胆。;) 这个阶段,您就别再练听力了,因为练了进步也不大。还记得开头那句话么?要想成功,得走捷径。 凡是不能在短时期内取得巨大进步的行动,都不必浪费时间去做。 不过,这时候的阅读材料成了问题:您会发现,过去看的东西觉得太浅没意思,看其他深的东西又看不懂。您还会发现一个新的有趣现象:那就是您想看的文章里,现在全是第一个等级的那些词,每个词您都确切知道它的含义,但整个文章您就是看不懂。;Q 那您该看什么呢?就看第二个等级的那些浅显文章啊。虽然浅显,但能帮助您不至于忘了过去背出来的成果。至于您看不懂的那些文章,别着急,等您和老外交流多了,您自然就懂了,那都是一些词组、俚语或文法组成的文章。:)
背单词捷径的第四条,就是:联想,联想,联想,.....背单词的第一个动作是什么?端详一下它的外貌。第二第三个动作呢?看看它的内涵和发音。而第四个动作,就应该是联想,再联想..... 联想它和其他背过的词有没有外表类似的?读音类似的?意思类似或相反的?如果有,就赶紧记在旁边,在另外那些词旁边也把这个词加上。这样,以后看见其他词,也会联想起这个词,等于又增加了一次见面机会。 而且,在第二、第三个等级的词汇里,还有许多单词是由"前缀","词根","后缀"组成的,前缀比如"re","in","pseud"...,后缀比?quot;er","a","oid"...,然后共同组成一个新词。如果掌握了这些前后缀,就可以很方便地猜出一个生词到底什么意思。另外,有很多读音相近的词也有相似的意思,这在俚语、诗歌和儿童用语中更普遍。如果每天记80个单词,一个月只能?400个单词,但掌握了这些规律后,实际上背的速度越来越快,几乎一万多单词都是不用背就会了的。不过,在GRE单词中,有很多是既没有和其他词的关系,又没有前后缀,看起来既简单又记不住的单词。这些词里边有希腊语,意大利语,德语,拉丁语,甚至还有日语译音。这就只能靠和别人解释来掌握了。(HL一定要注意!)
背单词捷径的第五条,就是:复习!记得快,忘得也就快,这是一个非常正常的规律。在背单词的过程中,复习就显得非常重要。俺总结复习的规律是:十个单词复习一遍,然后三十个单词,然后是以前所有没背下来的单词。复习的时候,同样不必细抠,粗略地扫一遍就可以了,但一定要想它的读音(因为英语是象声的)。最后背不下来的单词,一定是不常用的,因为老外一样背不下来。;) 每背完一个开头字母的单词,就要把前一个开头字母的复习一遍。然后每背完前文中括号里面的开头字母,就把上一个括号里的复习一遍。复习的时候,要先看英文翻译中文意思来一遍,然后再看着中文想它的英文单词再来一遍。 前面说过,在泛听,泛读中忘了的词要和背的时候就记不住的词分着记下来,就是为了在复习的时候区别对待。忘了的词,要每天看一遍,会了就划掉,而记不住的词则在背完了整个字母以后,单独背一下这些词,平常就不用看了。 GRE单词的复习方法非常奇怪:它需要进行填字游戏。也就是说,盖住单词的一部分,然后想整个词是怎么拼。另外,还得想它的同义词,近义词,反义词。 顺便说一句,考GRE的词和考TOFEL的词互相之间是毫不相关的,也就是说,您不会TOFEL的词,GRE也 有可能拿个高分。
方法一:
select top 10 b.* from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a,表名 b where b.主键字段 = a.主键字段 order by a.排序字段
10 = 每页记录数
20 = (当前页 + 1) * 每页记录数
以上语句即可以实现分页,但是最后取出的结果排序是升序,如果需要结果集为降序(例如时间),则有两种方法可以处理
1.使用以下语句,但效率可能要降低一些
select * from 表名 b, (select top 10 主键字段,排序字段 from (select top 20 主键字段,排序字段 from 表名 order by 排序字段 desc) a order by 排序字段 ) c where b.主键字段 = c.主键字段 order by c.排序字段 desc
2.在ado里处理,将记录集游标移到最后,然后前移
''以下为asp范例
set rsTemp = Server.CreateObject("adodb.recordset")
rsTemp.Open 语句,conn,1,1
rsTemp.MoveLast
for i = 1 to rsTemp.RecordCount
'取值....
rsTemp.MovePrevious
next
经测试,以上分页方法比使用临时表分页速度还要快,并且简单易用.
方法二:
大数据量下的分页
对于非常大的数据模型而言,分页检索时,每次都加载整个数据源非常浪费。通常的选择是检索页面大小的块区的数据,而非检索所有的数据,然后单步执行当前行。
本文演示ASP.net的DataGrid和Sql Server 实现大数据量下的分页,为了便于实现演示,数据表采用了Northwind数据库的Orders表(830条记录)。
如果数据表中有唯一的自增索引,并且这个字段没有出现断号现象。检索页面大小的块区数据就非常简单了。通过简单的Sql语句就可以实现这个功能:
select * from orders where orderid between 10248 and 10253
|
其中,开始编号为:(CurrentPageIndex - 1) * PageSize 结束编号为:CurrentPageIndex * PageSize
当然,如果这个字段断号不是很严重,而且允许不是很严格的按照每页条数分页,这样的方法也是可以用的。
如果这个字段断号,或者需要按照其他条件排序分页,就要复杂些了。首先要获得这个页面需要显示的编号,然后再按照这个编号获得需要的块区数据。根据编号获得块区数据很简单。不过用下面方式获得数据排序并不是按照指定的id列表顺序,这时候还要附加order by 命令。
select * from orders where orderid in (10248,10249,10250,10251,10252,10253) order by orderid desc
|
获得这个页面需要显示的编号列表就复杂多了,而且有多种方案:
方案一:维护一个表,这个表记录需要显示的这些编号排序顺序。(这个表可以是临时表,也可以是物理表)。下面演示了利用一个全局临时表。这个全局临时表记录需要显示的编号。注意排序,这里的order by 就是需要显示的排序顺序。
create table ##temptable(iid int IDENTITY (1, 1) NOT NULL,mainid int NOT NULL)insert ##temptable(mainid) select OrderID from orders order by OrderID descselect * from ##temptabledrop table ##temptable -- 实际执行时候,删除全部临时表当然不再这里执行。
|
这个临时表存在,获得指定分页的分块数据就很简单了。看下面代码:
create table ##temptable(iid int IDENTITY (1, 1) NOT NULL,mainid int NOT NULL)insert ##temptable(mainid) select OrderID from orders order by OrderID descdeclare @PageSize int,@CurrPage int,@strSQL varchar(2000),@IDStr varchar(1000)select @PageSize = 30select @CurrPage = 2select @IDStr = ''select @IDStr = @IDStr + ltrim(rtrim(str(MainID))) + ',' from ##temptable where iid between ((@CurrPage-1)*@PageSize+1) and @CurrPage*@PageSizeif @IDStr <> '' begin select @IDStr = left(@IDStr,len(@IDStr)-1)endselect @strSQL = 'select * from orders where OrderID in ('+@IDStr+') order by OrderID desc 'exec(@strSQL)drop table ##temptable
|
注意:实际使用这个方案的时候,还要考虑何时更新这个全局临时表,一般是放到计划任务中,定时更新这个汇总表。
方案二:每次都去查询,每次获得最新的编号顺序。由于这时候不存在这个临时表,书写获得需要显示页面的编号的字符串就需要点技巧,看下面的代码:
ASP.net 的 DataGrid 提供了使用这种分区的数据的方法。
DataGrid 通过 AllowCustomPaging 和 VirtualItemCount 属性支持块区操作。如果 AllowCustomPaging 为 true,则 DataGrid 不会根据 CurrentPageIndex 计算数据模型中的起始显示位置。DataGrid 将显示数据模型中的所有数据,而页导航栏将当前位置报告为 (VirtualItemCount+PageSize-1)/PageSize 之 CurrentPageIndex 页。下面的示例说明此功能。
declare @PageSize int,@CurrPage int,@topnum int,@previous intselect @PageSize = 30select @CurrPage = 2select @topnum = @CurrPage * @PageSizeselect @previous = (@CurrPage - 1) * @PageSizedeclare @i int,@IDStr nvarchar(500),@strSQL nvarchar(1000)select @i = 0select @strSQL = N''select @strSQL = @strSQL + N' select top '+str(@topnum)+ ' @i = @i + 1 'select @strSQL = @strSQL + N', @IdStr = 'select @strSQL = @strSQL + N'case when @i > '+str(@previous)+' then @IdStr + ltrim(rtrim(str(OrderID))) + '','' 'select @strSQL = @strSQL + N'else N''''end 'select @strSQL = @strSQL + N'from Orders 'select @strSQL = ltrim(rtrim(@strSQL)) + N' order by OrderID desc 'Select @IdStr = N''exec sp_executesql @strSQL,N'@i int,@IdStr varchar(500) output',@i,@IdStr outputif len(rtrim(ltrim(@IdStr))) > 0begin select @IdStr = left(@IdStr,len(@IdStr)-1)endselect @strSQL = 'select * from orders where OrderID in ('+@IDStr+')'exec(@strSQL)
|
protected void BindDataGrid(int currpage) { string strConn = "Data Source=(local);Integrated Security=SSPI;database=Northwind"; // 请确认 机器名/ASPNET 用户可以访问Northwind数据库 SqlCommand cmd = new SqlCommand(); SqlConnection conn = new SqlConnection(strConn); SqlParameter[] parms = new SqlParameter[] { new SqlParameter("@PageSize",SqlDbType.Int), new SqlParameter("@CurrPage",SqlDbType.Int), new SqlParameter("@SearchSql",SqlDbType.NVarChar,128), new SqlParameter("@Count",SqlDbType.Int), }; parms[0].Value = DataGrid1.PageSize; parms[1].Value = (currpage+1); // 数据库的分页算法第一页是1 DataGrid的第一页是0 parms[2].Value = DBNull.Value; parms[3].Direction = ParameterDirection.Output; parms[3].Value = DBNull.Value; DataSet DS = new DataSet(); try { if (conn.State != ConnectionState.Open) conn.Open(); cmd.Connection = conn; cmd.CommandText = "Selected_Page_List"; cmd.CommandType = CommandType.StoredProcedure; if (parms != null) { foreach (SqlParameter parm in parms) cmd.Parameters.Add(parm); } SqlDataAdapter DA = new SqlDataAdapter(cmd); DA.Fill(DS); int aa = Convert.ToInt32(parms[3].Value.ToString()); cmd.Parameters.Clear(); if (currpage == 0) { DataGrid1.VirtualItemCount = aa; } DataGrid1.CurrentPageIndex = currpage; DataGrid1.DataSource = DS; DataGrid1.DataBind(); } catch(Exception ewx) { conn.Close(); Response.Write (ewx.Message.ToString()); Response.End(); } } void Page_Load(Object sender, EventArgs E ) { if (!IsPostBack) { BindDataGrid(0); // 第一次打开这个页面,访问分页的第一页 } } void MyDataGrid_Page(Object sender, DataGridPageChangedEventArgs e) { BindDataGrid(e.NewPageIndex); }
|
如果你有更多数据量的表稍加修改,也可以使用本演示程序。其下是演示代码下载,演示代码使用的是方案二。使用方法看readme.txt文件。
整个演示代码 下载
http://chs.gotdotnet.com/quickstart/aspplus/samples/webforms/ctrlref/webctrl/datagrid/doc_datagrid.aspx#paging
这里演示了利用DataGrid 的这个功能(没有本文中讨论的利用存储过程获得分区数据)。如对DataGrid的这个功能不太熟悉,请先看这里。
方法三:
虽然 DataGrid 控件自己带了一个分页处理机制,但它是将符合查询条件的所有记录读入内存,然后进行分页显示的。随着符合条件的记录数目增多,就会出现运行效率问题,或者至少是资源的利用率下降。
下面的代码示例都以下面的表结构为准:
| |
Articles 表 |
SQL Server 类型 |
Oracle 类型 |
| PK |
Id |
int (自增) |
number(9) (插入时在当前最大值上加1) |
| |
Author |
nvarchar(10) |
nvarchar2(10) |
| |
Title |
nvarchar(50) |
nvarchar2(50) |
| |
PubTime |
datetime |
date |
SQL Server / Access 等微软产品中,我们通常的自定义分页有两种思路:
一种是以 ASP.NET Forum 为代表的、“临时表”方法:即在存储过程中建立一个临时表,该临时表包含一个序号字段(1,2,3,....)以及表的主键(其他能够唯一确定一行记录的字段也是可以的)字段。存储过程可能如下:(编号 SS1)
CREATE Procedure GetAllArticles_Paged
(
@PageIndex int,
@PageSize int,
@TotalRecords out int,
@TotalPages out int
)
AS
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int
-- Set the page bounds
SET @PageLowerBound = @PageSize * @PageIndex
SET @PageUpperBound = @PageLowerBound + @PageSize + 1
-- Create a temp table to store the select results
CREATE TABLE #tmp
(
RecNo int IDENTITY (1, 1) NOT NULL,
ArticleID int
)
INSERT INTO #tmp
SELECT [ID]
FROM Articles
ORDER BY PubTime DESC
SELECT A.*
FROM Articles A (nolock), #tmp T
WHERE A.ID = T.ArticleID AND
T.RecNo > @PageLowerBound AND
T.RecNo < @PageUpperBound
ORDER BY T.RecNo
GO
另一种可能更适合程序中“拼凑” SQL 语句:用两次 TOP 命令取得我们所要的分页数据,例如:(编号 SS2)
SELECT * FROM
(
SELECT TOP(PageSize) * FROM
(
SELECT TOP (PageSize * PageIndex) *
FROM Articles
ORDER BY PubTime DESC
)
ORDER BY PubTime ASC
)
ORDER BY PubTime DESC
这个的想法就是“掐头去尾”,还有不少分页的方法,这里就不一一列出了。
对于 Oracle 数据库,有几处不同严重妨碍了上面几个方法的实施,比如,Oracle 不支持 TOP 关键字:不过这个好像并不十分严重,因为它提供了 rownum 这个隐式游标,可以实现与 TOP 类似的功能,如:
SELECT TOP 10 ... FROM WHERE ...
要写成
SELECT ... FROM ... WHERE ... AND rownum <= 10
rownum 是记录序号(1,2,3...),但有一个比较麻烦的事情是:如果 SQL 语句中有 ORDER BY ... 排序的时候,rownum 居然是先“标号”后排序!这样,这个序号如果不加处理是不合乎使用需求的。
至于临时表,Oracle 的临时表和 SQL Server 的有很大不同,我还没搞懂这个东西,就不妄加揣测了。
国内网站中介绍 Oracle 分页的资料很少,我找到了一个国外站点(www.faqts.com)的一篇 FAQ,根据这篇文章的介绍,可以如下分页:(编号 Ora1)
SELECT * FROM
(
SELECT A.*, rownum r
FROM
(
SELECT *
FROM Articles
ORDER BY PubTime DESC
) A
WHERE rownum <= PageUpperBound
) B
WHERE r > PageLowerBound;
其中蓝色部分可以改为任意的、需要的 SQL SELECT 语句,这点倒是挺方便的。
方法四:
今天突然发现,Oracle原来可以这样实现分页功能:
select * from (select rownum rdd,field1,field2 from t_table where rownum<=400) where rdd>200
上述语句实现了从第201条记录开始处取200条记录
李开复2005年2月
今天,我回复了“开复学生网”开通以来的第1000个问题。关掉电脑后,始终有一封学生来信萦绕在我的脑海里,挥之不去:
开复老师:
就要毕业了。
回头看自己所谓的大学生活,
我想哭,不是因为离别,而是因为什么都没学到。
我不知,简历该怎么写,若是以往我会让它空白。
最大的收获也许是……对什么都没有的忍耐和适应……
这封来信道出了不少大三、大四学生的心声。大学期间,有许多学生放任自己、虚度光阴,还有许多学生始终也找不到正确的学习方向。当他们被第一次补考通知唤醒时,当他们收到第一封来自应聘企业的婉拒信时,这些学生才惊讶地发现,自己的前途是那么渺茫,一切努力似乎都为时已晚……
这“第四封信”是写给那些希望早些从懵懂中清醒过来的大学生,那些从未贪睡并希望把握自己的前途和命运的大学生以及那些即将迈进大学门槛的未来大学生们的。在这封信中,我想对所有同学说:
大学是人一生中最为关键的阶段。从入学的第一天起,你就应当对大学四年有一个正确的认识和规划。为了在学习中享受到最大的快乐,为了在毕业时找到自己最喜爱的工作,每一个刚进入大学校园的人都应当掌握七项学习:学习自修之道、基础知识、实践贯通、兴趣培养、积极主动、掌控时间、为人处事。只要做好了这七点,大学生临到毕业时的最大收获就绝不会是“对什么都没有的忍耐和适应”,而应当是“对什么都可以有的自信和渴望”。只要做好了这七点,你就能成为一个有潜力、有思想、有价值、有前途的快乐的毕业生。
大学:人生的关键
大学是人生的关键阶段。这是因为,进入大学是你终于放下高考的重担,第一次开始追逐自己的理想、兴趣。这是你离开家庭生活,第一次独立参与团体和社会生活。这是你不再单纯地学习或背诵书本上的理论知识,第一次有机会在学习理论的同时亲身实践。这是你第一次不再由父母安排生活和学习中的一切,而是有足够的自由处置生活和学习中遇到的各类问题,支配所有属于自己的时间。
大学是人生的关键阶段。这是因为,这是你一生中最后一次有机会系统性地接受教育。这是你最后一次能够全心建立你的知识基础。这可能是你最后一次可以将大段时间用于学习的人生阶段,也可能是最后一次可以拥有较高的可塑性、集中精力充实自我的成长历程。这也许是你最后一次能在相对宽容的,可以置身其中学习为人处世之道的理想环境。
大学是人生的关键阶段。在这个阶段里,所有大学生都应当认真把握每一个“第一次”,让它们成为未来人生道路的基石;在这个阶段里,所有大学生也要珍惜每一个“最后一次”,不要让自己在不远的将来追悔莫及。在大学四年里,大家应该努力为自己编织生活梦想,明确奋斗方向,奠定事业基础。
大学四年每个人都只有一次,大学四年应这样度过……
自修之道:从举一反三到无师自通
记得我在哥伦比亚大学任助教时,曾有位中国学生的家长向我抱怨说:“你们大学里到底在教些什么?我孩子读完了大二计算机系,居然连VisiCalc[1] 都不会用。”
我当时回答道:“电脑的发展日新月异。我们不能保证大学里所教的任何一项技术在五年以后仍然管用,我们也不能保证学生可以学会每一种技术和工具。我们能保证的是,你的孩子将学会思考,并掌握学习的方法,这样,无论五年以后出现什么样的新技术或新工具,你的孩子都能游刃有余。”
她接着问:“学最新的软件不是教育,那教育的本质究竟是什么呢?”
我回答说:“如果我们将学过的东西忘得一干二净时,最后剩下来的东西就是教育的本质了。
我当时说的这句话来自教育家B. F. Skinner的名言。所谓“剩下来的东西”,其实就是自学的能力,也就是举一反三或无师自通的能力。大学不是“职业培训班”,而是一个让学生适应社会,适应不同工作岗位的平台。在大学期间,学习专业知识固然重要,但更重要的还是要学习独立思考的方法,培养举一反三的能力,只有这样,大学毕业生才能适应瞬息万变的未来世界。我认识的不少在中国读完大学来美国念研究生的朋友。他们认为来美国后,不论是学习,工作还是生活他们最缺乏的是独立思考的能力因为在国内时他们很少独立思考和独立决策。
上中学时,老师会一次又一次重复每一课里的关键内容。但进了大学以后,老师只会充当引路人的角色,学生必须自主地学习、探索和实践。走上工作岗位后,自学能力就显得更为重要了。微软公司曾做过一个统计:在每一名微软员工所掌握的知识内容里,只有大约10%是员工在过去的学习和工作中积累得到的,其他知识都是在加入微软后重新学习的。这一数据充分表明,一个缺乏自学能力的人是难以在微软这样的现代企业中立足的。
自学能力必须在大学期间开始培养。许多同学总是抱怨老师教得不好,懂得不多,学校的课程安排也不合理。我通常会劝这些学生说:“与其诅咒黑暗,不如点亮蜡烛”。 大学生不应该只会跟在老师的身后亦步亦趋,而应当主动走在老师的前面。例如,大学老师在一个课时里通常要涵盖课本中几十页的信息内容,仅仅通过课堂听讲是无法把所有知识学通、学透的。最好的学习方法是在老师讲课之前就把课本中的相关问题琢磨清楚,然后在课堂上对照老师的讲解弥补自己在理解和认识上的不足之处。
中学生在学习知识时更多地是追求“记住”知识,而大学生就应当要求自己“理解”知识并善于提出问题。对每一个知识点,都应当多问几个“为什么”。一旦真正理解了理论或方法的来龙去脉,大家就能举一反三地学习其他知识,解决其他问题,甚至达到无师自通的境界。
事实上,很多问题都有不同的思路或观察角度。在学习知识或解决问题时,不要总是死守一种思维模式,不要让自己成为课本或经验的奴隶。只有在学习中敢于创新,善于从全新的角度出发思考问题,学生潜在的思考能力、创造能力和学习能力才能被真正激发出来。
《礼记·学记》上讲:“独学而无友,则孤陋而寡闻”。也就是说,大学生应当充分利用学校里的人才资源,从各种渠道吸收知识和方法。如果遇到好的老师,你可以主动向他们请教,或者请他们推荐一些课外的参考读物。除了资深的教授以外,大学中的青年教师、博士生、硕士生乃至自己的同班同学都是最好的知识来源和学习伙伴。每个人对问题的理解和认识都不尽相同,只有互帮互学,大家才能共同进步。
有些同学曾告诉我说,他们很羡慕我在读书时能有一位获得过图灵奖的大师传道授业。其实,虽然我非常推崇我的老师,但他在大学期间并没有教给我多少专业知识。他只是给我指明了大方向,让我分享他的经验,给我提供研究的资源,并教我做人的方法。他没有时间也没有必要指导我学习具体的专业知识。我在大学期间积累的专业知识都是通过自学获得的。刚入门时,我曾多次红着脸向我的师兄请教最基本的知识内容,开会讨论时我曾问过不少肤浅的问题,课余时间我还主动与同学探讨、切磋。“三人行必有我师”,大学生的周围到处是良师益友。只要珍惜这些难得的机会,大胆发问,经常切磋,我们就能学到最有用的知识和方法。
大学生应该充分利用图书馆和互联网,培养独立学习和研究的本领,为适应今后的工作或进一步的深造做准备。首先,除了学习老师规定的课程以外,大学生一定要学会查找书籍和文献,以便接触更广泛的知识和研究成果。例如,当我们在一门课上发现了自己感兴趣的课题,就应当积极去图书馆查阅相关文献,了解这个课题的来龙去脉和目前的研究动态。熟练和充分地使用图书馆资源,这是大学生特别是那些有志于科学研究的大学生的必备技能之一。读书时,应尽量多读一些英文原版教材。有些原版教材写得深入浅出,附有大量实例,比中文教材还适于自学。其次,在书本之外,互联网也是一个巨大的资源库,大学生们可以借助搜索引擎在网上查找各类信息。“开复学生网”开通半年以来,我发现很多同学其实并没有很好地掌握互联网的搜索技巧,有时他们提出的问题只要在搜索引擎中简单检索一下,就能轻易找到答案。还有些同学很容易相信网上的谣言,而不会利用搜索引擎自己查考、求证。除了搜索引擎以外,网上还有许多网站和社区也是很好的学习园地。
自学时,不要因为达到了学校的要求就沾沾自喜,也不要认为自己在大学里功课好就足够了。在二十一世纪的今天,人才已经变成了一个国际化的概念。当你对自己的成绩感到满意时,我建议你开始自学一些国际一流大学的课程。例如,美国麻省理工学院(MIT)的开放式课程已经在网上无偿发布出来,大家不妨去看看MIT的网上课程,做做MIT的网上试题。当你可以自如地掌握MIT课程时,你就可以更加自信地面对国际化的挑战了。
总之,善于举一反三,学会无师自通,这是大学四年中你可以送给自己的最好的礼物。
基础知识:数学、英语、计算机、互联网
我曾经说过,中国学生的一大优势是扎实的基础知识,如数学、物理等。但是,最近几年,同学们在目睹了很多速成的例子(如丁磊、陈天桥等)之后,也迫切希望能驶上成功的快车道。这渐渐形成了一种追求速成的浮躁风气。有许多大学生梦想在毕业后就立即能做“经理”、“老板”,还有许多大学生入学时直接选择了“管理”专业,因为他们认为从这样的专业毕业后马上就可以成为企业的管理者。可不少学生进入了管理专业后,才发现自己对本专业的学习毫无兴趣。其实,管理专业和其他专业一样,都是传授基础知识和基本方法的地方,没有哪个专业可以保证学生在毕业时就能走上领导岗位。无论同学们所学的是哪个专业,大学毕业才是个人事业的真正开始。想做企业领导或想做管理工作的同学也必须从基层做起,必须首先在人品方面学会做人,在学业方面打好基础。
如果说大学是一个学习和进步的平台,那么,这个平台的地基就是大学里的基础课程。在大学期间,同学们一定要学好基础知识其中包括数学、英语、计算机和互联网的使用,以及本专业要求的基础课程(如商学院的财务、经济等课程)。在科技发展日新月异的今天,应用领域里很多看似高深的技术在几年后就会被新的技术或工具取代。只有对基础知识的学习才可以受用终身。另一方面,如果没有打下好的基础,大学生们也很难真正理解高深的应用技术。最后,在许多的中国大学里,教授对基础课程也比对最新技术有更丰富的教学经验。
数学是理工科学生必备的基础。很多学生在高中时认为数学是最难学的,到了大学里,一旦发现本专业对数学的要求不高,就会彻底放松对数学知识的学习,而且他们看不出数学知识有什么现实的应用或就业前景。但大家不要忘记,绝大多数理工科专业的知识体系都建立在数学的基石之上。例如,要想学好计算机工程专业,那至少要把离散数学(包括集合论、图论、数理逻辑等)、线性代数、概率统计和数学分析学好;要想进一步攻读计算机科学专业的硕士或博士学位,可能还需要更高的数学素养。同时,数学也是人类几千年积累的智慧结晶,学习数学知识可以培养和训练人的思维能力。通过对几何的学习,我们可以学会用演绎、推理来求证和思考的方法;通过学习概率统计,我们可以知道该如何避免钻进思维的死胡同,该如何让自己面前的机会最大化。所以,大家一定要用心把数学学好,不能敷衍了事。学习数学也不能仅仅局限于选修多门数学课程,而是要知道自己为什么学习数学,要从学习数学的过程中掌握认知和思考的方法。
二十一世纪里最重要的沟通工具就是英语。有些同学在大学里只为了考过四级、六级而学习英语,有的同学仅仅把英语当作一种求职必备的技能来学习,甚至还有人认为学习和使用英语等于崇洋媚外。其实,学习英语的根本目的是为了掌握一种重要的学习和沟通工具。在未来的几十年里,世界上最全面的新闻内容,最先进的思想和最高深的技术,以及大多数知识分子间的交流都将用英语进行。因此,除非你甘心做一个与国际脱节的人,英语学习是至关重要的。在软件行业里,不但编程语言是以英语为基础设计出来的,最重要的教材、论文、参考资料、用户手册等资源也大多是用英语写就的。学英语绝不等于崇洋媚外。中国正在走向世界,中国需要学习西方的先进思想和先进科学技术,学好英语才是真正的爱国。
很多中国留学生的英语考试成绩不错,也高分考过四级、六级、托福,但是留学美国后上课时却很难听懂课程内容,和外国同学交流时就更加困难。我们该如何学好英语呢?既然英语是最重要的沟通工具,那么,最重要的学习方法就是尽量与实践结合起来,不能只“学”不“用”,更不能只靠背诵的方式学习英语。读书时,大家尽量阅读原版的专业教材(如果英语不够好,可以先从中英对照的教材看起),并适当地阅读一些自己感兴趣的专业论文,这可以同时提高英语和相关专业的知识水平。其次,提高英语听说能力的最好方法是直接与那些以英语为母语的外国人对话。现在有很多在中国学习和工作的外国人,他们中的不少人为了学中文,很愿意与中国学生对话、交流,这是很好的学习机会。此外,大家不要把学英语当作一件苦差事,完全可以用有趣的方法学习英语。例如,可以多看一些名人的对话或演讲,多看一些小说、戏剧甚至漫画。初学者可以找英文原版的教学节目和录像来学习,有一定基础的则应该看英文电视或电影。看一部英文电影时,最好先在有字幕的时候看一遍,同时查考生词、熟悉句式,然后在不加字幕的情况下再看一遍,仅靠耳朵去听。听英文广播也是很好的练习英文听力的方法,大家每天最好能抽出半小时到一小时的时间收听广播并尽量理解其中的内容,有必要的话还可以录下来反复收听。在互联网上也有许多互动式的英语学习网站,大家可以在网站上用游戏、自我测试、双语阅读等方式提升英语水平。总之,勇于实践、持之以恒是学习英语的必由之路。
信息时代已经到来,大学生在信息科学与信息技术方面的素养也已成为他们进入社会的必备基础之一。虽然不是每个大学生都需要懂得计算机原理和编程知识,但所有大学生都应能熟练地使用计算机、互联网、办公软件和搜索引擎,都应能熟练地在网上浏览信息和查找专业知识。在二十一世纪里,使用计算机和网络就像使用纸和笔一样是人人必备的基本功。不学好计算机,你就无法快捷全面地获得自己需要的知识或信息。
最后,每个特定的专业也有它自己的基础课程。以计算机专业为例,许多大学生只热衷于学习最新的语言、技术、平台、标准和工具,因为很多公司在招聘时都会要求这些方面的基础或经验。这些新技术虽然应该学习,但计算机基础课程的学习更为重要,因为语言和平台的发展日新月异,但只要学好基础课程(如数据结构、算法、编译原理、计算机原理、数据库原理等)就可以万变不离其宗。有位同学生动地把这些基础课程比拟为计算机专业的内功,而把新的语言、技术、平台、标准和工具比拟为外功。那些只懂得追求时髦的学生最终只知道些招式的皮毛,而没有内功的积累,他们是不可能成为真正的高手的。
虽然我一向鼓励大家追寻自己的兴趣,但在这里仍需强调,生活中有些事情即便不感兴趣也是必须要做的。例如,打好基础,学好数学、英语和计算机的使用就是这一类必须做的事情。如果你对数学、英语和计算机有兴趣,那你是幸运儿,可以享受学习的乐趣;但就算你没有兴趣,你也必须把这些基础打好。打基础是苦功夫,不愿吃苦是不能修得正果的。
实践贯通:“做过的才真正明白”
上高中时,许多学生会向老师提出“为什么?有什么用?”的问题,通常,老师给出的答案都是“不准问”。进入大学后,这些问题的答案应该是“不准不问”。在大学里,同学们应该懂得每一个学科的知识、理论、方法与具体的实践、应用如何结合起来,尤其是工科的学生更是如此。
有一句关于实践的谚语是这样说的:“我听到的会忘掉,我看到的能记住,我做过的才真正明白。
无论学习何种专业、何种课程,如果能在学习中努力实践,做到融会贯通,我们就可以更深入地理解知识体系,可以牢牢地记住学过的知识。因此,我建议同学们多选些与实践相关的专业课。实践时,最好是几个同学合作,这样,既可经过实践理解专业知识,也可以学会如何与人合作,培养团队精神。如果有机会在老师手下做些实际的项目,或者走出校门打工,只要不影响课业,这些做法都是值得鼓励的。外出打工或做项目时,不要只看重薪酬待遇(除非生活上确实有困难),有时候,即便待遇不满意,但有许多培训和实践的机会,我们也值得一试。
以计算机专业为例,实践经验对于软件开发来说更是必不可少的。微软公司希望应聘程序员的大学毕业生最好有十万行的编程经验。理由很简单:实践性的技术要在实践中提高。计算机归根结底是一门实践的学问,不动手是永远也学不会的。因此,最重要的不是在笔试中考高分,而是实践能力。但是,在与中国学生的交流过程中,我很惊讶地发现,中国某些学校计算机系的学生到了大三还不会编程。这些大学里的教学方法和课程的确需要更新。如果你不巧是在这样的学校中就读,那你就应该从打工、自学或上网的过程中寻求学习和实践的机会。在网上可以找到许多实践项目,例如,有一批爱好编程的学生建立了一个讨论软件技术的网站(www.diyinside.com),在其中共享他们的知识和实践经验,并成功举办了很多次活动(如在各大高校举办校园技术教育会议),还出版了帮助学生提高技术、解答疑难方面的图书,该网站有多位成员获得了“微软最有价值的专家”的称号。
培养兴趣:开拓视野,立定志向
孔子说:“知之者不如好之者,好之者不如乐之者。”我在“给中国学生的第三封信”中曾深入论述了快乐和兴趣是一个人成功的关键。如果你对某个领域充满激情,你就有可能在该领域中发挥自己所有的潜力,甚至为它而废寝忘食。这时候,你已经不是为了成功而学习,而是为了“享受”而学习了。在“第三封信”中,我也曾谈到我自己是如何在大学期间放弃了我不感兴趣的法律专业而进入我所热爱的计算机专业学习的。
有些同学问我,如何像我一样能找到自己的兴趣呢?我觉得,首先要客观地评估和寻找自己的兴趣所在:不要把社会、家人或朋友认可和看重的事当作自己的爱好;不要以为有趣的事就是自己的兴趣所在,而是要亲身体验它并用自己的头脑做出判断;不要以为有兴趣的事情就可以成为自己的职业,例如,喜欢玩网络游戏并不代表你会喜欢或有能力开发网络游戏;不要以为有兴趣就意味着自己有这方面的天赋,不过,你可以尽量寻找天赋和兴趣的最佳结合点,例如,如果你对数学有天赋但又喜欢计算机专业,那么你完全可以做计算机理论方面的研究工作。
最好的寻找兴趣点的方法是开拓自己的视野,接触众多的领域。唯有接触你才能尝试,唯有尝试你才能找到自己的最爱。而大学正是这样一个可以让你接触并尝试众多领域的独一无二的场所。因此,大学生应当更好地把握在校时间,充分利用学校的资源,通过使用图书馆资源、旁听课程、搜索网络、听讲座、打工、参加社团活动、与朋友交流、使用电子邮件和电子论坛等不同方式接触更多的领域、更多的工作类型和更多的专家学者。当年,如果我只是乖乖地到法律系上课,而不去尝试旁听计算机系的课程,我就不会去计算机中心打工,也不去找计算机系的助教切磋,就更不会发现自己对计算机的浓厚兴趣。
通过开拓视野和接触尝试,如果你发现了自己真正的兴趣爱好,这时就可以去尝试转系的可能性、尝试课外学习、选修或旁听相关课程;你也可以去找一些打工或假期实习的机会,进一步理解相关行业的工作性质;或者,努力去考自己感兴趣专业的研究生,重新进行一次专业选择。其实,本科读什么专业并不能完全决定毕业后的工作方向,正如我所强调的那样,大学期间的学习过程培养的是你的学习能力,只要具备了这种能力,即使从事的是全新的工作,你也能在边做边学的过程中获取足够的知识和经验。
除了“选你所爱”,大家也不妨试试“爱你所选”。有些同学后悔自己在入学时选错了专业,以至于对所学的专业缺乏兴趣,没有学习动力;有些同学则因为追寻兴趣而“走火入魔”,毕业后才发现荒废了本专业的课程;另一些同学因为在学习上遇到了困难或对本专业抱有偏见,就以兴趣为借口,不愿意面对自己的专业。这些做法都是不正确的。在大学中,转系可能并不容易,所以,大家首先应尽力试着把本专业读好,并在学习过程中逐渐培养自己对本专业的兴趣。此外,一个专业里可能有很多不同的领域,也许你对专业里的某一个领域会有兴趣。现在,有很多专业发展了交叉学科,两个专业的结合往往是新的增长点。因此,只要多接触、多尝试,你也许就会碰到自己真正感兴趣的方向。“数字笔”的发明人王坚博士在微软亚洲研究院负责用户界面的研究,可是谁又能想到他从本科到博士所学的都是心理学专业,而用户界面又正是计算机和心理学专业的最佳结合点。另一方面,就算你毕业后要从事其他的行业,你依然可以把自己的专业读好,这同样能成为你在新行业中的优势。例如,有一位同学不喜欢读工科,想毕业后进入服务业发展,我就建议他先把工科读好,将来可以在服务业中以精通技术作为自己的特长。
人生的路很长,每个人都可以有很多不同的兴趣爱好。在追寻兴趣之外,更重要的是要找寻自己终身不变的志向。有一本书的作者曾访问了几百个成功者,问他们有哪件事是他们今天已经懂得,但在年轻时却留下了遗憾的事情。在受访者的回答中,最多的一种是:“希望在年轻时就有前辈告诉我、鼓励我去追寻自己的理想和志向。”相比之下,兴趣固然关键,但志向更为重要。例如,我的志向是“使影响力最大化”,多年以来,我有许多兴趣爱好,如语音识别、对弈软件、多媒体、研究到开发的转换、管理学、满足用户的需求、演讲和写作、帮助中国学生等等,兴趣可以改变,但我的志向是始终不渝的。因此,大家不必把某种兴趣当作自己最后的目标,也不必把任何一种兴趣的发展道路完全切断,在志向的指引下,不同的兴趣完全可以平行发展,实在必要时再做出最佳的抉择。志向就像罗盘,兴趣就像风帆,两者相辅相成、缺一不可,它们可以让你驶向理想的港湾。
积极主动:果断负责,创造机遇
创立“开复学生网”时,我的初衷是“帮助学生帮助自己”。但让我很惊讶的是,更多的学生希望我直接帮他们做出决定,甚至仅在简短的几句自我介绍后就直接对我说:“只有你能告诉我,我该怎么做”。难道一个陌生人会比你更知道自己该怎么做吗?我慢慢认识到,这种被动的思维方式是从小在中国的教育环境中培养出来的。被动的人总是习惯性地认为他们现在的境况是他人和环境造成的,如果别人不指点,环境不改变,自己就只有消极地生活下去。持有这种态度的人,事业还没有开始,自己就已经被击败,我从来没见过这样消极的人可以取得持续的成功。
从大学的第一天开始,你就必须从被动转向主动,你必须成为自己未来的主人,你必须积极地管理自己的学业和将来的事业,理由很简单:因为没有人比你更在乎你自己的工作与生活。“让大学生活对自己有价值”是你的责任。许多同学到了大四才开始做人生和职业规划,而一个主动的学生应该从进入大学时就开始规划自己的未来。
积极主动的第一步是要有积极的态度。大家可以用我在“第三封信”里推荐的方法,积极规划自己的人生目标,追寻兴趣并尝试新的知识和领域。纳粹德国某集中营的一位幸存者维克托·弗兰克尔曾说过:“在任何特定的环境中,人们还有一种最后的自由,就是选择自己的态度。”
积极主动的第二步是对自己的一切负责,勇敢面对人生。不要把不确定的或困难的事情一味搁置起来。比如说,有些同学认为英语重要,但学校不考试就不学英语;或者,有些同学觉得自己需要参加社团磨练人际关系,但是因为害羞就不积极报名。但是,我们必须认识到,不去解决也是一种解决,不做决定也是一个决定,这样的解决和决定将使你面前的机会丧失殆尽。对于这种消极、胆怯的作风,你终有一天会付出代价的。
积极主动的第三步是要做好充分的准备:事事用心,事事尽力,不要等机遇上门;要把握住机遇,创造机遇。中国科技大学校长朱清时院士在大三时被分配到青海做铸造工人。但他不像其他同学那样放弃学习,整天打扑克、喝酒。他依然终日钻研数理化和英语。六年后,中国科学院要在青海做一个重要的项目,这时朱校长就脱颖而出,开始了他辉煌的事业。很多人可能说他运气好,被分配到缺乏人才的青海,才有这机会。但是,如果他没有努力学习,也无法抓住这个机遇。所以,做好充分的准备,当机遇来临时,你才能抓住它。
积极主动的第四步是“以终为始”,积极地规划大学四年。任何规划都将成为你某个阶段的终点,也将成为你下一个阶段的起点,而你的志向和兴趣将为你提供方向和动力。如果不知道自己的志向和兴趣,你应该马上做一个发掘志向和兴趣的计划;如果不知道毕业后要做什么,你应该马上制定一个尝试新领域的计划;如果不知道自己最欠缺什么,你应该马上写一份简历,找你的老师、朋友打分,或自己审阅,看看哪里需要改进;如果毕业后想出国读博士,你应该想想如何让自己在申请出国前有具体的研究经验和学术论文;如果毕业后想进入某个公司工作,你应该收集该公司的招聘广告,以便和你自己的履历对比,看自己还欠缺哪些经验。只要认真制定、管理、评估和调整自己的人生规划,你就会离你自己的目标越来越近。
掌控时间:事分轻重缓急,人应自控自觉
除了积极主动的态度,大学生还要学会安排自己的时间,管理自己的事务。一位同学是这么描述大学生活的:
“大学和高中相比似乎没有什么太大的区别,每天依旧是学习,每次考试后依旧是担心考试成绩……不同的只是大学里上网的时间和睡觉的时间多了很多,压力也小了很多。”
这位同学并不明白,“时间多了很多”正是大学与高中之间巨大的差别。时间多了,就需要自己安排时间、计划时间、管理时间
安排时间出了做一个时间表外,更重要的是“事分轻重缓急”。在《高效能人士的七个习惯》一书中,作者史蒂芬·柯维提出,“重要事”和“紧急事”的差别是人们浪费时间的最大理由之一。因为人的惯性是先做最紧急的事,但这么做会导致一些重要的事被荒废掉。例如,我认为这篇文章里谈到的各种学习都是“重要的”,但它们不见得都是老师布置的必修课业,采纳我的建议的同学们依然会因为考试、交作业等紧急的事情而荒废了打好基础、学习做人等重要的事情。因此,每天管理时间的一种好方法是,早上确定今天要做的紧急事和重要事,睡前回顾一下,这一天有没有做到两者的平衡。
每个人都有许多“紧急事”和“重要事”,想把每件事都做到最好是不切实际的。我建议大家把“必须做的事”和“尽量做的事”分开。必须做的事要做到最好,但尽量做的事尽力而为即可。建议大家用良好的态度和宽广的胸怀接受那些你暂时不能改变的事情,多关注那些你能够改变的事情。此外,还要注意生物钟的运行规律,按时作息,劳逸结合,这样才能在学习时有最好的状态。
大学四年是最容易迷失方向的时期。大学生必须有自控的能力,让自己交些好朋友,学些好习惯,不要沉迷于对自己无益的习惯(如网络游戏)里。一位积极、主动的中国学生在“开复学生网”上劝告其他同学:“不要玩游戏,至少不要玩网络游戏。我所认识的专业水平比较高的大学朋友中没有一个玩网络游戏的。沉迷于网络游戏是对于现实的逃避,是不愿面对自己不足的一面。我认为,要脱离网络游戏,就得珍惜自己宝贵的大学时间,找到自己感兴趣的方向,做一些有意义并能给自己带来满足感的事情。”
为人处事:培养友情,参与群体
很多大学生入校时都是第一次离开父母,离开自己生长的环境。进入校园开始集体生活后,如何与同学、朋友以及社团的同事相处就成为了大学生学习内容的一部分。大学是大家最后一次可以在相对宽松的环境中学习、培养、训练如何与人相处的机会。在未来,人们在社会里、在工作中与人相处的能力会变得越来越重要,甚至超过了工作本身。所以,大学生要好好把握机会,培养自己的交流意识和团队精神。
“人际交往能力不够强,人际圈子不够广,但又没有什么特长可以引起大家的注意,在社团里也不知道怎么和其他人有效地建立联系。”这是一些大学生在人际交往方面经常遇到的困惑。对于如何在大学期间提高人际交往能力,我的建议是:
第一,以诚待人,以责人之心责己、以恕己之心恕人。对别人要抱着诚挚、宽容的胸襟,对自己要怀着自我批评、有过必改的态度。与人交往时,你怎样对待别人,别人也会怎样对待你。这就好比照镜子一样,你自己的表情和态度,可以从他人对你流露出的表情和态度中一览无遗。你若以诚待人,别人也会以诚待你。你若敌视别人,别人也会敌视你。最真挚的友情和最难解的仇恨都是由这种“反射”原理逐步造成的。因此,当你想修正别人时,你应该先修正自己。你想别人怎么对你,你就应该怎么对人。你想他人理解你,你就要首先理解他人。
第二,培养真正的友情。如果能做到第一点,很多大学时的朋友就会成为你一辈子的知己。在一起求学和寻求自身发展的道路上,这样的友谊弥足珍贵。交朋友时,不要只去找与你性情相近或只会附和你的人做朋友。好朋友有很多种:乐观的朋友、智慧的朋友、脚踏实地的朋友、幽默风趣的朋友、激励你上进的朋友、提升你能力的朋友、帮你了解自己的朋友、对你说实话的朋友等等。此外,大学时谈恋爱也可以教你如何照顾别人,增进同理心和自控力,但恋爱这件事要随缘,不必为了谈恋爱而谈恋爱。
第三,学习团队精神和沟通能力。社团是微观的社会,参与社团是步入社会前最好的磨练。在社团中,可以培养团队合作的能力和领导才能,也可以发挥你的专业特长。但更重要的是,你要做一个诚心诚意的服务者和志愿者,或在担任学生工作时主动扮演同学和老师之间沟通桥梁的角色,并以此锻炼自己的沟通能力,为同学和老师服务。这样的学习过程也不会很轻松,挫折是肯定有的,但是不要灰心,大学社团里的人际交往是一种不用“付学费”的学习,犯了错误也可以重头来过.
第四,从周围的人身上学习。在班级里、社团中,多观察周围的同学,特别是那些你觉得交往能力和沟通能力特别强的同学,看他们是如何与人相处的。比如,看他们如何处理交往中的冲突、如何说服他人和影响他人、如何发挥自己的合作和协调能力、如何表达对他人的尊重和真诚、如何表示赞许或反对,如何在不冒犯他人的情况下充分展示个性等等。通过观察和模仿,你渐渐地会发现,自己的人际交往能力会有意想不到的改进。在学校里,每一个朋友都可以成为你的良师,他们的热心、幽默、机智、博学、正直、沟通、礼貌等品德都可以成为你的学习对象。同时那些你不喜欢的人和事也可以为你敲响警钟,警告你千万不要做那样的人和事。当然,你也应当慷慨地帮助每一个朋友,试着做他们的良师和模范。
第五,提高自身修养和人格魅力。如果觉得没有特长、没有爱好可能会成为自己人际交往能力提高的一个障碍,那么,你可以有意识地去选择和培养一些兴趣爱好。共同的兴趣和爱好也是你与朋友建立深厚感情的途径之一。很多在事业上有所建树的人都不是只会闭门苦读的书呆子,他们大多都有自己的兴趣和爱好。我在微软亚洲研究院的同事中就有绘画、桥牌和体育运动方面的高手。业余爱好不仅是人际交往的一种方式,还可以让大家发掘出自己在读书以外的潜能。例如,体育锻炼既可以发挥你的运动潜能,也可以培养你的团队合作精神。如果真的没有什么兴趣爱好,那么,多读些好书丰富自己的知识也可以改进自己的人际交往能力,因为没有什么比智慧和渊博更能体现一个人的人格魅力了。
所以,学会与人相处,这也是大学中的一门“必修课”。
对大学生们的期望
踏入大学校门时,你还是一个忙碌的、青涩的、被动的、为分数读书的、被家庭保护着的中学毕业生。
就读大学时,你应当掌握七项学习,学好自修之道、基础知识、实践贯通、兴趣培养、积极主动、掌控时间、为人处事。
经过大学四年,你会从思考中确立自我,从学习中寻求真理,从独立中体验自主,从计划中把握时间,从交流中锻炼表达,从交友中品味成熟,从实践中赢得价值,从兴趣中攫取快乐,从追求中获得力量。
离开大学时,只要做到了这些,你最大的收获将是“对什么都可以拥有的自信和渴望”。你就能成为一个有潜力、有思想、有价值、有前途的中国未来的主人翁。
所以,我认为大学四年应是这样度过。
--------------------------------------------------------------------------------
[1] VisiCalc是当时最热门的计算机应用软件,但它在二十年前就被淘汰了(这件事又一次证明了科技的发展是日新月异的)。而且,VisiCalc的使用方法也不是计算机系的学生应该学的。
1.RequestDispatcher.forward()
是在
服务器端起作用,当使用forward()时,Servlet engine传递HTTP请求从当前的Servlet or JSP到另外一个Servlet,JSP 或普通HTML文件,也即你的form提交至a.jsp,在a.jsp用到了forward()重定向至b.jsp,此时form提交的所有信息在b.jsp都可以获得,参数自动传递.
但forward()无法重定向至有frame的jsp文件,可以重定向至有frame的html文件,同时forward()无法在后面带参数传递,比如servlet?name=frank,这样不行,可以程序内通过response.setAttribute("name",name)来传至下一个页面.
重定向后浏览器地址栏URL不变.
例:在servlet中进行重定向
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException
{
response.setContentType("text/html; charset=gb2312");
ServletContext sc = getServletContext();
RequestDispatcher rd = null;
rd = sc.getRequestDispatcher("/index.jsp"); //定向的页面
rd.forward(request, response);
}
通常在servlet中使用,不在jsp中使用。
2.response.sendRedirect()
是在用户的
浏览器端工作,sendRedirect()可以带参数传递,比如servlet?name=frank传至下个页面,同时它可以重定向至不同的主机上,sendRedirect()可以重定向有frame.的jsp文件.
重定向后在浏览器地址栏上会出现重定向页面的URL。
sendRedirect()实际上是reponse.setStatus(302)的快捷方式,后者用于设置Http响应的状态代码。
例:在servlet中重定向
public void doPost(HttpServletRequest request,HttpServletResponse response)
throws ServletException,IOException
{
response.setContentType("text/html; charset=gb2312");
response.sendRedirect("/index.jsp");
}
由于response是jsp页面中的隐含对象,故在jsp页面中可以用response.sendRedirect()直接实现重定位。
注意:
(1).使用response.sendRedirect时,前面不能有HTML输出。
这并不是绝对的,不能有HTML输出其实是指不能有HTML被送到了浏览器。事实上现在的server都有cache机制,一般在8K(我是说JSP SERVER),这就意味着,除非你关闭了cache,或者你使用了out.flush()强制刷新,那么在使用sendRedirect之前,有少量的HTML输出也是允许的。
(2).response.sendRedirect之后,应该紧跟一句return;
我们已经知道response.sendRedirect是通过浏览器来做转向的,所以只有在页面处理完成后,才会有实际的动作。既然你已经要做转向了,那么后的输出还有什么意义呢?而且有可能会因为后面的输出导致转向失败。
比较:
(1).Request Dispatcher.forward()是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;
(2).response.sendRedirect()则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。
前者更加高效,在前者可以满足需要时,尽量使用RequestDispatcher.forward()方法.
注:在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用HttpServletResponse.sendRequest()方法。
3.<jsp:forward page="" />
它的底层部分是由RequestDispatcher来实现的,因此它带有RequestDispatcher.forward()方法的印记。
如果在<jsp:forward>之前有很多输出,前面的输出已使缓冲区满,将自动输出到客户端,那么该语句将不起作用,这一点应该特别注意。
另外要注意:它不能改变浏览器地址,刷新的话会导致重复提交
4.JSP中实现在某页面停留若干秒后,自动重定向到另一页面
在html文件中,下面的代码:
<meta http-equiv="refresh" content="300; url=target.jsp">
它的含义:在5分钟之后正在浏览的页面将会自动变为target.html这一页。代码中300为刷新的延迟时间,以秒为单位。targer.html为你想转向的目标页,若为本页则为自动刷新本页。
由上可知,可以通过setHeader来实现某页面停留若干秒后,自动重定向到另一页面。
关键代码:
String content=stayTime+";URL="+URL;
response.setHeader("REFRESH",content);
在我们不断塑造自我的过程中,影响最大的莫过于是选择乐观的态度还是悲观的态度。我们思想上的这种抉择可能给我们带来激励,也有可能阻滞我们前进。
清晰地规划目标是人生走向成功的第一步,但塑造自我却不仅限于规划目标。要真正塑造自我和自己想要的生活,我们必须奋起行动。莎士比亚说得好:"行动胜过雄辩。"
一旦掌握自我激励,自我塑造的过程也就随即开始。以下方法可以帮你塑造自我,塑造那个你一直梦寐以求的自我。
树立远景 迈向自我塑造的第一步,要有一个你每天早晨醒来为之奋斗的目标,它应是你人生的目标。远景必须即刻着手建立,而不要往后拖。你随时可以按自己的想法做些改变,但不能一刻没有远景。
离开舒适区 不断寻求挑战激励自己。提防自己,不要躺倒在舒适区。舒适区只是避风港,不是安乐窝。它只是你心中准备迎接下次挑战之前刻意放松自己和恢复元气的地方。
把握好情绪 人开心的时候,体内就会发生奇妙的变化,从而获得阵阵新的动力和力量。但是,不要总想在自身之外寻开心。令你开心的事不在别处,就在你身上。因此,找出自身的情绪高涨期用来不断激励自己。
调高目标 许多人惊奇地发现,他们之所以达不到自己孜孜以求的目标,是因为他们的主要目标太小、而且太模糊不清,使自己失去动力。如果你的主要目标不能激发你的想象力,目标的实现就会遥遥无期。因此,真正能激励你奋发向上的是,确立一个既宏伟又具体的远大目标。
加强紧迫感 20世纪作者Anais Nin(阿耐斯)曾写道:"沉溺生活的人没有死的恐惧"。自以为长命百岁无益于你享受人生。然而,大多数人对此视而不见,假装自己的生命会绵延无绝。惟有心血来潮的那天,我们才会筹划大事业,将我们的目标和梦想寄托在enis Waitley(丹尼斯)称之为 "虚幻岛" 的汪洋大海之中。其实,直面死亡未必要等到生命耗尽时的临终一刻。事实上,如果能逼真地想象我们的弥留之际,会物极必反产生一种再生的感觉,这是塑造自我的第一步。
撇开朋友 对于那些不支持你目标的 "朋友" ,要敬而远之。你所交往的人会改变你的生活。与愤世嫉俗的人为伍,他们就会拉你沉沦。结交那些希望你快乐和成功的人,你就在追求快乐和成功的路上迈出最重要的一步。对生活的热情具有感染力。因此同乐观的人为伴能让我们看到更多的人生希望。
迎接恐惧 世上最秘而不宣的秘密是,战胜恐惧后迎来的是某种安全有益的东西。哪怕克服的是小小的恐惧,也会增强你对创造自己生活能力的信心。如果一味想避开恐惧,它们会象疯狗一样对我们穷追不舍。此时,最可怕的莫过于双眼一闭假装它们不存在。
做好调整计划 实现目标的道路绝不是坦途。它总是呈现出一条波浪线,有起也有落。但你可以安排自己的休整点。事先看看你的时间表,框出你放松、调整、恢复元气的时间。即使你现在感觉不错,也要做好调整计划。这才是明智之举。在自己的事业波峰时,要给自己安排休整点。安排出一大段时间让自己隐退一下,即使是离开自己爱的工作也要如此。只有这样,在你重新投入工作时才能更富激情。
直面困难 每一个解决方案都是针对一个问题的。二者缺一不可。困难对于脑力运动者来说,不过是一场场艰辛的比赛。真正的运动者总是盼望比赛。如果把困难看作对自己的诅咒,就很难在生活中找到动力。如果学会了把握困难带来的机遇,你自然会动力陡生。
首先要感觉好 多数人认为,一旦达到某个目标,人们就会感到身心舒畅。但问题是你可能永远达不到目标。把快乐建立在还不曾拥有的事情上,无异于剥夺自己创造快乐的权力。记住,快乐是天赋权利。首先就要有良好的感觉,让它使自己在塑造自我的整个旅途中充满快乐,而不要再等到成功的最后一刻才去感受属于自己的欢乐。
加强排练 先"排演"一场比你要面对的珲要复杂的战斗。如果手上有棘手活而自己又犹豫不决,不妨挑件更难的事先做。生活挑战你的事情,你定可以用来挑战自己。这样,你就可以自己开辟一条成功之路。成功的真谛是:对自己越苛刻,生活对你越宽容;对自己越宽容,生活对你越苛刻。
立足现在 锻炼自己即刻行动的能力。充分利用对现时的认知力。不要沉浸在过去,也不要耽溺于未来,要着眼于今天。当然要有梦想、筹划和制订创造目标的时间。不过,这一切就绪后,一定要学会脚踏实地、注重眼前的行动。要把整个生命凝聚在此时此刻。
敢于竞争 竞争给了我们宝贵的经验,无论你多么出色,总会人外有人。所以你需要学会谦虚。努力胜过别人,能使自己更深地认识自己;努力胜过别人,便在生活中加入了竞争 "游戏 " 。不管在哪里,都要参与竞争,而且总要满怀快乐的心情。要明白最终超越别人远没有超越自己更重要。
内省 大多数人通过别人对自己的印象和看法来看自己。获得别人对自己的反映很不错,尤其正面反馈。但是,仅凭别人的一面之辞,把自己的个人形象建立在别人身上,就会面临严重束缚自己的危险。因此,只把这些溢美之词当作自己生活中的点缀。人生的棋局该由自己来摆。不要从别人身上找寻自己,应该经常自省并塑造自我。
走向危机 危机能激发我们竭尽全力。无视这种现象,我们往往会愚蠢地创造一种追求舒适的生活,努力设计各种越来越轻松的生活方式,使自己生活得风平浪静。当然,我们不必坐等危机或悲剧的到来,从内心挑战自我是我们生命力量的源泉。圣女贞德(Joan of Arc)说过:"所有战斗的胜负首先在自我的心里见分晓。"
精工细笔 创造自我,如绘巨幅画一样,不要怕精工细笔。如果把自己当作一幅正在描绘中杰作,你就会乐于从细微处做改变。一件小事做得与众不同,也会令你兴奋不已。总之,无论你有多么小的变化,点点都于你很重要。
敢于犯错 有时候我们不做一件事,是因为我们没有把握做好。我们感到自己"状态不佳"或精力不足时,往往会把必须做的事放在一边,或静等灵感的降临。你可不要这样。如果有些事你知道需要做却又提不起劲,尽管去做,不要怕犯错。给自己一点自嘲式幽默。抱一种打趣的心情来对待自己做不好的事情,一旦做起来了尽管乐在其中。
不要害怕拒绝 不要消极接受别人的拒绝,而要积极面对。你的要求却落空时,把这种拒绝当作一个问题: "自己能不能更多一点创意呢?"不要听见不字就打退堂鼓。应该让这种拒绝激励你更大的创造力。
尽量放松 接受挑战后,要尽量放松。在脑电波开始平和你的中枢神经系统时,你可感受到自己的内在动力在不断增加。你很快会知道自己有何收获。自己能做的事,不必祈求上天赐予你勇气,放松可以产生迎接挑战的勇气。
一生的缩影 塑造自我的关键是甘做小事,但必须即刻就做。塑造自我不能一蹴而就,而是一个循序渐进的过程。这儿做一点,那儿改一下,将使你的一天(也就是你的一生)有滋有味。今天是你整个生命的一个小原子,是你一生的缩影。
大多数人希望自己的生活富有意义。但是生活不在未来。我们越是认为自己有充分的时间去做自己想做的事,就越会在这种沉醉中让人生中的绝妙机会悄然流逝。只有重视今天,自我激励的力量才能汩汩不绝。