致读者 我从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页共48页
shhgs@wgqqh.com 1 ✁ ✂ 48 ✁ 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
3:控制程序流程 就像有感知力的生物那样,程序应该有能力操控它的世界,并且在执行过 程中作决定 Java让你用运算符( operator)来控制数据,用执行控制语句来作决定。 Java继承了C++,因此C和C++程序员会对它的语句和运算符感到很 亲切。不过Java也作了一些改进和简化。 如果你觉得这一章还是有点难,那么先去看看本书附送的多媒体CD ROM: Foundations for Java。它里边包括了讲课的录音,幻灯片,练 习以及答案。设计这个课程的目的就是要让你尽快地掌握学习Java所需 的基础知识。 使用Java运算符 运算符需要一到两个参数,并且会产生一个新的值。参数的使用方法与普 通方法的调用不同,但是结果是相同的。加(+),减(-),乘(*),除 (/),以及等号(=)的用法同其它编程语言完全相同。 运算符作用于操作数( operand),并且会产生一个值。此外,运算符还被 用于修改操作数的值。这被称为副作用( side effect)。我们经常看到的, 用运算符来修改操作数的操作就是利用了这种副作用,但是你应该记住, 这种做法所产生的值也是可以用的,它同那些没有副作用的运算符是一 样 几乎所有的运算符都只能作用于 primitive。但是‘= 以及 !=’是例外,它们可以作用于任何对象(由此也是对象方面一个很让人 头晕的问题。)此外, String类也支持‘+’和‘+=’。 优先级 当一个表达式包含多个运算符的时候,运算符的优先顺序会决定该怎样计 算这个表达式的值。Java有一套判断计算顺序的特殊规则。其中最简单 的一条就是“先乘除后加减”。程序员们经常会把其余的优先顺序都给忘 了,所以你应该使用括号来明确的指明计算的顺序。例如: a=x+y-2/2+z 同一个表达式,加上两组括号之后就会有截然不同的意思 a=x+(y-2)/(2+z); 第2页共48页
shhgs@wgqqh.com 2 ✁ ✂ 48 ✁ 3: }mã(0Y¦âvwm;ãA(F^ â*1NO Java Ç¡ (operator)Q!"#!Q1NO Java ö$, C++æò C C++â%º¥A( g &'X^ Java [1,3Ìð()* ÄÅ¡ 3ûmCUYZ/+?@ ×4â(A(%Ç¡BCrDEF; Java Dî (GHI Java î%3ge×"ºJ03×K(L"(çµM µ(NXOÅ>(ä(+)P(-)Q(*)R (/)E 1)1"(operan d )ºJ03×Lò½ T )ïð1"(L TUV1(side effect)Wõ·<g( Qïð1"(1X, V1¡vwYZ TDJ0(L[ÝE(AYÌmV1( 3 ¦ ijDm( Æó;1) p rim itive[=\[==\E< [! =\]½AWÝE1)^¥_(`ò[¥_µ¶3×Ǫ ab(cd)ò½Strin g e[fg[+\[+=\ 3×hi´2jV× (M (k/lºNOwÕ¦@ ×hi´(LJava m3mno@l(pqrs§*b)t (3uÍ/QRäPÏâ%Wõ·ºP§o(k/lƬv ,DE¡vwç3SQwx(yw@(l]Ä0 a = x + y - 2/2 + z; 3×hi´äJez3Suºm{|X(¹}0 a = x + (y - 2)/(2 + z);
赋值 赋值用的是≡运算符。它的意思是“算出等号右边的值(通常称为 rvalue),然后拷贝到等号的左边(通常称为/aue)”。 rvalue可以是任 何常量,变量或者是能产生值的表达式,而laue则只能是一个明确 的,有名字的变量( named variable)。(也就是说,必须要有一个物理空 间来存储这个值。)比如,你可以将常量赋给变量 但是你不能将值赋给常量——也就是说,常量是不能当Ⅳ value的。(你不 能说4=a) 给 primitive赋值还是比较简单的。由于 primitive持有的是实实在在的 值,而不是指向对象的 reference,因此为 primitive赋值的时候,你是 将内容从一个地方直接拷贝到另一个地方。假设a和b都是 primitive, 如果你说a=b,那么b的内容会被拷贝到a。如果接下来你又修改了 a,那么很显然b是不会受到影响的。对于程序员来说,在绝大多数情况 下,这正是他们所需要的 但是,给对象赋值的时候,情况就有所不同了。只要你想操控对象,你就 得通过 reference来进行操作,所以当你“在对象之间”进行赋值的时 候,实际上你是在拷贝它的 reference。也就是说,如果c和d都是对 象,而你说c〓d,结果就成了c和d都指向原先只有d指着的那个对 象了。下面的例程演示了这种行为 // c03: Assignment. java // Assignment with objects is a bit tricky import com. bruceeckel. simpletest. class Number public class Assignment i static Test monitor new Test ( public static void main(String[] args) t Number nl= new Number(i Number n2 new Number()i System. out. println("l: nl. i:#+ nl.i+ sy n1.i=2 第3页共48页
shhgs@wgqqh.com 3 ✁ ✂ 48 ✁ ~L( = A(¹}Í!-S1(L(·U rvalue)|g-S(1(·U lvalue)Ïrval ue ÝE^ · ;J0L(hi´àl val ue só;3×wx (mÑ((n am ed variab l e)([$%m3×¢ 4Q ×L)8Ä¡ÝE·~¬0 a = 4; ¡X;L~¬·[$·X; l val ue ((¡X ;$ 4 = a; ) ¬ p rim itive ~L8)t(`) p rim itive gm(ññFF( LàXy¥_( referen ceæò p rim itive ~L(M¡ R3×rµÒg3×rµ? a b Æ p rim itive ÄÅ¡$ a=b YZ b (RºTg aÄÅQ¡üïð, aYZ| b Xºg (¥)â%Q$FÂV" GWDî%( ¬¥_~L(MmDX,ó%¡|¥_¡ ^ referen ce Q(1DE ¡ÍF¥_u4Ï(~L( MñJ¡FA( referen ce[$ÄÅ c d Æ¥ _à¡$ c = dOÅ, c d Æyå/óm d y.(Y×¥ _,¶(]â, 0 //: c03:Assignment.java // Assignment with objects is a bit tricky. import com.bruceeckel.simpletest.*; class Number { int i; } public class Assignment { static Test monitor = new Test(); public static void main(String[] args) { Number n1 = new Number(); Number n2 = new Number(); n1.i = 9; n2.i = 47; System.out.println("1: n1.i: " + n1.i + ", n2.i: " + n2.i); n1 = n2; System.out.println("2: n1.i: " + n1.i + ", n2.i: " + n2.i); n1.i = 27;
System. out. println("3: nl.i: #+ nl.i+ n2.i:"+n2.i); String[] t "2:n1.i:47,n2.i:47 3:n1.i:27,n2.i:27 首先,提请大家注意一下新加的这一彳 import com. bruceeckel. simpletest *i 它引入了专门用于测试本书代码的“ simpletext”类库,我们会到第 15章再详细讲解这个类库。在 Assignment类的开始部分,有这么 static Test monitor new Test(i 它会创建一个名为 monitor的的Test类的实例,而Test又是 simpletext的一个类。在main()的最后,你还会看到 monitor. expect (new string[] i 1:n1.i:9,n2.i:47", 2:n1.i:47,n2 "3:n1.i:27,n2.i:27 }) 这是用 String数组表示的,程序的预期输出。程序运行的时候,不但会 把输出打印岀来,而且会拿它同数组作比较,以检验是不是正确。于是你 会发现,如果程序用到了 simpletext,那它肯定会调用显示程序输出的 expect()。这样你就能从源代码里看到正确的输出了 Number类很简单。main()创建了两个 Number类的实例(n1和 n2)。它们的i被分别赋上了不同的值,然后把n2赋给n1,再修改 n1。在很多别的编程语言里,你可以设想n1和n2自始至终都是两个 独立的对象。但在Java里面,由于是对 reference进行赋值,因此从 expect()语句可以看出,对n1的修改会影响到n2!这是因为,n1 和n2都是 reference,而这两个 reference又指向同一个对象。(原先 那个在n1里面的 reference,指向的是保存9这个值的对象。在赋值过 第4页共48页
shhgs@wgqqh.com 4 ✁ ✂ 48 ✁ System.out.println("3: n1.i: " + n1.i + ", n2.i: " + n2.i); monitor.expect(new String[] { "1: n1.i: 9, n2.i: 47", "2: n1.i: 47, n2.i: 47", "3: n1.i: 27, n2.i: 27" }); } } ///:~ ~/¹3Kä( 30 import com.bruceeckel.simpletest.*; At, )¡¢ £(ÍsimpletestÏe¤Wºg 15 ûÁ¥¦©£ ×e¤F Assignmen t e(m Z3 0 static Test monitor = new Test(); Aº§¨3×Ñ mon itor (( Test e(ñ]à Test ü simpletest (3×eF main ( ) (b¡º<g0 monitor.expect(new String[] { "1: n1.i: 9, n2.i: 47", "2: n1.i: 47, n2.i: 47", "3: n1.i: 27, n2.i: 27" }); Strin g "zh(â(?@©!â(MXº P©!]ª!QຫA"z18E¬Xx)¡ ºìíÄÅâg, simpletestYA®OºNâ©!( expect( ) ¦¡;¯£Ë<gx(©!, N umber e)tmain ( )§¨,e× N umber e(ñ] (n 1 n 2 )AW( i T°~J,X(L|P n 2 ~¬ n 1Áïð n 1FV°(áâË¡ÝE?| n 1 n 2 ý±:Æe× ²³(¥_F Java ˶`)¥ referen ce (~Læò expect( )ÝE<!¥ n 1 (ïðº g n 2 ´ æn 1 n 2 Æ referen ceà e× referen ce üy3×¥_(å/ Y×F n 1 ˶( referen cey(µ 9 ×L(¥_F~L^
程中,这个 reference被覆盖了,实际上就是被扔掉了;而它所指的那 个对象会被垃圾回收器清理掉。) 这种现象通常被称为 aliasing(别名效应),这是Java操控对象的基本方 法。但是如果你想避免 aliasing又应该怎么作呢?你可以进一步对具体 的数据成员进行赋值: n1,i= n2.i 与丢掉一个对象,再将n1和n2都绑到同一个对象相比,这样就能保留 两个相互独立的对象了。不过你很快就会知道直接操控对象内部的数据是 很麻烦的,而且有违OO设计的原则。这个课题不是一两句话就能讲清楚 的,所以我们把它放到附录A里,我们会在那里专门探讨 aliasing的。 在此期间,你只要知道为对象赋值可能会与你的初衷大相径庭就行了 方法调用期间的 aliasing 把对象传给方法的时候,也会有 aliasing的问题: //: c03: Passobject. java Passing objects to methods may not be what / you're used to. import com. bruceeckel. simpletest. class Letter i public class Passobject i static Test monitor =new Test ()i static void f(Letter y) t y public static void main(String[] args)t Letter x new letterof X. c System. out. println("1:xC:#+x c)i System. out. println ("2:xc:+ monitor. expect (new String[] i 在很多编程语言里,方法f()会在它的作用域里对参数 Letter y作一份 拷贝。但这里传的还是 reference,所以下面这行 第5页共48页
shhgs@wgqqh.com 5 ✁ ✂ 48 ✁ â* × referen ce T¶·,ñJT¸¹, àADy(Y ×¥_ºTº»¼½¾¿¢¹) í_·TU aliasing (°ÑØv) Java ¥_(G µ ÄÅ¡|ÀÁ al iasin g üvwÕZ1ÂÊ¡ÝE(3Ã¥Ä/ ("#%(~L0 n1.i = n2.i; Ź3×¥_Á n 1 n 2 ÆÆg3×¥_>8 ¦;µÇ e×>Ȳ³(¥_,X^¡CºÉÒ¥_("# ÊË(àmÌ OO ?@(ås ×4dX3e;©¿Í (DEWPAkg,5 A ËWºFYË ÎÏ al iasin g ( Fò@4¡ó%É¥_~LÝ;º¡(fÐÂ>ÑÒ, aliasin g P¥_Ó¬µ(M[ºm al iasin g (cd0 //: c03:PassObject.java // Passing objects to methods may not be what // you're used to. import com.bruceeckel.simpletest.*; class Letter { char c; } public class PassObject { static Test monitor = new Test(); static void f(Letter y) { y.c = 'z'; } public static void main(String[] args) { Letter x = new Letter(); x.c = 'a'; System.out.println("1: x.c: " + x.c); f(x); System.out.println("2: x.c: " + x.c); monitor.expect(new String[] { "1: x.c: a", "2: x.c: z" }); } } ///:~ FVáâ˵ f( )ºFA(1ÔË¥" Letter y 13ú ËÓ( referen ceDE¶
y·c="2" 实际上会修改代()外面的对象。 expect()里面的输出可以印证这 Aliasing及其解决方案是个很复杂的课题。虽然我们要到附录A才会作 全面讲解,但是你现在就得知道有这个问题,这样才会注意到 阱 数学运算符 在绝大多数编程语言里面,基本的数学运算符都是相同的:加(+),减( ),除(/),乘(*),取模(%,它会返回整数相除之后的余数)。整数相 除,会把运算结果的小数点后面全部截掉,而不是作四舍五入 Java也有可以同时进行计算和赋值的简写符号。它用运算符加一个等号 来表示,在Java语言中,这种表示方法能一致地用于所有的运算符(只 要是有意义就行了)。比如,你要为变量x加4,然后再赋给x,就写:x 下面这个例程演示了数学运算符的用法: //: c03: Mathops. java / Demonstrates the mathematical operators mport com. bruceeckel. simpletest. import java.util public class Mathops static Test monitor new Test o // Shorthand to print a string and an int: c void printInt (stri int 1) System. out. println( / Shorthand to print a string and a float static void printFloat (string s, float f)t System. out. println(s+ public static void main(String[] args)t // Create a random number generator, / seeds with current time by default Random rand new Random()i / Choose value from 1 to 100 rand. nextInt(100)+ 1 k rand. nextInt(100)+1 printInt("i",3)i printInt ("k",k) Int("3+k",i ki printInt("3-k", i i =k,ii printInt("k/i", i i =k* ji printInt("k*i", i) i=k号j; printing("k号j",i) 第6页共48页
shhgs@wgqqh.com 6 ✁ ✂ 48 ✁ y.c = 'z'; ñJºïð f( )½¶(¥_expect( )˶(©!ÝEªÕ 3 C Al iasin g ×Ö×(4dØ|W%g,5 A Ùº1 c¶©£¡íFÉm ×cd ¦Ùº¹g3ÌÍÚ ÛÏ FÂV"áâ˶G ("F Æ>(0ä(+)P(- )R(/)Q(*)ÜÝ(AºÞ¼¤">Ru(o")¤"> RºPOÅ(ß"C¶c{¹àX1àáât Java [mÝE(@~L()« SA ä3×-S QhF Java * hµ;3ãr)Dm( (ó %m¹ä, )8Ä¡% x ä 4|Á~¬ x«0x += 4 ¶ ×]â,"F (0 //: c03:MathOps.java // Demonstrates the mathematical operators. import com.bruceeckel.simpletest.*; import java.util.*; public class MathOps { static Test monitor = new Test(); // Shorthand to print a string and an int: static void printInt(String s, int i) { System.out.println(s + " = " + i); } // Shorthand to print a string and a float: static void printFloat(String s, float f) { System.out.println(s + " = " + f); } public static void main(String[] args) { // Create a random number generator, // seeds with current time by default: Random rand = new Random(); int i, j, k; // Choose value from 1 to 100: j = rand.nextInt(100) + 1; k = rand.nextInt(100) + 1; printInt("j", j); printInt("k", k); i = j + k; printInt("j + k", i); i = j - k; printInt("j - k", i); i = k / j; printInt("k / j", i); i = k * j; printInt("k * j", i); i = k % j; printInt("k % j", i); j %= k; printInt("j %= k", j);
/ Floating-point number tests float u, v, w; / applies to doubles, too Float ()i tFloat( printFloat(v,v) printFloat("w" u=v+ wi printEloat("v+ w", u) u=v-w; printFloat ("v-w",u) u=v* wi printFloat("v *w",u)i wi p / the following works for / char, by and double: u + vi printFloat ("u + v", u) i printFloat (u -=v", u) tFloat ("u * u/= v; printFloat("u/= v", u) monitor. expect (new string[] t 号号方 ?\\d+m 各k=-?\d+ 号号j\1+k d+", 告号j-k=-2\\d+ 号k/j=-?1\d+", 各号k\\ 号kj ?\\d+ 各号方=k ?\\d+m 号各 ?\\d+\.\d+(E-?\\d)?", 各号W=-?\d+\\.\d+(E-?\d)? 各号V+W=-?\d+.\d+(E-?\d)?? =-2\\d+\\.\\d+(E-?\\d)??", 号号V ?\\d+\\.\\d+(E-?\\d)??", ?\\d+\\.\\d+(E-?\\d)?? 号号u\\+=V=-?\d+\\.\d+(E-?\\d)?? V=-?\d+\.d+(E-?\\d)??", 号号u*=V=-?\d+\.\\d+(E-2\d)??”, 各号u/=V=-?\d+).\d+(E-?\hd)?? }); ///:~ 首先看到的是两个为节省敲键盘而定义的打印方法: printing()会先打 印一个 String,然后再接一个int:而 printFloat()会先打印一个 String,再打印一个 float 程序先创建一个 Random对象,以产生数字。由于创建过程中没有使用 参数,因此Java会用当前的时间作随机数生成器的种子。程序用 Random对象的 nextInt()和 nextFloat()方法生成了很多不同种 类的随机数(你也可以用 nextLong()或 nextDouble() 取模运算符( modulus operator)会将随机数生成器产生的数字限制在操 作数减一的范围之内(这里就是99) 正则表达式( Regular expressions) 第7页共48页
shhgs@wgqqh.com 7 ✁ ✂ 48 ✁ // Floating-point number tests: float u,v,w; // applies to doubles, too v = rand.nextFloat(); w = rand.nextFloat(); printFloat("v", v); printFloat("w", w); u = v + w; printFloat("v + w", u); u = v - w; printFloat("v - w", u); u = v * w; printFloat("v * w", u); u = v / w; printFloat("v / w", u); // the following also works for // char, byte, short, int, long, // and double: u += v; printFloat("u += v", u); u -= v; printFloat("u -= v", u); u *= v; printFloat("u *= v", u); u /= v; printFloat("u /= v", u); monitor.expect(new String[] { "%% j = -?\\d+", "%% k = -?\\d+", "%% j \\+ k = -?\\d+", "%% j - k = -?\\d+", "%% k / j = -?\\d+", "%% k \\* j = -?\\d+", "%% k % j = -?\\d+", "%% j %= k = -?\\d+", "%% v = -?\\d+\\.\\d+(E-?\\d)?", "%% w = -?\\d+\\.\\d+(E-?\\d)?", "%% v \\+ w = -?\\d+\\.\\d+(E-?\\d)??", "%% v - w = -?\\d+\\.\\d+(E-?\\d)??", "%% v \\* w = -?\\d+\\.\\d+(E-?\\d)??", "%% v / w = -?\\d+\\.\\d+(E-?\\d)??", "%% u \\+= v = -?\\d+\\.\\d+(E-?\\d)??", "%% u -= v = -?\\d+\\.\\d+(E-?\\d)??", "%% u \\*= v = -?\\d+\\.\\d+(E-?\\d)??", "%% u /= v = -?\\d+\\.\\d+(E-?\\d)??" }); } } ///:~ ~/<g(e×åæçèéàOä(]ªµ0prin tIn t( )º/] ª3× Strin g|Á3× in t à prin tFl oat( )º/]ª3× Strin gÁ]ª3× fl oat â/§¨3× Ran dom ¥_EJ0"`)§¨^â*mç "æò Java º (41¸ê"0¾(â Ran dom ¥_( nextIn t( ) nextFl oat( )µ0,VX e(¸ê" (¡[ÝE nextLon g( ) nextDou ble( )) ÜÝ (m odul us operator)º¸ê"0¾J0("ë!F 1"P3(ìíu ( Ë 99) ( Regu lar expression s)
由于程序用到了随机数,因此输出的结果会一次和一次不一样,所以 expect()就不能再像过去那样,一字不错地告诉你会输出些什么了。要 解决这个问题, expect()语句就得用到一种 Java JDK1.4所引入 的,被称为正则表达式的新的特性(但是在Per和 Python之类的语言 里,这已经是老特性了)。尽管我们要到第12章才会全面介绍这一无比 强大的工具,但如果这里不作介绍的话,你就没法看下去了。目前,你只 要能读懂 expect()就行了,要想全面了解这部分内容,请查阅JDK文 档的 javautil. regex. Pattern部分。 正则表达式是一种用通用术语描述字符串的方法,这样你就可以说:“如 果字符串里有这些东西,那么它同我正在找的东西相匹配。”比如,要表 示一个可能有也可能没有的负号,你可以在负号后面加一个问号,就像这 表示整数,就是表示一个或多个阿拉伯数字。在正则表达式中,阿拉伯数 字用“d’表示的,但是在Java的 String里,你必须再加一个反斜杠 才能把它“转义”成一个反斜杠:“\\d’。要在正则表达式里表示“ 个或多个前述的表达式”,你就得用“+’。所以要表示“前面可能有个 减号,后边跟着一串阿拉伯数字”,你得写: ?\\d+ 这就是上面那段程序的 expect()语句的第一行。 expect()语句的各行中,开头部分的‘%%’(注意一下,空格是为 增强可读性而加的),都不属于正则表达式。这是 simpletext所使用的 标记,它表示这行剩下来的部分是一个正则表达式。所以仅从 simpletext的 expect()语句,你是看不到普通的正则表达式的。 其它字符,只要它不属于正则表达式特有的专用字符,都要求完全匹配。 所以第一行: 各号j=-21\d+ =’会做精确匹配。但是在第三行,“j+k’里面的‘+’必须进行 转义,因为跟‘*’一样,在正则表达式里它也是一个特殊字符。作完这 些介绍之后,剩下几行你就应该能看懂了。如果本书后面的 expect() 里面又用到了正则表达式的新特性的话,我们会再作解释。 第8页共48页
shhgs@wgqqh.com 8 ✁ ✂ 48 ✁ `)âg,¸ê"æò©!(Oź3¨3¨X3¦DE expect( )X;Á}^+Y¦3Xîr#¡º©!ÌïZ,% £N ×cdexpect( )g3 Java JD K 1. 4 Dt (TUshi´(K(pð(F Perl Pyth on ue( Ë ôõñpð, )BòW%g 12 ûÙºc¶ 3Ü8 óÂ(yÄÄÅ ËX1(¡ûÏ8Ä%h 3×Ý;m[Ý;m(¿S¡ÝEF¿S¶ä3×cS} ¦0 -? h¤"h3× V×üýþ"Fshi´*üýþ" [\d\h(F Java ( Strin g Ë¡Áä3× Ù;PAÍäÏ3×0[\\d\%Fshi´ËhÍ3 × V×(hi´Ï¡[+\DE%hͶÝ;m× PS1.3÷üýþ"Ï¡«0 -?\\d+ J¶Yâ( expect( )(3 expect( )(*a([%%\(¹3è, óÝðàä()ÆX )shi´ simpletest Dç( YAh Q(3×shi´DE simpletest ( expect( )¡<XgM(shi´( §A ó%AX )shi´pm( Æ% cû DE30 %% j = -?\\d+ [j = \ºTxûFL[j + k\˶([+\( äæ[*\3¦Fshi´ËA[3×pq 1 Ìui¡vw;<=,ÄÅ ¶( expect( ) ˶üg,shi´(Kpð(WºÁ1£
单元的加号和减号运算符 单元的减号(-)和加号(+)与双元减号和加号完全相同。编译器会通过的表 达式来判断这个运算符到底是单元的还是双元的。例如,表达式 的意思就很明显。此外编译器也可以认出: 但是读代码的人就惨了,所以还是这样写更清楚: 单元减号表示负数。有单元减号就得有单元的正号,只是不起任何作用。 自动递增与递减 同C一样,Java里面到处都是简写。简写可以让程序写起来简单,至于 读取来怎么样,那得看情况。 递增和递减运算符(通常也被称作“自动递增auto- increment”和“自 动递减auto- decrement”运算符)是两种比较好的简写。递减的运算符 是一-,它的意思是“减小一个单位”。而递增得运算符是++,它的意 思是“增加一个单位”。比方说a是一个int,那么++a实际上同(a a+1)是等效的。递增和递减不但修改变量的值,而且会把这个值返 给变量。 这两个运算符还各有两个版本,也就是常提到的前置( prefix)和后置 ( postfix)。“前置递增(pre-iη crement)”的意思是+十运算符出现在 变量或者表达式的前面,而“后置递增(post- increment)”的意思是 ++运算符出现在变量或者表达式的后面。同样,“前置递减(pre decrement)”是指-运算符出现在变量或者表达式的前面,“后置递 减( post-decrement)”的意思是一-运算符出现在变量或者表达式的后 面。对于前置递增和前置递减,(也就是十+a和--a),操作过程是会先 计算再返回。对于后置递增和后置递减,(也就是a++和a-),操作步 骤是先返回再计算。下面就是举例 //: c03: AutoInc. java // Demonstrates the + and - operators mport com. bruceeckel. simpletest *i 第9页共48页
shhgs@wgqqh.com 9 ✁ ✂ 48 ✁ t(PS(-)äS(+ )PSäSc>Ὰ^(h i´Qno × ght((]Ähi´ x = -a; (¹}wò½á¾[ÝEÙ!0 x = a * -b; £(ª,DE ¦«¿Í0 x = a * (-b); tPSh¿"mtPSmt(SóX9^1 C 3¦Java ˶gÆ)«)«ÝEÇâ«9Q)t±) ÜQÕZ¦Y< P (·[TU1Íý auto-in crem en tÏÍý P auto-decrem en tÏ ) e8()«P( -- A(¹}ÍPß3×tÏà ++ A(¹ }Íä3×tÏ8µ$ a 3× in tYZ ++a ñJ( a = a +1 )-Ø(PXïð(LàºP ×LÞ ¼¬ e× me× [·g((p refix) (postfix)Í (pre-increment)Ï(¹} ++ !íF hi´(¶àÍ (post-increment)Ï(¹} ++ !íF hi´(¶¦ÍP(predecrement)Ïy -- !íF hi´(¶Í P(post-decrement)Ï(¹} -- !íF hi´( ¶¥)P([ ++a --a)1^âº/ @ÁÞ¼¥)P([ a++ a--)1à /Þ¼Á@¶]0 //: c03:AutoInc.java // Demonstrates the ++ and -- operators. import com.bruceeckel.simpletest.*;
public class AutoInc static Test monitor new Test( public static void main(String[] args)i System. out. println("i System. out. println("++i # +++i);//Pre Increment In( System. out. println("--i : "+--i);//Pre- decrement System. out. println("i "+i--);//Post decrement System. out. println("i monitor.expect (new String[] i "i++:2 i-11 }///:~ 你可以看到,运算符前置的时候,你所得到的是经过计算的值,但是运算 符后置的情况下,你所得到的是还未进行过处理的值。这是唯一一种(除 了赋值之外)还有其它附带用途的操作符。(也就是说,不是要用它来进行 计算,而是要用它来修改操作数。) 递增运算符也是C++命名的原因之一,它的寓意是“比C更进一步” 在早期的Java访谈中, Bill Joy(Sun的创建者之一)说,“Java C++-”(C++的递减)。他的意思是,Java是一种去除了C++中多余 的复杂性的语言,因此它是一种更为简单的语言。随着本书的进展,你会 看到很多地方都变得更简单了,但是Java还不到“比C++简单得多” 的地步。 关系运算符 关系运算符会产生 boolean类型的结果。它们会判断操作数的值的大小 关系。如果这种关系为真,则关系表达式返回true,如果这个关系为 假,则返回 false。关系运算符有小于(),小于等于(=),相等(==)以及不等(!=)。所有的内置数据类型都能比 较相等( equivalence)和不等( nonequivalence),但是对 boolean类 型,除了相等和不等的之外,其它比较是没意义的 测试对象的相等性 第10页共48页
shhgs@wgqqh.com 10 ✁ ✂ 48 ✁ public class AutoInc { static Test monitor = new Test(); public static void main(String[] args) { int i = 1; System.out.println("i : " + i); System.out.println("++i : " + ++i); // Preincrement System.out.println("i++ : " + i++); // Postincrement System.out.println("i : " + i); System.out.println("--i : " + --i); // Predecrement System.out.println("i-- : " + i--); // Postdecrement System.out.println("i : " + i); monitor.expect(new String[] { "i : 1", "++i : 2", "i++ : 2", "i : 3", "--i : 2", "i-- : 2", "i : 1" }); } } ///:~ ¡ÝE )ß)-)( =)>-(==)E-(equival en ce)X-(n on equival en ce)¥ boolean e $R,>-X-(u½§A8¹ä(