??xml version="1.0" encoding="utf-8" standalone="yes"?>MM131亚洲国产美女久久,中文字幕无码精品亚洲资源网久久,亚洲一区中文字幕在线电影网
http://m.tkk7.com/nighthawk/category/2524.html享受JAVAQn受h?/description>zh-cn Wed, 28 Feb 2007 20:30:02 GMT Wed, 28 Feb 2007 20:30:02 GMT 60 设计模式之Adapter(适配? http://m.tkk7.com/nighthawk/articles/10432.htmlnighthawk nighthawk Thu, 18 Aug 2005 06:41:00 GMT http://m.tkk7.com/nighthawk/articles/10432.html http://m.tkk7.com/nighthawk/comments/10432.html http://m.tkk7.com/nighthawk/articles/10432.html#Feedback 0 http://m.tkk7.com/nighthawk/comments/commentRss/10432.html http://m.tkk7.com/nighthawk/services/trackbacks/10432.html 适配器模式定? 两个不兼容的类U合在一起用,属于l构型模?需要有Adaptee(被适配?和Adaptor(适配?两个w䆾.
Z使用? 我们l常到要将两个没有关系的类l合在一起?W一解决Ҏ(gu)是:(x)修改各自cȝ接口Q但是如果我们没有源代码Q或者,我们不愿意ؓ(f)?jin)一个应用而修改各自的接口?怎么?
使用AdapterQ在q两U接口之间创Z个合接?淯?.
如何使用? 实现Adapter方式,其实"think in Java"?cd?一节中已经提到,有两U方式:(x)l合(composition)和(h)?inheritance).
假设我们要打桩,有两U类Q方形桩 圆Ş? public class SquarePeg{ public void insert(String str){ System.out.println("SquarePeg insert():"+str); }
}
public class RoundPeg{ public void insertIntohole(String msg){ System.out.println("RoundPeg insertIntoHole():"+msg); } }
现在有一个应?需要既打方形桩,又打圆Ş?那么我们需要将q两个没有关pȝcȝ合应?假设RoundPeg我们没有源代?或源代码我们不想修改,那么我们使用Adapter来实现这个应?
public class PegAdapter extends SquarePeg{ private RoundPeg roundPeg;
public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)
public void insert(String str){ roundPeg.insertIntoHole(str);}
}
在上面代码中,RoundPeg属于Adaptee,是被适配?PegAdapter是Adapter,Adaptee(被适配者RoundPeg)和Target(目标SquarePeg)q行适配.实际上这是将l合Ҏ(gu)(composition)和(h)?inheritance)Ҏ(gu)l合q用.
PegAdapter首先l承SquarePegQ然后用new的组合生成对象方式,生成RoundPeg的对象roundPegQ再重蝲父类insert()Ҏ(gu)。从q里,你也?jin)解使用new生成对象和用extendsl承生成对象的不?前者无需对原来的cM?甚至无需要知道其内部l构和源代码.
如果你有些Java使用的经验,已经发现Q这U模式经怋用?/P>
q一步?/B> 上面的PegAdapter是(h)承了(jin)SquarePeg,如果我们需要两边(h)承,即(h)承SquarePeg 又(h)承RoundPeg,因ؓ(f)Java中不允许多(h)承,但是我们可以实现(implements)两个接口(interface)
public interface IRoundPeg{ public void insertIntoHole(String msg); }
public interface ISquarePeg{ public void insert(String str);
}
下面是新的RoundPeg 和SquarePeg, 除了(jin)实现接口q一区别Q和上面的没什么区别?BR>public class SquarePeg implements ISquarePeg{ public void insert(String str){ System.out.println("SquarePeg insert():"+str); }
}
public class RoundPeg implements IRoundPeg{ public void insertIntohole(String msg){ System.out.println("RoundPeg insertIntoHole():"+msg); } }
下面是新的PegAdapter,叫做two-way adapter:
public class PegAdapter implements IRoundPeg,ISquarePeg{ private RoundPeg roundPeg; private SquarePeg squarePeg; // 构造方?BR> public PegAdapter(RoundPeg peg){this.roundPeg=peg;} // 构造方?BR> public PegAdapter(SquarePeg peg)(this.squarePeg=peg;)
public void insert(String str){ roundPeg.insertIntoHole(str);}
}
q有一U叫Pluggable Adapters,可以动态的获取几个adapters中一个。用Reflection技术,可以动态的发现cM的PublicҎ(gu)?BR> ?JDON
]]>JSP/Servlet 中的汉字~码问题 http://m.tkk7.com/nighthawk/articles/9222.htmlnighthawk nighthawk Thu, 04 Aug 2005 07:02:00 GMT http://m.tkk7.com/nighthawk/articles/9222.html http://m.tkk7.com/nighthawk/comments/9222.html http://m.tkk7.com/nighthawk/articles/9222.html#Feedback 0 http://m.tkk7.com/nighthawk/comments/commentRss/9222.html http://m.tkk7.com/nighthawk/services/trackbacks/9222.html 每个国家Q或区域Q都规定?jin)计机信息交换用的字符~码集,如美国的扩展 ASCII? 中国?GB2312-80Q日本的 JIS {,作ؓ(f)该国?区域内信息处理的基础Q有着l一~码的重要作用。字W编码集按长度分?SBCSQ单字节字符集)(j)QDBCSQ双字节字符集)(j)两大cR早期的软gQ尤其是操作pȝQ,Z(jin)解决本地字符信息的计机处理Q出C(jin)各种本地化版本(L10NQ,Z(jin)区分Q引q了(jin) LANG, Codepage {概c(din)但是由于各个本地字W集代码范围重叠Q相互间信息交换困难QY件各个本地化版本独立l护成本较高。因此有必要本地化工作中的共性抽取出来,作一致处理,特别的本地化处理内定w低到最。这也就是所谓的国际化(I18NQ。各U语a信息被进一步规范ؓ(f) Locale 信息。处理的底层字符集变成了(jin)几乎包含?jin)所有字形的 Unicode? 现在大部分具有国际化特征的Y件核?j)字W处理都是以 Unicode 为基的,在Y件运行时Ҏ(gu)当时?Locale/Lang/Codepage 讄定相应的本地字W编码设|,q依此处理本地字W。在处理q程中需要实?Unicode 和本地字W集的相互{换,甚或?Unicode Z间的两个不同本地字符集的怺转换。这U方式在|络环境下被q一步g伸,M|络两端的字W信息也需要根据字W集的设|{换成可接受的内容? Java 语言内部是用 Unicode 表示字符的,遵守 Unicode V2.0。Java E序无论是从/往(xin)文gpȝ以字W流?写文Ӟq是往(xin) URL q接?HTML 信息Q或?URL q接d参数|都会(x)有字W编码的转换。这样做虽然增加?jin)编E的复杂度,Ҏ(gu)引vhQ但却是W合国际化的思想的? 从理Z来说Q这些根据字W集讄而进行的字符转换不应该生太多问题。而事实是׃应用E序的实际运行环境不同,Unicode 和各个本地字W集的补充、完善,以及(qing)pȝ或应用程序实现的不规范,转码时出现的问题时时困扰着E序员和用户? 2. GB2312-80QGBKQGB18030-2000 汉字字符集及(qing) Encoding 其实解决 JAVA E序中的汉字~码问题的方法往(xin)往(xin)很简单,但理解其背后的原因,定位问题Q还需要了(jin)解现有的汉字~码和编码{换? GB2312-80 是在国内计算机汉字信息技术发展初始阶D制定的Q其中包含了(jin)大部分常用的一、二U汉字,?9 区的W号。该字符集是几乎所有的中文pȝ和国际化的Y仉支持的中文字W集Q这也是最基本的中文字W集。其~码范围是高?sh)?xa1Q?xfeQ低位也?0xa1-0xfeQ汉字从 0xb0a1 开始,l束?0xf7feQ? GBK ?GB2312-80 的扩展,是向上兼容的。它包含?20902 个汉字,其编码范围是 0x8140-0xfefeQ剔除高?sh)?0x80 的字位。其所有字W都可以一对一映射?Unicode 2.0Q也是?JAVA 实际上提供了(jin) GBK 字符集的支持。这是现阶段 Windows 和其它一些中文操作系l的~省字符集,但ƈ不是所有的国际化Y仉支持该字W集Q感觉是他们q不完全知道 GBK 是怎么回事。值得注意的是它不是国家标准,而只是规范。随着 GB18030-2000国标的发布,它将在不久的来完成它的历史使命? GB18030-2000(GBK2K) ?GBK 的基上进一步扩展了(jin)汉字Q增加了(jin)藏、蒙{少数民族的字Ş。GBK2K 从根本上解决?jin)字位不够,字Ş不的问题。它有几个特点, 它ƈ没有定所有的字ŞQ只是规定了(jin)~码范围Q留待以后扩充? ~码是变长的Q其二字节部分与 GBK 兼容Q四字节部分是扩充的字Ş、字位,其编码范围是首字?0x81-0xfe、二字节0x30-0x39、三字节 0x81-0xfe、四字节0x30-0x39? 它的推广是分阶段的,首先要求实现的是能够完全映射?Unicode 3.0 标准的所有字形? 它是国家标准Q是强制性的?BR>现在q没有Q何一个操作系l或软g实现?GBK2K 的支持,q是现阶D和来汉化的工作内宏V? Unicode 的介l?.....免?jin)吧? JAVA 支持的encoding中与中文~程相关的有Q?有几个在JDK文中未列出) ASCII 7-bit, ?ascii7 ISO8859-1 8-bit, ?8859_1,ISO-8859-1,ISO_8859-1,latin1... GB2312-80 同gb2312,gb2312-1980,EUC_CN,euccn,1381,Cp1381, 1383, Cp1383, ISO2022CN,ISO2022CN_GB...... GBK (注意大小?,同MS936 UTF8 UTF-8 GB18030 (现在只有IBM JDK1.3.?有支?, 同Cp1392,1392 JAVA 语言采用Unicode处理字符. 但从另一个角度来_(d)在javaE序中也可以采用非Unicode的{码,重要的是保证E序入口和出口的汉字信息不失真。如完全采用ISO-8859-1来处理汉字也能达到正的l果。网l上行的许多解x(chng)法,都属于这U类型。ؓ(f)?jin)不致引h淆,本文不对q种Ҏ(gu)作讨论? 3. 中文转码??'、ؕ码的由来 两个方向转换都有可能得到错误的结果:(x) Unicode-->Byte, 如果目标代码集不存在对应的代码,则得到的l果?x3f. 如:(x) "\u00d6\u00ec\u00e9\u0046\u00bb\u00f9".getBytes("GBK") 的结果是 "?ìéF?ù", Hex 值是3fa8aca8a6463fa8b4. 仔细看一下上面的l果Q你?x)发现\u00ec被{换ؓ(f)0xa8ac, \u00e9被{换ؓ(f)\xa8a6... 它的实际有效位变长了(jin)Q?q是因ؓ(f)GB2312W号Z的一些符可映射C些公qW号~码Q由于这些符号出现在ISO-8859-1或其它一些SBCS字符集中Q故它们在Unicode中编码比较靠前,有一些其有效位只?位,和汉字的~码重叠(其实q种映射只是~码的映,在显C时仔细不是一L(fng)。Unicode 中的W号是单字节宽,汉字中的W号是双字节? . 在Unicode\u00a0--\u00ff 之间q样的符h20个。了(jin)解这个特征非帔R要!由此׃隄解ؓ(f)什么JAVA~程中,汉字~码的错误结果中常常?x)出C些ؕ?其实是符号字W?, 而不全是'?'字符, 比如上面的例子?BR> Byte-->Unicode, 如果Byte标识的字W在源代码集不存在,则得到的l果?xfffd. 如:(x) Byte ba[] = {(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1}; new String(ba,"gb2312"); l果???, hex 值是"\ufffd\u554a". 0x8140 是GBK字符Q按GB2312转换表没有对应的|取\ufffd. (h意:(x)在显CuniCodeӞ因ؓ(f)没有对应的本地字W,所以也适用上一U情况,昄Z??".) 实际~程中,JSP/Servlet E序得到错误的汉字信息,往(xin)往(xin)是这两个q程的叠加,有时甚至是两个过E叠加后反复作用的结? 4. JSP/Servlet 汉字~码问题?qing)?WAS 中的解决办法 4.1 常见?encoding 问题的现?BR>|上常出现的 JSP/Servlet encoding 问题?sh)般都表现?browser 或应用程序端Q如: 览器中看到?Jsp/Servlet 面中的汉字怎么都成???? 览器中看到?Servlet 面中的汉字怎么都成?jin)ؕ码? JAVA 应用E序界面中的汉字怎么都成?jin)方块? Jsp/Servlet 面无法昄 GBK 汉字? JSP 面中内嵌在<%...%>,<%=...%>{Tag包含?JAVA code 中的中文成了(jin)qQ但面的其它汉字是对的? Jsp/Servlet 不能接收 form 提交的汉字? JSP/Servlet 数据库读写无法获得正的内容?BR>隐藏在这些问题后面的是各U错误的字符转换和处理(除第3个外Q是因ؓ(f) Java font 讄错误引v的)(j)。解决类似的字符 encoding 问题Q需要了(jin)?Jsp/Servlet 的运行过E,(g)查可能出现问题的各个炏V? 4.2 JSP/Servlet web ~程时的 encoding 问题 q行于Java 应用服务器的 JSP/Servlet ?Browser 提供 HTML 内容 其中有字W编码{换的地方? JSP ~译。Java 应用服务器将Ҏ(gu) JVM ?file.encoding D?JSP 源文Ӟ~译生成 JAVA 源文Ӟ再根?file.encoding 值写回文件系l。如果当前系l语a支持 GBKQ那么这时候不?x)出?encoding 问题。如果是英文的系l,?LANG ?en_US ?Linux, AIX ?SolarisQ则要将 JVM ?file.encoding 值置?GBK 。系l语a如果?GB2312Q则Ҏ(gu)需要,定要不要设|?file.encodingQ将 file.encoding 设ؓ(f) GBK 可以解决潜在?GBK 字符q问题 Java 需要被~译?.class 才能?JVM 中执行,q个q程存在与a.同样?file.encoding 问题。从q里开?servlet ?jsp 的运行就cM?jin),只不q?Servlet 的编译不是自动进行的。对于JSPE序, 对生的JAVA 中间文g的编译是自动q行?在程序中直接调用sun.tools.javac.Mainc?. 因此如果在这一步出现问题的? 也要(g)查encoding和OS的语a环境Q或者将内嵌在JSP JAVA Code 中的?rn)态汉字{?Unicode, 要么?rn)态文本输Z要放?JAVA code 中?对于Servlet, javac ~译时手工指?encoding 参数可以了(jin)? Servlet 需要将 HTML 面内容转换?browser 可接受的 encoding 内容发送出厅R依赖于?JAVA App Server 的实现方式,有的查?Browser ?accept-charset ?accept-language 参数或以其它猜的方式定 encoding |有的则不。因此采用固定encoding 也许是最好的解决Ҏ(gu)。对于中文网,可在 JSP ?Servlet 中设|?contentType="text/html; charset=GB2312"Q如果页面中有GBK字符Q则讄为contentType="text/html; charset=GBK"Q由于IE ?Netscape对GBK的支持程度不一P作这U设|时需要测试一下?BR>因ؓ(f)16?JAVA char在网l传送时?位会(x)被丢弃,也ؓ(f)?jin)确保Servlet面中的汉字Q包括内嵌的和servletq行q程中得到的Q是期望的内码,可以?PrintWriter out=res.getWriter() 取代 ServletOutputStream out=res.getOutputStream(). PrinterWriter 根据contentType中指定的charset作{?(ContentType需在此之前指定Q?; 也可以用OutputStreamWriter装 ServletOutputStream cdƈ用write(String)输出汉字字符丌Ӏ?BR>对于 JSPQJAVA Application Server 应当能够保在这个阶D将嵌入的汉字正传送出厅R? q是解释 URL 字符 encoding 问题。如果通过 get/post 方式?browser q回的参数g包含汉字信息Q?servlet 无法得到正的倹{SUN?J2SDK 中,HttpUtils.parseName 在解析参数时Ҏ(gu)没有考虑 browser 的语a讄Q而是得到的值按 byte 方式解析。这是网上讨论得最多的 encoding 问题。因是设计缺P只能?bin 方式重新解析得到的字W串Q或者以 hack HttpUtils cȝ方式解决。参考文?2 均有介绍Q不q最好将其中的中?encoding GB2312?CP1381 都改?GBKQ否则遇?GBK 汉字Ӟq是?x)有问题? Servlet API 2.3 提供一个新的函?HttpServeletRequest.setCharacterEncoding 用于在调?request.getParameter(“param_name? 前指定应用程序希望的 encodingQ这有助于d解决q个问题?BR>4.3 IBM Websphere Application Server 中的解决Ҏ(gu) WebSphere Application Server Ҏ(gu)准的 Servlet API 2.x 作了(jin)扩展Q提供较好的多语a支持。运行在中文的操作系l中Q可以不作Q何设|就可以很好地处理汉字。下面的说明只是对WAS是运行在英文的系l中Q或者需要有GBK支持时有效? 上述c,d情况QW(xu)AS 都要查询 Browser 的语a讄Q在~省状况下, zh, zh-cn {均被映ؓ(f) JAVA encoding CP1381Q注意:(x) CP1381 只是{同?GB2312 的一?codepageQ没?GBK 支持Q。这样做我想是因为无法确?Browser q行的操作系l是支持GB2312, q是 GBKQ所以取其小。但是实际的应用pȝq是要求面中出?GBK 汉字Q最著名的是朱ȝ名字中的“镕"(rong2 Q?xe946Q\u9555)Q所以有时还是需要将 Encoding/Charset 指定?GBK。当?WAS 中变更缺省的 encoding 没有上面说的那么ȝ(ch)Q针?a,bQ参考文?5Q在 Application Server 的命令行参数中指?-Dfile.encoding=GBK 卛_Q?针对 dQ在 Application Server 的命令行参数中指?Ddefault.client.encoding=GBK。如果指定了(jin)-Ddefault.client.encoding=GBKQ那么c情况下可以不再指定charset? 上面列出的问题(sh)q有一个关于Tag<%...%>,<%=...%>中的 JAVA 代码里包含的?rn)态文本未能正显C的问题Q在WAS中的解决Ҏ(gu)是除?jin)设|正的file.encoding, q需要以相同Ҏ(gu)讄-Duser.language=zh -Duser.region=CN。这与JAVA locale的设|有兟? 4.4 数据库读写时?encoding 问题 JSP/Servlet ~程中经常出?encoding 问题的另一个地Ҏ(gu)d数据库中的数据? 行的关pL据库pȝ都支持数据库 encodingQ也是说在创徏数据库时可以指定它自q字符集设|,数据库的数据以指定的~码形式存储。当应用E序讉K数据Ӟ在入口和出口处都?x)?encoding 转换?对于中文数据Q数据库字符~码的设|应当保证数据的完整? GB2312QGBKQUTF-8 {都是可选的数据?encodingQ也可以选择 ISO8859-1 (8-bit)Q那么应用程序在写数据之前须?16Bit 的一个汉字或 Unicode 拆分成两?8-bit 的字W,L据之后则需两个字节合qv来,同时q要判别其中?SBCS 字符。没有充分利用数据库 encoding 的作用,反而增加了(jin)~程的复杂度QISO8859-1不是推荐的数据库 encoding。JSP/Servlet~程Ӟ可以先用数据库管理系l提供的理功能(g)查其中的中文数据是否正确? 然后应当注意的是d来的数据?encodingQJAVA E序中一般得到的?Unicode。写数据时则相反? 4.5 定位问题时常用的技? 定位中文encoding问题通常采用最W的也是最有效的办法——在你认为有嫌疑的程序处理后打印字符串的内码。通过打印字符串的内码Q你可以发现什么时候中文字W被转换成UnicodeQ什么时候Unicode被{回中文内码,什么时候一个中文字成了(jin)两个 Unicode 字符Q什么时候中文字W串被{成了(jin)一串问P什么时候中文字W串的高?sh)被截掉了(jin)…? 取用合适的h字符串也有助于区分问题的cd。如Q”aa啊aa?jng)aa?{中q间、GB、GBK特征字符均有的字W串。一般来_(d)英文字符无论怎么转换或处理,都不?x)失真(如果遇到了(jin),可以试着增加q箋(hu)的英文字母长度)(j)?BR> 5. l束? 其实 JSP/Servlet 的中文encoding q没有想像的那么复杂Q虽然定位和解决问题没有定规Q各U运行环境也各不然Q但后面的原理是一L(fng)。了(jin)解字W集的知识是解决字符问题的基。不q,随着中文字符集的变化Q不仅仅?java ~程Q中文信息处理中的问题还是会(x)存在一D|间的? 6. 参考文? Character Problem Review Java ~程技术中汉字问题的分析及(qing)解决 GB18030 Setting language encoding in web applications: Websphere applications Server ]]>介绍U程、线E类?qing)Runnable。(转)(j) http://m.tkk7.com/nighthawk/articles/9219.htmlnighthawk nighthawk Thu, 04 Aug 2005 06:59:00 GMT http://m.tkk7.com/nighthawk/articles/9219.html http://m.tkk7.com/nighthawk/comments/9219.html http://m.tkk7.com/nighthawk/articles/9219.html#Feedback 1 http://m.tkk7.com/nighthawk/comments/commentRss/9219.html http://m.tkk7.com/nighthawk/services/trackbacks/9219.html 用户期望E序能展C异的性能。ؓ(f)?jin)满个期望,你的E序常常使用到线E。在q篇文章中我们开始练?fn)用线E。你学?fn)到U程、线E类?qing)Runnable? 用户不喜Ƣ反应迟钝的软g。当用户单击一个鼠标时Q他们希望程序立卛_应他们的hQ即使程序正处于Ҏ(gu)的运行之中,比如Z很长的文重编늠或等待一个网l操作的完成。对用户响应很慢的程序其性能拙劣。ؓ(f)提高E序性能Q开发者一般用线E? q篇文章是探索线E的W一部䆾。虽然你可能认ؓ(f)U程是一U难于掌握的事物Q但我打向你显C线E是易于理解的。在q篇文章中,我将向你介绍U程和线E类Q以?qing)讨论Runnable。此外,在后面的文章中,我将探烦(ch)同步Q通过锁)(j)Q同步的问题Q比如死锁)(j)Q等?通知机制Q时序安排(有优先权和没有优先权Q,U程中断Q计时器Q挥发性,U程l和U程本地变量? 阅读关于U程设计的整个系列:(x) ·W?部䆾Q介l线E和U程c,以及(qing)Runnable ·W?部䆾Q用同步ɾU程串行化访问关键代码部? 注意 q篇文章?qing)其应用E序的三个相关线E练?fn)与applets不同。然而,我在应用E序中介l的多数应用到applets。主要不同的是:(x)Z(jin)安全的原因,不是所有的U程操作都可以放C个applet中(我将在以后的文章中讨论appletsQ? 什么是U程Q? U程的概念ƈ不难于掌握:(x)它是E序代码的一个独立的执行通道。当多个U程执行Ӟl由相同代码的一个线E的通道通常与其它的不同。例如,假设一个线E执行一D늛当于一个if-else语句的if部分的字节代码时Q而另一个线E正执行相当于else部分的字节代码。JVM怎样保持对于每一个线E执行的跟踪呢?JVMl每一个线E它自己的方法调用堆栈。另外跟t当前指令字节代码,Ҏ(gu)堆栈跟踪本地变量QJVM传递给一个方法的参数Q以?qing)方法的q回倹{? 当多个线E在同一个程序中执行字节代码序列Ӟq种行ؓ(f)叫作多线E。多U程在多斚w有利于程序:(x) ·当执行其它Q务时多线EGUIQ图形用L(fng)面)(j)E序仍能保持对用L(fng)响应Q比如重~页码或打印一个文档? ·带线E的E序一般比它们没有带线E的副本E序完成得快。这其表现在线E运行在一个多处理器机器上Q在q里每一个线E都有它自己的处理器? Java通过java.lang.Threadcd成多U程。每一个线E对象描qC个单独的执行U程。那些运行发生在U程的run()Ҏ(gu)中。因为缺省的run()Ҏ(gu)什么都不做Q你必须创徏Thread子类q载run()以完成有用的工作。练?fn)列?中领略一个在Thread中的U程?qing)多U程Q? 列表1. ThreadDemo.java // ThreadDemo.java class ThreadDemo { public static void main (String [] args) { MyThread mt = new MyThread (); mt.start (); for (int i = 0; i < 50; i++) System.out.println ("i = " + i + ", i * i = " + i * i); } } class MyThread extends Thread { public void run () { for (int count = 1, row = 1; row < 20; row++, count++) { for (int i = 0; i < count; i++) System.out.print ('*'); System.out.print ('\n'); } } } 列表1昄?jin)一个由cThreadDemo和MyThreadl成的应用程序的源代码。类ThreadDemo通过创徏一个MyThread对象驱动应用E序Q开始一个与其对象相关的U程q执行一D|C个正方Ş表的代码。相反, MyThread重蝲Thread的run()Ҏ(gu)打印Q通过标准输入)(j)一个由星ŞW号l成的直角三角Ş? 当你键入java ThreadDemoq行应用E序Ӟ JVM创徏一个运行main()Ҏ(gu)的开始线E。通过执行mt.start ()Q开始线E告诉JVM创徏一个执行包含MyThread对象的run()Ҏ(gu)的字节代码指令的W二个线E。当start()Ҏ(gu)q回Ӟ开始线E@环执行打C个正方Ş表,此时另一个新U程执行run()Ҏ(gu)打印直角三角形? 输出?x)象什么样呢?q行ThreadDemo可以看到。你注意到每一个线E的输出与其它线E的输出怺交替。这L(fng)l果是因Z个线E将它们的输出都发送到?jin)同L(fng)标准输出? 注意 多数Q不是所有)(j)JVM讑֤使用下层q_的线E性能。因为那些性能是^台特有的Q你的多U程E序的输出顺序可能与一些h的其他输出的序不一栗这U不同是׃时序的安排,我将在这一pd的稍后探讨这一话题? U程c? 要精通写多线E代码,你必首先理解创建Threadcȝ多种Ҏ(gu)。这部䆾探讨这些方法。明地_(d)你将学到开始线E的Ҏ(gu)Q命名线E,使线E休眠,军_一个线E是否激z,一个线E与另一个线E相联,和在当前U程的线E组?qing)子l中列D所有激zȝU程。我也会(x)讨论U程调试辅助E序?qing)用L(fng)E与监督U程的对比? 我将在以后的文章中介l线E方法的余下部䆾QSun不赞成的Ҏ(gu)除外? 警告 Sun有一些不赞成的线E方法种c,比如suspend()和resume()Q因为它们能锁住你的E序或破坏对象。所以,你不必在你的代码中调用它们。考虑到针对这些方法工作区的SDK文gQ在q篇文章中我没有包含q些Ҏ(gu)? 构造线E? Thread有八个构造器。最单的是:(x) ·Thread()Q用~省名称创徏一个Thread对象 ·Thread(String name)Q用指定的name参数的名U创Z个Thread对象 下一个最单的构造器是Thread(Runnable target)和Thread(Runnable target, String name)?除Runnable参数之外Q这些构造器与前q的构造器一栗不同的是:(x)Runnable参数识别提供run()Ҏ(gu)的线E之外的对象。(你将在这文章稍后学到Runnable。)(j)最后几个构造器是Thread(String name)QThread(Runnable target)Q和Thread(Runnable target, String name)。然而,最后的构造器包含?jin)一个ؓ(f)?jin)组l意囄ThreadGroup参数? 最后四个构造器之一QThread(ThreadGroup group, Runnable target, String name, long stackSize)Qo(h)人感兴趣的是它能够让你指定想要的U程Ҏ(gu)调用堆栈的大。能够指定大将证明在用递归Ҏ(gu)Q一Uؓ(f)何一个方法不断重复调用自w的技术)(j)优美地解决一些问题的E序中是十分有帮助的。通过明确地设|堆栈大,你有时能够预防StackOverflowErrors。然而,太大导致OutOfMemoryErrors。同PSun方法调用堆栈的大小看作q_依赖。依赖^収ͼҎ(gu)调用堆栈的大可能改变。因此,在写调用Thread(ThreadGroup group, Runnable target, String name, long stackSize)代码前仔l考虑你的E序分枝? 开始你的运载工? U程cM于运载工P(x)它们程序从开始移动到l束。Thread 和Thread子类对象不是U程。它们描qC个线E的属性,比如名称和包含线E执行的代码Q经׃个runQ)(j)Ҏ(gu)Q。当一个新U程执行runQ)(j)Ӟ另一个线E正调用Thread或其子类对象的start()Ҏ(gu)。例如,要开始第二个U程Q应用程序的开始线E—它执行mainQ)(j)—调用startQ)(j)。作为响应,JVM和^C起工作的U程操作代码保U程正确地初始化q调用Thread或其子类对象的run()Ҏ(gu)? 一旦start()完成Q多重线E便q行。因为我们趋向于在一U线性的方式中思维Q我们常发现当两个或更多U程正运行时理解q发Q同Ӟ(j)行ؓ(f)是困隄。因此,你应该看看显CZ旉Ҏ(gu)一个线E正在哪里执行(它的位置Q的图表。下囑ְ是这样一个图表? 与时间对比一个开始线E和一个新建线E执行位|的行ؓ(f) 图表昄?jin)几个重要的旉D:(x) ·开始线E的初始? ·U程开始执行mainQ)(j)瞬间 ·U程开始执行startQ)(j)的瞬? ·start()创徏一个新U程q返回mainQ)(j)的瞬? ·新线E的初始? ·新线E开始执行runQ)(j)的瞬? ·每个U程l束的不同瞬? 注意新线E的初始化,它对runQ)(j)的执行,和它的结束都与开始线E的执行同时发生? 警告 一个线E调用start()后,在runQ)(j)Ҏ(gu)退出前q发调用那方法将Dstart()掷出一个java.lang.IllegalThreadStateException对象? 怎样使用名称 在一个调试会(x)话期_(d)使用用户友好方式从另一个线E区别其中一个线E证明是有帮助的。要区分其中一个线E,Javal一个线E取一个名U。Thread~省的名U是一个短U连字符和一个零开始的数字W号。你可以接受Java的缺省线E名U或选择使用你自q。ؓ(f)?jin)能够自定义名称QThread提供带有name参数和一个setName(String name)Ҏ(gu)的构造器。Thread也提供一个getName()Ҏ(gu)q回当前名称。表2昄?jin)怎样通过Thread(String name)创徏一个自定义名称和通过在run()Ҏ(gu)中调用getName()(g)索当前名Uͼ(x) ?.NameThatThread.java // NameThatThread.java class NameThatThread { public static void main (String [] args) { MyThread mt; if (args.length == 0) mt = new MyThread (); else mt = new MyThread (args [0]); mt.start (); } } class MyThread extends Thread { MyThread () { //~译器创建等价于super()的字节代? } MyThread (String name) { super (name); //名UC递给Thread类 } public void run () { System.out.println ("My name is: " + getName ()); } } 你能够在命o(h)行向MyThread传递一个可选的name参数。例如,java NameThatThread X 建立X作ؓ(f)U程的名U。如果你指定一个名U失败,你将看到下面的输出:(x) My name is: Thread-1 如果你喜Ƣ,你能够在MyThread(String name)构造器中将super(name)调用改变成setName(String name)调用——作为setName(name)后一U方法调用达到同样徏立线E名U的目的——作为super(name)我作为练?fn)保留给你们? 注意 Java主要名U指zq行main() Ҏ(gu)的线E,开始线E。你特别要看看当开始线E掷Z个例外对象时在线E“main”的例外昄的JVM的缺省例外处理打印消息? 休眠或停止休? 在这一栏后面,我将向你介绍动画——在一个表面上重复d形,q稍微不同于完成一个运动画面。要完成动画Q一个线E必d它显CZ个连l画面时中止。调用Thread的静(rn)态sleep(long millis)Ҏ(gu)一个线E中止millis毫秒。另一个线E可能中断正在休眠的U程。如果这U事发生Q正在休眠的U程醒来ƈ从sleep(long millis)Ҏ(gu)掷出一个InterruptedException对象。结果,调用sleep(long millis)的代码必d一个try代码块中出现——或代码Ҏ(gu)必须在自qthrows子句中包括InterruptedException? Z(jin)Csleep(long millis)Q我写了(jin)一个CalcPI1应用E序。这个应用程序开始了(jin)一个新U程便于用一个数学运法则计数学常量pi的倹{当新线E计时Q开始线E通过调用sleep(long millis)中止10毫秒。在开始线E醒后,它将打印pi的|其中新线E存贮在变量pi中。表3l出?jin)CalcPI1的源代码Q? ?. CalcPI1.java // CalcPI1.java class CalcPI1 { public static void main (String [] args) { MyThread mt = new MyThread (); mt.start (); try { Thread.sleep (10); //休眠10毫秒 } catch (InterruptedException e) { } System.out.println ("pi = " + mt.pi); } } class MyThread extends Thread { boolean negative = true; double pi; //~省初始化ؓ(f)0.0 public void run () { for (int i = 3; i < 100000; i += 2) { if (negative) pi -= (1.0 / i); else pi += (1.0 / i); negative = !negative; } pi += 1.0; pi *= 4.0; System.out.println ("Finished calculating PI"); } } 如果你运行这个程序,你将看到输出如下Q但也可能不一P(j)Q? pi = -0.2146197014017295 完成计算PI Z么输Z正确呢?毕竟Qpi的值应q似{于3.14159。回{是Q开始线E醒得太快了(jin)。在新线E刚开始计piӞ开始线E就醒过来读取pi的当前值ƈ打印其倹{我们可以通过?0毫秒延迟增加为更长的值来q行补偿。这一更长的|不幸的是它是依赖于^台的Q将l新U程一个机?x)在开始线E醒q来之前完成计算?后面Q你学CU不依赖q_的技术,它将防止开始线E醒来直到新U程完成? 注意 U程同时提供一个sleep(long millis, int nanos)Ҏ(gu)Q它?yu)线E休眠millis 毫秒和nanos U秒。因为多数基于JVM的^台都不支持纳U的分解度QJVM U程处理代码纳U数字四舍五入成毫秒数字的近似倹{如果一个^C支持毫秒U的分解度,JVM U程处理代码毫U数字四舍五入成q_支持的最分解度的q似倍数? 它是ȝq是zȝQ? 当一个程序调用Thread的start()Ҏ(gu)Ӟ在一个新U程调用run()之前有一个时间段Qؓ(f)?jin)初始化Q。run()q回后,在JVM清除U程之前有一D|间通过。JVM认ؓ(f)U程立即ȀzM先于U程调用run()Q在U程执行run()期间和run()q回后。在q时间间隔期_(d)Thread的isAlive()Ҏ(gu)q回一个布?yu)(dng)真倹{否则,Ҏ(gu)q回一个假倹{? isAlive()在一个线E需要在W一个线E能够检查其它线E的l果之前{待另一个线E完成其run()Ҏ(gu)的情形下证明是有帮助的。实质上Q那些需要等待的U程输入一个while循环。当isAlive()为其它线E返回真值时Q等待线E调用sleep(long millis) (?sleep(long millis, int nanos))周期性地休眠 (避免费更多的CPU循环)。一旦isAlive()q回假|{待U程便检查其它线E的l果? 你将在哪里用这L(fng)技术呢Q对于v动器Q一个CalcPI1的修改版本怎么P在打印pi的值前开始线E在哪里{待新线E的完成Q表4的CalcPI2源代码示范了(jin)q一技术:(x) ?. CalcPI2.java // CalcPI2.java class CalcPI2 { public static void main (String [] args) { MyThread mt = new MyThread (); mt.start (); while (mt.isAlive ()) try { Thread.sleep (10); //休眠10毫秒 } catch (InterruptedException e) { } System.out.println ("pi = " + mt.pi); } } class MyThread extends Thread { boolean negative = true; double pi; //~省初始化成0.0 public void run () { for (int i = 3; i < 100000; i += 2) { if (negative) pi -= (1.0 / i); else pi += (1.0 / i); negative = !negative; } pi += 1.0; pi *= 4.0; System.out.println ("Finished calculating PI"); } } CalcPI2的开始线E在10毫秒旉间隔休眠Q直到mt.isAlive ()q回假倹{当那些发生Ӟ开始线E从它的while循环中退出ƈ打印pi的内宏V如果你q行q个E序Q你看到如下的输出Q但不一定一P(j)Q? 完成计算PI pi = 3.1415726535897894 q不Q现在看上去更精了(jin)Q? 注意 一个线E可能对它自p用isAlive() Ҏ(gu)。然而,q毫无意义,因ؓ(f)isAlive()一直返回真倹{? 合力 因ؓ(f)while循环/isAlive()Ҏ(gu)/sleep()Ҏ(gu)技术证明是有用的,Sun其打包q三个方法组成的一个组合里Qjoin()Qjoin(long millis)和join(long millis, int nanos)。当当前U程想等待其它线E结束时Q经由另一个线E的U程对象引用调用join()。相反,当它惛_中Q意线E等待其它线E结束或{待直到millis毫秒和nanosU秒l合通过Ӟ当前U程调用join(long millis)或join(long millis, int nanos)?作ؓ(f)sleep()Ҏ(gu)QJVM U程处理代码对join(long millis)和join(long millis,int nanos)Ҏ(gu)的参数值四舍五入??的CalcPI3源代码示范了(jin)一个对join()的调用:(x) ?. CalcPI3.java // CalcPI3.java class CalcPI3 { public static void main (String [] args) { MyThread mt = new MyThread (); mt.start (); try { mt.join (); } catch (InterruptedException e) { } System.out.println ("pi = " + mt.pi); } } class MyThread extends Thread { boolean negative = true; double pi; //~省初始化成0.0 public void run () { for (int i = 3; i < 100000; i += 2) { if (negative) pi -= (1.0 / i); else pi += (1.0 / i); negative = !negative; } pi += 1.0; pi *= 4.0; System.out.println ("Finished calculating PI"); } } CalcPI3的开始线E等待与MyThread对象有关被mt引用的线E结束。接着开始线E打印pi的|其gCalcPI2的输Z栗? 警告 不要试图当前线E与其自w连接,因ؓ(f)q样当前U程要永远{待? 怎样使用名称 在一个调试会(x)话期_(d)使用用户友好方式从另一个线E区别其中一个线E证明是有帮助的。要区分其中一个线E,Javal一个线E取一个名U。Thread~省的名U是一个短U连字符和一个零开始的数字W号。你可以接受Java的缺省线E名U或选择使用你自q。ؓ(f)?jin)能够自定义名称QThread提供带有name参数和一个setName(String name)Ҏ(gu)的构造器。Thread也提供一个getName()Ҏ(gu)q回当前名称。表2昄?jin)怎样通过Thread(String name)创徏一个自定义名称和通过在run()Ҏ(gu)中调用getName()(g)索当前名Uͼ(x) ?.NameThatThread.java // NameThatThread.java class NameThatThread { public static void main (String [] args) { MyThread mt; if (args.length == 0) mt = new MyThread (); else mt = new MyThread (args [0]); mt.start (); } } class MyThread extends Thread { MyThread () { //~译器创建等价于super()的字节代? } MyThread (String name) { super (name); //名UC递给Thread类 } public void run () { System.out.println ("My name is: " + getName ()); } } 你能够在命o(h)行向MyThread传递一个可选的name参数。例如,java NameThatThread X 建立X作ؓ(f)U程的名U。如果你指定一个名U失败,你将看到下面的输出:(x) My name is: Thread-1 如果你喜Ƣ,你能够在MyThread(String name)构造器中将super(name)调用改变成setName(String name)调用——作为setName(name)后一U方法调用达到同样徏立线E名U的目的——作为super(name)我作为练?fn)保留给你们? 注意 Java主要名U指zq行main() Ҏ(gu)的线E,开始线E。你特别要看看当开始线E掷Z个例外对象时在线E“main”的例外昄的JVM的缺省例外处理打印消息? 休眠或停止休? 在这一栏后面,我将向你介绍动画——在一个表面上重复d形,q稍微不同于完成一个运动画面。要完成动画Q一个线E必d它显CZ个连l画面时中止。调用Thread的静(rn)态sleep(long millis)Ҏ(gu)一个线E中止millis毫秒。另一个线E可能中断正在休眠的U程。如果这U事发生Q正在休眠的U程醒来ƈ从sleep(long millis)Ҏ(gu)掷出一个InterruptedException对象。结果,调用sleep(long millis)的代码必d一个try代码块中出现——或代码Ҏ(gu)必须在自qthrows子句中包括InterruptedException? Z(jin)Csleep(long millis)Q我写了(jin)一个CalcPI1应用E序。这个应用程序开始了(jin)一个新U程便于用一个数学运法则计数学常量pi的倹{当新线E计时Q开始线E通过调用sleep(long millis)中止10毫秒。在开始线E醒后,它将打印pi的|其中新线E存贮在变量pi中。表3l出?jin)CalcPI1的源代码Q? ?. CalcPI1.java // CalcPI1.java class CalcPI1 { public static void main (String [] args) { MyThread mt = new MyThread (); mt.start (); try { Thread.sleep (10); //休眠10毫秒 } catch (InterruptedException e) { } System.out.println ("pi = " + mt.pi); } } class MyThread extends Thread { boolean negative = true; double pi; //~省初始化ؓ(f)0.0 public void run () { for (int i = 3; i < 100000; i += 2) { if (negative) pi -= (1.0 / i); else pi += (1.0 / i); negative = !negative; } pi += 1.0; pi *= 4.0; System.out.println ("Finished calculating PI"); } } 如果你运行这个程序,你将看到输出如下Q但也可能不一P(j)Q? pi = -0.2146197014017295 完成计算PI Z么输Z正确呢?毕竟Qpi的值应q似{于3.14159。回{是Q开始线E醒得太快了(jin)。在新线E刚开始计piӞ开始线E就醒过来读取pi的当前值ƈ打印其倹{我们可以通过?0毫秒延迟增加为更长的值来q行补偿。这一更长的|不幸的是它是依赖于^台的Q将l新U程一个机?x)在开始线E醒q来之前完成计算?后面Q你学CU不依赖q_的技术,它将防止开始线E醒来直到新U程完成? 注意 U程同时提供一个sleep(long millis, int nanos)Ҏ(gu)Q它?yu)线E休眠millis 毫秒和nanos U秒。因为多数基于JVM的^台都不支持纳U的分解度QJVM U程处理代码纳U数字四舍五入成毫秒数字的近似倹{如果一个^C支持毫秒U的分解度,JVM U程处理代码毫U数字四舍五入成q_支持的最分解度的q似倍数? 它是ȝq是zȝQ? 当一个程序调用Thread的start()Ҏ(gu)Ӟ在一个新U程调用run()之前有一个时间段Qؓ(f)?jin)初始化Q。run()q回后,在JVM清除U程之前有一D|间通过。JVM认ؓ(f)U程立即ȀzM先于U程调用run()Q在U程执行run()期间和run()q回后。在q时间间隔期_(d)Thread的isAlive()Ҏ(gu)q回一个布?yu)(dng)真倹{否则,Ҏ(gu)q回一个假倹{? isAlive()在一个线E需要在W一个线E能够检查其它线E的l果之前{待另一个线E完成其run()Ҏ(gu)的情形下证明是有帮助的。实质上Q那些需要等待的U程输入一个while循环。当isAlive()为其它线E返回真值时Q等待线E调用sleep(long millis) (?sleep(long millis, int nanos))周期性地休眠 (避免费更多的CPU循环)。一旦isAlive()q回假|{待U程便检查其它线E的l果? 你将在哪里用这L(fng)技术呢Q对于v动器Q一个CalcPI1的修改版本怎么P在打印pi的值前开始线E在哪里{待新线E的完成Q表4的CalcPI2源代码示范了(jin)q一技术:(x) ?. CalcPI2.java // CalcPI2.java class CalcPI2 { public static void main (String [] args) { MyThread mt = new MyThread (); mt.start (); while (mt.isAlive ()) try { Thread.sleep (10); //休眠10毫秒 } catch (InterruptedException e) { } System.out.println ("pi = " + mt.pi); } } class MyThread extends Thread { boolean negative = true; double pi; //~省初始化成0.0 public void run () { for (int i = 3; i < 100000; i += 2) { if (negative) pi -= (1.0 / i); else pi += (1.0 / i); negative = !negative; } pi += 1.0; pi *= 4.0; System.out.println ("Finished calculating PI"); } } CalcPI2的开始线E在10毫秒旉间隔休眠Q直到mt.isAlive ()q回假倹{当那些发生Ӟ开始线E从它的while循环中退出ƈ打印pi的内宏V如果你q行q个E序Q你看到如下的输出Q但不一定一P(j)Q? 完成计算PI pi = 3.1415726535897894 q不Q现在看上去更精了(jin)Q? 注意 一个线E可能对它自p用isAlive() Ҏ(gu)。然而,q毫无意义,因ؓ(f)isAlive()一直返回真倹{? 合力 因ؓ(f)while循环/isAlive()Ҏ(gu)/sleep()Ҏ(gu)技术证明是有用的,Sun其打包q三个方法组成的一个组合里Qjoin()Qjoin(long millis)和join(long millis, int nanos)。当当前U程想等待其它线E结束时Q经由另一个线E的U程对象引用调用join()。相反,当它惛_中Q意线E等待其它线E结束或{待直到millis毫秒和nanosU秒l合通过Ӟ当前U程调用join(long millis)或join(long millis, int nanos)?作ؓ(f)sleep()Ҏ(gu)QJVM U程处理代码对join(long millis)和join(long millis,int nanos)Ҏ(gu)的参数值四舍五入??的CalcPI3源代码示范了(jin)一个对join()的调用:(x) ?. CalcPI3.java // CalcPI3.java class CalcPI3 { public static void main (String [] args) { MyThread mt = new MyThread (); mt.start (); try { mt.join (); } catch (InterruptedException e) { } System.out.println ("pi = " + mt.pi); } } class MyThread extends Thread { boolean negative = true; double pi; //~省初始化成0.0 public void run () { for (int i = 3; i < 100000; i += 2) { if (negative) pi -= (1.0 / i); else pi += (1.0 / i); negative = !negative; } pi += 1.0; pi *= 4.0; System.out.println ("Finished calculating PI"); } } CalcPI3的开始线E等待与MyThread对象有关被mt引用的线E结束。接着开始线E打印pi的|其gCalcPI2的输Z栗? 警告 不要试图当前线E与其自w连接,因ؓ(f)q样当前U程要永远{待? 查询z跃U程 在有些情形下Q你可能想了(jin)解在你的E序中哪些线E是Ȁzȝ。Thread支持一Ҏ(gu)法帮助你完成q个dQ?activeCount()?enumerate(Thread [] thdarray)。但那些Ҏ(gu)只工作在当前U程的线E组中。换句话_(d)那些Ҏ(gu)只识别属于当前线E的同一U程l的z跃U程?(我将在以后的pd文章中讨论线E组——一U组l机制? ?rn)态activeCount()Ҏ(gu)q回在当前线E的U程l中正在z跃q行的线E数量。一个程序利用这个方法的整数q回D定一个Thread引用数组的大。检索那些引用,E序必须调用?rn)态enumerate(Thread [] thdarray)Ҏ(gu)。这个方法的整数q回值确定Thread引用存贮在数l中的enumerate(Thread []thdarray)的L。要看这些方法如何一起工作,h看表6Q? ?. Census.java // Census.java class Census { public static void main (String [] args) { Thread [] threads = new Thread [Thread.activeCount ()]; int n = Thread.enumerate (threads); for (int i = 0; i < n; i++) System.out.println (threads [i].toString ()); } } 在运行时Q这个程序会(x)产生如下的输出:(x) Thread[main,5,main] 输出昄一个线E,开始线E正在运行。左边的main表示U程的名U?昄U程的优先权Q右边的main表示U程的线E组。你也许很失望不能在输出中看CQ何系l线E,比如垃圾攉器线E。U限制由Thread的enumerate(Thread [] thdarray) Ҏ(gu)产生Q它仅询问当前线E线E组的活跃线E。然而, ThreadGroupcd含多Uenumerate()Ҏ(gu)允许你捕获对所有活跃线E的引用而不线E组。在E后的系列中Q探讨ThreadGroup时我向你显C如何列举所有的引用? 警告 当重申一个数l时不要依靠activeCount()的返回倹{如果你q样做了(jin)Q你的程序将冒掷Z个NullPointerException对象的风险。ؓ(f)什么呢Q在调用activeCount()和enumerate(Thread [] thdarray)之间Q一个或更多U程可能l束。结果, enumerate(Thread [] thdarray)能够复制数U程引用q它的数l。因此,仅考虑activeCount()的返回g为数l可能大的最大倹{同P考虑enumerate(Thread [] thdarray)的返回g为在一个程序对那种Ҏ(gu)调用时活跃线E的数目? 反臭? 如果你的E序出现故障q且你怀疑问题出在线E,通过调用Thread的dumpStack()和toString()Ҏ(gu)你能够了(jin)解到U程的更多细节。静(rn)态dumpStack()Ҏ(gu)提供一个new Exception ("Stack trace").printStackTrace ()的封装,打印一个追t当前线E的堆栈。toString()依据下面格式q回一个描q线E的名称、优先权和线E组的字W串Q?Thread[thread-name,priority,thread-group]. (在稍后的pd中你学到更多关于优先权的知识? 技? 在一些地方,q篇文章提到?jin)当前线E的概念。如果你需要访问描q当前线E的Thread对象Q则调用Thread的静(rn)态currentThread()Ҏ(gu)。例QThread current = Thread.currentThread ()? {pȝ 不是所有线E都被^{创建。它们被分成两类Q用户和监督。一个用L(fng)E执行着对于E序用户十分重要的工作,工作必须在程序结束前完成。相反,一个监督线E执行着后勤事务Q比如垃圾收集)(j)和其它可能不?x)对应用E序的主要工作作?gu)A(ch)献但对于应用E序l箋(hu)它的主要工作却非常必要的后台d。和用户U程不一P监督U程不需要在应用E序l束前完成。当一个应用程序的开始线E(它是一个用L(fng)E)(j)l束ӞJVM(g)查是否还有其它用L(fng)E正在运行。如果有QJVM׃(x)L应用E序l束。否则,JVM׃(x)l束应用E序而不监督线E是否正在运行? 当一个线E调用一个线E对象的start()Ҏ(gu)Ӟ新的已经开始的U程是一个用L(fng)E。那是缺省的。要建立一个线E作为监督线E,E序必须在调用start()前调用Thread的一个带布尔真值参数的setDaemon(boolean isDaemon)Ҏ(gu)。稍后,你可以通过调用Thread的isDaemon()Ҏ(gu)(g)查一个线E是否是监督U程。如果是监督U程那个Ҏ(gu)q回一个布?yu)(dng)真倹{? Z(jin)让你试试用户和监督线E,我写?jin)一个UserDaemonThreadDemoQ? ?. UserDaemonThreadDemo.java // UserDaemonThreadDemo.java class UserDaemonThreadDemo { public static void main (String [] args) { if (args.length == 0) new MyThread ().start (); else { MyThread mt = new MyThread (); mt.setDaemon (true); mt.start (); } try { Thread.sleep (100); } catch (InterruptedException e) { } } } class MyThread extends Thread { public void run () { System.out.println ("Daemon is " + isDaemon ()); while (true); } } ~译?jin)代码后Q通过Java2 SDK的java命o(h)q行UserDaemonThreadDemo。如果你没有使用命o(h)行参数运行程序,例如java UserDaemonThreadDemoQ?new MyThread ().start ()执行。这D代码片断开始一个在q入一个无限@环前打印Daemon is false的用L(fng)E?你必LCtrl-C或一个等价于l束一个无限@环的l合按键?因ؓ(f)新线E是一个用L(fng)E,应用E序在开始线E结束后仍保持运行。然而,如果你指定了(jin)臛_一个命令行参数Q例如java UserDaemonThreadDemo xQmt.setDaemon (true)执行q且新线E将是一个监督线E。结果,一旦开始线E从100毫秒休眠中醒来ƈl束Q新的监督线E也结束? 警告 如果U程开始执行后调用setDaemon(boolean isDaemon)Ҏ(gu)QsetDaemon(boolean isDaemon)Ҏ(gu)掷Z个IllegalThreadStateException对象? Runnable 学习(fn)前面部䆾的例子后Q你可能认ؓ(f)引入多线E进入一个类L要求你去扩展Threadq将你的子类重蝲Thread's run()Ҏ(gu)。然而那q不L一U选择。Java对(h)承的强制执行止一个类扩展两个或更多个类。结果,如果一个类扩展?jin)一个无U程c,那个cd不能扩展Thread. 假限制Q怎样才可能将多线E引入一个已l扩展了(jin)其它cȝc?q运的是Q?Java的设计者已l意识到不可能创建Thread子类的情形M(x)发生的。这D产生java.lang.Runnable接口和带Runnable参数的Thread构造器Q如Thread(Runnable target)? Runnable接口声明?jin)一个单独方法v名:(x)void run()。这个v名和Thread的run()Ҏ(gu)|名一样ƈ作ؓ(f)U程的执行入口服务。因为Runnable是一个接口,Mc都能通过一个implements子句包含q类头和提供一个适当的run()Ҏ(gu)实现接口。在执行旉Q程序代码能从那个类创徏一个对象或runnableq将runnable的引用传递给一个适当的Thread构造器。构造器和Thread对象一起存贮这个引用ƈ保一个新U程在调用Thread对象的start()Ҏ(gu)后调用runnable的run()Ҏ(gu)。示范如?Q? ?.RunnableDemo.java // RunnableDemo.java class RunnableDemo { public static void main (String [] args) { Rectangle r = new Rectangle (5, 6); r.draw (); //用随机选择的宽度和高度M同的长方? new Rectangle (); } } abstract class Shape { abstract void draw (); } class Rectangle extends Shape implements Runnable { private int w, h; Rectangle () { //创徏一个绑定这个runnable的新Thread对象q开始一个将调用q个runnable? //run()Ҏ(gu)的线E? new Thread (this).start (); } Rectangle (int w, int h) { if (w < 2) throw new IllegalArgumentException ("w value " + w + " < 2"); if (h < 2) throw new IllegalArgumentException ("h value " + h + " < 2"); this.w = w; this.h = h; } void draw () { for (int c = 0; c < w; c++) System.out.print ('*'); System.out.print ('\n'); for (int r = 0; r < h - 2; r++) { System.out.print ('*'); for (int c = 0; c < w - 2; c++) System.out.print (' '); System.out.print ('*'); System.out.print ('\n'); } for (int c = 0; c < w; c++) System.out.print ('*'); System.out.print ('\n'); } public void run () { for (int i = 0; i < 20; i++) { w = rnd (30); if (w < 2) w += 2; h = rnd (10); if (h < 2) h += 2; draw (); } } int rnd (int limit) { //?<=x<界限范围内返回一个随机数字x return (int) (Math.random () * limit); } } RunnableDemoqRunnableDemoQShape和Rectanglel成。类RunnableDemo通过创徏一个Rectangle对象驱动应用E序—通过调用对象的draw()Ҏ(gu)—和通过创徏W二个什么都不做的RectanglecR相反,Shape和Rectanglel成?jin)一个基于shape层次的类。Shape是抽象的因ؓ(f)它提供一个抽象的draw()Ҏ(gu)。各Ushapec,比如RectangleQ扩展Shape和描q它们如何画它们自己的重载draw()。以后,我可能决定引入一些另外的shapec,创徏一个Shape数组Q通过调用Shape的draw()Ҏ(gu)要求每一个Shape元素d自己? RunnableDemo 作ؓ(f)一个不带多U程的简单程序生。后面我军_引入多线E到RectangleQ这h能够用各U宽度和高度ȝU矩形。因为Rectangle扩展Shape (Z(jin)以后的多态性原?Q我没有其它选择只有让Rectangle实现Runnable。同P在Rectangle()构造器内,我不得不一个Rectangle runnablel定C个新的Thread对象q调用Thread的start()Ҏ(gu)开始一个新的线E调用Rectangle的run()Ҏ(gu)ȝ形? 因ؓ(f)包括在这文章中的RunnableDemo的新输出太长?jin),我徏议你自己~译q运行程序? 技? 当你面对一个类不是能扩展Thread是能实现Runnable的情形时Q你选择哪种Ҏ(gu)Q如果这个类已经扩展?jin)其它类Q你必须实现Runnable。然而,如果q个cL有扩展其它类Q考虑一下类的名U。名U将暗示q个cȝ对象不是U极的就是消极的。例如,名称Ticker暗示它的对象是积极的。因此,Tickercd扩展ThreadQƈ且Ticker对象被作ؓ(f)专门的Thread对象。相反,Rectangle暗示消极对象—Rectangle对象对于它们自己什么也不做。因此,Rectanglecd实现Runnable,q且Rectangle 对象用Thread对象(Z(jin)试或其它意?代替成ؓ(f)专门的Thread对象? 回顾 用户期望E序辑ֈ优异的性能。一U办法是用线E完成那些Q务。一个线E是一条程序代码的独立执行通道。线E有益于ZGUI的程序,因ؓ(f)它们允许那些E序当执行其它Q务时仍对用户保持响应。另外,带线E的E序比它们没带线E的副本E序完成的快。这对于q行在多处理器机器上的情形尤其明显,在这里每一个线E有它自q处理器。Thread和Thread子类对象描述?jin)线Eƈ与那些实体相兟뀂对于那些不能扩展Thread的类Q你必须创徏一个runnable以利用多U程的优ѝ?img src ="http://m.tkk7.com/nighthawk/aggbug/9219.html" width = "1" height = "1" /> ]]> 谈Java的输入输出流 http://m.tkk7.com/nighthawk/articles/9218.htmlnighthawk nighthawk Thu, 04 Aug 2005 06:57:00 GMT http://m.tkk7.com/nighthawk/articles/9218.html http://m.tkk7.com/nighthawk/comments/9218.html http://m.tkk7.com/nighthawk/articles/9218.html#Feedback 0 http://m.tkk7.com/nighthawk/comments/commentRss/9218.html http://m.tkk7.com/nighthawk/services/trackbacks/9218.html
Java语言的输入输出功能是十分强大而灵zȝQ美中不的是看上去输入输出的代码ƈ不是很简z,因ؓ(f)你往(xin)往(xin)需要包装许多不同的对象。在Javacd中,IO部分的内Ҏ(gu)很庞大的Q因为它涉及(qing)的领域很q泛:标准输入输出Q文件的操作Q网l上的数据流Q字W串,对象,zip文g?...本文的目的是为大家做一个简要的介绍?/P>
是一个很形象的概念,当程序需要读取数据的时候,׃(x)开启一个通向数据源的,q个数据源可以是文gQ内存,或是|络q接。类似的Q当E序需要写入数据的时候,׃(x)开启一个通向目的地的。这时候你可以想象数据好像在q其中“流”动一P如下图:(x)
Java中的分ZU,一U是字节,另一U是字符,分别由四个抽象类来表C(每种包括输入和输出两种所以一共四个)(j):InputStreamQOutputStreamQReaderQW(xu)riter。Java中其他多U多样变化的均是由它们z出来?
在这其中InputStream和OutputStream在早期的Java版本中就已经存在?jin),它们是基于字节流的,而基于字W流的Reader和W(xu)riter是后来加入作充的。以上的层次图是Javacd中的一个基本的层次体系Q如果你感兴想?jin)解更多内容的话Q可以到Sun公司主页 获取更多信息?/P>
在这四个抽象cMQInputStream和Reader定义?jin)完全相同的接口Q?/P>
int read()int read(char cbuf[])int read(char cbuf[], int offset, int length)
而OutputStream和W(xu)riter也是如此Q?/P>
int write(int c)int write(char cbuf[])int write(char cbuf[], int offset, int length)
q六个方法都是最基本的,read()和write()通过Ҏ(gu)的重载来d一个字节,或者一个字节数l?/P>
更多灉|多变的功能是由它们的子类来扩充完成的。知道了(jin)Java输入输出的基本层ơ结构以后,本文在这里想l大家一些以后可以反复应用例子,对于所有子cȝl节?qing)其功能q不详细讨论?/P>
public class IOStreamDemo { public void samples() throws IOException {
//1. q是从键盘读入一行数?q回的是一个字W串 BufferedReader stdin =new BufferedReader(new InputStreamReader(System.in)); System .out.print("Enter a line:" ); System .out.println(stdin.readLine());
//2. q是从文件中逐行d数据
BufferedReader in = new BufferedReader(new FileReader("IOStreamDemo.java" )); String s, s2 = new String(); while ((s = in.readLine())!= null ) s2 += s + "\n" ; in.close();
//3. q是从一个字W串中逐个d字节 StringReader in1 = new StringReader(s2); int c; while ((c = in1.read()) != -1) System .out.print((char)c);
//4. q是一个字W串写入文g try { BufferedReader in2 = new BufferedReader(new StringReader(s2)); PrintWriter out1 = new PrintWriter(new BufferedWriter(new FileWriter("IODemo.out" ))); int lineCount = 1; while ((s = in2.readLine()) != null ) out1.println(lineCount++ + ": " + s); out1.close(); } catch (EOFException e) { System .err.println("End of stream" ); } }
}
对于上面的例子,需要说明的有以下几点:(x)
1. BufferedReader ?FONT color=#ff0000>Reader的一个子c,它具有缓冲的作用Q避免了(jin)频繁的从物理讑֤中读取信息。它有以下两个构造函敎ͼ(x)
BufferedReader (Reader in) BufferedReader (Reader in, int sz)
q里的sz是指定缓冲区的大?/P>
它的基本Ҏ(gu)Q?/P>
void close( ) //关闭? void mark(int readAheadLimit) //标记当前位置 boolean markSupported() //是否支持标记 int read() //l承自Reader的基本方?/FONT> int read(char [] cbuf, int off, int len) //l承自Reader的基本方?/FONT> String readLine() //d一行内容ƈ以字W串形式q回 boolean ready() //判断是否已l做好读入的准备 void reset() //重设到最q的一个标?/FONT> long skip(long n) //跌指定个数的字W读?/FONT>
2. InputStreamReader ?FONT color=#000000>InputStream?FONT color=#000000>Reader之间的桥梁,׃System.in是字节流Q需要用它来包装之后变(sh)ؓ(f)字符供l? BufferedReader 使用?BR>
3. PrintWriter out1 = new PrintWriter(new BufferedWriter(new FileWriter("IODemo.out" )));
q句话体C(jin)Java输入输出pȝ的一个特点,Z(jin)辑ֈ某个目的Q需要包装好几层。首先,输出目的地是文gIODemo.outQ所以最内层包装的是FileWriterQ徏立一个输出文件流Q接下来Q我们希望这个流是缓冲的Q所以用BufferedWriter 来包装它以达到目的,最后,我们需要格式化输出l果Q于是将PrintWriter 包在最外层?BR>
Java提供?jin)这样一个功能,标准的输入输出{向,也就是说Q我们可以将某个其他的流设ؓ(f)标准输入或输出流Q看下面q个例子Q?/P>
import java.io.*;
public class Redirecting { public static void main(String [] args) throws IOException { PrintStream console = System.out; BufferedInputStream in = new BufferedInputStream ( new FileInputStream ( "Redirecting.java" )); PrintStream out = new PrintStream ( new BufferedOutputStream ( new FileOutputStream ("test.out" ))); System.setIn(in); System.setOut(out);
BufferedReader br = new BufferedReader ( new InputStreamReader (System.in)); String s; while ((s = br.readLine()) != null ) System.out.println(s); out.close(); System.setOut(console); } }
在这里java.lang.System的静(rn)态方?/P>
static void setIn(InputStream in) static void setOut(PrintStream out)
提供?jin)重新定义标准输入输出流的方法,q样做是很方便的Q比如一个程序的l果有很多,有时候甚臌页昄Q这样不便于观看l果Q这是你可以将标准输出定义ؓ(f)一个文件流Q程序运行完之后打开相应的文件观看结果,q观了(jin)许多?/P>
Java有着另一个重要的用途,那就是利用对象流对对象进行序列化。下面将开始介l这斚w的问题?/P>
在一个程序运行的时候,其中的变量数据是保存在内存(sh)的,一旦程序结束这些数据将不会(x)被保存,一U解决的办法是将数据写入文gQ而Java中提供了(jin)一U机Ӟ它可以将E序中的对象写入文gQ之后再从文件中把对象读出来重新建立。这是所谓的对象序列化Java中引入它主要是ؓ(f)?jin)RMIQRemote Method InvocationQ和Java Bean所用,不过在^时应用中Q它也是很有用的一U技术?/P>
所有需要实现对象序列化的对象必首先实现Serializable接口。下面看一个例子:(x)
import java.io.*;import java.util.*;
public class Logon implements Serializable { private Date date = new Date(); private String username; private transient String password;
Logon(String name, String pwd) { username = name; password = pwd; }
public String toString() { String pwd = (password == null ) ? "(n/a)" : password; return "logon info: \n " + "username: " + username + "\n date: " + date + "\n password: " + pwd; }
public static void main(String[] args) throws IOException , ClassNotFoundException { Logon a = new Logon("Morgan" , "morgan83" ); System .out.println( "logon a = " + a); ObjectOutputStream o = new ObjectOutputStream ( new FileOutputStream ("Logon.out" )); o.writeObject(a); o.close();
int seconds = 5; long t = System .currentTimeMillis() + seconds * 1000; while (System .currentTimeMillis() < t) ; ObjectInputStream in = new ObjectInputStream ( new FileInputStream ("Logon.out" )); System .out.println( "Recovering object at " + new Date()); a = (Logon)in.readObject(); System .out.println("logon a = " + a); } }
cLogon是一个记录登录信息的c,包括用户名和密码。首先它实现?jin)接口SerializableQ这标志着它可以被序列化。之后再mainҎ(gu)?FONT color=#ff0000>ObjectOutputStream o = new ObjectOutputStream ( new FileOutputStream ("Logon.out" ));新徏一个对象输出流包装一个文件流Q表C对象序列化的目的地是文件Logon.out。然后用Ҏ(gu)writeObject 开始写入。想要还原的时候也很简?FONT color=#ff0000>ObjectInputStream in = new ObjectInputStream ( new FileInputStream ("Logon.out" ));新徏一个对象输入流以文件流Logon.out为参敎ͼ之后调用readObjectҎ(gu)可以了(jin)?/P>
需要说明一点,对象序列化有一个神奇之处就是,它徏立了(jin)一张对象网Q将当前要序列化的对象中所持有的引用指向的对象都包含v来一起写入到文gQ更为奇妙的是,如果你一ơ序列化?jin)好几个对象Q它们中相同的内容将?x)被׃n写入。这的确是一个非常好的机制。它可以用来实现深层拯Q有x(chng)层拷贝的问题?A >JavaWorld上有一文章做?jin)几U实现方法的介绍和比较,有兴者可以去看看?/P>
关键字transient在这里表C当前内容将不被序列化,比如例子中的密码Q需要保密,所以没有被写入文g?/P>
对Java的输入输出功能,浅的介绍到这里,本文的目的只是开一个好_(d)希望能让大家对Java输入输出有个基本的认识Q更多更为全面的信息在http://java.sun.com有权威的说明?/P>
]]>
վ֩ģ壺
һ |
ҰһƵ |
ͽXXXX |
Ů18ëƬƵ |
ëƬѹۿƵȫ |
ˮĻӰ |
1000žžʮδֹۿ |
Ʒɫ |
Ʒ |
ҹƵ |
һaɫƬþٸһHƬѷ |
ҹҹƵ |
Ļþ |
ձĻ |
avav߲ |
˾ƷŮ˾þþ
|
ˬָ߳Ƶ |
Ʒҹ߹ۿ |
ѹۿһëƬ |
һɫëƬ |
þþƷ |
߿ƬvѹۿƵ777 |
ɫͼѧ |
һһƬѲi |
Ʒϵ߹ۿ |
͵v͵v |
99þþùƷţţĴ |
ҹɼӰԺ |
Ů˾ƷƵ |
㽶Ƶѿ |
ƷƵѿ |
777777 |
99þþù |
þþþAVר |
һ߹ۿƵ |
鵺̳Ƶ |
AV˾Ʒ߹ۿ |
ëɫëƬѹۿ |
þþƷר |
ձҺ |
벻?Ƭ |