友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
富士康小说网 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

Java编程思想第4版[中文版](PDF格式)-第20部分

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!



个0。若两个输入位的某一个是1,但不全都是1,那么按位 XOR (^,异或)在输出位里生成一个 1。按位 

NOT (~,也叫作“非”运算符)属于一元运算符;它只对一个自变量进行操作(其他所有运算符都是二元运 

算符)。按位NOT 生成与输入位的相反的值——若输入 0,则输出1;输入 1,则输出0。  

按位运算符和逻辑运算符都使用了同样的字符,只是数量不同。因此,我们能方便地记忆各自的含义:由于 

 “位”是非常“小”的,所以按位运算符仅使用了一个字符。  

按位运算符可与等号(=)联合使用,以便合并运算及赋值:&=,|=和^=都是合法的(由于~是一元运算符, 

所以不可与=联合使用)。  

我们将boolean (布尔)类型当作一种“单位”或“单比特”值对待,所以它多少有些独特的地方。我们可 

执行按位AND,OR 和 XOR,但不能执行按位 NOT (大概是为了避免与逻辑 NOT 混淆)。对于布尔值,按位运算 

符具有与逻辑运算符相同的效果,只是它们不会中途“短路”。此外,针对布尔值进行的按位运算为我们新 

增了一个XOR 逻辑运算符,它并未包括在“逻辑”运算符的列表中。在移位表达式中,我们被禁止使用布尔 

运算,原因将在下面解释。  



3。1。8  移位运算符  



移位运算符面向的运算对象也是二进制的“位”。可单独用它们处理整数类型(主类型的一种)。左移位运 

算符()则将运算符左边的运算对象向右移动运算符右侧指定的位数。“有符号”右移位运算符使用了 

 “符号扩展”:若值为正,则在高位插入0;若值为负,则在高位插入1。Java 也添加了一种“无符号”右 

移位运算符(》》》),它使用了“零扩展”:无论正负,都在高位插入0。这一运算符是C 或C++没有的。  

若对 char,byte 或者 short 进行移位处理,那么在移位进行之前,它们会自动转换成一个 int。只有右侧的 

5 个低位才会用到。这样可防止我们在一个 int数里移动不切实际的位数。若对一个 long 值进行处理,最后 

得到的结果也是 long。此时只会用到右侧的 6 个低位,防止移动超过 long 值里现成的位数。但在进行“无 

符号”右移位时,也可能遇到一个问题。若对 byte 或 short 值进行右移位运算,得到的可能不是正确的结果 

 (Java 1。0 和Java 1。1 特别突出)。它们会自动转换成 int 类型,并进行右移位。但“零扩展”不会发 

生,所以在那些情况下会得到…1 的结果。可用下面这个例子检测自己的实现方案:  

  

//: URShift。java  

// Test of unsigned right shift  

  

public class URShift {  



                                                                        68 


…………………………………………………………Page 70……………………………………………………………

  public static void main(String'' args) {  

    int i = …1;  

    i 》》》= 10;  

    System。out。println(i);  

    long l = …1;  

    l 》》》= 10;  

    System。out。println(l);  

    short s = …1;  

    s 》》》= 10;  

    System。out。println(s);  

    byte b = …1;  

    b 》》》= 10;  

    System。out。println(b);  

  }  

} ///:~  

  

移位可与等号(=或》》》=)组合使用。此时,运算符左边的值会移动由右边的值指定的位数,再将得 

到的结果赋回左边的值。  

下面这个例子向大家阐示了如何应用涉及“按位”操作的所有运算符,以及它们的效果:  

  

//: BitManipulation。java  

// Using the bitwise operators  

import java。util。*;  

  

public class BitManipulation {  

  public static void main(String'' args)  {  

    Random rand = new Random();  

    int i = rand。nextInt();  

    int j = rand。nextInt();  

    pBinInt(〃…1〃; …1);  

    pBinInt(〃+1〃; +1);  

    int maxpos = 2147483647;  

    pBinInt(〃maxpos〃; maxpos);  

    int maxneg = …2147483648;  

    pBinInt(〃maxneg〃; maxneg);  

    pBinInt(〃i〃; i);  

    pBinInt(〃~i〃; ~i);  

    pBinInt(〃…i〃; …i);  

    pBinInt(〃j〃; j);  

    pBinInt(〃i & j〃; i & j);  

    pBinInt(〃i | j〃; i | j);  

    pBinInt(〃i ^ j〃; i ^ j);  

    pBinInt(〃i 》 5〃; i 》》 5);  

    pBinInt(〃(~i) 》》 5〃; (~i) 》》 5);  

    pBinInt(〃i 》》》 5〃; i 》》》 5);  

    pBinInt(〃(~i) 》》》 5〃; (~i) 》》》 5);  

  

    long l = rand。nextLong();  

    long m = rand。nextLong();  

    pBinLong(〃…1L〃; …1L);  

    pBinLong(〃+1L〃; +1L);  



                                                                                           69 


…………………………………………………………Page 71……………………………………………………………

    long ll = 9223372036854775807L;  

    pBinLong(〃maxpos〃; ll);  

    long lln = …9223372036854775808L;  

    pBinLong(〃maxneg〃; lln);  

    pBinLong(〃l〃; l);  

    pBinLong(〃~l〃; ~l);  

    pBinLong(〃…l〃; …l);  

    pBinLong(〃m〃; m);  

    pBinLong(〃l & m〃; l & m);  

    pBinLong(〃l | m〃; l | m);  

    pBinLong(〃l ^ m〃; l ^ m);  

    pBinLong(〃l 》 5〃; l 》》 5);  

    pBinLong(〃(~l) 》》 5〃; (~l) 》》 5);  

    pBinLong(〃l 》》》 5〃; l 》》》 5);  

    pBinLong(〃(~l) 》》》 5〃; (~l) 》》》 5);  

  }  

  static void pBinInt(String s; int i) {  

    System。out。println(  

      s + 〃; int: 〃 + i + 〃; binary: 〃);  

    System。out。print(〃   〃);  

    for(int j = 31; j 》=0; j……)  

      if(((1 =0; i……)  

      if(((1L  5; int: 1846303; binary:   

      00000000000111000010110000011111  

(~i) 》》 5; int: …1846304; binary:   

      11111111111000111101001111100000  

i 》》》 5; int: 1846303; binary:   

      00000000000111000010110000011111  

(~i) 》》》 5; int: 132371424; binary:   

      00000111111000111101001111100000  

  

数字的二进制形式表现为“有符号 2 的补值”。  



3。1。9  三元 ifelse 运算符  



这种运算符比较罕见,因为它有三个运算对象。但它确实属于运算符的一种,因为它最终也会生成一个值。 

这与本章后一节要讲述的普通 if…else 语句是不同的。表达式采取下述形式:  

  

布尔表达式 ? 值 0:值 1  

  

若“布尔表达式”的结果为true,就计算“值0”,而且它的结果成为最终由运算符产生的值。但若“布尔 

表达式”的结果为 false,计算的就是“值 1”,而且它的结果成为最终由运算符产生的值。  

当然,也可以换用普通的if…else 语句(在后面介绍),但三元运算符更加简洁。尽管 C 引以为傲的就是它 

是一种简练的语言,而且三元运算符的引入多半就是为了体现这种高效率的编程,但假若您打算频繁用它, 

还是要先多作一些思量——它很容易就会产生可读性极差的代码。  

可将条件运算符用于自己的“副作用”,或用于它生成的值。但通常都应将其用于值,因为那样做可将运算 

符与 if…else 明确区别开。下面便是一个例子:  

  

static int ternary(int i) {  

return i 《 10 ? i * 100 : i * 10;  

}  

  

可以看出,假设用普通的if…else 结构写上述代码,代码量会比上面多出许多。如下所示:  

  

static int alternative(int i) {  

if (i 《 10)  



                                                                                              71 


…………………………………………………………Page 73……………………………………………………………

return i * 100;  

return i * 10;  

}  

  

但第二种形式更易理解,而且不要求更多的录入。所以在挑选三元运算符时,请务必权衡一下利弊。  



3。1。10  逗号运算符  



在C 和C++里,逗号不仅作为函数自变量列表的分隔符使用,也作为进行后续计算的一个运算符使用。在 

Java 里需要用到逗号的唯一场所就是for 循环,本章稍后会对此详加解释。  



3。1。11  字串运算符+   



这个运算符在Java 里有一项特殊用途:连接不同的字串。这一点已在前面的例子中展示过了。尽管与+的传 

统意义不符,但用+来做这件事情仍然是非常自然的。在 C++里,这一功能看起来非常不错,所以引入了一项 

 “运算符过载”机制,以便C++程序员为几乎所有运算符增加特殊的含义。但非常不幸,与C++的另外一些限 

制结合,运算符过载成为一种非常复杂的特性,程序员在设计自己的类时必须对此有周到的考虑。与C++相 

比,尽管运算符过载在Java 里更易实现,但迄今为止仍然认为这一特性过于复杂。所以Java 程序员不能象 

C++程序员那样设计自己的过载运算符。  

我们注意到运用“String + ”时一些有趣的现象。若表达式以一个String 起头,那么后续所有运算对象都必 

须是字串。如下所示:  

  

int x = 0; y = 1; z = 2;  

String sString = 〃x; y; z 〃;  

System。out。println(sString + x + y + z);  

  

在这里,Java 编译程序会将 x,y 和 z 转换成它们的字串形式,而不是先把它们加到一起。然而,如果使用 

下述语句:  

  

System。out。println(x + sString);  

  

那么早期版本的Java 就会提示出错(以后的版本能将x 转换成一个字串)。因此,如果想通过“加号”连接 

字串(使用 Java 的早期版本),请务必保证第一个元素是字串(或加上引号的一系列字符,编译能将其识别 

成一个字串)。  



3。1。12  运算符常规操作规则  



使用运算符的一个缺点是括号的运用经常容易搞错。即使对一个表达式如何计算有丝毫不确定的因素,都容 

易混淆括号的用法。这个问题在Java 里仍然存在。  

在C 和C++中,一个特别常见的错误如下:  

  

while(x = y) {  

//。。。  

}  

  

程序的意图是测试是否“相等”(==),而不是进行赋值操作。在C 和C++中,若y 是一个非零值,那么这 

种赋值的结果肯定是true。这样使可能得到一个无限循环。在Java 里,这个表达式的结果并不是布尔值, 

而编译器期望的是一个布尔值,而且不会从一个 int数值中转换得来。所以在编译时,系统就会提示出现错 

误,有效地阻止我们进一步运行程序。所以这个缺点在Java 里永远不会造成更严重的后果。唯一不会得到编 

译错误的时候是x 和y 都为布尔值。在这种情况下,x = y 属于合法表达式。而在上述情况下,则可能是一 

个错误。  

在C 和C++里,类似的一个问题是使用按位AND 和 OR,而不是逻辑AND 和 OR。按位AND 和 OR 使用两个字符 

之一(&或 |),而逻辑AND 和OR 使用两个相同的字符(&&或 ||)。就象“=”和“==”一样,键入一个字符 



                                                                               72 


…………………………………………………………Page 74……………………………………………………………

当然要比键入两个简单。在Java 里,编译器同样可防止这一点,因为它不允许我们强行使用一种并不属于的 

类型。  



3。1。13  造型运算符  



 “造型”(Cast )的作用是“与一个模型匹配”。在适当的时候,Java 会将一种数据类型自动转换成另一 

种。例如,假设我们为浮点变量分配一个整数值,计算机会将 int 自动转换成float。通过造型,我们可明 

确设置这种类型的转换,或者在一般没有可能进行的时候强迫它进行。  

为进行一次造型,要将括号中希望的数据类型(包括所有修改符)置于其他任何值的左侧。下面是一个例 

子:  

  

void casts() {  

int i = 200;  

long l = (long)i;  

long l2 = (long)200;  

}  

  

正如您看到的那样,既可对一个数值进行造型处理,亦可对一个变量进行造型处理。但在这儿展示的两种情 

况下,造型均是多余的,因为编译器在必要的时候会自动进行 int值到 long 值的转换。当然,仍然可以设置 

一个造型,提醒自己留意,也使程序更清楚。在其他情况下,造型只有在代码编译时才显出重要性。  

在C 和C++中,造型有时会让人头痛。在Java 里,造型则是一种比较安全的操作。但是,若进行一种名为 

 “缩小转换”(Narrowing Conversion)的操作(也就是说,脚本是能容纳更多信息的数据类型,将其转换 

成容量较小的类型),此时就可能面临信息丢失的危险。此时,编译器会强迫我们进行造型,就好象说: 

 “这可能是一件危险的事情——如果您想让我不顾一切地做,那么对不起,请明确造型。”而对于“放大转 

换”(Widening conversion),则不必进行明确造型,因为新类型肯定能容纳原来类型的信息,不会造成任 

何信息的丢失。  

Java 允许我们将任何主类型“造型”为其他任何一种主类型,但布尔值(bollean)要除外,后者根本不允 

许进行任何造型处理。“类”不允许进行造型。为了将一种类转换成另一种,必须采用特殊的方法(字串是 

一种特殊的情况,本书后面会讲到将对象造型到一个类型“家族”里;例如,“橡树”可造型为“树”;反 

之亦然。但对于其他外来类型,如“岩石”,则不能造型为“树”)。  

  

1。 字面值  

最开始的时候,若在一个程序里插入“字面值”(Literal),编译器通常能准确知道要生成什么样的类型。 

但在有些时候,对于类型却是暧昧不清的。若发生这种情况,必须对编译器加以适当的“指导”。方法是用 

与字面值关联的字符形式加入一些额外的信息。下面这段代码向大家展示了这些字符。  

  

//: Literals。java  

  

class Literals {  

  char c = 0xffff; // max char hex value  

  byte b = 0x7f; // max byte hex value  

  short s = 0x7fff; // max short hex value  

  int i1 = 0x2f; // Hexadecimal (lowercase)  

  int i2 = 0X2F; // Hexadecimal (uppercase)  

  int i3 = 0177; // Octal (leading zero)  

  // Hex and Oct also work with long。  

  long n1 = 200L; // long suffix  

  long n2 = 200l; // long suffix  

  long n3 = 200;  

  //! long l6(200); // not allowed  

  float f1 = 1;  

  float f2 = 1F; // float suffix  

  float f3 = 1f; // float suffix  



                                                                                    73 


…………………………………………………………Page 75……………………………………………………………

  float f4 = 1e…45f; // 10 to the power  

  float f5 = 1e+9f; // float suffix  

  double d1 = 1d; // double suffix  

  double d2 = 1D; // double suffix  

  double d3 = 47e47d; // 10 to the power  

} ///:~  

  

十六进制(Base 16)——它适用于所有整数数据类型——用一个前置的 0x 或 0X 指示。并在后面跟随采用大 

写或小写形式的0…9 以及a…f。若试图将一个变量初始化成超出自身能力的一个值(无论这个值的数值形式 

如何),编译器就会向我们报告一条出错消息。注意在上述代码中,最大的十六进制值只会在char,byte 以 

及 short 身上出现。若超出这一限制,编译器会将值自动变成一个 int,并告诉我们需要对这一次赋值进行 

 “缩小造型”。这样一来,我们就可清楚获知自己已超载了边界。  

八进制(Base 8)是用数字中的一个前置0 以及0…7 的数位指示的。在 C,C++或者Java 中,对二进制数字 

没有相应的“字面”表示方法。  

字面值后的尾随字符标志着它的类型。若为大写或小写的 L,代表 long;大写或小写的F,代表 float;大写 

或小写的D,则代表double。  

指数总是采用一种我们认为很不直观的记号方法:1。39e…47f。在科学与工程学领域,“e”代表自然对数的 

基数,约等于2。718 (Java 一种更精确的double 值采用 Math。E 的形式)。它在象“1。39 ×e 的…47 次方”这 

样的指数表达式中使用,意味着“1。39×2。718 的…47 次方”。然�
返回目录 上一页 下一页 回到顶部 赞(11) 踩(15)
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!