Java编程入门(2.5):表达式

(点击 上方公众号 ,可快速关注) 英文:math.hws.edu 译文:唐尤华 链接:http://www.importnew.com/17127.html 这一节会更深入地探讨表达式


回忆一下,表达式是一段用来表示或计算值的代码

表达式可以是文字、变量、函数调用或者这些的组合,由 +、> 这样的运算符连接到一起


表达式的值可以赋给变量,在子程序调用中用作参数,或者与其它的值组合成为更复杂的表达式


(这些值在某些情况下甚至可以被忽略掉,如果你想要这么干;这比你想象中更加常见)


表达式是编程的基础
到目前为止,本书都只是顺带提到了表达式

这一节会告诉你一个完整的故事(这里会忽略一些不常见的运算符)


表达式的基础由文字(比如674、3.14、true 和 ‘X’)、变量和函数调用组成


还记得函数是一个带返回值的子程序

你可能已经看到过一些函数的例子,比如 TextIO 类的输入程序以及 Math 类的数学计算函数


Math 类还包含了一组数学常量,在数学表达式中非常有用:Math.PI 表示 π (圆周率)、 Math.E 表示 e(自然对数的基数)


这些“常量”实际上都是 Math 类中 double 类型的成员变量


它们是数学常量的近似值,实际的精确值要求无限长度的数字

标准的 Integer 类包含了一组与 int 数据类型相关的常量:Integer.MAX_VALUE 是最大的 int,2147483647;Integer.MIN_VALUE 是最小的 int,-2147483648


类似地,Double 类包含了一些与 double 类型相关的常量


Double.MAX_VALUE 是最大的 double 值,而 Double.MIN_VALUE 是最小的正值


Double 还包含了表示无限的数值,Double.POSITIVE_INFINITY 和 Double.NEGATIVE_INFINITY


而特殊的 Double.NaN 表示未定义值

例如,Math.sqrt(-1)  的结果就是 Double.NaN


文字、变量和函数调用都是简单表达式
通过运算符可以将简单表达式组合成复杂表达式
运算符包括 + 将两个数值相加,> 比较两个值大小,等等

当表达式中包含了若干运算符时,就会出现优先顺序问题,它决定了运算符在计算式如何分组


例如,在表达式 “A + B * C” 中,B*C 会先计算,然后结果再与 A 相加


我们说,乘法  (*) 的优先级比加法  (+) 高

如果默认的优先级顺序不是你想要的,那么可以使用括号明确指定你期望的分组


例如,你可以使用”(A + B) * C” 表明希望先将 A 与 B 相加再乘以 C


这一节的后面会对Java中的运算符细节进行详细地解释

Java提供了很多运算符,我不会每个都进行介绍,但是这里会给出大多数最重要的运算符说明


2.5.1 算数运算符 算数运算符包括加法、减法、乘法和除法


它们的符号分别是 +、-、* 和  /

这些操作可以用于任意类型的数值:byte、short、int、long、float或double


(它们还可以用在 char 类型上,在这种情况下 char 被当做 integer 使用;char 会被转换成它的 Unicode 代码,并代入算数运算符操作


)当计算机实际计算时,计算中的两个值必须是相同类型

如果你的程序告诉计算机输入了两个不同类型的值,那么计算机会将其中一个转换成另一个的类型


例如,计算 37.4 + 10 这个表达式时,计算机会将整数 10 转换成实数 10.0,然后计算 37.4 + 10.0


这被称为类型转换

通常,你不必关心表达式中的类型转换,因为计算机会替你自动完成


两个数值(如果必要,会对其中一个进行类型转换)计算后的结果与其类型一致


如果两个 int 相乘,结果是 int;两个 double 相乘,结果是 double,这是可预见的结果


但是,当你使用 / 时必须非常小心

如果两个 int 相除,结果是 int;如果商是分数,会被舍去


例如,7/2 结果是 3,而不是 3.5

假设N是整型变量,那么 N/100 结果不是整数,并且当 N 大于 1 时 1/N 等于 0!这是很多常见编程错误的根源


可以把其中一个运算符改为实数,强迫计算机输出实数:比如,当计算机处理 1.0/N 时,首先把N转成实数,从而与 1.0 的类型匹配,这样得到的结果就是实数


Java还提供了计算除法操作的余数
计算余数的运算符为 %

如果 A 和 B 都是整数,那么 A % B 表示 A 除以 B 的余数


(然而,对于负数Java中的 % 与数学计算中的“取模”操作不同


在Java中,如果 A 或 B 为负数,那么 A % B 的结果也是负数)


例如,7 % 2 等于 1,34577 % 100 等于 77,50 % 8 等于 2


% 常被用来测试给定整数是奇数还是偶数:如果 N % 2 等于0,那么N是偶数;如果 N % 2 等于 1,那么N是奇数


一般来说,你可以通过 N % M 结果是否为 0,判断整数N是否可以被M取模


% 运算符也适用于实数
通常,A % B 表示从 A 中移除多个 B 后遗留的数值
例如,7.52 % 0.5 等于 0.02
最后,你还可能需要一元减法运算符,得到一个数的负数
例如,-X 等价于 (-1)*X
出于完备性考虑,Java还提供了一元加法运算符,例如 +X
景观在实际中没有任何作用

顺便说一下,+ 操作还可以用来向 String 字符串连接任意类型的值


当你使用 + 连接字符串时,这是另一种形式的类型转换,任意的对象都会自动转换为 String


2.5.2 增加和减少 你会发现,为变量加 1 是编程中极其常见的操作,为变量减 1 也一样


可以像下面这样赋值为变量加 1: counter  =  counter + 1; goalsScored  =  goalsScored + 1; x = x + 1 语句的结果是,用变量 x 原来的值加 1 后再赋值给变量 x


也可以用 x++ 得到相同的效果(或者你可能会喜欢写成 ++x)


实际上,这么写会改变 x 的值,得到的效果与 “x = x + 1″ 一样


上面的两个语句可以改为: counter++; goalsScored++; 类似地,你也可以写 x–(或 –x)从 x 中减1


也就是说,x– 与 x = x – 1 执行了相同的计算
向变量加 1 称为变量递增,从变量减 1 称为变量递减
运算符 ++ 和 — 分别被称为递增运算符和递减运算符

这些运算符可以用于任何数值类型的变量,以及char类型的变量( ‘A’++  结果是 ‘B’)


通常,运算符 ++ 和 — 用在语句中,比如 “x++;”  或 “x–;”


这些语句是改变 x 值的指令

然而,将x++、++x、x–或–x作为表达式或表达式的一部分也是合法的


也就是说,你可写出下面的代码: y = x++; y = ++x; TextIO.putln(–x); z = (++x) * (y–); “y = x++;”的效果是 x 变量加1,然后把某个值赋给 y


赋给 y 的值是表达式 x 加 1 之前的值

因此,假设 x 等于 6,那么 “y = x++;” 执行后,会将 x 变为 7,但是 y 的值被赋为 6


因为赋给 y 的是 x 加 1 前的旧值
而另一种写法,++x 会得到加1后的新值

所以,还是假设 x 等于 6, ”y = ++x;” 会把 x 和 y 都变为 7


运算符 — 也是类似的用法

特别要注意,x = x++; 这个语句没有改变 x 的值!这是因为赋给 x 的是 x 的旧值,即在语句执行前 x 的值


最终结果是,x 增加了 1,但是马上被改回了原来的值!你还需要记住,x++ 不等同 于 x + 1


表达式 x++ 改变了 x 的值,而 x + 1 没有改变

这里会让你感到困惑,我从学生程序中看到很多由此造成的bug


我的建议是:不要写这种让人困惑的代码
++ 和 — 只在单独的语句使用,不要用成表达式
在接下来的示例中,我会遵循这条建议

2.5.3 关系运算符 Java提供了布尔比例和布尔表达式表示条件,条件结果可以为 true 或 false


组织布尔表达式可以通过关系运算符比较两个值

A == B       Is A “equal to” B? A != B       Is A “not equal to” B? A < B        Is A "less than" B? A > B        Is A “greater than” B? A <= B       Is A "less than or equal to" B? A >= B       Is A “greater than or equal to” B? A == B       A“等于”B? A != B       A“不等于”B? A < B        A“小于”B? A > B        A“大于”B? A <= B       A“小于等于”B? A >= B       A“大于等于”B? 这些运算符可以比较任意数值类型的数值


还可以用来比较char类型的值

对字符串来说,< 和 >被定义为根据字符的 Unicode值 进行比较(结果并不是完全按照字母顺序比较,这可能不是你想要的


所有大写字母小于小写字母

) 使用布尔表达式时你应当记住,对计算机而言,布尔值并没有什么特殊的地方


在下一章中,你会看到如何在循环和分支中使用它们
你可以像赋值给数字变量一样,给布尔变量赋布尔值
函数会返回布尔值
顺带说一下,运算符 == 和 != 也可以用来比较布尔值
在某些情况下这是非常有用的

例如,你可以看下面这段代码: boolean sameSign; sameSign = ((x > 0) == (y > 0)); 关系运算符 <、>、<= 和 >= 不能比较String值


你可以合法地使用 == 和 != 来比较字符串,但是由于对象行为的差别,可能不会得到你期望的结果


(== 运算符可以检查两个对象的内存地址是否相同,而不是判断对象中值是否相等


对某些对象,在某种情况下,你可能想要做类似的检查——但字符串不行


下一章我会再讨论这个话题

)相反地,你要使用 equals()、equalsIgnoreCase() 和 compareTo(),这些在2.3.3章节中进行了讨论,如何比较两个字符串


另一个 == 和 != 不起作用的地方是与 Double.NaN 比较


这个常量表示 double 类型的未定义值

无论 x 的值是否为 Double.NaN,x == Double.NaN 和 x != Double.NaN 都会返回 false!要检测实数类型的值 x 是否为 Double.NaN,可以使用函数 Double.isNaN(x) 返回判断结果


2.5.4  布尔运算符 在英语中,复杂条件通过 and、or 和 not 组合在一起


例如,“If there is a test and you did not study for it…”,and、or 和 not 都是布尔运算符,在Java中也同样存在


在Java中,布尔运算符“and”表示为 &&
&& 运算符用来结合 2 个布尔值
结果还是 1 个布尔值

如果两个值都是 true,那么结果为 true;如果其中一个为 false,那么结果为 false


例如,如果 x 等于 0 并且 y 等于 0,“(x == 0) && (y == 0)” 结果为true


布尔运算符“or”在Java中表示为 ||
(由两个垂直的行字符 | 组成

)如果 A 或 B 其中一个为 true 或者都为 true,那么表达式 “A || B” 结果为 true


只有 A 和 B 同时为 false 时,“A || B” 结果为 false


运算符 && 和 || 被称为短路版本的布尔运算符
也就是说,&& 或 || 的第二个操作符不一定会计算

考虑下面这个测试 (x != 0) && (y/x > 1) 假设 x 的值实际为 0,在这种情况下,y/x 是未定义的触发运算(除0)


然而,计算机永远不会执行这个触发,因为当计算机对 (x != 0) 计算结果时,发现结果为 false


这时计算机知道  ((x != 0) && 任意表达式) 一定会为 false


因此,它不会再计算对第二个运算符求值
运算被短路,从而避免了除0的情况
(这个听起来有点偏技术性,事实也是如此
但有时候,会让编程生活更轻松些
) 布尔运算符“not”是一元运算符
在Java中用 ! 表示,写在单个运算对象的前面

例如,假设 test 是一个布尔变量,那么 test = ! test; 将会对 test 的值取反,从 true 变为 false 或者从 false 变为 true


2.5.5 条件运算符 任何优秀的编程语言都有一些漂亮的小功能


虽然不是必须的功能,但可以让你在使用时感觉很酷
Java也有,条件运算符中的三元运算符

它有 3 个操作数,有 2 个部分:,? 和 : 组合在一起


三元运算符形式如下: boolean-expression ? expression1 : expression2 计算机会检测布尔表达式的值


如果值为 true,会计算 expression1,否则计算 expression2


例如: next = (N % 2 == 0) ? (N/2) : (3*N+1); 如果N是偶数,会把 N/2 赋给 next(即 N % 2 == 0 为 true);如果N是基数,会把 (3*N+1) 赋给 next(这里的括号不是必须的,但是会让表达式更容易理解)


2.5.6  赋值运算符和类型转换 你可能已经对赋值表达式非常熟悉,使用 “=” 将表达式赋值给变量


实际上,在某种意义上 = 也是运算符,可以将它用作表达式或者作为复杂表达式一部分


表达式 A=B 与向 A 赋值的语句作用相同

因此,如果你想要把 B 的值赋给 A,同时判断值是否为 0,可以这么写: if ( (A=B) == 0 )… 通常,我会强调不要那么做! 通常,表达式中右边的类型必须和左边一致


然而,在某些情况下,计算机会对表达式的值自动转换,以匹配变量的类型


比如在数值类型 byte、short、int、ong、float、double 中,列表中靠前的类型数值可以自动转换为列表中靠后的类型


int A; double X; short B; A = 17; X = A;    // OK; A is converted to a double //OK;A被自动转换为double类型 B = A;    // illegal; no automatic conversion //非法;不能从int自动转换为short //       from int to short 在不影响语义的情况下,转换应当自动进行


任何int应当可以被转换为数值相同的 double 类型
然而,int 值中有一些超过了short 类型的合法范围

比如不能将 100000 转为 short,因为 short 的最大值是 32767. 在某些情况下,比如在不能自动转换的情况下你可能想要进行强制转换


这里,你需要使用类型转换
类型转换可以把类型名放在括号里,放在你需要转换的数值前面

例如: int A; short B; A = 17; B = (short)A;  // OK; A is explicitly type cast // OK;A可以显示地把数值转换为short类型 //      to a value of type short 你可以将任何数值类型转换为其他数值类型


然而,你应当注意,转换过程中可能会改变数值
例如,(short)100000 等于 -31072

(-31072 是通过 100000 丢掉2个字节,保留 4 个字节得到的 short 值——转换中会丢失 2 个字节的信息


) 当你将实数转为整型时,小数部分被丢掉了
例如,(int)7.9453 等于 7
另一个类型转换的例子,从 1 到 6 范围中得到随机整数

函数 Math.random() 会返回 0.0 到 0.9999… 之间的实数,因此 6*Math.random() 的结果在 0.0 和 5.999… 之间


类型转换操作符 (int) 可以用来将结果转为整形:(int)(6*Math.random())


因此,(int)(6*Math.random()) 结果会得到 0、1、2、3、4、5 之间的某个整数


要得到 1 到 6 之间的随机数,可以加 1:”(int)(6*Math.random()) + 1″


(6*Math.random() 周围的括号是必须的,因为用括号来保证运算的优先顺序;如果没有括号,类型转换运算符只能对 6 起效)


char 类型与整型几乎等价
你可以将 char 类型赋值给任意整型变量

你还可以将 0 到 65535 内的常量赋值给 char 变量


你还可以显示地在 char 和数值型之间进行类型转换,比如 (char)97 得到 ‘a’,(int)’+’ 是 43,(char)(‘A’ + 2) 等于 ‘C’


String 和其它类型之间的不能进行类型转换

任意类型转换成字符串的一种方法,可以将他们与一个空字符串连接


例如,”” + 42 可以得到字符串 “42″

但是还有更好的办法,使用 String 类中的静态方法 String.valueOf(x)


String.valueOf(x) 返回输入 x 转换得到的 String


例如,String.valueOf(42) 返回字符串 “42″


而且,如果 ch 是一个 char 变量,那么 String.valueOf(ch) 返回长度为 1 的字符串,字符串中内容是 ch 变量中的唯一一个字符


也可以将特定字符串转换为其它类型的值

例如,字符串”10″ 可以被转转换为整型值 10,而字符串 “17.42e-2″ 可以转换为 double 值 0.1742


在Java中,这些转换可以由内建方法完成

标准的 Integer类 提供了静态成员函数,可以将 String 转为 int


特别地,如果 str 是任意的 String 表达式,Integer.parseInt(str) 会试着将str的值转为int类型


例如 Integer.parseInt(“10″) 得到 int 值 10


如果 Integer.parseInt 传入的参数是非法的 int 值,那么会返回错误


类似地,标准的 Double 类提供了 Double.parseDouble方法


如果 str 是 String 类型,调用 Double.parseDouble(str) 方法会试图将 str 转为 double 类型的值


当 str 表示的是非法 double 值,就会返回错误
让我们会到赋值语句
Java有许多赋值运算符变种,用来保存类型
例如,”A += B” 等价于 “A = A + B”

除了关系运算符,每个Java运算符都可以处理 2 个操作数,这样就得到了 1 个类似的赋值运算符


例如: x -= y;     // same as:   x = x – y; 等价于:x = x – y; x *= y;     // same as:   x = x * y; 等价于:x = x * y; x /= y;     // same as:   x = x / y; 等价于:x = x / y; x %= y;     // same as:   x = x % y; 等价于:x = x % y; q &&= p;    // same as:   q = q && p;  (for booleans q and p) 等价于:q = q && p; (q和p都是布尔型) 组合式赋值运算符 += 甚至可以用于String


回忆一下,+ 运算符可以将 string 作为其中的一个操作数,表示连接操作


既然 str += x 等价于 str = str + x,那么当 += 将 string 作为操作数时,这个操作就把右边的操作数附到字符串的结尾


例如,如果 str 的值是 “tire”,那么语句 str += ’d’; 就会返回 str 值为 “tired”


2.5.7  优先规则 如果在表达式中使用了多个运算符,并且没有使用括号来显示地指定计算顺序,那么你就需要考虑优先规则来确定实际的计算顺序


(建议:不要让你自己和程序的阅读者产生困惑,大方地使用括号吧


) 下面是本章讨论过的运算符列表,按照优先级从高(第一个计算)到低(最后计算)顺序排列: Unary operators: 一元运算符:             ++, –, !, unary -, unary +, type-cast Multiplication and division: 乘法和除法:  *,  /,  % Addition and subtraction: 加法和减法:     +,  – Relational operators: 关系运算符:        <,  >,  <=,  >= Equality and inequality: 相等和不等:     ==,  != Boolean and: 布尔与:                 && Boolean or: 布尔或:                  || Conditional operator: 条件运算符:        ?: Assignment operators: 赋值运算符:        =,  +=,  -=,  *=,  /=,  %= 同一行的运算符优先级相等


在没有括号的情况下,优先级相同的运算符串在一起,一元运算符和赋值运算符的计算顺序是从右到左,而剩下的其它运算符计算顺序是从左到右


例如 A*B/C 表示 (A*B)/C,而 A=B=C 表示 A=(B=C)


技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等


点击《 值得关注的技术和设计公众号 》,发现精彩!


发表回复