程序中的运算(运算符)

2023-01-24 0 707

提到运算,你可能会立即想到加、减、乘、除四则运算以及“九九乘法表”。想当年背诵“九九乘法表”,那简直是一场噩梦啊!时隔多年,心中依然隐隐作痛。不过,现在学习使用编程语言来进行运算,要比我们自己用公式和“九九乘法表”高效的多,而且,也容易得多,因为真正的运算过程都交给机器处理,我们只需要提供数据和计算规则就行了。

紧接着上次课程的内容,现在我们学习在 Java 语言中如何进行数据的运算。怀揣着孩子一样的好奇,看看 Java 是怎么把数据一步一步运算出结果的。

以下是这节课的主要知识点:

十八般武艺:Java中的运算符算术运算:先学会舞刀弄枪赋值运算:给技能加个点比较运算:是骡子是马,拉出来遛遛逻辑运算:谁是谁非,总要有个说法三目运算:选择困难症的终极答案

第一关 十八般武艺:Java中的运算符

拥有程序思维的第一步,就是要学会用计算机、或者说编写程序帮我们处理数据,而不是我们自己动手。Java 语言中有很多进行数据运算的方式,就如前面所说,包括但不限于:算术运算、比较运算、逻辑运算、赋值运算、三目运算等。每一种运算方式,又都包含了很多的运算符,我把这些运算符形象的称为“十八般武艺”,学习 Java 中的运算,就是学习这些运算符的使用,也就是修炼这“十八般武艺”的过程。

程序中的运算(运算符)

运算符,顾名思义,就是对数据(常量和变量)进行运算的符号。我们把数据用运算符连接起来,就构成了可以进行运算的表达式。比如 1 + 2、3 * 4 等等。看看这行代码:

public class Test{ public static void main(String[] args) { int number = 1 + 2; // 使用加号(+)将常量1和2连接起来,构成了一个加法表达式,并把运算结果赋值给变量number System.out.println(number); // 输出number的值 } }

上面的式子就是使用运算符加号(+)将常量 1 和 2 连接起来,构成了一个加法表达式,并把运算结果赋值给变量 number,不出意外的话,打印结果应该是:

3

事实上,参与运算的数据可能会有很多,也有可能是变量、常量等混合在一起进行运算,比如(接上面代码):

public class Test{ public static void main(String[] args) { int number = 1 + 2; // 使用加号(+)将常量1和2连接起来,构成了一个加法表达式,并把运算结果赋值给变量number System.out.println(number); // 输出number的值 int count = number + 10; // 变量和常量同时参与运算 System.out.println(count); // 输出计算结果 } }

打印结果:

13

除此之外,运算的方式也有很多,加法、减法、取余(取模)、比较运算等等,但它们都有一个共同的特点:每个表达式都会有一个运算结果。我们根据表达式运算结果的数据类型,将表达式的类型进行归纳分类,比如:

整型表达式:运算结果为整数。比如: 1 + 2、10 * 20、5 – 3,它们的运算结果都是整数

浮点型表达式:运算结果为浮点数。比如:3.14 * 2、0.618 + 0.382、3.0 / 1,它们的运算结果都是浮点数

布尔型表达式:运算结果为布尔类型的值。比如:2 > 1、(20-10) < 15,它们的运算结果都是布尔型:要么true、要么false。

练好了运算符、表达式的基本功,现在,我们可以开始学习真正的武艺了。

第二关 算术运算(上):学会舞刀弄枪

先来几个简单的招式,好好复习我们小学时期的算术运算。Java 中的算术运算符【大概、也许】有七种:

程序中的运算(运算符)

前面四个运算符还算常见:+、 -、 *、 / ,虽然乘号(*)和除号(/)跟我们以前见到的长得不一样,但并不难理解。百分号(%)在这里是“取余”、“取模”的意思,也就是说,使用百分号(%)可以得到数字 7 除以 3 之后的余数:1。而 ++ 和 — 就比较陌生了,它们分别代表数据 “自增1” 和 “自减1”,这种运算是我们以前没见过的,接下来,我手把手教你每个招式——运算符的用法。

2.1 加、减、乘、除

先学会舞刀弄枪——四则运算的用法,上代码:

public class Test{ public static void main(String[] args) { int num1 = 3; int num2 = 4; int num3 = 5; int num4 = 10; // 1.加法运算 int add = num1 + num2; // 2.减法运算 int subtract = num2 – num1; // 3.乘法运算 int multiply = num2 * num3; // 4.除法运算 int divide = num4 / num3; // 分别输出运算结果 System.out.println(add); // 输出加法计算结果 System.out.println(subtract); // 输出减法计算结果 System.out.println(multiply); // 输出乘法计算结果 System.out.println(divide); // 输出除法计算结果 } }

输出结果:

7 1 20 2

运算结果完全没有新意。如果你把上面的数据类型换成其它类型的整数,结果也不会有什么意外——如果不小心翻车,请点击传送门。但是如果换成浮点数,可能会遇到惊喜——Java 中的浮点数运算不够精确,快去试一试,看你能不能找到彩蛋!

除法运算有个细节要注意:如果相除的两个数进行运算,除不尽怎么办?猜想一下,下面这个行代码会得到什么结果:

System.out.println(7 / 3); // 7 / 3结果是什么2.333还是2还是1

看结果:

public class Test{ public static void main(String[] args) { System.out.println(7 / 3); } }

结果居然是2!为什么会这样?

切记一点:除法运算符( / ),得到两个数据相除的商,在 Java 语言中,整数除以整数结果还是整数,如果除不尽,会舍弃余数。也就是说,7 / 3 的商是2,余数为1,因为参与运算的被除数、除数都是整数(int类型),所以计算结果还是整数,舍弃了余数部分,结果是2。

是不是有一种恍然大悟的感觉。这是 Java 中的运算与我们以前的认知第一个不一样的地方。

考一考:算术运算符

下面代码的运算结果是什么?

public class Test{ public static void main(String[] args) { int int1 = 10; long lon1 = 20; System.out.println(lon1 + int1 * (int1 + lon1) / lon1); } }

A. 30

B. 45

C. 35

D. 300

答案:C

解析:依然遵循这样的规则——“先乘除后加减,有括号先算括号里面的”,所以答案是35

2.2 取模、自增(++)和自减(–)

再教你三个进阶招式(%、 ++、 –):

public class Test{ public static void main(String[] args) { int num1 = 3; int num2 = 4; int num3 = 5; int num4 = 10; int remainder = num3 % num1; // 取模/取余运算,5对3取模,结果是? System.out.println(remainder); // 输出取模运算结果 num2++; // num2自增1 num4–; // num4自减1 System.out.println(num2); // 输出自增之后的运算结果 System.out.println(num4); // 输出自减之后的运算结果 } }

输出结果:

2 5 9

百分号(%)是取模运算,也叫取余运算,是除法运算的一种扩展,只不过除法运算得到的结果是商,而取模运算得到的结果是余数。如果两个数进行取模运算,结果是0,意味着什么?没错,这就是整除的效果,所以,取模运算(%)可以用来判断两个数是否能够整除,也就是说,被除数是除数的倍数。

加加(++)和减减(–)运算是让变量进行自增或自减。这里要注意,不能将这两个运算符直接使用到常量上,比如下面的代码是错误的:

1++; // 不允许常量自增或自减

思考一下,为什么?那是因为常量的概念,规定了它不能够被修改,所以,如果你想要获得2,那么直接使用字面值常量2就行了,完全不需要使用另一个常量进行运算。还有个细节,上面的代码,也可以把 ++ 和 — 放到变量的前面,这样的运算结果是一样的(放在变量前、后的代码不能同时存在,否则数据会进行两次运算哦):

来,我们试试把++和–写到前面

++num2; // num2自增1 –num4; // num4自减1 public class Test{ public static void main(String[] args) { int num1 = 3; int num2 = 4; int num3 = 5; int num4 = 10; int remainder = num3 % num1; // 取模/取余运算,5对3取模,结果是? System.out.println(remainder); // 输出取模运算结果 // num2++; // num2自增1 // num4–; // num4自减1 ++num2; // num2自增1 –num4; // num4自减1 System.out.println(num2); // 输出自增之后的运算结果 System.out.println(num4); // 输出自减之后的运算结果 } }

输出结果没有变化:

5 9

当然,加加(++)和减减(–)也可以像别的运算符那样,把结果赋值给一个新的变量,就像这样:

public class Test{ public static void main(String[] args) { int num1 = 3; int num2 = 4; int num3 = 5; int num4 = 10; int num5 = num2++; // 猜想:num2自增1,然后赋值给新变量num5 int num6 = num4–; // 猜想:num4自减1,然后赋值给新变量num6 System.out.println(num5); // 输出自增之后的运算结果 System.out.println(num6); // 输出自减之后的运算结果 } }

输出结果:

4 10

咦,怎么还是原来的值?难道是没有发生运算?

看来我们的猜想不正确,原来

int num5 = num2++; // 结论:num2先赋值给新变量num5,然后才进行自增运算 int num6 = num4–; // 结论:num4先赋值给新变量num6,然后才进行自减运算

我把加加(++)和减减(–)放到变量前面试试:

public class Test{ public static void main(String[] args) { int num1 = 3; int num2 = 4; int num3 = 5; int num4 = 10; // int num5 = num2++; // 结论:num2先赋值给新变量num5,然后才进行自增运算 // int num6 = num4–; // 结论:num4先赋值给新变量num6,然后才进行自减运算 int num5 = ++num2; // 猜想:num2自增1,然后赋值给新变量num5 int num6 = num4; // 猜想:num4自减1,然后赋值给新变量num6 System.out.println(num5); // 输出自增之后的运算结果 System.out.println(num6); // 输出自减之后的运算结果 } }

输出结果:

5 9

终于变成正确答案了,吓得我长舒了一口气。。。

int num5 = ++num2; // 结论:num2自增1,然后赋值给新变量num5 int num6 = –num4; // 结论:num4自减1,然后赋值给新变量num6

但是,这是为什么呢?加加(++)和减减(–)放到变量前和放到变量后为什么结果不一样,到底数据有没有进行运算呢?剖析一下这两招:

单独使用:放在变量前或后结果一样参与其它运算:

在变量前,先自增(自减) ,再进行其它运算

在变量后,先以原值进行其它运算,再自增(自减)

所以,第一次把 ++ 和 — 放在变量后,是把变量原来的值赋值给新变量,而自增(或自减)的值被抛弃了,因此打印的是原来的值;第二次把 ++ 和 — 放在变量前,是把运算之后的值赋值给新变量,因此打印的是计算之后的数据,这就是加加(++)和减减(–)这两个运算符的底层原理。 ++ 和 — 是一种特殊的运算,这是再一次不同于我们以往认知的一个地方。

来,放在一起比较一下:

public class Test{ public static void main(String[] args) { int num1 = 3; int num2 = 4; int num3 = 5; int num4 = 10; int remainder = num3 % num1; // 取模/取余运算,5对3取模,结果是? System.out.println(remainder); // 输出取模运算结果 // num2++; // num2自增1 // num4–; // num4自减1 ++num2; // num2自增1 num4; // num4自减1 System.out.println(num2); // 输出自增之后的运算结果 System.out.println(num4); // 输出自减之后的运算结果 // int num5 = num2++; // num2赋值给新变量num5,然后自增1 // int num6 = num4–; // num4赋值给新变量num6,然后自减1 int num5 = ++num2; // num2自增1,然后赋值给新变量num5 int num6 = num4; // num4自减1,然后赋值给新变量num6 System.out.println(num5); // 输出自增之后的运算结果 System.out.println(num6); // 输出自减之后的运算结果 } }

考一考:取模运算

三位数的十位?比如:324。

答案:

public class Test{ public static void main(String[] args) { int number = 324; int noGeWei = number / 10; // 除以10,商32(Java中整数除以整数还是整数,舍弃了余数) int shiWei = noGeWei % 10; // 再对10取余,商3,余数为2,即十位 System.out.println(“324的十位数是:” + shiWei); } }

考一考:自增(++)、自减(–)运算

下面代码的运算结果是什么?

public class Test{ public static void main(String[] args) { int a = 5; a++; int b = ++a; System.out.println(“a: “ + a); System.out.println(“b: “ + b); } }

A. 5,5

B. 5,6

C. 6,6

D. 6,7

E. 7,6

F. 7,7

答案:F

解析:变量 a 自增了两次,所以是7

变量 b 是在变量 a 第二次自增之后被赋值的,因为加加(++)在变量前(第四行代码),所以 b 也是7

# 第三关 算术运算(下):学会舞文弄墨

3.1 字符型(char)的算术运算

基础操作太简单,敢不敢玩点高级的?

来一波神奇操作:

int ch = a + 0; // 字符型数据进行运算结果是

先不着急看结果,你能不能看懂上面的代码?想要弄明白上面的代码,必须解决下面两个问题:

为什么字符型数据可以进行算术运算?字符型数据是如何进行运算的?

上次课我们提到过,字符数据类型(char)代表世界上所有国家和地区的文字、数字、特殊符号等,而这些字符数据,在计算机中是如何存储的呢?其实,计算机中所有数据的存储和运算都是以二进制形式进行的。也就是说,计算机会把所有的数据都转化成数值,然后将该数值以二进制的形式存储在磁盘上,也因此,进行运算的过程,是以二进制的形式进行的。你想知道二进制数据的运算过程?想得美,我是不会教的。回到上面的问题,你需要知道的是:

每个字符型数据在计算机中都是一个数值,所以它可以进行算术运算。既然字符型数据是数值,就把它对应的数值拿过来进行运算好了。

那么问题来了,每个字符对应的数值是多少呢?秀一波操作:

public class Test{ public static void main(String[] args) { System.out.println(a + 0); // 任意数值 + 0 = 数值本身 System.out.println(0 * 1); // 任意数值 * 1 = 数值本身 } }

算术运算符的加法和乘法,都有特殊技巧:任意数值 + 0 = 数值本身;任意数值 * 1 = 数值本身。所以,上面的输出结果是:

97 48

也就是说,字符型字面值常量 a 和 0 在计算机中存储的数值分别是97和48。所以,这两个字符相加的结果是:

145

这就是字符型(char)的算术运算,归根结底,就是把 char 类型的字符转成了 int 型的数值进行运算的。

如果有个表,能随时查阅常见的字符和对应的数值就好了!

美国信息交换标准码对照表(ASCII码对照表,读音“阿斯key”),里面记录了常见字符和对应数值的对照关系,拿走不谢:

[ASCII码对照表.pdf] 资料/ASCII码对照表.pdf

太多记不住?没关系,常用的字符 a – z、A – Z、0 – 9 对应的数值都是连续的,只记住它们的开始和结尾字符对应的数值就行了:

程序中的运算(运算符)

怎么样老铁,这服务还算到位不?

到底为止,Java 语言的八种基本数据类型,只剩下布尔(boolean)类型没有涉及。你以为要讲 boolean 类型了?偏不!因为:

Java 语言中,布尔类型(boolean)不参与算术运算。

咳咳。

3.2 数据类型转换

欢迎来到传送门。我是机器人砰砰,有何指示?

“代码翻车了,都是你教的,你自己看:

程序中的运算(运算符)

“不就是个编译错误嘛,不怕不怕哈!

我们来分析一下错误信息:

Incompatible types. // 不兼容的类型 Required: byte // 需要 byte 类型 Found: int // 却找到 int 类型

看起来好像是类型不匹配的问题:程序需要一个 byte 类型,却得到了一个 int 类型。”

“可我的代码里根本没有用 int 类型啊,这是怎么肥四?”

“是这样的,byte、short 这两种类型在运算的时候,会先把它们转成 int,然后再进行运算,运算结果也是 int,int 类型就是这么来的。”

“那现在怎么办?”

“需要把运算结果进行数据类型转换。就像这样:

// 强制类型转换:把 b2 – b1 的结果进行转换,从 int 类型转成 byte 类型 byte b3 = (byte)(b2 b1);

这就是强制类型转换操作。把 b2 – b1 的结果进行转换,从 int 类型转成 byte 类型。由于 int 类型的范围大于 byte 类型,所以叫“强制类型转换”,也叫“显式类型转换”;反过来,如果是从 byte 类型转成 int 类型,叫“自动类型转换”,也叫“隐式类型转换”,因为这个过程是默认发生的。比如下面的代码,就是隐式类型转换:

byte x = 3; int y = x; // byte 类型的变量 x 赋值给 int 类型的变量 y隐式类型转换

隐式转换是自动发生的,不需要我们进行任何操作。Java 语言中的八种基本数据类型,除了布尔类型(boolean)不参与算术运算之外,其它的七种类型自动转换的顺序是这样的:

程序中的运算(运算符)

之所以是这样的顺序,是因为它们各自的表示范围不同,从小到大依次是:byte、short、char、int、long、float、double。小类型与大类型一起运算,除了 byte、short、char 这三种类型或默认提升成 int 类型进行运算之外,其它情况都会自动将小类型提升成大类型,然后再进行运算,运算结果也是大类型。看看这行代码:

public class Test{ public static void main(String[] args) { System.out.println(7 / 3.0); // 除法运算,被除数是整数,除数是浮点数,结果是几?什么数据类型? } }

输出:

2.3333333333333335

结果是一个无限循环小数,这个结果很有意思:第一:结果是浮点数;第二,结果不是2,跟前面遇到的情况不同;第三,结果最后一位如果是四舍五入的值,应该是3而不是5。解决这三个问题:第一,数字7默认是 int 类型,3.0 是 double 类型,根据上面的自动类型转换图示,运算过程会将 int 类型的7转成 double类型,所以结果也是 double 类型;第二,既然运算结果是 double 类型,那么除不尽的部分,也就是小数点后面的值,不会被舍弃,所以结果不是2;第三,在 Java 语言中,浮点数参与运算,很有可能会造成精度的损失,这是 Java 语言浮点数运算的一个缺陷,所以小数点最后的数值是不可预知的。

除此之外,强制类型转换也并非是万能的,甚至,有的时候可能会发生意想不到的结果。比如这段代码:

public class Test{ public static void main(String[] args) { int first = 131; int second = 1; byte bys = (byte)(first – second); // 将两个 int 类型的值进行运算,然后将结果强转为小类型 byte System.out.println(bys); // 输出结果 } }

输出:

-126

怎么会这样?难道不应该是130吗,居然还是个负数?

那是因为,byte 类型只能表示 -128 到 127 之间的数据,超出这个范围的数据,只能继续从 -128算起,所以,将 int 类型的130转成 byte 类型的数据运算过程是这样的:

127 + 1 = -128 // 130 = 127 + 3 -128 + 1 = -127 -127 + 1 = -126 // 最终结果是 -126。

最终结果是 -126。

多种不同的数据类型的好处在于,可以根据内存和计算量的大小进行选择,缺点也很明显,不同类型的数据进行运算时,有可能发生严重的兼容性问题。Java 是一种强类型的语言,这意味着两点:一,每一种数据都必须有非常清晰的类型;二、不同类型的数据进行运算,必须保证彼此的兼容性。不同类型的数据之间可能会进行运算,而这些数据取值范围不同,存储方式不同,直接进行运算可能会造成数据损失,所以当且仅当大类型数据可以转换为小类型数据(而不会发生精度损失)时,才进行转换。关于数据类型转换,你需要知道的:

程序中的运算(运算符)

强制类型转换操作是要把运算“结果”转成小类型,而不是只转换变量,这是操作中容易出现的错误,为此,进行强转时一定要注意格式:

小类型 变量名 = (小类型)大类型数据;

参考完整代码:

public class Test{ public static void main(String[] args) { byte b1 = 3; byte b2 = 4; // byte b3 = b2 – b1; // 报错 // 强制类型转换:把 b2 – b1 的结果进行转换,从 int 类型转成 byte 类型 byte b3 = (byte)(b2 b1); System.out.println(b3); // 输出结果 /* 隐式转换 */ byte x = 3; int y = x; // 将 byte 类型的变量 x 赋值给 int 类型的变量 y,隐式类型转换 /* 多数据类型参与运算,结果是几?什么数据类型? */ System.out.println(7 / 3.0); // 除法运算,被除数是整数,除数是浮点数,结果是 2.3333333333333335 /* 数据太大,小类型接收不了 */ int first = 131; int second = 1; byte bys = (byte)(first second); // 将两个 int 类型的值进行运算,然后将结果强转为小类型 byte System.out.println(bys); // 输出结果:-126 } }

考一考:强制类型转换

下面的代码运算结果是什么?

public class Test{ public static void main(String[] args) { short s1 = 20; short s2 = 30; short s3 = (short)s2 s1; System.out.println(s3); } }

A. 20

B. 30

C. 10

D. 编译错误

答案:D

解析:强转操作仅仅对变量 s2 有效,所以 (short)s2 – s1 的结果还是int 类型,直接赋值给short 类型的 s3,编译报错

3.3 加号(+)的骚操作

加号(+)除了作为算术运算符之外,还有一个骚气蓬勃的技能,在开发中使用频率非常高,那就是作为字符串连接符来使用:

public class Test{ public static void main(String[] args) { int age = 10; String desc = “我的年龄是”; System.out.println(desc + age); // 直接使用加号,将字符串和数值连接起来 } }

输出结果:

我的年龄是10

这里的加号起到字符串拼接的作用,所以,它现在是字符串连接符。除了用在两个变量中间,常量也是通吃:

public class Test{ public static void main(String[] args) { System.out.println(“我是一个” + “小小的石头。”); // 直接使用加号,将两个字符串连接起来 } }

输出结果:

我是一个小小的石头。

可问题是,为什么前面的算术运算可以得到数值的结果,到这里却是字符串了呢?

这就是加号(+)的骚操作了。编译器会自动识别加号(+)两边的数据类型,如果有任意一个数据是字符串类型,那么此时加号(+)就是字符串连接符,否则就是算术运算符,这就是根源。

考一考:加号(+)的特殊用法

下面代码的执行结果是什么?

public class Test{ public static void main(String[] args) { int a = 3; int b = 5; char c = 1; String s = “A”; System.out.println(a – b + c + s + c + b); } }

答案:47A15

解析:

第一个操作: a – b,算术运算,结果是 -2;

是47;

第三个操作:47 + s,字符串拼接,所以是 “47A”;

第四个操作:”47A” + c,字符串拼接,”47A1″;

第五个操作:”47A1″ + b,字符串拼接,最终结果是 47A15

3.4 算术运算小结

对算术运算符进行小结。

/ :除法运算符,得到两个数据相除的商。

特点:Java中整数除以整数结果还是整数。%:取模(取余)运算,得到两个数据相除的余数。

特点:可以用来判断两个数是否能够整除。布尔类型(boolean)不参与算术运算。加号两边是数值型数据时,进行加法运算

‘a’、‘0’等字符型数据参与运算时,用该字符在计算机中所表示的数值进行运算加号两边有任意一边是字符串时,进行字符串的拼接。++ 和 — :自增1 / 自减1

单独使用:

放在变量前或后结果一样参与运算:

在变量前,先自增(自减) ,再进行其它运算

在变量后,先以原值进行其它运算,再自增(自减)当且仅当大类型数据可以转换为小类型数据(而不会发生精度损失)时,才进行转换。注意强转的固定格式。

第四关 赋值运算:给技能加个点

在学习变量之前,我们可能并不常见“赋值”这种说法,而且,我们也不会把等号(=)说成是赋值符号。没错,这个词应该是计算机的专有词汇。在编程语言里,我们把等号(=)称作“赋值符号”,它的含义是:把右边的数据赋给左边的变量。赋值的过程就像玩游戏时给技能加点一样,原来的技能没有点亮,加点(被赋值)之后就可以使用这个技能了;原来的技能等级太低,也可以通过加点(赋一个更大的值)升级技能。赋值符号不止一种,最常见的就是由基本赋值运算符(=)结合算术运算符,这样就得到一些扩展运算符,比如:

程序中的运算(运算符)

基本赋值运算符的用法,我就不啰嗦了。来看看扩展赋值运算符这几个招式怎么玩。

public class Test{ public static void main(String[] args) { int num = 10; num += 10; // 使用 += 赋值运算符 System.out.println(num); // 输出运算后的结果 } }

输出:

20

很明显,通过加等赋值运算符(+=)的运算,变量 num 的值变成了20,这是怎么回事呢?

其实很简单,扩展赋值运算符相当于算术运算符与赋值运算符的合体,所以这些运算符既有算术运算符的功能,又有赋值运算符的功能,也就是说,下面的两行代码的效果的一样的:

// 这两行代码的效果一样 num += 10; num = num + 10;

说白了,扩展赋值运算符无非就是一种简写形式。

难道扩展赋值运算符的好处仅仅如此吗?当然不是。再看一段代码:

public class Test{ public static void main(String[] args) { short s = 1; // s = s + 1; // 报错 s += 1; // 相当于 s = (short)(s + 1); System.out.println(“s:” + s); // s:2 } }

通过前面的学习,我们知道,short 类型的变量 s 如果直接加上常量1,会变成 int 类型,所以像 s = s + 1; 这样的代码肯定会报错,但是 s += 1; 却没有任何问题,这说明了加等赋值运算符(+=)是已经进行了强制类型转换的动作的,这就是扩展赋值运算符的好处:省略了强制类型转换的操作。

当然,其它的扩展赋值运算符的特点是一样的,这里就不赘述了。

赋值运算符很好理解,你以后勤加练习就可以了!

第三关 比较运算:是骡子是马,拉出来遛遛

小的时候,常听父母说:你是哥哥/姐姐,比弟弟妹妹们大,要让着点弟弟妹妹。小时候比年龄、比个头,然后比学习成绩;长大了比工资,比男/女朋友,再然后比谁的房子大,谁的车子好;人到中年就开始比孩子,老了之后比儿子、孙子。唉,人这一生,比赢了就得意,比输了就失意。比较无处不在,真是让人又恨又爱。

比较运算符又叫关系运算符,顾名思义,就是用来比较两个值之间的大小关系的。比如下面这段代码:

public class Test{ public static void main(String[] args) { System.out.println(5 > 3); // 5 和 3比较,5 大于 3成立吗 } }

输出结果:

true

很明显,5大于3是成立的,所以结果是true。反之,如果不成立,结果就是false。所以,无论谁大谁小,谁胜谁败,比较的结果只有两种情况:true 和 false。也就是说,比较/关系运算符的运算结果都是布尔( boolean )类型,要么是true,要么是false。常见的关系运算符:

程序中的运算(运算符)

在 Java 中比较两个数据的大小关系,使用比较运算符(或者说关系运算符),这里要注意,比较两个数据是否相等,用的是双等号(==),因为在 Java 中,一个等号(=)的意思是“赋值”;而“不等于”符号是一个感叹号加一个等号(!=),一定要注意它们的写法。来看一段代码:

public class Test{ public static void main(String[] args) { // 定义三个变量 int a = 10; int b = 20; int c = 10; // ==: 等于 System.out.println(a == b); // false System.out.println(a == c); // true System.out.println(“—————————–“); // !=: 不等于 System.out.println(a != b); // true System.out.println(a != c); // false System.out.println(“—————————–“); // >: 大于 System.out.println(a > b); // false System.out.println(a > c); // false System.out.println(“—————————–“); // >=: 大于等于 System.out.println(a >= b); // false System.out.println(a >= c); // true System.out.println(“—————————–“); } }

比较运算符和赋值运算一样好理解。唯一值得注意的点,就是双等号(==)和单等号(=)的区别:

public class Test{ public static void main(String[] args) { // 定义三个变量 int a = 10; int b = 20; int c = 10; //注意: ==是判断是否相等, =是赋值的意思 System.out.println(a == b); //判断变量a和变量b的值是否相等, false System.out.println(a = b); //将变量b的值赋值给变量a, 然后打印结果, 20 } }

双等号是比较相等,单等号是赋值的意思,切记。

第四关 逻辑运算:谁是谁非,总要有个说法

一提起“逻辑运算”,我们往往想到的是复杂的运算过程,其实不然,现在介绍的逻辑运算非常简单:用于判断“并且”、“或者”、“除非”等逻辑关系。对比前面介绍的比较运算符,逻辑运算和比较运算好像都需要判断,所以它们的运算结果类型是一致的:boolean 类型,逻辑运算相对于比较运算更复杂的一点是,逻辑运算比较多个结果之间的逻辑,而比较运算一般只需要比较两个数据之间的一个结果。有点绕,举个栗子:

public class Test{ public static void main(String[] args) { int a = 10, b = 20, c = 30; // 声明三个变量 System.out.println(a > b); // 比较运算,比较两个数据的大小,只有一个结果 System.out.println(a > b && b > c); // 逻辑运算,有两个比较结果参与逻辑与(&&)运算 } }

输出:

false false

两次输出都是false。也就是说,比较运算和逻辑运算的结果,都是 boolean 类型,只不过逻辑运算符两端一般连接值为布尔类型的关系表达式,而比较运算两端连接的是数据。常见的逻辑运算符:

程序中的运算(运算符)

上面的代码使用了逻辑运算符:&&(逻辑与,并且)。它的含义是,当两边的表达式结果同时为 true 时,逻辑运算的结果才是 true,反过来说,只要任意一边的结果为 false,那么整个逻辑运算的结果就是 false。相应的,逻辑或(||)的含义是:只要有任意一边是 true,逻辑运算的结果就是 true。逻辑非(!)表示否定,它的含义是:如果原来的结果为 true,加上 ! 结果就是 false,反之,如果原来的结果为 false,加上 ! 则为 true。

韦小宝喜欢娶媳妇儿,他的要求有三个:第一,是女性;第二,要好看;第三,要身材好。来一段代码:

public class Test{ public static void main(String[] args) { /* 案例: 韦小宝找媳妇儿 要求有三个: 第一,是女性; 第二,要容貌好看; 第三,要身材好。 */ int sex = 1; // 性别。-1-女,1-男 int rongMao = 1; // 容貌。0-不好看,>0 好看 int shenCai = 0; // 身材。0-身材不好,1-身材好 // 刚开始眼光比较高, 要求既要长得好看,还要身材好 System.out.println(sex == 1); // 性别是女性 System.out.println(rongMao > 0 && shenCai == 1); // 容貌好看,身材好 System.out.println(“———————“); // 韦小宝发现那样的媳妇儿不好找, 于是降低了择偶标准, 只要长得好看, 或者身材好就行 System.out.println(sex == 1); // 性别是女性 System.out.println(rongMao > 0 || shenCai == 1); System.out.println(“———————“); // 韦小宝发现那样的媳妇儿不好找, 于是降低了择偶标准, 只要不是男的就行 System.out.println(sex != 1); // 性别不是男 System.out.println(“———————“); } }

输出结果:

true false ——————— true true ——————— true ———————

找个媳妇儿不容易,且行且珍惜。

这里再提一下逻辑非(!)的使用。它表示否定,如果原来的值是 false,加上 ! 就变成了 true,也就是说:双重否定表示肯定。当代码里有多个逻辑非(!)同时出现时,可以直接把偶数个逻辑非(!)直接去掉,因为结果不会改变:

public class Test{ public static void main(String[] args) { System.out.println(!!!!!!true); // 6个逻辑非,结果还是 true } }

输出:

true

逻辑与(&&)和逻辑或(||)分别有一个双胞胎兄弟,它们也是逻辑与和逻辑或,但是它们看起来瘦骨嶙峋,好像是营养不良:

逻辑与:&

逻辑或:|

没错,我们通常称这些逻辑运算符为:双与(&&)和单与(&);双或(||)和单或(|)。虽然这俩双胞胎兄弟看起来瘦瘦的,但是它们的功能并没有缩水。它们(单与、或)仍然具备和兄长们一样的逻辑处理功能,但是区别在于,双与(&&)和双或(||)都具备“短路”的效果,而瘦兄弟们没有:

public class Test{ public static void main(String[] args) { int a = 3, b = 5, c = 4; int d = 0; System.out.println(a > b && (d = b) > c); // 使用双与进行逻辑运算,第一个条件不成立,第二个条件是否执行 System.out.println(d); // d 的值是几,0还是5? } }

使用双与进行逻辑运算,第一个条件 a > b 是不成立的,那么第二个条件是否执行?变量 d 是否被成功赋值成 b 的值?,看运算结果,知道给变量 d 赋值的动作 (d = b) 并没有成功,也就意味着,当双与(&&)前面的条件不成立(结果是 false),后面的代码就不再执行了,这就是所谓“短路”的效果。换成单与(&)呢:

public class Test{ public static void main(String[] args) { int a = 3, b = 5, c = 4; int d = 0; System.out.println(a > b & (d = b) > c); // 使用单与进行逻辑运算,第一个条件不成立,第二个条件是否执行 System.out.println(d); // d 的值是几,0还是5? } }

输出结果:

false 5

很明显,单与(&)没有“短路”的效果。

双或(||)执行逻辑类似:前面的条件结果为 true,那么就意味着整个逻辑表达式一定为 true,所有后面的代码就不执行了;反之,前面的条件为 false,后面仍然进行判断,而单或(|)没有这样的效果。先来看双或(||)的效果:

public class Test{ public static void main(String[] args) { int a = 3, b = 5, c = 4; int d = 0; System.out.println(b > a || (d = b) > c); // 使用双或进行逻辑运算,第一个条件成立,第二个条件是否执行 System.out.println(d); // d 的值是几,0还是5? } }

输出:

true 0

换成单或(|):

public class Test{ public static void main(String[] args) { int a = 3, b = 5, c = 4; int d = 0; System.out.println(b > a | (d = b) > c); // 使用双或进行逻辑运算,第一个条件成立,第二个条件是否执行 System.out.println(d); // d 的值是几,0还是5? } }

输出:

true 5

总的来说:双与(&&)和单与(&)的逻辑判断能力是一样的,只不过双与(&&)有短路的效果,而单与(&)没有;双或(||)与单或(|)的道理相同。

考一考:逻辑运算符

下面代码的输出结果是什么?

public class Test{ public static void main(String[] args) { int x, y = 0, z = 1; boolean r1 = (x = 0) > y & (z = y) == 1; boolean r2 = y < z || (x = 2) > (z = z +1); System.out.println(r1 && r2); System.out.println(x); System.out.println(y); System.out.println(z); } }

A. true; x=0; y=0; z=0

B. true; x=2; y=0; z=1

C. false; x=2; y=0; z=1

D. false; x=0; y=0; z=2

答案:C

解析:

r1的结果:

(x = 0)是赋值动作,此时 x = y = 0,所以 (x = 0) > y 的结果是false;

(z = y)也是赋值动作,这是 z = y = 0,所以 (z = y) == 1 的结果也是false;

逻辑单与(&),由于两边都会执行,所以最后的结果是:x = y = z = 0;

r1 是 false & false,结果是 false;

r2的结果:

y < z 的结果是 false;

(x = 2) 和 (z = z + 1) 都是赋值动作,所以这时,x = 2, z = 1, 双或(||)右边的表达式结果为 true;

r2 是:false || true ,结果是true

最后的输出:r1 && r2 相当于 false && true,结果是false; x = 2, y = 0, z = 1

第五关 三目运算:选择困难症的终极答案

当你面临两难选择时,该怎么办?现在教你终极答案:三目运算。

三目运算,又叫三元运算,即由三部分组成,格式如下:

关系表达式 ? 结果1 : 结果2

三目运算符由问号(?)和冒号(:)两个特殊符号隔开了三部分:

第一部分:关系表达式,代表了你现在遇到的问题

第二部分:结果1,第一个可能的结果,代表了你面临的第一个选择

第三部分:结果2,第二个可能的结果,代表了你面临的第二个选择

世事变化无常,与其面临选择时慌不择路,倒不如将选择权交给前面的条件,条件满足,则选择第一个结果,不满足则选择第二个结果,这就是以不变应万变。所以,三目运算符的执行流程是这样的:

如果关系表达式结果为true,运算后的结果是结果1

如果关系表达式结果为false,运算后的结果是结果2

举个栗子,求两个整数的较大值:

public class Test{ public static void main(String[] args) { // 定义两个整数变量 int a = 100; int b = 20; int max = (a >= b) ? a : b ; // 通过三元运算符计算他们的较大值 System.out.println(“max: “ + max); // 将结果打印到控制台上 } }

上面第三行代码的含义是:当 a >= b 这个条件成立时,较大值为 a,否则为 b。很明显,最后的输出结果是变量 a 的值:

100

如果有三个数据呢?很简单,三目运算符是可以嵌套的:

public class Test{ public static void main(String[] args) { // 定义两个整数变量 int a = 100; int b = 20; int c = 50; int max = (a >= b) ? (a > c ? a : c) : (b > c ? b : c) ; // 通过三元运算符计算他们的最大值 System.out.println(“max: “ + max); // 将最大结果打印到控制台上 } }

最大值仍然是100:

100

虽然三目运算符可以嵌套,但是代码的可读性明显变差了,所以,开发中是不建议这样做的。你可以把它们拆开,一步一步求取最大值。

考一考:三目运算符

下面代码的运算结果是什么?

public class Test{ public static void main(String[] args) { int a = 3, b = 5; int c; int result = (c = b) > a ? c a : c + a; System.out.println(result); } }

A. 2

B. 5

C. 8

D. 编译错误

答案:A

解析:

(c = b) 是赋值操作,相当于 b > a 结果是 true;这时 a = 3, b = 5, c = 5;

由于三目运算符的问号(?)左边结果为 true,所以结果是 c – a,也就是 2。而 c + a 没有执行。

课程总结

现在,对赋值运算符、比较运算符、逻辑运算符、三目运算符做个总结:

赋值运算符:

1.常见的赋值运算符:=、+=、*=、-=、/=、%=

2.扩展赋值运算符的好处:自动强转

3.注意:

= 表示赋值操作,不是相等

== 用来表示相等

比较/关系运算符:

1.关系运算符是用来描述两个变量的大小是否为相等关系的

2.常见的关系运算符:>、<、==、!=、>=、<=

3.注意:关系运算符 == 和赋值运算符 = 的区别

逻辑运算符:

1.用于判断“并且”、“或者”、“除非”等逻辑关系的运算符

2.逻辑运算符两端连接关系表达式,或逻辑表达式

3.逻辑运算符的运算结果为布尔值:true或false

4.双与和双或都有短路效果,单与和单或没有

5.偶数个逻辑非 ! 结果不变

三目/三元运算符:

1.格式: 关系表达式 ? 结果1 : 结果2;

2.执行流程:

关系表达式结果为true,三元运算符结果为“结果1”;

关系表达式结果为false,三元运算符结果为“结果2”;

这么多运算符,到底它们的优先级顺序是怎样的呢?

一句顺口溜请好好练练~~~

单目乘除为关系,逻辑三目后赋值。

单目:单目运算符+ –(负数) ++ — 等 乘除:算数单目运算符* / % + – 为:位移单目运算符<< >> (暂时没有讲到) 关系:关系单目运算符> < >= <= == != 逻辑:逻辑单目运算符&& || & | ^ 三目:三目单目运算符A > B ? X : Y 后:无意义,仅仅为了凑字数 赋值:赋值=

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务