致读者 我从2002年7月开始翻译这本书,当时还是第二版。但是翻完前言和介绍部分 后, chinapub就登出广告,说要出版侯捷的译本。于是我中止了翻译,等着侯 先生的作品。 我是第一时间买的这本书,但是我失望了。比起第一版,我终于能看懂这本书 了,但是相比我的预期,它还是差一点。所以当 Bruce eckel在他的网站上公开 本书的第三版的时候,我决定把它翻译出来。 说说容易,做做难。一本1000多页的书不是那么容易翻的。期间我也曾打过退 堂鼓,但最终还是全部翻译出来了。从今年的两月初起,到7月底,我几乎放 弃了所有的业余时间,全身心地投入本书的翻译之中。应该说,这项工作的难 度超出了我的想像。 首先,读一本书和翻译一本书完全是两码事。英语与中文是两种不同的语言 用英语说得很畅的句子,翻成中文之后就完全破了相。有时我得花好几分钟, 用中文重述一句我能用几秒钟读懂的句子。更何况作为读者,一两句话没搞 懂,并不影响你理解整本书,但对译者来说,这就不一样了 其次,这是一本讲英语的人写给讲英语的人的书,所以同很多要照顾非英语读 者的技术文档不同,它在用词,句式方面非常随意。英语读者会很欣赏这 点,但是对外国读者来说,这就是负担了。 再有, Bruce eckel这样的大牛人,写了1000多页,如果都让你读懂,他岂不是 太没面子?所以,书里还有一些很有“禅意”的句子。比如那句著名的“The genesis of the computer revolution was in a machine. The genesis of our programming languages thus tends to look like that machine.”我就一直没吃准该怎 么翻译。我想大概没人能吃准,说不定 Bruce要的就是这个效果 这是一本公认的名著,作者在技术上的造诣无可挑剔。而作为译者,我的编程 能力差了很多。再加上上面讲的这些原因,使得我不得不格外的谨慎。当我重 读初稿的时候,我发现需要修改的地方实在太多了。因此,我不能现在就公开 全部译稿,我只能公开已经修改过的部分。不过这不是最终的版本,我还会继 续修订的。 本来,我准备到10月份,等我修改完前7章之后再公开。但是,我发现我又有 点要放弃了,因此我决定给自己一点压力,现在就公开。以后,我将修改完 章就公开一章,请关注www.wgqgh.com/shags/tii.htm 如果你觉得好,请给告诉我,你的鼓励是我工作的动力;如果你觉得不好,那 就更应该告诉我了,我会参考你的意见作修改的。我希望能通过这种方法,译 出一本配得上原著的书。 2003年9月8日 第1页共26页
shhgs@wgqqh.com 1 ✁ ✂ 26 ✁ 2002 7 chinapub !"#$%!&'( )*+,-.& /0(12 345( 67,893:);<= ,>8(?@AB3CDE Bruce Eckel FG(HIJK (L(MNOPA!Q $$RSTTU3 1000 VW(XYZRS(@4[\]^_ `ab:c!Q,d(ef9g 7 hijk l,Dm(no4cpqrst (u*vw$ xy1(U z{!,(|} ~/3 3 ce* eX( $(* uc,>mi * 3;i=(13e =X ¡¢£¤ ¥Q$ X3¦, §¨ 3 ©(ª«¬©(ª(DEV%®¯ (°± ²XAF³´µ¶¯·¸¹º»¼ 3 C¥½¾Q$ ¿À, ÁmBruce Eckel ¦(Âê«, 1000 VWÄÅÆÇ¡=GÈX É¶Ê DEËm3ÌmÍιÏ(8ÄYÐÑ(ÍThe genesis of the computer revolution was in a machine. The genesis of our programming languages thus tends to look like that machine.Ï3ÒÓÔwÕ Z|ÂÖª;ÓÔ$XO Bruce %( רŠ3 KÙ(ÑÐ1F°±J(ÚÛÜÝÞßà1(áâ ;ãB,VÁäJJ¶©( ÌåæçXXè½(éê fë(Mìíî%ïð(rµñFÉV,æòX;íFK cëó;Kôõïð^(X^ Xb:( ºö ÷ïø( QÔùg 10 ú-ïð 7 ûuÁKìíüm C%kl,æòNO¬ý þ3CãíFKEïð3 ûK3û www.wgqqh.com/shhgs/tij.html ÄÅ¡¬#¡(ay1( ã ÄÅ¡XY vw#,º¡(¹ 1ïð(7;^ µ !3 JåÐ( shhgs 2003 9 8
2:万物皆对象 虽然Java是建立在C++之上的,但它是一个更为“纯粹”的面向对象 的语言。 C++和Java都是混合语言,但是Java的软件设计师们并不认为这种混 合性会像它在C++里那么重要。混合语言能让你用多种风格进行编程: C++之所以要成为一种混合语言,是因为它必须为C提供向后兼容。由 于C++是C语言的超集,所以它包括了许多C语言的一些不怎么得人心 的特性,从某些方面讲这些东西让C++变得复杂得过了头。 java语言假定你只需要使用面向对象的编程方法。这就意味着,你必须 在开始之前就把脑筋转到面向对象的世界(除非你已经身在其中了)。把精 力花在这个地方的好处就是,你能用一种较其它OOP语言更易学易用的 编程语言进行开发。本章,我们会先学习Java程序的基本组成,然后会 看到Java的一切,甚至Java程序本身都是对象 用 reference操控对象 每种编程语言都有它自己的操控数据的方法。有时程序员必须时刻记着他 们正在进行何种操控。你是在直接操控对象呢,还是通过一些间接的表示 方法(就是C或C++的指针),用特殊的语法进行操控呢? Java把这一切都简化了。你可以用一种前后一致的语法,把一切都当对 象来处理。虽然你把一切都“当作”对象,但实际上你所操控的那个标识 符是对象的“ reference”。o你可以把它想成一个带遥控器 ( reference)的电视机(对象)。只要你还拿着 reference,你就还连着电 视机,但是当有人说“换台”或是“把声音调低点”的时候,你操控的实 际上是那个 reference,然后再让它去和那个对象打交道。如果你想在房 间里走一走,那么带上遥控器/ reference就行了,不必扛着电视机。 此外,即便没有电视机,遥控器也能独立存在。也就是说, reference并 不一定要连着对象。所以,如果要保存一个单词或句子,只要创建一个 String的 reference就行了: string s 但是你创建的“只是”一个 reference,而不是对象。如果你现在就往s 送消息,那么你将会得到一个错误(在运行的时候),因为s实际上并没有 连到任何东西(没有电视机)。比较安全的做法是,创建 reference的时候 就对它进行初始化 String s ="asdf"i 第2页共26页
shhgs@wgqqh.com 2 ✁ ✂ 26 ✁ 2: Java F C+ + uJ(A3×ÍÏ(¶¥ ( C+ + Java Æ Java ( !"XÙ #º}AF C+ + ËYZ%;Ç¡V$è%&áâ C+ + uDE%3æA'( C )*+R, ) C+ + C ({-DEA./,0V C (3ÌXÕZªq (1#2̵¶© Ì34Ç C+ + 567^,8 Java 9O¡óî%ç¶¥(áâµ ¹:.¡'( FuP;<=g¶¥(>?(@¯¡ôõpF§*,)PA ãF ×rµ(B¡;3C§A OOP SDS( áâ%&ì û"º/DE Java âF(G Hº <g Java (3IJK Java âF pÆ¥ reference LáâÆmAý þ(MNOP(µmâFQ'(RS.G "TF%&MN¡FÒUMN¥V^3Ì4U(WX µ( C Y C+ + (Z[)1\(%&MNVÊ Java P 3IÆ]^,¡ÝE33_(P3IÆ ¥ QB¢¡P3IÆÍ 1Ï¥ñ`J¡DMN(Y×ab c¥(ÍreferenceÏ ✄ ☎ ✆ ✝ ¡ÝEPA|3×deNf (reference)(ghi(¥)ó%¡j. reference¡k.g hi mª$ÍlmÏYÍPnopqCÏ(M¡MN(ñ `JY× referenceÁÇArY×¥]stÄÅ¡|Fu 4Ëv3vZJ/referenceX'w.ghi ò½xymghieNf[;z{F[$reference X3O%k.¥DEÄÅ%|{3×}³Yó%~3× String ( reference &, String s; ¡~(ÍóÏ3× referenceàX¥ÄÅ¡íF s YZ¡ºg3× (F&(M)æ s ñ`Jm kg34(mghi)8Cc(T~ reference (M ¥A%&f^ String s = "asdf";
不过这里用到了Java的一个特有的特性:字符串可以用以引号括起来的 文字进行初始化。通常情况下,你必须用一种更为通用的方法对对象进行 初始化 你必须创建所有的对象 创建完 reference之后,你就得往上面连新的对象了。大体上,你得用 new关键词来作这件事。关键词new的意思是,“给我创建一个新的 那种类型的对象。”所以在上述例程中,你可以用: String s new String("asdf " 它不仅表示“给我创建一个新的 String”,而且还用字符串参数告诉它 “如何”制作这个 String。 当然, String并不是唯一的类。Java还带了很多现成的类。更重要的是 你还可以创建你自己的类。实际上,Java编程基本上就是在创建类,而 这正是本书要教给你的 数据存在哪里 对程序运行时各部分是如何展开的—特别是内存是如何分配的,作一个 直观的描述还是很有必要的。数据可以存储在以下六个地方: 1.寄存器( registers)。这是反映最快的存储,因为它所处位置不同:在处理 器里。不过寄存器的数量非常有限,所以它是由编译器分配的。你非但 不能直接控制寄存器,甚至连它存在的证据也找不到 栈( stack)。位于“常规内存区( general random- access memory area)”里, 处理器可以通过栈指针( stack pointer)对它进行直接访问。栈指针向下移 就创建了新的存储空间,向上移就释放内存空间。这是仅次于寄存器的 最快、最有效率的分配内存的方法。由于Jaa编译器必须生成能控制栈 指针上移和下移的代码,所以程序编译的时候,那些将被存储在栈中的 数据的大小和生命周期必须是已知的。这使得程序的灵活性收到了限 制,所以尽管Java把某些数据——特别是对象的 reference存放在栈里, 但对象本身并没有放在栈里。 3.堆(heap)。这是一段“多用途的内存池”( general- purpose pool of memory,也在内存里面),所有Java对象都保存在这里。同栈不同,堆 的优点是,分配空间的时候,编译器无需知道该分配多少空间,或者这 些数据会在堆里呆多长时间。因此使用堆的空间会比较灵活。只要你想 创建对象,用new就行了,程序执行的时候自会在堆里分配空间。当然 第3页共26页
shhgs@wgqqh.com 3 ✁ ✂ 26 ✁ X^ Ëg, Java (3×1m(1#cÝEE/9Q( %&f^·¡'(3(µ¥¥%& f^ ~ reference u¡J¶k(¥,ÂJ¡ new ³Q1 ³ new (¹Í¬~3×( Y(¥ÏDEFJâ*¡ÝE String s = new String("asdf"); AXWXͬ~3×( StringÏàcO#A ÍÄÏ1 × String String X3(Java d,Ví(%( ¡ÝE~¡ý þ(ñ`JJava áâG JF~à T %¬¡( ¥âF&Ä(1{Ä(13× Ò (¡m'%(OPÝE{¢FE£×rµ 1. (registers) ¤¥b¦({¢æADB§¨XFB¢ fËX^©{f(Oª¯·m«DEA,áf(¡¯ X;ÒUN©{fJKkA{F(¬P[Xg 2. (stack)§)Í·®{¯ (general random-access memory area)ÏË B¢fÝE^°Z[(stack pointer)¥A%&ÒU±²°Z[³ ~,({¢´4J³µk{´4 ¨)©{f( b¦¶ bmØ·({(µ,) Java áf'(0;N° Z[J³³(¸DEâFá(MY̹{¢F°*( OP(º0»¼@'(ô½( çâF(¾¿#Àg,« DEÁ Java P2ÌOP1¥( reference {kF°Ë ¥ pmkF°Ë 3. (heap) 3ÃÍVÄ({ÅÏ(general-purpose pool of memory[F{˶)Dm Java ¥Æ|{F ˰XÆ (ÇC´4(MáfÜî½twVÈ´4Y ÌOPºFÆËÉVÊ4æòçÆ(´4º8C¾¿ó%¡| ~¥ new &,âFË&(MýºFÆË´4
你得为这种灵活性付出代价,分配堆的存储空间要比分配栈的慢一些(假 如你能像C++那样在栈里创建对象的话)。 4.静态存储( (static storage)。“静态”在这里的意思就是“在固定的位置” (尽管它还是在RAM里面)。静态存储里面的数据在整个程序的运行期间 都能访问到。你可以用 static关键词来指明对象中的某个元素是静态 的,但是Java对象本身是决不会放到静态存储中去的。 5.固定存储( constant storage。常量值通常直接放在程序里,这样它们就 不会被改动了,因而也更安全。有时常量还能为自己设置界限,这样在 嵌入式系统中,你就能选择是不是把它们放到ROM里面去 6.非内存的存储(Non- RAM Storage)。如果数据完全独立于程序,那么即使 程序不运行,它也应该也还在;即使程序失去了对数据的控制,它也仍 然还在。两个最主要的例子是“流对象( streamed object)”和“ persistent 对象( persistent object)”。大致上说,前者是一种会被送往另一台机器 的,由对象转化而成的字节流;而后者则是保存在磁盘上的,能在程序 中止之后仍保存对象状态的文件。这类存储的奥妙就在于,对象被转化 成了某种能保存在其它介质上的东西,但是要用的时候,又能在内存里 重建。Java提供了“轻量级 persistence( lightweight persistence)”的支 持。未来Java可能会提供更为完整的 persistence的支持 特例: primitive类型 有一种编程时会经常用到的数据类型,会被当作特例来处理。你可以把它 想成“ primitive(原始)”类型。之所以要把它单独列出来,是因为用 new创建对象——特别是像简单变量之类的小对象的时候,效率不是太 高,因为它们都是放在堆里的。对于这类数据,Java承袭了C和C++ 的办法。也就是说,这个变量不是用new来创建的,相反,这里所创建 的是一个“非 reference”的“自动”变量。这个变量保存着值,并且存 储在栈中,因而效率会比较高 Java决定了每种 primitive类型的大小。它不会像其它语言那样,随机 器架构的不同而不同。这种变量大小的一致性是Java程序可移植的基础 Primitive类型大小「最小「最大 Wrapper类型 boolean Boolean char 16-bit Unicode o Unicode 216-1 Character byte 8-bit-128 +127 Byte short 16-bit-2 15 +215-1 Short int 32-bt-2 31-1 Integer long 64-bit-2 Long float 32-bit IEEE754 IEEE754 Float double 64-bit IEEE754 IEEE754 Double 第4页共26页
shhgs@wgqqh.com 4 ✁ ✂ 26 ✁ ¡ ¾¿#Ì!¸ÍÆ({¢´4%8°(Î3Ì(9 Ä¡;} C++Y¦F°Ë~¥() 4. (static storage)ÍÏÐÏF Ë(¹ÍFÑO(§¨Ï (ÁÂAF RAM ˶)ÏÐ{¢Ë¶(OPF¤×âF(&@4 Æ;±²g¡ÝE static ³QZÒ¥*(2×ÓÔÏÐ ( Java ¥ pNXºkgÏÐ{¢*r( 5. (constant storage)·ªÕ·ÒUkFâFË ¦A" Xº¹ð ,æà[cm·ª;ý þ¨?« ¦F Öt´×Ø*¡;ÙÚXPA"kg ROM ˶r 6. (Non-RAM storage)ÄÅOPcz)âFYZxç âFX&A[vw[F xçâF6r,¥OP(NA[Û Fe×bÜ%(ÍÝ¥ (streamed object) ÏÍpersistent ¥(persistent object)ÏÂ_J$3º¹Þ3mif (,¥=^à(ßÝ àà|{FáâJ(;FâF *+uÛ|{¥ãÐ( {¢(äåF)¥¹=^ ,2;|{F§AæJ(34%(Mü;F{Ë Java )*,Íçªè persistence (lightweight persistence) Ï(é êëQ Java Ý;º)*¤( persistence (éê : prim i tive m3áâºõ·g(OPº¹ 11QB¢¡ÝEPA |Íp ri m iti ve(å)ÏuDE%PA}zì!Qæ new ~¥1}]}5ªu(º¥(MØ·XÉ íæA"ÆkFÆË(¥) OPJava îï, C C+ + (ð[$ ×5ªX new Q~(>¤ ËD~ (3×ͯ referenceÏ(Íý Ï5ª ×5ª|{.Õ{ ¢F°*æàØ·º8Cí Java NO,L p ri m iti ve (ºAXº}§AY¦¸i fñò(XàX 5ªÂº(3_# Java âFݳó(Gô u3 Prim itive Wrapper bool ean — — — Bool ean char 16-b it U ni code 0 U ni code 2 ✞ ✟ - 1 Character byte 8-b it -128 + 127 Byte sh ort 16-b it -2 ✞ ✠ + 2 ✞ ✠ —1 Sh ort int 3 2-b it -2 ✡☛✞ + 2 ✡☛✞ —1 Integer l ong 64-b it -2 ✟ ✡ + 2 ✟ ✡ —1 Long fl oat 3 2-b it IE E E 754 IE E E 754 Fl oat dou bl e 64-b it IE E E 754 IE E E 754 Dou bl e
void Void 所有的数字类型都是带符号的,所以不用再找 unsigned类型了 这里没说 boolean的大小;它只能存储true和 false这两个值 (literal values true or false) Primitive类型的“ wrapper”类允许你在堆里创建一个表示这个 primitive型数据的对象。这个对象不是 primitive型的。例如: 或者你也可以这样: Character c= new Character(x' 为什么要这么作呢?我们会在后面的章节讲的。 高精度的数值 Java还包括两个能进行高精度算术运算的类: BigInteger和 BigDecimal。虽然它们也马马虎虎能被算作是“ wrapper”类,但是 没有哪个 primitive类型能和这两个类相对应 这两个类都提供了能模拟 primitive类型的操作的方法。也就是说,你可 以用 BigInteger或 BigDecima完成任何int或 float能完成的工 作,只是你不能使用操作符,而只能调用方法。此外,由于牵涉到的东西 比较多,因而速度会稍微慢些。实际上这是在用速度换精度 BigInteger支持任意精度的整数。也就是说,它可以精确地表示任意 大的自然数,所以运算的时候不会丢失任何信息 BigDecimal能表示任意精度的浮点数;因此,你可以用它来进行精度 要求极高的货币兑换的计算。 欲知这两个类的构造函数和方法调用方面的细节,请查阅JDK文档 Java中的数组 实际上所有编程语言都有数组。使用C和C++的数组是有风险的,因为 在它们眼里,数组只是一段内存。如果程序访问到数组之外的内存,或者 第5页共26页
shhgs@wgqqh.com 5 ✁ ✂ 26 ✁ void — — — Void Dm(OÆdc(DEXÁ u nsi gned , Ë$ bool ean (º Aó;{¢ tru e fal se e×Õ (l iteral val u es tru e or fal se) P ri m iti ve (Íwrap perÏõ0¡FÆË~3×WX × p ri m itve OP(¥ ×¥X p ri m iti ve (Ä char c = 'x'; Character C = new Character(c); Y¡[ÝE ¦ Character C = new Character('x'); öZ% Z1VÊ "ºF¶(ûß©( Java ./e×;%&íAz÷±÷(BigInteger BigDecim alA"[øøùù;¹÷1Íwrap perÏ mú× p ri m iti ve ; e×>¥v eׯ)*,;ûü p ri m iti ve (M1(µ[$¡Ý E BigInteger Y BigDecim al int Y fl oat ;(y 1ó¡X;çM1càó;pµò½,)ýþg(34 8CVæàzºÎÌñ`J FzlAz BigInteger éê¹Az(¤O[$AÝEArWX¹ Â(ýODE÷(MXº6 BigDecim al ;WX¹Az(CO æò¡ÝEAQ%&Az % í( l( ÷ ½ e×(òÚOµpµ¶(ß JD K ² Java ñ`JDmáâÆmOHç C C+ + (OHm$(æ FA"ËOHó3Ã{ÄÅâF±²gOHu½({Y