概述
在 Java 中,使用算术运算符+、-、*、/ 表示加、减、乘、除运算。当参与 / 运算的两个操作数都是整数时,表示整数除法;否则,表示浮点除法。整数的求余操作(有时称为取模)用 % 表示。例如,15/2 等于 7,15%2 等于 1,15.0/2 等于 7.5。
需要注意,整数被 0 除将会产生一个异常,而浮点数被 0 除将会得到无穷大或 NaN 结果。可以在赋值语句中采用一种简化的格式书写二元算术运算符。
例如,
等价于
(通常,将运算符放在赋值号的左侧,如 *= 或 %=。)
注释:可移植性是 Java 语言的设计目标之一。无论在哪个虚拟机上运行,同一运算应该得到同样的结果。对于浮点数的算术运算,实现这样的可移植性是相当困难的。double类型使用 64 位存储一个 double 数值,而有些处理器使用 80 位浮点寄存器。这些寄存器增加了中间过程的计算精度。例如,下列运算:
很多 Intel 处理器计算 x * y,并且将结果存储在 80 位的寄存器中,再除以 z 并将结果截断为 64 位。这样可以得到一个更加精确的计算结果,并且还能够避免产生指数溢出。但是,这个结果可能与始终在 64 位机器上计算的结果不一样。因此,Java 虚拟机的最初规范规定所有的中间计算都必须进行截断。这种行为遭到了数值计算团体的反对。截断计算不仅可能导致溢出,而且由于截断操作需要消耗时间,所以在计算速度上要比精确计算慢。为此,Java 程序设计语言承认了最优性能与理想结果之间存在的冲突,并给予了改进。在默认情况下,虚拟机设计者允许将中间计算结果采用扩展的精度。但是,
40 第 3 章 Java 的基本程序设计结构
对于使用 strictfp 关键字标记的方法必须使用严格的浮点计算来产生理想的结果。例如,
可以把 main 方法标记为
于是,在 main 方法中的所有指令都将使用严格的浮点计算。如果将一个类标记为strictfp,这个类中的所有方法都要使用严格的浮点计算。
实际的计算方式将取决于 Intel 处理器。在默认情况下,中间结果允许使用扩展的指数,但不允许使用扩展的尾数(Intel 芯片在截断尾数时并不损失性能)。因此,这两种方式的区别仅仅在于采用默认的方式不会产生溢出,而采用严格的计算有可能产生溢出。
如果没有仔细阅读这个注释,也没有什么关系。对大多数程序来说,浮点溢出不属于大问题。在本书中,将不使用 strictfp 关键字。
3.5.1 自增运算符与自减运算符
当然,程序员都知道加 1、减 1 是数值变量最常见的操作。在 Java 中,借鉴了 C 和 C++的实现方式,也使用了自增、自减运算符:n++ 将变量 n 的当前值加 1 ;n–– 将 n 的值减 1。例如:
n 的值将变为 13。因为这些运算符改变了变量的值,所以它的操作数不能是数值。例如,4++ 就是一条非法的语句。
实际上,这两个运算符有两种形式。上面介绍的是运算符放在操作数后面的“后缀”形式,还有一种“前缀”形式,++n。两种方式都是对变量值加 1。但在表达式中,这两种形式就有区别了。前缀方式先进行加 1 运算;后缀方式则使用变量原来的值。
我们建议不要在其他表达式的内部使用 ++,这样编写的代码很容易令人困惑,并会产生烦人的 bug。
(当然,++ 运算符作为 C++ 语言名称的一部分,也引发了有关程序设计语言的第一个笑话。C++ 的反对者认为这种语言的名称也存在着 bug,他们说:“因为只有对它改进之后,我们才有可能使用它,所以它的名字应该命名为 ++C。”)
3.5.2 关系运算符与 boolean 运算符
Java 包含各种关系运算符。其中,使用两个等号 = = 检测是否相等。例如,3 = = 7 的值为 false。
使用 != 检测是否不相等。例如,3 != 7 的值为 true。
另外,经常使用的运算符还有 <(小于)、>(大于)、<=(小于等于)和 >=(大于等于)。
3.5 运算符 41
Java 沿用了 C++ 的习惯,用 && 表示逻辑“与”、用 || 表示逻辑“或”。从 != 运算符很容易看出,! 表示逻辑“非”。&& 和 || 是按照“短路”方式求值的。如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。如果用 && 对两个表达式进行计算:
并且第一个表达式值为 false,结果不可能为真。因此,第二个表达式的值就没有必要计算了。这种方式可以避免一些错误的发生。例如,表达式:
当 x 为 0 时,不会计算第二部分。因此,若 x 为 0,1/x 不被计算,也不会出现除以 0 的错误。与之类似,对于 expression1 || expression2,当第一个表达式为 true 时,结果自动为 true,
不必再计算第二部分。
最后,Java 支持三元操作符 ?:。在很多时候,这个操作符非常有用。表达式
当条件 condition 为真时计算第 1 个表达式,否则计算第 2 个表达式。例如:
返回 x 和 y 中较小的那个值。3.5.3 位运算符
在处理整型数值时,可以直接对组成整型数值的各个位进行操作。这意味着可以使用屏蔽技术获得整数中的各个位。位运算符包括:
&(" 与 ")、|(" 或 ")、^(" 异或 ")、~(" 非 ")
这些运算符在位模式下工作。例如,如果 n 是一个整型变量,并且用二进制表示的 n 从右数第 4 位为 1,那么
返回 1 ;否则返回 0。通过运用 2 的幂次方的 & 运算可以将其他位屏蔽掉,而只保留其中的某一位。
注释:& 和 | 运算符应用于布尔值,得到的结果也是布尔值。这两个运算符与 && 和 ||的运算非常类似,只是不按“短路”方式计算。即在得到计算结果之前,一定要计算两个操作数的值。
另外,“ >>”和“ <<”运算符将二进制位进行右移或左移操作。当需要建立位模式屏蔽某些位时,使用这两个运算符十分方便:
最后,>>> 运算符将用 0 填充高位;>> 运算符用符号位填充高位。没有 <<< 运算符。
警告:对移位运算符右侧的参数需要进行模 32 的运算(除非左边的操作数是 long 类型,在这种情况下需对右侧操作数模 64)。例如,1 << 35 与 1 << 3 或 8 是相同的。
42 第 3 章 Java 的基本程序设计结构
C++ 注释:在 C 或 C++ 中无法确定 >> 操作执行的是算术移位(扩展符号位),还是逻辑移位(高位填 0)。在执行中将会选择效率较高的一种。这就是说,在 C/C++ 中,>> 运算符实际上只是为非负数定义的。Java 消除了这种含糊性。
3.5.4 数学函数与常量
在 Math 类中,包含了各种各样的数学函数。在编写不同类别的程序时,可能需要的函数也不同。
要想计算一个数值的平方根,可以使用 sqrt 方法:
注释:println 方法和 sqrt 方法存在微小的差异。println 方法操作一个定义在 System 类中的 System.out 对象。但是,Math 类中的 sqrt 方法处理的不是对象,这样的方法被称为静态方法。有关静态方法的详细内容请参看第 4 章。
在 Java 中,没有幂运算,因此需要借助于 Math 类的 pow 方法。语句:
将 y 的值设置为 x 的 a 次幂(xa)。pow 方法有两个 double 类型的参数,其返回结果也为double 类型。
Math 类提供了一些常用的三角函数:
还有指数函数以及它的反函数— 自然对数以及以 10 为底的对数:最后,Java 还提供了两个用于表示 p 和 e 常量的近似值:
提示:不必在数学方法名和常量名前添加前缀“ Math.”,只要在源文件的顶部加上下面这行代码就可以了。
例如:
在第 4 章中将讨论静态导入。
注释:在 Math 类中,为了达到最快的性能,所有的方法都使用计算机浮点单元中的例程。如果得到一个完全可预测的结果比运行速度更重要的话,那么就应该使用 StrictMath类。它使用“自由发布的 Math 库”(fdlibm)实现算法,以确保在所有平台上得到相同的结果。有关这些算法的源代码请参看 www.netlib.org/fdlibm(当 fdlibm 为一个函数提供了多个定义时,StrictMath 类就会遵循 IEEE 754 版本,它的名字将以“e”开头)。
3.5.5 数值类型之间的转换在程序运行时,经常需要将一种数值类型转换为另一种数值类型。图 3-1 给出了数值类
型之间的合法转换。
3.5 运算符 43
图 3-1 数值类型之间的合法转换
在图 3-1 中有 6 个实心箭头,表示无信息丢失的转换;有 3 个虚箭头,表示可能有精度损失的转换。例如,123 456 789 是一个大整数,它所包含的位数比 float 类型所能够表达的位数多。当将这个整型数值转换为 float 类型时,将会得到同样大小的结果,但却失去了一定的精度。
当使用上面两个数值进行二元操作时(例如 n + f,n 是整数,f 是浮点数),先要将两个操作数转换为同一种类型,然后再进行计算。
● 如果两个操作数中有一个是 double 类型,另一个操作数就会转换为 double 类型。● 否则,如果其中一个操作数是 float 类型,另一个操作数将会转换为 float 类型。
● 否则,如果其中一个操作数是 long 类型,另一个操作数将会转换为 long 类型。
● 否则,两个操作数都将被转换为 int 类型。
3.5.6 强制类型转换
在上一小节中看到,在必要的时候,int 类型的值将会自动地转换为 double 类型。但另一方面,有时也需要将 double 转换成 int。在 Java 中,允许进行这种数值之间的类型转换。
44 第 3 章 Java 的基本程序设计结构
当然,有可能会丢失一些信息。在这种情况下,需要通过强制类型转换(cast)实现这个操作。强制类型转换的语法格式是在圆括号中给出想要转换的目标类型,后面紧跟待转换的变量名。例如:
这样,变量 nx 的值为 9。强制类型转换通过截断小数部分将浮点值转换为整型。如果想对浮点数进行舍入运算,以便得到最接近的整数(在很多情况下,希望使用这种
操作方式),那就需要使用 Math.round 方法:
现在,变量 nx 的值为 10。当调用 round 的时候,仍然需要使用强制类型转换(int)。其原因是 round 方法返回的结果为 long 类型,由于存在信息丢失的可能性,所以只有使用显式的强制类型转换才能够将 long 类型转换成 int 类型。
警告:如果试图将一个数值从一种类型强制转换为另一种类型,而又超出了目标类型的表示范围,结果就会截断成一个完全不同的值。例如,(byte)300 的实际值为 44。
C++ 注释:不要在 boolean 类型与任何数值类型之间进行强制类型转换,这样可以防止发生错误。只有极少数的情况才需要将布尔类型转换为数值类型,这时可以使用条件表达式 b ? 1:0。
3.5.7 括号与运算符级别
表 3-4 给出了运算符的优先级。如果不使用圆括号,就按照给出的运算符优先级次序进行计算。同一个级别的运算符按照从左到右的次序进行计算(除了表中给出的右结合运算符外。)例如,由于 && 的优先级比 || 的优先级高,所以表达式
等价于
又因为 += 是右结合运算符,所以表达式
等价于
也就是将 b += c 的结果(加上 c 之后的 b)加到 a 上。
C++ 注释:与 C 或 C++ 不同,Java 不使用逗号运算符。不过,可以在 for 语句中使用逗号分隔表达式列表。
表 3-4运算符
运算符优先级
[ ] . ( ) ( 方法调用 )
! ~ ++ -- + ( 一元运算 ) - ( 一元运算 ) ( ) ( 强制类型转换 ) new 从右向左
*/ % 从左向右
+– 从左向右
<< >> >>> 从左向右
< <= > >= instanceof 从左向右
== != 从左向右
& 从左向右
^ 从左向右
| 从左向右
&& 从左向右
|| 从左向右
?: 从右向左
= += – = *= /= %= &= |= ^= <<= >>= >>>= 从右向左
最后
以上就是纯真白羊为你收集整理的java核心技术卷1---运算符的全部内容,希望文章能够帮你解决java核心技术卷1---运算符所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复