@@ -681,7 +681,7 @@ float f4 = 1e-43f; //10 的幂数
681681## 移位运算符
682682
683683
684- 移位运算符面向的运算对象也是二进制的“位”。可单独用它们处理整数类型 (基本类型的一种)。左移位运算符 ` << ` 能将其左边的运算对象向左移动右侧指定的位数(在低位补 0)。右移位运算符 ` >> ` 则相反。位移运算符有 “正”、“负”值” :若值为正,则在高位插入 0;若值为负,则在高位插入 1。Java 也添加了一种“不分正负”的右移位运算符(>>>),它使用了“零扩展”(zero extension):无论正负,都在高位插入 0。这一运算符是 C/C++ 没有的。
684+ 移位运算符面向的运算对象也是二进制的“位”。它们只能用于处理整数类型 (基本类型的一种)。左移位运算符 ` << ` 能将其左边的运算对象向左移动右侧指定的位数(在低位补 0)。右移位运算符 ` >> ` 则相反。右移位运算符有 “正”、“负”值:若值为正,则在高位插入 0;若值为负,则在高位插入 1。Java 也添加了一种“不分正负”的右移位运算符(>>>),它使用了“零扩展”(zero extension):无论正负,都在高位插入 0。这一运算符是 C/C++ 没有的。
685685
686686如果移动 ** char** 、** byte** 或 ** short** ,则会在移动发生之前将其提升为 ** int** ,结果为 ** int** 。仅使用右侧的 5 个低阶位。这可以防止我们移动超过 ** int** 范围的位数。若对一个 ** long** 值进行处理,最后得到的结果也是 ** long** 。
687687
@@ -837,7 +837,7 @@ i >>> 5, int: 97591828, binary:
837837```
838838
839839
840- 结尾的两个方法 ` printBinaryInt() ` 和 ` printBinaryLong() ` 它们分别操作一个 ** int** 和 ** long** 值,并转换为二进制格式输出,同时附有简要的文字说明。除了演示 ** int** 和 ** long** 的所有位运算符的效果之外,本示例还显示 ** int** 和 ** long** 的最小值、最大值、+1 和 -1 值,以便我们了解它们的形式。注意高位代表符号:0 表示正,1 表示负。上面显示了 ** int** 部分的输出。数字的二进制表示称为有符号的两个补数。
840+ 结尾的两个方法 ` printBinaryInt() ` 和 ` printBinaryLong() ` 分别操作一个 ** int** 和 ** long** 值,并转换为二进制格式输出,同时附有简要的文字说明。除了演示 ** int** 和 ** long** 的所有位运算符的效果之外,本示例还显示 ** int** 和 ** long** 的最小值、最大值、+1 和 -1 值,以便我们了解它们的形式。注意高位代表符号:0 表示正,1 表示负。上面显示了 ** int** 部分的输出。数字的二进制表示称为有符号的两个补数。
841841
842842
843843<!-- Ternary-if-else-Operator -->
@@ -862,9 +862,9 @@ static int ternary(int i) {
862862
863863static int standardIfElse (int i ) {
864864 if (i < 10 )
865- return i * 100 ;
865+ return i * 100 ;
866866 else
867- return i * 10 ;
867+ return i * 10 ;
868868}
869869
870870 public static void main (String [] args ) {
@@ -922,16 +922,16 @@ x, y, z (summed) = 3
9229220
923923```
924924
925- ** 注意** :上例中第 1 输出语句的执行结果是 ` 012 ` 而并非 ` 3 ` ,这是因为编译器将其分别转换为其字符串形式然后与字符串变量 ** s** 连接。在第 2 条输出语句中,编译器将开头的变量转换为了字符串,由此可以看出,这种转换与数据的位置无无关系 ,只要当中有一条数据是字符串类型,其他非字符串数据都将被转换为字符串形式并连接。最后一条输出语句,我们可以看出 ` += ` 运算符可以拼接其右侧的字符串连接结果并重赋值给自身变量 ` s ` 。括号 ` () ` 可以控制表达式的计算顺序,以便在显示 ** int** 之前对其进行实际求和。
925+ ** 注意** :上例中第 1 输出语句的执行结果是 ` 012 ` 而并非 ` 3 ` ,这是因为编译器将其分别转换为其字符串形式然后与字符串变量 ** s** 连接。在第 2 条输出语句中,编译器将开头的变量转换为了字符串,由此可以看出,这种转换与数据的位置无关 ,只要当中有一条数据是字符串类型,其他非字符串数据都将被转换为字符串形式并连接。最后一条输出语句,我们可以看出 ` += ` 运算符可以拼接其右侧的字符串连接结果并重赋值给自身变量 ` s ` 。括号 ` () ` 可以控制表达式的计算顺序,以便在显示 ** int** 之前对其进行实际求和。
926926
927927请注意主方法中的最后一个例子:我们经常会看到一个空字符串 ` "" ` 跟着一个基本类型的数据。这样可以隐式地将其转换为字符串,以代替繁琐的显式调用方法(如这里可以使用 ** Integer.toString()** )。
928928
929-
930929<!-- Common-Pitfalls-When-Using-Operators -->
930+
931931## 常见陷阱
932932
933933
934- 使用运算符时很容易犯的一个错误是,在还没搞清楚表达式的计算方式时就试图忽略括号 ` () ` 。在 Java 中也一样。 在 C++ 中你甚至可能凡这样极端的错误 .代码示例:
934+ 使用运算符时很容易犯的一个错误是,在还没搞清楚表达式的计算方式时就试图忽略括号 ` () ` 。在 Java 中也一样。 在 C++ 中你甚至可能犯这样极端的错误 .代码示例:
935935
936936``` java
937937while (x = y) {
@@ -970,7 +970,7 @@ public class Casting {
970970
971971诚然,你可以这样地去转换一个数值类型的变量。但是上例这种做法是多余的:因为编译器会在必要时自动提升 ** int** 型数据为 ** long** 型。
972972
973- 当然,为了程序逻辑清晰或提醒自己留意,我们也可以显式地类型转换。在其他情况下,类型转换型只有在代码编译时才显出其重要性。在 C/C++ 中,类型转换有时会让人头痛。在 Java 里,类型转换则是一种比较安全的操作。但是,若将数据类型进行“向下转换”(** Narrowing Conversion** )的操作(将容量较大的数据类型转换成容量较小的类型),可能会发生信息丢失的危险。此时,编译器会强迫我们进行造型 ,好比在提醒我们:该操作可能危险,若你坚持让我这么做,那么对不起,请明确需要转换的类型。 对于“向上转换”(** Widening conversion** ),则不必进行显式的类型转换,因为较大类型的数据肯定能容纳较小类型的数据,不会造成任何信息的丢失。
973+ 当然,为了程序逻辑清晰或提醒自己留意,我们也可以显式地类型转换。在其他情况下,类型转换型只有在代码编译时才显出其重要性。在 C/C++ 中,类型转换有时会让人头痛。在 Java 里,类型转换则是一种比较安全的操作。但是,若将数据类型进行“向下转换”(** Narrowing Conversion** )的操作(将容量较大的数据类型转换成容量较小的类型),可能会发生信息丢失的危险。此时,编译器会强迫我们进行转型 ,好比在提醒我们:该操作可能危险,若你坚持让我这么做,那么对不起,请明确需要转换的类型。 对于“向上转换”(** Widening conversion** ),则不必进行显式的类型转换,因为较大类型的数据肯定能容纳较小类型的数据,不会造成任何信息的丢失。
974974
975975除了布尔类型的数据, Java 允许任何基本类型的数据转换为另一种基本类型的数据。此外,类是不能进行类型转换的。为了将一个类转换为另一个类型,需要使用特殊的方法(后面将会学习到如何在父子类之间进行向上/向下转型,例如,“橡树”可以转换为“树”,反之亦然。而对于“岩石”是无法转换为“树”的)。
976976
@@ -1029,28 +1029,28 @@ public class RoundingNumbers {
10291029Math.round(above): 1
10301030Math.round(below): 0
10311031Math.round(fabove): 1
1032- ` Math.round(fbelow): 0
1032+ Math.round(fbelow): 0
10331033```
10341034
1035- 自从 ` round() ` 方法成为了 ` java.lang ` 的一部分,我们就无需通过 ` import ` 来使用了 。
1035+ 因为 ` round() ` 方法是 ` java.lang ` 的一部分,所以我们无需通过 ` import ` 就可以使用 。
10361036
10371037<!-- Promotion -->
10381038### 类型提升
10391039
10401040
1041- 你会发现,如果我们对小于 ** int** 的基元数据类型 (即 ** char** 、** byte** 或 ** short** )执行任何算术或按位操作,这些值会在执行操作之前类型提升为 ** int** ,并且结果值的类型为 ** int** 。若想重新使用到较小的类型 ,必须使用强制转换(由于重新分配回一个较小的类型,结果可能会丢失精度)。通常,表达式中最大的数据类型是决定表达式结果的数据类型。** float** 型和 ** double** 型相乘,结果是 ** double** 型的;** int** 和 ** long** 相加,结果是 ** long** 型。
1041+ 你会发现,如果我们对小于 ** int** 的基本数据类型 (即 ** char** 、** byte** 或 ** short** )执行任何算术或按位操作,这些值会在执行操作之前类型提升为 ** int** ,并且结果值的类型为 ** int** 。若想重新使用较小的类型 ,必须使用强制转换(由于重新分配回一个较小的类型,结果可能会丢失精度)。通常,表达式中最大的数据类型是决定表达式结果的数据类型。** float** 型和 ** double** 型相乘,结果是 ** double** 型的;** int** 和 ** long** 相加,结果是 ** long** 型。
10421042
10431043
10441044<!-- Java-Has-No-sizeof -->
10451045## Java没有sizeof
10461046
10471047
1048- 在 C/C++ 中,经常 需要用到 ` sizeof() ` 方法来获取数据被分配的字符大小。在 C/C++ 中,` sizeof() ` 最常见的应用就是“移植”。不同数据在不同机器上可能有不同的大小,所以在进行大小敏感的运算时,程序员必须对这些类型有多大做到心中有数。例如,一台计算机可用 32 位来保存整数,而另一台只用 16 位保存。显然,在第一台机器中,程序可保存更大的值。所以,移植是令 C/C++ 程序员颇为头痛的一个问题。
1049-
1050- Java 不需要` sizeof() ` 方法来满足这种需求,因为所有类型的默认大小在不同平台间是相同的。我们不必考虑这个层次的移植问题 —— Java 本身就是一种“与平台无关”的语言。
1048+ 在 C/C++ 中,经常需要用到 ` sizeof() ` 方法来获取数据项被分配的字节大小。C/C++ 中使用 ` sizeof() ` 最有说服力的原因是为了移植性,不同数据在不同机器上可能有不同的大小,所以在进行大小敏感的运算时,程序员必须对这些类型有多大做到心中有数。例如,一台计算机可用 32 位来保存整数,而另一台只用 16 位保存。显然,在第一台机器中,程序可保存更大的值。所以,移植是令 C/C++ 程序员颇为头痛的一个问题。
10511049
1050+ Java 不需要 ` sizeof() ` 方法来满足这种需求,因为所有类型的大小在不同平台上是相同的。我们不必考虑这个层次的移植问题 —— Java 本身就是一种“与平台无关”的语言。
10521051
10531052<!-- A-Compendium-of-Operators -->
1053+
10541054## 运算符总结
10551055
10561056上述示例分别向我们展示了哪些基本类型能被用于特定的运算符。基本上,下面的代码示例是对上述所有示例的重复,只不过概括了所有的基本类型。这个文件能被正确地编译,因为我已经把编译不通过的那部分用注释 ` // ` 过滤了。代码示例:
@@ -1474,7 +1474,7 @@ public class AllOps {
14741474
14751475** 注意** :** boolean** 类型的的运算是受限的。你能为其赋值 ` true ` 或 ` false ` ,也可测试它的值是否是 ` true ` 或 ` false ` 。但你不能对其作加减等其他运算。
14761476
1477- 在 ** char** , ** byte** 和 ** short** 类型中,我们可以看到算术运算符的“类型转换”效果。我们必须要显式强制类型转换才能将结果重新赋值为原始类型。对于 ** int** 类型的运算则不用转换,因为默认就是 ** int** 型。虽然我们不用再停下来思考这一切是否安全,但是两个大的 int 型相乘时 ,结果有可能超出 ** int** 型的范围,这种情况下结果会发生溢出。下面的代码示例:
1477+ 在 ** char** , ** byte** 和 ** short** 类型中,我们可以看到算术运算符的“类型转换”效果。我们必须要显式强制类型转换才能将结果重新赋值为原始类型。对于 ** int** 类型的运算则不用转换,因为默认就是 ** int** 型。虽然我们不用再停下来思考这一切是否安全,但是两个大的 int 型整数相乘时 ,结果有可能超出 ** int** 型的范围,这种情况下结果会发生溢出。下面的代码示例:
14781478
14791479``` java
14801480// operators/Overflow.java
@@ -1498,7 +1498,7 @@ bigger = -4
14981498
14991499编译器没有报错或警告,运行时一切看起来都无异常。诚然,Java 是优秀的,但是还不足够优秀。
15001500
1501- 对于 ** char** ,** byte** 或者 ** short** ,混合赋值并不需要类型转换。即使为它们执行转型操作,也会获得与直接算术运算相同的结果。另外,省略类型转换可以使代码显得更加简练。总之,除 ** boolean** 以外,其他任何两种基本类型间都可进行类型转换。当我们进行向下转换类型时,需要注意结果的范围是否溢出,否则我们就很可能在不知不觉种丢失精度 。
1501+ 对于 ** char** ,** byte** 或者 ** short** ,混合赋值并不需要类型转换。即使为它们执行转型操作,也会获得与直接算术运算相同的结果。另外,省略类型转换可以使代码显得更加简练。总之,除 ** boolean** 以外,其他任何两种基本类型间都可进行类型转换。当我们进行向下转换类型时,需要注意结果的范围是否溢出,否则我们就很可能在不知不觉中丢失精度 。
15021502
15031503
15041504<!-- Summary -->
@@ -1508,11 +1508,7 @@ bigger = -4
15081508
15091509[ ^ 1 ] : 我在 * Pomona College* 大学读过两年本科,在那里 47 被称之为“魔法数字”(* magic number* ),详见 [ 维基百科] ( https://en.wikipedia.org/wiki/47_(number) ) 。
15101510
1511-
1512-
1513- [ ^ 2 ] : * John Kirkham* 说过, “自 1960 年我开始在 IBM 1620 上开始编程起,至 1970 年之间,FORTRAN 一直都是一种全大写的编程语言。这可能是因为许多早期的输入设备都是旧的电传打字机,使用了5 位波特码,没有小写字母的功能。
1514-
1515- 指数符号中的 e 也总是大写的,并且从未与自然对数底数 e 混淆,自然对数底数 e 总是小写的。 e 简单地代表指数,通常 10 是基数。那时,八进制也被程序员广泛使用。虽然我从未见过它的用法,但如果我看到一个指数符号的八进制数,我会认为它是以 8 为基数的。我记得第一次看到指数使用小写字母 e 是在 20 世纪 70 年代末,我也发现它令人困惑。这个问题出现的时候,小写字母悄悄进入了 Fortran。如果你真的想使用自然对数底,我们实际上有一些函数要使用,但是它们都是大写的。
1511+ [ ^ 2 ] : * John Kirkham* 说过, “自 1960 年我开始在 IBM 1620 上开始编程起,至 1970 年之间,FORTRAN 一直都是一种全大写的编程语言。这可能是因为许多早期的输入设备都是旧的电传打字机,使用了5 位波特码,没有小写字母的功能。指数符号中的 e 也总是大写的,并且从未与自然对数底数 e 混淆,自然对数底数 e 总是小写的。 e 简单地代表指数,通常 10 是基数。那时,八进制也被程序员广泛使用。虽然我从未见过它的用法,但如果我看到一个指数符号的八进制数,我会认为它是以 8 为基数的。我记得第一次看到指数使用小写字母 e 是在 20 世纪 70 年代末,我也发现它令人困惑。这个问题出现的时候,小写字母悄悄进入了 Fortran。如果你真的想使用自然对数底,我们实际上有一些函数要使用,但是它们都是大写的。
15161512
15171513
15181514<!-- 分页 -->
0 commit comments