??xml version="1.0" encoding="utf-8" standalone="yes"?>
但是Q我最q惊奇地发现Q不知道怎么正确清理资源的h大有人在Q即使是一些java老手?
看一个例子先Q?
java代码:
void f(){
Connection conn = ...;
Statement stmt = conn.createStatement();
ResultSet rset = ...;
...
}
典型的jdbcE序。但是也是典型的光着屁股Q其臭如兰地走出厕所的典范。哎Q你擦屁股了吗?
有的哥们振振有辞Q我不用,我的jdbc driver/我的应用服务?garbage collector会处理的?
q是典型的糊涂蛋逻辑。没有close()Qjdbc driver, 应用服务器怎么知道你是拉完了,q是光着屁股出去接个电话先?难不成这driver都智能地会算命了Q?
garbage collector倒确实管得了。不q,garbage collector不一定运行啊。你要是?0G得内存,要是你的E序q?0MQgarbage collector说不定就一直睡大觉。而且Q就它,也许{你光着屁股上班被警察抓h之后才匆匆赶刎ͼ你等的v吗?
好,有h_那我擦,我擦Q我擦擦擦。行了吧Q?
java代码:
void f(){
Connection conn = ...;
Statement stmt = conn.createStatement();
ResultSet rset = ...;
rset.close();
conn.close();
...
}
呵呵。我的傻哥们Q你只擦了靠q后背的那三公分Q剩下的嘛,别h看不见你׃得省土块儿了是么Q?
按jdbc标准QResultSet, Statement, Connection都要close()Q也许有的driver会在Connection关闭的时候同时正清理ResultSet, StatementQ但是,q没有一条规定让所有的driver都这么做?
另外Q也怽的Connection是从一个池里面来的Q它只是回到池中去,如果你不关闭Statement, ResultSetQ下一个拿到这个Connection的h也许倒霉了!
做事要有始有l,既然开始擦了,擦q净点儿Q行不?Q那个,谁谁谁,借我个防毒面具先Q)
okQ有个讲卫生的小dq样擦:
java代码:
void f(){
Connection conn = ...;
Statement stmt = conn.createStatement();
ResultSet rset = ...;
rset.close();
stmt.close();
conn.close();
...
}
然后z洋得意地说Q我是好孩子Q我天天擦屁屁?
是啊Q多听话的孩子呀。可惜,某天Q这孩子正坐在马桶上着呢,妈妈喊了嗓子Q二dQ吃饭啦?
哦!吃饭。二d裤子都没提就H出来了Q熏得妈妈一个跟头?
什么问题,d做事一根筋Q不能打扎ͼ一旦有异常情况出现Q屁股就忘了擦了?
所以,我这里郑重提醒大ӞL"try-finally"Q它独有ҎQ防止侧?..Q糟了,串台了)
是啊Qjava老手们都不是dQ都知道用try-finally的,可是Q别,你现在就保不齐擦没擦屁股呢!
常见擦法Q?
java代码:
void f(){
Connection conn = null;
Statement stmt = null;
ResultSet rset = null;
try{
conn = ...;
stmt = ...;
rset = ...;
...
}
finally{
if(rset!=null)rset.close();
if(stmt!=null)stmt.close();
if(conn!=null)conn.close();
}
}
嗯。怎么说呢。挺聪明的。都学会if(xxx!=null)q种传说中条件判断的上古l学了?
可惜Q你屁股大,一张纸不够Q你用了W一张纸Q满意地看着它圆满地完成了金灿灿的Q务,再用W二张,靠,只太薄,破了Q一手金灿灿圎ͼ象带了个金戒指。你大怒,Pl尘而去。于是也忘了W三张纸Q?
哥们儿,close()是可以出异常的,你rset关了Qstmt.close()出现了异常,但是conn׃了Q?
q日有位室外高hQ据说是D子高徒,鉴于怜我世hQ不擦屁股的实多的高情操,亲手赚写一本绝世擦功秘c,其文,其意高,除了擦不q净之外Q真可以说是U霸擦林?
java代码:
void close(Connection conn){
try{
if(conn!=null) conn.close();
}
catch(Exception e){
e.printStackTrace();
}
}
void close(ResultSet rset){
...
}
void close(Statement rset){
...
}
void f(){
Connection conn = null;
Statement stmt = null;
ResultSet rset = null;
try{
conn = ...;
stmt = ...;
rset = ...;
...
}
finally{
close(rset);
close(stmt);
close(conn);
}
}
哈,你们不能U擦破了׃接着擦啊Q甚臛_而化之,不能擦股用具有了问题半途而废呀Q?
具信Q该高h以此法擦遍天下凡十数载,未有擦而无功者?
可惜Q高人却忽视了,除了U怼出故障,甚至大而化之,一切擦P如土块儿Q木条儿Q手指)都可能出现故障,q有别的地方也会出故障地Q?
除了ExceptionQ还有Error啊,我的高hQ如果close(rset)抛了一个ErrorQ你的close(stmt), close(conn)不都歇菜了?
后来Q高人在《绝世武功补遗》里面解释说QError代表不可恢复错误Q说明整个排泄大业都受阻了,所以根本不应该试图对这U情况做M处理Q你也处理不了(自然也隐含此时你也根本无法擦屁股了的论断Q。Q何试囑֜q种情况下仍然固执擦屁股的做法都是倒行逆施Q螳臂当车,必然被历史的车轮所늢?
此书一处,天下辟易。其革命性之pQ难以估量。具有关斚w评论QSunq个公共厕所的try-finallyq个工具的设定本w就是不合理的,应该被历史R轮撵的Q因为try-finally居然试图在出现Error的时候去做一些事情!是可忍,C可忍Q?
可以预见Qtry-finally被sund废弃Qƈ且向q大公众做公开道歉以检讨多q来的欺骗造成的恶劣媄响?
另外Q公厕的构造也受到质疑Q因Z旦有一个拉客在擦的时候某一步无可挽回地p|Q比如,太紧张,手一抖,U掉C坑里Q又L伸手捞不着Q,那么他就大摇大摆不再l箋擦,而如果碰巧此人刚吃了萝卜Q就会把整个厕所里的其它拉客都熏得无法l。(x一个app server吧。你一个程序歇菜,乐得L假不擦了Q别Z跟着倒霉Q)
嘿嘿Q那么,你擦了吗Q你肯定你擦了?擦干净了?
q好Q我们翻遍上古秘c,最l在北京山顶zh的失传宝典《呼|擦!》中发现了一个据U绝对干净的擦法,那就是-Q-Q-Q-Q-Q-Q?
一下一下擦Q?
具体操作办法如下Q?
java代码:
void f(){
finalConnection conn = ...;
try{
finalStatement stmt = ...;
try{
finalResultSet rset = ...;
try{
...
}
finally{rset.close();}
}
finally{stmt.close();}
}
finally{conn.close();}
}
其诀H就是,每徏立一个需要清理的资源Q就用一个try-finally来保证它可以被清理掉?
如此QQ何时候,你都是屁股干q静静地d卫生间?
哪。好多圣人门徒跟我说Q这样一下一下擦Q姿劉K怸雅观Q看看那嵌套的try块吧Q,有违古礼。我们反对!
靠,你说孔丑儿古q是山顶zh古?Q?
屁股q泛着呛_呢,q拽什么“雅”?
而且Q要是死要面子,也可以拉个帘子,擦的时候别让h看见嘛。比如:
java代码:
interface ResultListener{
void call(ResultSet rset);
}
class SqlReader{
void read(ResultListener l){
finalConnection conn = ...;
try{
finalStatement stmt = ...;
try{
finalResultSet rset = ...;
try{
l.call(rset);
}
finally{rset.close();}
}
finally{stmt.close();}
}
finally{conn.close();}
}
}
q一下一下擦的动作都藏在SqlReaderq个帘子里,你直接在ResultListener里面拉不p了?
那位高h说了Q这太复杂,׃ؓ了擦个屁股不倹{?
q个嘛,g值的另说Q你那个单,是单单地擦不干净屁股。要不您q脆别擦得了Q更单呢q。反正您出门儿就愣说擦的是Chanel香水儿就是了。有啥比瞪眼儿说白话儿简单?
对了Q?我还忘了一个条ƾ:
是擦屁股的时候按序擦。谁?/span>q厕所的,要让人家?/span>出去?
“什么狗屁规则?“那位问了?
q个q个--Q啊Q你猜猜~~~Q?
嗯,对了Q是q样的,上厕所都不着急,姗姗来迟Q上课更不着急,更喜Ƣ迟CQ对不对Q而谁上课天天q到早退q不担心毕业Q当然是太子党了Q是不?
人家都太子党了,你还不让人家先出去?z腻味了你?Q此处尾韌拉长Q而且向上拐)
反正啊,具体_ResultSet最后创建,但是要先兟?
Statement其次。Connection最后?
当然了,也许在你的环境下Q次序错了也没出事情。但是,׃吃Y饭的Q吃软gq口饭的Q图啥?不就图个攑ֿ吗?上厕所囑֕Q不图个别让太子党抓去当兔子吗Q?
也许某个driverҎ序不敏感Q但是不好说哪天你换个环境就忽然她奶奶的敏感了呢Q?
比如吧,你有connection pool, conn.close()把connectionq回到connection?
你要是先conn.close()Q好嘛,connection先回到pool了,正好别的U程里面{着要connectionQ立马这个connection又给分配出去了?
q下齐了Q你statement, resultsetq没兛_Q那边事故单位领导就找上门了。什么香Ҏa的桌子,什么桐油炸丸子Q全l你送来了。这不添堵吗Q?
好在Q在我们《呼|擦!》宝怸记蝲的“一下一下擦”神功,老少咸宜Q童叟无ƺ,有道是:法擦大法好,不如法擦冰箱好!
跑题了。反正是Q只要你一个try-finally对应一个资源,你就不可能在ơ序上出错。自然而然的就是后入先出的堆栈l构?
反观别的擦法Q就没有q个效果Q次序如何,全靠你自己掌握。弄错了Q系l也不告诉你。等着吃桐油炸丸子吧?
q也是我们推q一下一下擦的一个原因?/span>
上一ơ由ajoo?005-6-09 周四, 下午12:45修改Qd修改??/span>
当应用服务器初始化servlet实例之后Qؓ客户端请求提供服务之前,它会调用q个servlet的init()Ҏ。在一个servlet的生命周期中Qinit()Ҏ只会被调用一ơ。通过在init()Ҏ中缓存一些静态的数据或完成一些只需要执行一ơ的、耗时的操作,可大大地提高系l性能?br /> 例如Q通过在init()Ҏ中徏立一个JDBCq接池是一个最佳例子,假设我们是用jdbc2.0的DataSource接口来取得数据库q接Q在通常的情况下Q我们需要通过JNDI来取得具体的数据源。我们可以想象在一个具体的应用中,如果每次SQLh都要执行一ơJNDI查询的话Q那pȝ性能会急剧下降。解x法是如下代码Q它通过~存DataSourceQ得下一ơSQL调用时仍然可以l利用它Q?br />public class ControllerServlet extends HttpServlet
{
private javax.sql.DataSource testDS = null;
public void init(ServletConfig config) throws ServletException
{
super.init(config);
Context ctx = null;
try
{
ctx = new InitialContext();
testDS = (javax.sql.DataSource)ctx.lookup("jdbc/testDS");
}
catch(NamingException ne)
{
ne.printStackTrace();
}
catch(Exception e)
{
e.printStackTrace();
}
}
public javax.sql.DataSource getTestDS()
{
return testDS;
}
...
...
}
Ҏ 2:止servlet和JSP 自动重蝲(auto-reloading)
Servlet/JSP提供了一个实用的技术,卌动重载技术,它ؓ开发h员提供了一个好的开发环境,当你改变servlet和JSP面后而不必重启应用服务器。然而,q种技术在产品q行阶段对系l的资源是一个极大的损耗,因ؓ它会lJSP引擎的类装蝲?classloader)带来极大的负担。因此关闭自动重载功能对pȝ性能的提升是一个极大的帮助?br />
Ҏ 3: 不要滥用HttpSession
在很多应用中Q我们的E序需要保持客L的状态,以便面之间可以怺联系。但不幸的是׃HTTPh天生无状态性,从而无法保存客L的状态。因此一般的应用服务器都提供了session来保存客L状态。在JSP应用服务器中Q是通过HttpSession对像来实现session的功能的Q但在方便的同时Q它也给pȝ带来了不的负担。因为每当你获得或更新sessionӞpȝ者要对它q行Ҏ的序列化操作。你可以通过对HttpSession的以下几U处理方式来提升pȝ的性能Q?br />
1?如果没有必要Q就应该关闭JSP面中对HttpSession的缺省设|: 如果你没有明指定的话,每个JSP面都会~省地创Z个HttpSession。如果你的JSP中不需要用session的话Q那可以通过如下的JSP面指示W来止它:
Q?@ page session="false"%Q?
2?不要在HttpSession中存攑֤的数据对像:如果你在HttpSession中存攑֤的数据对像的话,每当对它q行dӞ应用服务器都对其进行序列化Q从而增加了pȝ的额外负担。你在HttpSession中存攄数据对像大Q那pȝ的性能׃降得快?br />
3、当你不需要HttpSessionӞ快地释攑֮Q当你不再需要sessionӞ你可以通过调用HttpSession.invalidate()Ҏ来释攑֮?br />
4、尽量将session的超时时间设得短一点:在JSP应用服务器中Q有一个缺省的session的超时时间。当客户在这个时间之后没有进行Q何操作的话,pȝ会将相关的session自动从内存中释放。超时时间设得越大,pȝ的性能׃低Q因此最好的Ҏ是量使得它的g持在一个较低的水^?br />
Ҏ 4: 页面输行压~?br />
压羃是解x据冗余的一个好的方法,特别是在|络带宽不够发达的今天。有的浏览器支持gzip(GNU zip)q行来对HTML文gq行压羃Q这U方法可以戏剧性地减少HTML文g的下载时间。因此,如果你将servlet或JSP面生成的HTML面q行压羃的话Q那用户׃觉得面览速度会非常快。但不幸的是Q不是所有的览器都支持gzip压羃Q但你可以通过在你的程序中查客L览器是否支持它。下面就是关于这U方法实现的一个代码片D:
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
OutputStream out = null
String encoding = request.getHeader("Accept-Encoding");
if (encoding != null && encoding.indexOf("gzip") != -1)
{
request.setHeader("Content-Encoding" , "gzip");
out = new GZIPOutputStream(request.getOutputStream());
}
else if (encoding != null && encoding.indexOf("compress") != -1)
{
request.setHeader("Content-Encoding" , "compress");
out = new ZIPOutputStream(request.getOutputStream());
}
else
{
out = request.getOutputStream();
}
...
...
}
Ҏ 5: 使用U程?/font>
应用服务器缺省地为每个不同的客户端请求创Z个线E进行处理,qؓ它们分派service()ҎQ当service()Ҏ调用完成后,与之相应的线E也随之撤消。由于创建和撤消U程会耗费一定的pȝ资源Q这U缺省模式降低了pȝ的性能。但所q的是我们可以通过创徏一个线E池来改变这U状c另外,我们q要个线E池讄一个最线E数和一个最大线E数。在应用服务器启动时Q它会创建数量等于最线E数的一个线E池Q当客户有请求时Q相应地从池从取Z个线E来q行处理Q当处理完成后,再将U程重新攑օ到池中。如果池中的U程不够地话Q系l会自动地增加池中线E的数量Q但总量不能过最大线E数。通过使用U程池,当客Lh急剧增加Ӟpȝ的负载就会呈现的qx的上升曲U,从而提高的pȝ的可伸羃性?br />
Ҏ 6: 选择正确的页面包含机?/font>
在JSP中有两种Ҏ可以用来包含另一个页面:1、用include指示W?Q?@ includee file=”test.jsp?%Q??、用jsp指示W?Qjsp:includee page=”test.jsp?flush=”true?Q?。在实际中我发现Q如果用第一U方法的话,可以使得pȝ性能更高?br />
Ҏ 7:正确地确定javabean的生命周?/font>
JSP的一个强大的地方是对javabean的支持。通过在JSP面中用<jsp:useBeanQ标{,可以javabean直接插入C个JSP面中。它的用方法如下:
Qjsp:useBean id="name" scope="page|request|session|application" class=
"package.className" type="typeName"Q?br />Q?jsp:useBeanQ?
其中scope属性指Zq个bean的生命周期。缺省的生命周期为page。如果你没有正确地选择bean的生命周期的话,它将影响pȝ的性能?br /> 举例来说Q如果你只想在一ơ请求中使用某个beanQ但你却这个bean的生命周期设|成了sessionQ那当这ơ请求结束后Q这个bean仍然保留在内存中,除非session时或用户关闭浏览器。这样会耗费一定的内存Qƈ无谓的增加了JVM垃圾攉器的工作量。因此ؓbean讄正确的生命周期,q在bean的命结束后快地清理它们,会用系l性能有一个提高?br />
其它一些有用的Ҏ
1、在字符串连接操作中量不用“+”操作符Q在java~程中,我们常常使用“+”操作符来将几个字符串连接v来,但你或许从来没有惛_q它居然会对pȝ性能造成影响吧?׃字符串是帔RQ因此JVM会生一些时的对像。你使用的“+”越多,生成的时对像就多Q这样也会给pȝ性能带来一些媄响。解决的Ҏ是用StringBuffer对像来代曎쀜+”操作符?br />
2?避免使用System.out.println()ҎQ由于System.out.println()是一U同步调用,卛_调用它时Q磁盘I/O操作必须{待它的完成Q因此我们要量避免对它的调用。但我们在调试程序时它又是一个必不可的方便工具Qؓ了解册个矛盾,我徏议你最好用Log4j工具(http://Jakarta.apache.org )Q它既可以方便调试,而不会生System.out.println()q样的方法?br />
3?ServletOutputStream ?PrintWriter的权?使用PrintWriter可能会带来一些小的开销Q因为它所有的原始输出都{换ؓ字符来输出Q因此如果用它来作为页面输出的话,pȝ要负担一个{换过E。而用ServletOutputStream作ؓ面输出的话׃存在一个问题,但它是以二进制进行输出的。因此在实际应用中要权衡两者的利弊?br />
ȝ
本文的目的是通过对servlet和JSP的一些调优技术来极大地提高你的应用程序的性能Qƈ因此提升整个J2EE应用的性能。通过q些调优技术,你可以发现其实ƈ不是某种技术^収ͼ比如J2EE?NET之争Q决定了你的应用E序的性能Q重要是你要对这U^台有一个较为深入的了解Q这样你才能从根本上对自q应用E序做一个优化!
String s="[b]q是_体[/b]";
String result;
result = Regex.eregi_replace("[b](.+?)[/b]","?1?, s);
System.out.println(result);
//打印l果是:
//“这是粗体“?
q么单吗Q是的,我们只需要将其它的UBB Tag作类似的替换实CUBB代码的解析了?
下面l出一个UBBc?
/***************************UbbCode.java****************************************/
import java.util.regex.Matcher; //导入所需要的c?br />import java.util.regex.Pattern;
public class UbbCode //cd?br />{
private String source; //待{化的HTML代码字符?br />private String ubbTags[]; //UBB标记数组
private String htmlTags[]; //HTML标记数组
//初始?分别为UBB标记数组和HTML标记数组赋?br />public UbbCode()
{
byte byte0 = 74;
source = new String();
ubbTags = new String[byte0];
htmlTags = new String[byte0];
ubbTags[0] = "[b]";
htmlTags[0] = "<b>";
ubbTags[1] = "[/b]";
htmlTags[1] = "</b>";
ubbTags[2] = "[i]";
htmlTags[2] = "<em>";
ubbTags[3] = "[/i]";
htmlTags[3] = "</em>";
ubbTags[4] = "[quote]";
htmlTags[4] = "<div style=\"border-style:dashed;background-color:#CCCCCC;border-width:thin;border-color:#999999\"><br><em>";
ubbTags[5] = "[/quote]";
htmlTags[5] = "</em><br><br></div>";
ubbTags[6] = "[/size]";
htmlTags[6] = "</font>";
ubbTags[7] = "[size=6]";
htmlTags[7] = "<font style=\"font-size:6px\">";
ubbTags[8] = "[size=8]";
htmlTags[8] = "<font style=\"font-size:8px\">";
ubbTags[9] = "[size=10]";
htmlTags[9] = "<font style=\"font-size:10px\">";
ubbTags[10] = "[size=12]";
htmlTags[10] = "<font style=\"font-size:12px\">";
ubbTags[11] = "[size=14]";
htmlTags[11] = "<font style=\"font-size:14px\">";
ubbTags[12] = "[size=18]";
htmlTags[12] = "<font style=\"font-size:18px\">";
ubbTags[13] = "[size=24]";
htmlTags[13] = "<font style=\"font-size:24px\">";
ubbTags[14] = "[size=36]";
htmlTags[14] = "<font style=\"font-size:36px\">";
//字体
ubbTags[15] = "[/font]";
htmlTags[15] = "</font>";
ubbTags[16] = "[font=Arial]";
htmlTags[16] = "<font face=\"Arial\">";
ubbTags[17] = "[font=Arial Black]";
htmlTags[17] = "<font face=\"Arial Black\">";
ubbTags[18] = "[font=Verdana]";
htmlTags[18] = "<font face=\"Verdana\">";
ubbTags[19] = "[font=Times New Roman]";
htmlTags[19] = "<font face=\"Times New Roman\">";
ubbTags[20] = "[font=Garamond]";
htmlTags[20] = "<font face=\"Garamond\">";
ubbTags[21] = "[font=Courier New]";
htmlTags[21] = "<font face=\"Courier New\">";
ubbTags[22] = "[font=Webdings]";
htmlTags[22] = "<font face=\"Webdings\">";
ubbTags[23] = "[font=Wingdings]";
htmlTags[23] = "<font face=\"Wingdings\">";
ubbTags[24] = "[font=隶书]";
htmlTags[24] = "<font face=\"隶书\">";
ubbTags[25] = "[font=q圆]";
htmlTags[25] = "<font face=\"q圆\">";
ubbTags[26] = "[font=Ҏ舒体]";
htmlTags[26] = "<font face=\"Ҏ舒体\">";
ubbTags[27] = "[font=Ҏ姚体]";
htmlTags[27] = "<font face=\"Ҏ姚体\">";
ubbTags[28] = "[font=仿宋_GB2312]";
htmlTags[28] = "<font face=\"仿宋_GB2312\">";
ubbTags[29] = "[font=黑体]";
htmlTags[29] = "<font face=\"黑体\">";
ubbTags[30] = "[font=华文彩云]";
htmlTags[30] = "<font face=\"华文彩云\">";
ubbTags[31] = "[font=华文l黑]";
htmlTags[31] = "<font face=\"华文l黑\">";
ubbTags[32] = "[font=华文新魏]";
htmlTags[32] = "<font face=\"华文新魏\">";
ubbTags[33] = "[font=华文中宋]";
htmlTags[33] = "<font face=\"华文中宋\">";
ubbTags[34] = "[font=华文行楷]";
htmlTags[34] = "<font face=\"华文行楷\">";
ubbTags[35] = "[font=楷体_GB2312]";
htmlTags[35] = "<font face=\"楷体_GB2312\">";
ubbTags[36] = "[font=隶书]";
htmlTags[36] = "<font face=\"隶书\">";
ubbTags[37] = "[font=华文楷体]";
htmlTags[37] = "<font face=\"华文楷体\">";
ubbTags[38] = "[font=宋体]";
htmlTags[38] = "<font face=\"宋体\">";
ubbTags[39] = "[font=新宋?;
htmlTags[39] = "<font face=\"新宋体\">";
ubbTags[40] = "[font=q圆";
htmlTags[40] = "<font face=\"q圆\">";
//字体颜色
ubbTags[41] = "[red]";
htmlTags[41] = "<font color=\"red\">";
ubbTags[42] = "[/red]";
htmlTags[42] = "</font>";
ubbTags[43] = "[blue]";
htmlTags[43] = "<font color=\"blue\">";
ubbTags[44] = "[/blue]";
htmlTags[44] = "</font>";
ubbTags[45] = "[yellow]";
htmlTags[45] = "<font color=\"yellow\">";
ubbTags[46] = "[/yellow]";
htmlTags[46] = "</font>";
ubbTags[47] = "[green]";
htmlTags[47] = "<font color=\"green\">";
ubbTags[48] = "[/green]";
htmlTags[48] = "</font>";
ubbTags[49] = "[f]";
htmlTags[49] = "<marquee width=\"400\" scrolldelay=\"30\" scrollamount=\"1\" onmouseover=\"this.stop()\" onmouseout=\"this.start()\">";
//标题
ubbTags[50] = "[h1]";
htmlTags[50] = "<h1>";
ubbTags[51] = "[/h1]";
htmlTags[51] = "</h1>";
ubbTags[52] = "[h2]";
htmlTags[52] = "<h2>";
ubbTags[53] = "[/h2]";
htmlTags[53] = "</h2>";
ubbTags[54] = "[h3]";
htmlTags[54] = "<h3>";
ubbTags[55] = "[/h3]";
htmlTags[55] = "</h3>";
ubbTags[56] = "[h4]";
htmlTags[56] = "<h4>";
ubbTags[57] = "[/h4]";
htmlTags[57] = "</h4>";
ubbTags[58] = "[h5]";
htmlTags[58] = "<h5>";
ubbTags[59] = "[/h5]";
htmlTags[59] = "</h5>";
ubbTags[60] = "[h6]";
htmlTags[60] = "<h6>";
ubbTags[61] = "[/h6]";
htmlTags[61] = "</h6>";
ubbTags[62] = "[hr]";
htmlTags[62] = "<hr>";
ubbTags[63] = "[img]";
htmlTags[63] = "<br><img src=\"";
ubbTags[64] = "[/img]";
htmlTags[64] = "\"><br>";
ubbTags[65] = "[center]";
htmlTags[65] = "<div align=\"center\">";
ubbTags[66] = "[/center]";
htmlTags[66] = "</div>";
ubbTags[67] = "[/f]";
htmlTags[67] = "</marquee>";
ubbTags[68] = "[left]";
htmlTags[68] = "<div align=\"left\">";
ubbTags[69] = "[/left]";
htmlTags[69] = "</div>";
ubbTags[70] = "[right]";
htmlTags[70] = "<div align=\"right\">";
ubbTags[71] = "[/right]";
htmlTags[71] = "</div>";
ubbTags[72] = "[u]";
htmlTags[72] = "<u>";
ubbTags[73] = "[/u]";
htmlTags[73] = "</u>";
}
private String replace(String s, String s1, String s2) {
StringBuffer stringbuffer = new StringBuffer();
for(int i = 0; i < s1.length(); i++) {
char c = s1.charAt(i);
switch(c) {
case 91: // '['
stringbuffer.append("\\[");
break;
case 93: // ']'
stringbuffer.append("\\]");
break;
default:
stringbuffer.append(c);
break;
}
}
Pattern pattern = Pattern.compile(stringbuffer.toString());
Matcher matcher = pattern.matcher(s);
StringBuffer stringbuffer1 = new StringBuffer();
for(boolean flag = matcher.find(); flag; flag = matcher.find())
matcher.appendReplacement(stringbuffer1, s2);
return matcher.appendTail(stringbuffer1).toString();
}
private String replaceNormalUBBCode(String s)
{
String s1 = new String(s);
for(int i = 0; i < ubbTags.length; i++)
s1 = replace(s1, ubbTags[i], htmlTags[i]);
return s1;
}
private String replaceURL(String s)
{
StringBuffer stringbuffer = new StringBuffer(s);
String s1 = new String();
int i = s.indexOf("[url]");
int j = s.indexOf("[/url]");
if(i != -1 && j != -1 && i < j)
{
String s2 = s.substring(i + 5, j);
String s3 = "<a href=\"" + s2 + "\">" + s2 + "</a>";
stringbuffer.replace(i, j + 6, s3);
}
return stringbuffer.toString();
}
private String replaceEmail(String s)
{
StringBuffer stringbuffer = new StringBuffer(s);
String s1 = new String();
int i = s.indexOf("[email]");
int j = s.indexOf("[/email]");
if(i != -1 && j != -1 && i < j)
{
String s2 = s.substring(i + 7, j);
String s3 = "<a href=\"mailto:" + s2 + "\">" + s2 + "</a>";
stringbuffer.replace(i, j + 8, s3);
}
return stringbuffer.toString();
}
public void setSource(String s)
{
source = s;
}
public String getResult()
{
return source;
}
public void run()
{
for(source = replaceNormalUBBCode(source); source.indexOf("[url]") != -1 && source.indexOf("[/url]") != -1;
source = replaceURL(source));
for(; source.indexOf("[email]") != -1 && source.indexOf("[/email]") != -1; source = replaceEmail(source));
}
}
|
q段代码的主要部分调用了两个很相q的ҎQchangeObj()和changePri()。唯一不同的是它们一个把对象作ؓ输入参数Q另一个把Java中的基本cdint作ؓ输入参数。ƈ且在q两个函C内部都对输入的参数进行了改动。看gLҎQ程序输出的l果却不太一栗changeObj()Ҏ真正的把输入的参数改变了Q而changePri()Ҏ对输入的参数没有M的改变?/p>
从这个例子知道Java对对象和基本的数据类型的处理是不一L。和C语言一P当把Java的基本数据类型(如intQcharQdouble{)作ؓ入口参数传给函数体的时候,传入的参数在函数体内部变成了局部变量,q个局部变量是输入参数的一个拷贝,所有的函数体内部的操作都是针对q个拯的操作,函数执行l束后,q个局部变量也完成了它的使命Q它影响不到作ؓ输入参数的变量。这U方式的参数传递被UCؓ"g?。而在Java中用对象的作为入口参数的传递则~省?引用传?Q也是说仅仅传递了对象的一?引用"Q这?引用"的概念同C语言中的指针引用是一L。当函数体内部对输入变量改变Ӟ实质上就是在对这个对象的直接操作?/p>
除了在函C值的时候是"引用传?Q在M?Q?向对象变量赋值的时候都?引用传?。如Q?/p>
|
W一句是在内存中生成一个新的PassObj对象Q然后把q个PassObj的引用赋l变量objAQ第二句是把PassObj对象的引用又赋给了变量objB。此时objA和objB是两个完全一致的变量Q以后Q何对objA的改变都{同于对objB的改变?/p>
即明白了Java语言中的"指针"概念也许q会不经意间犯下面的错误?/p>
看一看下面的很简单的代码Q先是声明了一个Hashtable和StringBuffer对象Q然后分四次把StriingBuffer对象攑օ到Hashtable表中Q在每次攑օ之前都对q个StringBuffer对象append()了一些新的字W串Q?/p>
|
如果你认出的l果是:
get StringBufffer 1 from Hashtable: abc,
get StringBufffer 2 from Hashtable: abc,defQ?
get StringBufffer 3 from Hashtable: abc,def,mno,
get StringBufffer 4 from Hashtable: abc,def,mno,xyz.
那么你就要回q头再仔l看一看上一个问题了Q把对象时作为入口参Cl函敎ͼ实质上是传递了对象的引用,向Hashtable传递StringBuffer对象也是只传递了q个StringBuffer对象的引用!每一ơ向Hashtable表中put一ơStringBufferQƈ没有生成新的StringBuffer对象Q只是在Hashtable表中又放入了一个指向同一StringBuffer对象的引用而已?/p>
对Hashtable表存储的M一个StringBuffer对象Q更切的说应该是对象的引用Q的改动Q实际上都是对同一?StringBuffer"的改动。所以Hashtableq不能真正存储能对象Q而只能存储对象的引用。也应该知道q条原则对与Hashtable怼的Vector, List, Map, Set{都是一L?/p>
上面的例E的实际输出的结果是Q?/p>
|
![]() ![]() |
![]()
|
Java最基本的概念就是类Q类包括函数和变量。如果想要应用类Q就要把cȝ成对象,q个q程被称?cȝ实例?。有几种Ҏ把类实例化成对象Q最常用的就是用"new"操作W。类实例化成对象后,意味着要在内存中占据一块空间存攑֮例。想要对q块I间操作p应用到对象的引用。引用在Java语言中的体现是变量Q而变量的cd是q个引用的对象。虽然在语法上可以在生成一个对象后直接调用该对象的函数或变量,如:
|
但由于没有相应的引用Q对q个对象的用也只能局限这条语句中了?/p>
![]() ![]() |
![]()
|
在实际编E过E中Q我们常常要遇到q种情况Q有一个对象AQ在某一时刻A中已l包含了一些有效|此时可能会需要一个和A完全相同新对象BQƈ且此后对BM改动都不会媄响到A中的|也就是说QA与B是两个独立的对象Q但B的初始值是由A对象定的。在Java语言中,用简单的赋D句是不能满q种需求的。要满q种需求虽然有很多途径Q但实现cloneQ)Ҏ是其中最单,也是最高效的手Dc?/p>
Java的所有类都默认承java.lang.Objectc,在java.lang.ObjectcM有一个方法clone()。JDK API的说明文档解释这个方法将q回Object对象的一个拷贝。要说明的有两点Q一是拷贝对象返回的是一个新对象Q而不是一个引用。二是拷贝对象与用new操作W返回的新对象的区别是q个拯已经包含了一些原来对象的信息Q而不是对象的初始信息?/p>
![]() ![]() |
![]()
|
一个很典型的调用clone()代码如下Q?/p>
|
有三个值得注意的地方,一是希望能实现clone功能的CloneClasscdCCloneable接口Q这个接口属于java.lang包,java.lang包已l被~省的导入类中,所以不需要写成java.lang.Cloneable。另一个值得h意的是重载了clone()Ҏ。最后在clone()Ҏ中调用了super.clone()Q这也意味着无论clonecȝl承l构是什么样的,super.clone()直接或间接调用了java.lang.Objectcȝclone()Ҏ。下面再详细的解释一下这几点?/p>
应该说第三点是最重要的,仔细观察一下Objectcȝclone()一个nativeҎQnativeҎ的效率一般来说都是远高于java中的非nativeҎ。这也解释了Z么要用Object中clone()Ҏ而不是先new一个类Q然后把原始对象中的信息赋到新对象中Q虽然这也实Cclone功能。对于第二点Q也要观察ObjectcM的clone()q是一个protected属性的Ҏ。这也意味着如果要应用clone()ҎQ必ȝ承Objectc,在Java中所有的cL~省l承ObjectcȝQ也׃用关心这点了。然后重载clone()Ҏ。还有一点要考虑的是Z让其它类能调用这个clonecȝclone()ҎQ重载之后要把clone()Ҏ的属性设|ؓpublic?/p>
那么clonecMؓ什么还要实现Cloneable接口呢?E微注意一下,Cloneable接口是不包含MҎ的!其实q个接口仅仅是一个标志,而且q个标志也仅仅是针对ObjectcMclone()Ҏ的,如果clonecL有实现Cloneable接口Qƈ调用了Object的clone()ҎQ也是调用了super.Clone()ҎQ,那么Object的clone()Ҏ׃抛出CloneNotSupportedException异常?/p>
以上是clone的最基本的步骤,惌完成一个成功的cloneQ还要了解什么是"影子clone"?深度clone"?/p>
![]() ![]() |
![]()
|
下面的例子包含三个类UnCloneAQCloneBQCloneMain。CloneBcd含了一个UnCloneA的实例和一个intcd变量Qƈ且重载clone()Ҏ。CloneMaincd始化UnCloneAcȝ一个实例b1Q然后调用clone()Ҏ生成了一个b1的拷贝b2。最后考察一下b1和b2的输出:
|
输出的结果说明intcd的变量aInt和UnCloneA的实例对象unCA的clonel果不一_intcd是真正的被clone了,因ؓ改变了b2中的aInt变量Q对b1的aInt没有产生影响Q也是_b2.aInt与b1.aInt已经占据了不同的内存I间Qb2.aInt是b1.aInt的一个真正拷贝。相反,对b2.unCA的改变同时改变了b1.unCAQ很明显Qb2.unCA和b1.unCA是仅仅指向同一个对象的不同引用Q从中可以看出,调用ObjectcMclone()Ҏ产生的效果是Q先在内存中开辟一块和原始对象一LI间Q然后原h贝原始对象中的内宏V对基本数据cdQ这L操作是没有问题的Q但寚w基本cd变量Q我们知道它们保存的仅仅是对象的引用Q这也导致clone后的非基本类型变量和原始对象中相应的变量指向的是同一个对象?/p>
大多时候,q种clone的结果往往不是我们所希望的结果,q种clone也被UCؓ"影子clone"。要惌b2.unCA指向与b2.unCA不同的对象,而且b2.unCA中还要包含b1.unCA中的信息作ؓ初始信息Q就要实现深度clone?/p>
![]() ![]() |
![]()
|
把上面的例子Ҏ深度clone很简单,需要两个改变:一是让UnCloneAcM实现和CloneBcMLclone功能Q实现Cloneable接口Q重载clone()ҎQ。二是在CloneB的clone()Ҏ中加入一句o.unCA = (UnCloneA)unCA.clone();
E序如下Q?/p>
|
可以看出Q现在b2.unCA的改变对b1.unCA没有产生影响。此时b1.unCA与b2.unCA指向了两个不同的UnCloneA实例Q而且在CloneB b2 = (CloneB)b1.clone();调用的那一刻b1和b2拥有相同的|在这里,b1.i = b2.i = 11?/p>
要知道不是所有的c都能实现深度clone的。例如,如果把上面的CloneBcM的UnCloneAcd变量ҎStringBuffercdQ看一下JDK API中关于StringBuffer的说明,StringBuffer没有重蝲clone()ҎQ更Z重的是StringBufferq是一个finalc,q也是说我们也不能用l承的办法间接实现StringBuffer的clone。如果一个类中包含有StringBuffercd对象或和StringBuffer怼cȝ对象Q我们有两种选择Q要么只能实现媄子cloneQ要么就在类的clone()Ҏ中加一句(假设是SringBuffer对象Q而且变量名仍是unCAQ: o.unCA = new StringBuffer(unCA.toString()); //原来的是Qo.unCA = (UnCloneA)unCA.clone();
q要知道的是除了基本数据cd能自动实现深度clone以外QString对象是一个例外,它clone后的表现好象也实C深度cloneQ虽然这只是一个假象,但却大大方便了我们的~程?/p>
![]() ![]() |
![]()
|
Clone中String和StringBuffer的区?/font>
应该说明的是Q这里不是着重说明String和StringBuffer的区别,但从q个例子里也能看出Stringcȝ一些与众不同的地方?/p>
下面的例子中包括两个c,CloneCcd含一个Stringcd变量和一个StringBuffercd变量Qƈ且实Cclone()Ҏ。在StrClonecM声明了CloneCcd变量c1Q然后调用c1的clone()Ҏ生成c1的拷贝c2Q在对c2中的String和StringBuffercd变量用相应的Ҏ改动之后打印l果Q?/p>
|
打印的结果可以看出,Stringcd的变量好象已l实C深度cloneQ因为对c2.str的改动ƈ没有影响到c1.strQ难道Java把Sringcȝ成了基本数据cdQ其实不Ӟq里有一个小的把戏Q秘密就在于c2.str = c2.str.substring(0,5)q一语句Q实质上Q在clone的时候c1.str与c2.str仍然是引用,而且都指向了同一个String对象。但在执行c2.str = c2.str.substring(0,5)的时候,它作用相当于生成了一个新的StringcdQ然后又赋回lc2.str。这是因为String被Sun公司的工E师写成了一个不可更改的c(immutable classQ,在所有StringcM的函数都不能更改自n的倹{下面给出很单的一个例子:
package clone; public class StrTest { public static void main(String[] args) { String str1 = "This is a test for immutable"; String str2 = str1.substring(0,8); System.out.println("print str1 : " + str1); System.out.println("print str2 : " + str2); } } /* RUN RESULT print str1 : This is a test for immutable print str2 : This is */
例子中,虽然str1调用了substring()ҎQ但str1的值ƈ没有改变。类似的QStringcM的其它方法也是如此。当然如果我们把最上面的例子中的这两条语句
|
Ҏ下面q样Q?/p>
|
L了重新赋值的q程Qc2.str也就不能有变化了Q我们的把戏也就露馅了。但在编E过E中只调?/p>
|
语句是没有Q何意义的?/p>
应该知道的是在Java中所有的基本数据cd都有一个相对应的类Q象Integercd应intcdQDoublecd应doublecd{等Q这些类也与Stringcȝ同,都是不可以改变的cR也是_q些的类中的所有方法都是不能改变其自n的值的。这也让我们在编clonecȝ时候有了一个更多的选择。同时我们也可以把自qcȝ成不可更改的cR?/p>
![]() |
![]()
|
|
![]() |
|