致读者 我从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;?(@¯¡ôõ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
还未进行初始化就使用了这些内存(很常见的编程错误),那么很有可能会 发生一些无法预料的后果。 安全性是Java最看重的目标之一,因此许多困扰C和C++程序员的问 题在Java里面已经不复存在了。数组肯定会被初始化,而要想访问数组 以外的内存也已经不可能了。边界检查的代价就是,每个数组都会多占用 些内存,而且程序运行的时候也会有些开销。不过设计者们相信,与安 全性的增强和编程效率的提升相比,这点代价值。 当你创建对象数组的时候,实际上你是在创建 reference的数组。这些 reference会被自动地初始化为一个特殊的值:null。这是个关键词,意 思就是“没有”。Java一看到n山l就知道这个 reference没有指向任 何对象。使用 reference之前,你必须用对象对它进行赋值,如果你试 图用一个仍然是nu的 reference,那么程序运行的时候就会报错。由 此,Java防止了许多常见的数组错误。 你可以创建 primitive的数组。编译器也会进行初始化,因为它会把数组 的内存全部清零 以后章节会更详细的讲解数组。 你永远不需要清理对象 在绝大多数编程语言中,要弄清变量生命周期的概念都会耗费大量的精 力。变量要生存多长时间?如果要由你来进行清理,那么应该在什么时候 进行清理?在变量生命周期方面含糊其辞,会导致很多bug。这一章会告 诉你,Java是怎样通过接管所有的清理工作来大大简化这个问题的 作用域 绝大多数过程语言都有“作用域( scope)”的概念。它决定了在这个作用 域里定义的变量的可见性与生命周期。在C,C++和]ava中,作用域是 由花括号{}的位置决定。所以,假如 int x =12 // Only x available tq=96 / Both x &g available / Only x available // g" out of scope" 在作用域中定义的变量只能用到这个作用域的结尾。 第6页共26页
shhgs@wgqqh.com 6 ✁ ✂ 26 ✁ ë%&f^ç, Ì{(· (áâ )YZmÝ;º ì03ÌÜ?(Å c# Java b c#(áâØ·() >8 C¸ÍÕ ¡~¥OH(Mñ`J¡F~ reference (OH Ì reference º¹ý rf^3×1\(Õnu l l ׳¹ ÍmÏJava 3<g nu l l½t × reference mZ ¥ç reference u¡'(¥¥A%&!ÕÄÅ¡" #3×Û nu l l( referenceYZâF&(Mº$, òJava %+,0V· (OH ¡ÝE~ p ri m iti ve (OHáf[º%&f^æAºPOH ({c&' Eûߺ((©£OH F)ÂVOáâ*%*&5ª0»¼@(Ö+ƺ,-ª(A ã5ª%0{VÊ4Ê ÄÅ%,¡Q%&&¢YZvwFöZM %&&¢Ê F5ª0»¼@µ¶./§0º1_V b u g 3ûº# ¡Java Õ¦^UÂDm(&¢y1QÂÂ]^ ײ( )ÂVO^âÆmÍ12 (scope)Ï(Ö+ANO,F ×1 2ËO3(5ª(Ý #0»¼@F CC+ + Java *12 ,/{ }(§¨NODE9Ä { int x = 12; // Only x available { int q = 96; // Both x & q available } // Only x available // q “out of scope” } F12*O3(5ªó;g ×12(45
"之后的这行文字都是注释。 缩进使得Java的代码更易读。由于Java是一种形式自由的语言,因此 额外的空格,跳格以及回车不会对程序的结构造成任何影响。 注意,虽然在C和C++里这种写法是完全合法的,但是你不能在Java 里这么写: int x=96:// Illegal 编译器会说变量x已经定义过了。于是C和C++的,在更大的作用域里 “隐藏”变量的能力,就被Java给禁了。因为Java的设计者们认为这 会导致令人费解的程序。 对象的作用域 Java对象的生命周期同 primitive的不同。当你用new创建Java对象 之后,它会晃荡到作用域外面。如果你写: string s= new string("a string") 1// End of scope s这个 reference会在作用域结束之后消失。但是s所指的那个 String 仍然还占着内存。在这段代码里,你已经没法访问这个对象了,因为唯 指向它那个的 reference现在已经出了作用域。在后面的章节中你还会 看到,在程序的运行过程中,它是怎样传递和复制对象的 reference 的 只要你还用得着,那些用new创建的对象就不会跑开。看来用了这种方 法之后,C++里面的一大堆问题在Java里面已经不复存在了。C++编 程中最难的问题就是,等你要用对象的时候,你根本没法从语言中获得这 个对象还在不在的信息。而且更重要的是,用C++编程,你得亲自进行 对象的清理。 这就带来了一个有趣的问题。如果java就这样只管造对象,不管清理对 象,那么又是谁在那里阳止对象填满内存进而把程序给搞宕的呢?这确实 是C++所面临的问题。但这里就有点魔法了。Java有一个垃圾回收器 ( gabage collector),它会看着所有用new创建的对象,并且还会知道 第7页共26页
shhgs@wgqqh.com 7 ✁ ✂ 26 ✁ ‘//’ u( & Ƶ 6%ç Java (¸S,) Java 37´ý,(æò 8½(´è9èE:;Ï5ª(;ã¹ Java ¬?,æ Java ( "Ù º1_@ª-£(âF Java ¥(0»¼@ p ri m iti ve (X ¡ new ~ Java ¥ uAºABg12½¶ÄÅ¡« { String s = new String("a string"); } // End of scope s × reference ºF124Cu6 s DZ(Y× String Û.{F øˡôõ±² ×¥,æ3 ZAY×( reference íFôõ!,12F¶(ûß*¡º <gFâF(&^â*AÕ¦DE6¥( reference ( ó%¡.YÌ new ~(¥XºF<Q, µ uC+ + ˶(3ÂƲF Java ˶ôõX6{F,C+ + á â*bU(²-¡%¥(M¡G *H ×¥FXF(à%( C+ + áâ¡Iý%& ¥(&¢ dQ,3×mJ(²ÄÅ Java ¦óÂÚ¥XÂ&¢¥ YZüKFYËL+¥MN{%àPâF¬O(VÊ ñ C+ + D¶P(² ËmCQ,Java m3×RS;Àf (gabage collector)Aº<.Dm new ~(¥º½t
其中的哪些已经没有 reference指着了。然后它会释放那些没有 reference指着的对象所占据的内存,这样内存就能被新的对象用了。这 就是说你永远也不必为重新申请内存而操心。你只要创建对象就行了,用 完之后它们自会离开。这样就彻底解决了因程序员忘了释放内存而产生 的,所谓“内存泄漏( memory leak)”的编程问题了。 创建新的数据类型:类 如果万物皆对象,那么特定类型的对象的特征和行为又是由什么决定的 呢?换言之,类是由什么组成的?或许你会想应该有一个叫“type”的 关键词,当然这种想法很有道理。然而长久以来,绝大多数的面向对象的 语言都用了 class这个关键词,它的意思是“现在我要告诉你这种新类 型的对象都有些什么特征。” class关键词(这个词太常用了,所以本书 的后面部分就不再用黑体字表示了)后面跟着新类型的名字。例如 class ATypeName i / Class body goes here */ 尽管类的正文部分只是一段注释(星斜杠以及在它们中间的东西,后面会 讲到的),但是这已经是一种新的类型了。虽然没什么大用,但是你可以 用new来创建这种类型的对象: ATypeName a new ATypeName ( 但是除非你为它定义一些方法,否则你没法让它去作更多的事情(也就 是,你不能向它发送任何有意义的消息)。 数据成员与方法 你定义类的时候(ava编程要做的就是定义类,创建这些类的对象, 然后再向这些对象发送消息),你可以往类里放两种元素:数据(feld, 有时也被称为数据成员),以及方法( method,有时也被称为成员函数 member function)。数据可以是任何类型的,能通过 reference进行 操控的对象。它也可以是任何一种 primitive数据(这时它就不是 reference了)。如果它是一个对象的 reference,那么你就必须用一种 被称为构造函数( constructor会在第4章详细讲述)的特殊方法对它进行 初始化,这样才能确保将它连上一个真实的对象(用new,就像前面看到 的)。如果这是个 primitive数据,那么你可以在定义的时候直接对它进 行初始化。(过一会就能看到, reference也可以在定义时进行初始化。) 每个对象都会保存它自己的数据成员;数据成员不能共享。下面就是一个 带数据成员的类 第8页共26页
shhgs@wgqqh.com 8 ✁ ✂ 26 ✁ §*(úÌôõm reference Z.,AºµkYÌm reference Z.(¥DP({ ¦{;¹(¥, $¡TU[X'V{àMq¡ó%~¥&, uA"ýºW ¦Xh£N,æâFQY,µk{àZ0 (D[Í{\](m em ory l eak)Ï(áâ², ÄÅ^_`¥YZ1O(¥(1a&ü,öZNO( VÊ lu,öZH(Ê Y0¡º|vwm3×b ÍtypeÏ( ³ |mt¢àÊcEQ)ÂVO(¶¥( Æ, class ׳A(¹ÍíF%#¡ (¥ÆmÌöZ1aÏclass ³ ( ׳ɷ,DE (¶XÁdWX,)¶e.(ÑÄ class ATypeName { /* Class body goes here */ } ÁÂ(T ó3õ (fghE:FA"*4(34¶º ©g() ôõ3(,öZ¡ÝE new Q~ (¥ ATypeName a = new ATypeName(); @¯¡AO33̵ià¡ÇAr1V(([ ¡X;Aìm¹3( ) ¡O3(M (Java áâ%T(O3~ Ì(¥ Á Ì¥ì )¡ÝEËkeÓÔOP (field m[¹jOPQ )E:µ (methodm[¹jQO member function)OPÝE(;^ reference %& MN(¥A[ÝE3 p ri m iti ve OP ( AX reference ,)ÄÅA3×¥( referenceYZ¡'(3 ¹jòÚO(constructor ºF 4 û(©)(1\µ¥A%& f^ ¦k;|AkJ3×lñ(¥( new}¶<g ()ÄÅ × p ri m iti ve OPYZ¡ÝEFO3(MÒU¥A% &f^(^3º;<greference [ÝEFO3%&f^) Lץƺ|{Aý þ(OPQ OPQX;mn¶3× dOPQ(
class Dataonly t float f boolean b 这个类做不了任何事情,但是你可以创建这样一个对象 Dataonly d= new Dataonly( 你可以为这些数据成员赋值,但是首先你必须得知道该如何引用对象的数 据成员。你必须先声明对象的 reference的名字,然后加一个“点”, 后面再跟上对象成员的名字: objectReference. member 例如: d f 11f;// 'f after number indicates float constant db= false 此外,对象里面还会包含一些可能会需要修改的其它对象。这么作,只要 接着“往下点”就行了。就像这样: my Plane. leftTank capacity =100 Dataonly类除了保存数据,其它什么都干不了,因为它没带方法。要 想理解方法是怎样工作的,你必须先理解参数( arguerηt)和返回值 ( return value)的概念。这两个概念,我们过一会就讲。 primitive成员的缺省值 当 primitive数据成为类的成员时,即便你不进行初始化,它也会得到 个默认的值: Primitive类型「缺省值 boolean False 第9页共26页
shhgs@wgqqh.com 9 ✁ ✂ 26 ✁ class DataOnly { int i; float f; boolean b; } ×TX,¡ÝE~ ¦3×¥ DataOnly d = new DataOnly(); ¡ÝE ÌOPQ!Õ~/¡'(½twÄ¥(O PQ¡'(/nÒ¥( reference (Ñä3×ÍCÏ ¶ÁeJ¥Q(Ñ objectReference.member Ä d.i = 47; d.f = 1.1f; // ‘f’ after number indicates float constant d.b = false; ò½¥Ë¶º..3ÌÝ;ºî%ïð(§A¥ Z1ó% U.ÍCÏ&,} ¦ myPlane.leftTank.capacity = 100; DataOnl y @,|{OP§AöZÆoX,æAdµ% |¢£µÕ¦y1(¡'(/¢£O(argument)p;Õ (return value)(Ö+ e×Ö+"^3º© prim itive p ri m iti ve OP(Qxy¡X%&f^A[ºg3 ×qÙ(Õ Prim itive bool ean Fal se
char \u0000'(null) byte (byte)o short (short)o 0 long OL 0.0 double 0.0d 定要记住,只有在“变量被用作类的成员”时,Java才能确保它获得 这些默认值。这样就确保了 primitiⅳve的类的成员肯定能得到初始化(这 是C++做不到的),因此也降低了产生bug的可能性。但是对程序来 说,这些初始值可能并不正确,甚至不合法。因此最好还是自己来做初始 化 “本地”变量——也就是非类数据成员的变量,就享受不到这种保障 了。因此,如果你在方法中定义了: 那么这个x可以是任意值(这同C和C++)的又相同了;它不会自动地 初始化为零。在你用ⅹ之前,你得先为它赋一个值。如果你忘了,Java 这点要比C++强的多:它会给你一个编译时( compile-time)错误,告诉 你这个变量可能没有初始化。(很多C++的编译器会对为初始化的变量发 出警告,但是在Java中,这就成了错误。 方法、参数和返回值 很多语言(比方说C和C++)是用函数( function)这个术语来表示有名 字的子程序的。对于Java,更常见的术语是“方法( method)”,也就 是“怎样去做这件事”。如果你一定要把它认做是函数,那也没什么不可 以。实际上这只是用词的不同,不过本书采纳了]ava通常所用的术语, 方法 在Java里面,方法决定了对象能接受哪些消息。在本节中,你会看到定 义方法竟然是如此简单。 方法的基本的组成包括方法的名字,参数,返回类型,以及方法的正文。 下面就是它的基本形式: returnType methodName( /* Argument list */ / Method body * 第10页共26页
shhgs@wgqqh.com 10 ✁ ✂ 26 ✁ char ‘\u 0000’ ( nu l l) byte ( byte) 0 sh ort ( sh ort) 0 int 0 l ong 0L fl oat 0. 0f dou bl e 0. 0d 3O%SrómFÍ5ª¹1(QÏJava k;|AH ÌqÙÕ ¦|, p ri m iti ve ((QO;gf^ ( C+ + TXg()æò[sq,Z0 b u g (Ý;#¥âFQ $ ÌfÕÝ;XTJKXæòbý þQTf ^ Í rÏ5ª[¯OPQ(5ªntXg |u ,æòÄÅ¡Fµ*O3, int x; YZ × x ÝE¹Õ ( C C+ + )(ü>, AXºý r f^'F¡ x u¡/A!3×ÕÄÅ¡Y,Java C%8 C+ + (VAº¬¡3×á (com p i l e-ti m e) # ¡ ×5ªÝ;mf^(V C+ + (áfº¥f^(5ªì !v#F Java * , ) !" V (8µ$ C C+ + )O (function) ×±QWXmÑ (âF(¥) Java· (±Íµ (method)Ï[ ÍÕ¦rT ÏÄÅ¡3O%PAÙTOY[öZXÝ Eñ`J ó³(XX^ wx, Java ·D(± ÍµÏ F Java ˶µNO,¥;UtúÌF ß*¡º<gO 3µyÄò]} µ(G (H./µ(ÑOp;E:µ(T ¶A(G 7´ returnType methodName( /* Argument list */ ) { /* Method body */ }