第3章〓Java语言基础(下) 算法+数据结构=程序。 ——尼克劳斯·威茨(Niklaus Wirth,Pascal语言之父,1984 年图灵奖获得者) 理论永远是灰色的,而实践之树常青。 ——恩格斯(德国思想家,马克思主义创始人之一) 名家观点  熟悉Java运算符的应用: 算术运算符、关系运算符、逻辑运算符、位运算符、赋值运算符、条件运算符、其他运算符。  掌握Java表达式的计算。  熟练掌握Java流程控制语句及其应用: if…else…、switch…case…default…finally、for、while、do…while、break/continue、return。  熟练掌握Java一维数组、二维数组及其应用。  掌握数组工具类Arrays的常用方法。  掌握常见英文单词。 本章学习目标 从三峡大坝、港珠澳大桥到中国高速公路网; 从“北斗三号”全球卫星导航系统到“辽宁号”航空母舰、“东风快递”(东风17弹道导弹)和“歼20”; 从大疆无人机到C919、C929等国产大飞机; 从华为等公司的5G技术、鸿蒙操作系统、HMS服务到量子通信技术(“墨子号”); 以及中国的“新四大发明”(高速铁路、扫码支付、共享单车和网络购物)。“中国制造”正在逐步转型为“中国智造”“中国创造”,从民用产品到国防军工产品,从引进技术到输出技术,从自主创新到制定标准,鼓舞人心的“中国制造”频频刷屏,一张张有底气的大国名片背后,是一位位大国工匠奋斗的身影。 什么是工匠精神?狭义地讲,工匠精神是指匠人在制造产品时追求高品质,一丝不苟,拥有耐心与恒心的态度和精神。广义的工匠精神则是“从业人员的一种价值取向与行为表现,与其人生观和价值观紧密相连,是其从业过程中对职业的态度和精神理念”。由此,可以认为工匠精神是“从业者为追求产品、服务的高品质而具有的高度责任感、专注甚至痴迷、持之以恒、精益求精、勇于创新等精神”。 课程思政——工匠精神 在各行各业,许多品牌的背后是工匠精神。中药行业著名老字号“同仁堂”在300多年的风雨历程中,历代同仁堂人始终恪守“炮制虽繁必不敢省人工,品味虽贵必不敢减物力”的古训,培养“修合无人见,存心有天知”的自律意识,树立了制药过程中兢兢业业、精益求精的“严细精神”。 “坐而论道,不如起而行之”。应让工匠精神真正扎根在我们的精神价值和理想信念中。 3.1运算符和表达式 3.1.1机器数 1. 进位计数制 进位计数制是利用固定的数字符号和统一的规则来计数的方法,被用得最多的是十进制。在十进制中,基数为10,有0~9共计10个数码,采用“逢十进一”的规则。 二进制、八进制、十进制、十六进制的基数、数码和位权如表31所示。 表31常用进位计数制的基数、数码和位权 进制基数(逢基数进位)数码位权备注 二进制20、12n 八进制80、1、2、3、4、5、6、78n 十进制100、1、2、3、4、5、6、7、8、910n 十六进制160、1、2、3、4、5、6、7、8、9 A、B、C、D、E、F16n 所有进制数可以采用每位上的数码乘以该位的位权再求和的方法转换为对应的十进制数。举例如下: (01111000)2=0×27+1×26+1×25+1×24+1×23+0×22+0×21+0×20 =(120)10 (143.6)8=1×82+4×81+3×80+6×8-1=(99.75)10 (63.C)16=6×161+3×160+C×16-1=(99.75)10 2. 十进制数与二、八、十六进制数之间的转换 十进制数须先转换为二进制,然后将三位二进制合为一位以转换为八进制,或将四位二进制合一位以转换为十六进制。举例如表32所示。 表32十、二、八、十六进制数的转换 ←|→ 数值 位权 27 26 25 24 23 22 21 20 2-1 2-2 2-3 128 64 32 16 8 4 21 0.5 0.25 0.1252-4 十进制99.75 0 1 1 0 0 0 1 1 1 1 0 0 十六进制63.C 6 3 C 八进制143.6 1 4 3 6 注意: 二~十六进制数转换,按箭头方向取四位合一位,不满四位时整数最高位前补0(小数最低位后补0)。二~八进制数转换,从个位开始三位合一位,不满三位时整数最高位前补0(小数最低位后补0)。 3. 机器数的原码、反码和补码 (1) 正数的原码、反码和补码相同。 (2) 负数的反码符号位不变,数值部分按位取反。 (3) 负数的补码符号位不变,数值部分在反码最低位加1。 举例如下: -99的原码、反码和补码如表33所示。为简单起见,字长暂定为8位,最高位为符号位(实际上可能是32位或64位)。 表33-99的原码、反码和补码示例 机器数符号位 0正1负 数值部分7Bits 6432168421 -99的原码11100011 -99的反码10011100 -99的补码10011101 31运算符和 表达式 运算符之于程序员犹如菜刀之于厨师、宝剑之于侠客。程序员利用各种各样的运算符完成不同的运算。按运算功能划分,运算符可分为7类,如表34所示。 表34运算符分类(按运算功能) 分类运算符 算术运算符+、-、*、/、++、--、% 关系运算符<、<=、>、>=、==、!= 逻辑运算符&、&&、||、|、!、^ 位运算符&、|、~、^、>>、>>>、 赋值运算符=、+=、-=、*=、/=、&=、|=、%=、<<=、>>=、>>>= 条件运算符?: 其他运算符(类型)、.、[]、()、instanceof、new 【拓展知识31】我眼中的程序员。程序员就像一个厨师,数据就是需要加工的食材,运算符就像厨师手中的菜刀,处理过程(算法)就是厨师的煎炒烹炸,最后输出的是一盘色香味俱全的菜品。 3.1.2算术运算符 算术运算符包括+、-、* 、/、++、--、%。操作数类型要求是除逻辑类型之外的基本数据类型。需要单独说明的运算符如下,其余不再赘述。 (1) /: 当操作数均为整数类型时,商自动取整。 (2) ++和--: 前置运算先进行自增或自减运算,再使用操作数变量的值; 后置运算先使用操作数变量的值,再进行自增或自减运算。 【拓展知识32】符号“+”的三种应用。 (1) 正负号中的正号。 (2) 算术运算中的加法符号,如20+10。 (3) 字符串表达式的连接操作,只要+两侧的操作数中有一个为字符串类型,则先将另一个操作数转换为字符串类型的数据,然后进行连接操作,相当于String类的concat()方法。 【示例程序31】算术运算符演示程序(ArithOprTest.java)。 功能描述: 本程序演示了算术运算符中重要而且容易出错的操作符%、/、+。 01public class ArithOprTest { 02public static void main(String[] args) { 03System.out.println(10%3); //取余,控制台输出1 04System.out.println(10/3); //整除,控制台输出3 05System.out.println(3/6*12); //优先级自左向右,控制台输出0 06System.out.println(3.0/6*12); //取余,控制台输出6 07//System.out.println(10/0); //被0除,出错 08System.out.println(10+"*"+20); //控制台输出10*20 09System.out.println(10+20+"*"); //控制台输出30* 10System.out.println(10+20+'*'); //控制台输出72 11System.out.println("*"+10+20); //控制台输出*1020 12} 13} 注意: (1) '*'的ASCII码为42。 (2) 第7行如果去掉单行注释,将抛出异常java.lang.ArithmeticException: / by zero,程序中止运行。 3.1.3关系运算符 关系运算符包括: <、<=、>、>=、==、!=,主要用于比较两个操作数的大小。 注意: (1) 当操作数是基本数据类型时直接比较它们的值。 (1) 当操作数是引用类型时,用==比较的是对象的地址,如果要想比较对象的内容,则使用equals()方法。 3.1.4逻辑运算符 逻辑运算符包括: 与运算符&、或运算符|、短路与&&、短路或||、取反运算符!、异或运算符^。 计算机采用二进制,实现了算术运算和逻辑运算的统一。逻辑与(&)相当于算术乘(*),逻辑或(|)相当于算术加(+),异或运算(^)是不同为true,相同为false。逻辑运算符如表35所示。 表35逻辑运算符 aba&b 算术乘(a*b)a|b 算术加(a+b)!aa^b false 0false 0false 0false 0true 1false 0 false 0true 1false 0true 1true 1true 1 true 1false 0false 0true 1false 0true 1 true 1true 1true 1true 1false 0false 0 注意: true相当于1, false相当于0。 (1) &(|)用在整型(byte、short、char、int、long)之间时是位运算符,用在逻辑数据之间是逻辑运算符。 (2) 与&和短路&&、或|和短路||的两侧要求必须是布尔表达式,区别在于: 短路与判断第一个条件为false时,第二个条件不再计算和判断。短路或判断第一个条件为true时,第二个条件不再计算和判断。与和短路与、或和短路或的最大区别是运算效率不同。 【示例程序32】逻辑运算符演示程序(LogicOprTest.java)。 功能描述: 本程序演示了逻辑运算符中重要而且容易出错的操作符%、/、+。 01public class LogicOprTest { 02public static void main(String[] args) { 03boolean a=true; 04boolean b=false; 05System.out.println(a&b); //控制台输出: false 06System.out.println(a|b); //控制台输出: true 07System.out.println(!a); //控制台输出: false 08System.out.println(a^b); //控制台输出: true 09boolean b1=(2==1)|(2==2)|(2==3)|(2==4); 10System.out.println(b1); //控制台输出: true 11boolean b2=(2==1)||(2==2)||(2==3)||(2==4); 12System.out.println(b2); //控制台输出: true 13boolean b3=(2>1)&(2>3)&(4>3)&(5>4); 14System.out.println(b3); //控制台输出: false 15boolean b4=(2>1)&&(2>3)&&(4>3)&&(5>4); 16System.out.println(b4); //控制台输出: false 17} 18} 注意: (1) 第9行全部运算完才能赋值,第11行运算到2==2时结果已出运算中止、赋值,第11行效率更高。 (2) 第13行全部运算完才能赋值,第15行运算到2>3时结果已出运算中止、赋值,第15行效率更高。 3.1.5位运算符 位运算只能对byte、short、char、int、long类型的数据进行,低于int型的操作数自动转换为int(32位)。 位运算符的学习要求包含进位计算制、进制转换、原码、反码和补码等相关知识,详细请参考3.1.1节。在Java中采用补码形式进行机器数的存储,最高位为符号位(0代表+,1代表-)。Java位运算符主要包括: &(按位与)、|(按位或)、^(按位异或)、~(按位取反)、<<(左移位)、>>(带符号位右移位)、>>>(不带符号右移位,补0)。 a&b运算过程如表36所示,a|b运算过程如表37所示,a^b运算过程如表38所示,~a运算过程如表39所示,a2运算过程如表310所示。为简单起见,字长暂定为8位,最高位为符号位。 int a=0b0010_0111; //39 int b=0b0111_1101; //125 表36按位与 与运算符号位数 值 部 分运算结果 a0010011139 b01111101125 a&b0010010137 表37按位或 或运算符号位数 值 部 分运算结果 a0010011139 b01111101125 a|b 01111111127 表38按位异或 异或运算符号位数 值 部 分运算结果 a0010011139 b01111101125 a^b0101101090 a^b^b0010011139 表39按位取反 非运算符号位数 值 部 分运算结果 a0010011139 ~a11011000-40 表310左移位 左移位符号位数 值 部 分运算结果 a0010011139 ← a10100111078 数值部分左移1位,低位补0 【示例程序33】移位运算符演示(ShiftTest.java)。 功能描述: 本程序演示了a&b,a|b,a^b,a^b^b,~a,a2等移位运算的结果。 01public class ShiftTest { 02public static void main(String[] args) { 03int a=39; 04int b=125; 05int c=-27; 06System.out.println("(39)10: "+Integer.toBinaryString(39)); 07//控制台输出: (39)10: 100111 08System.out.println("(125)10: "+Integer.toBinaryString(125)); 09//控制台输出: (125)10: 1111101 10System.out.println("(-27)10: "+Integer.toBinaryString(-27)); 11//控制台输出32位二进制,最高位为符号位: (-27)10: 111111111111111111111111111 1200101 13System.out.println(a&b); //与运算 14//控制台输出: 37 15System.out.println("a&b: "+Integer.toBinaryString(a&b)); 16//控制台输出: a&b: 100101 17System.out.println(a|b); //或运算 18//控制台输出: 127 19System.out.println("a|b: "+Integer.toBinaryString(a|b)); 20//控制台输出: a|b: 1111111 21System.out.println(~a); //按位取反,包括符号位 22//控制台输出: -40 23System.out.println("~a: "+Integer.toBinaryString(~a)); 24//输出~a: 11111111111111111111111111011000 25System.out.println(a^b); //异或运算: 不同true,相同false 26//控制台输出: 90 27System.out.println(a^b^b); //a和b进行两次异或,得到a的原值 28//控制台输出: 39 29System.out.println("a<<2: "+(a<<2)); //左移位 30//扩大4倍,控制台输出: 156 31} 32} 【拓展知识33】计算机为什么采用二进制?物理电路采用二进制设计原理简单,性能稳定,抗干扰能力强; 二进制实现了算术运算和逻辑运算的统一; 二进制运算规则简单,减法可以转换为加上负数的补码,同理,乘除运算也可以转换为加法,最后的运算归结为加法和移位。二进制编码规则简单,易于加密压缩、差错控制、存储和传输等。 3.1.6条件运算符 语法格式如下: 逻辑表达式1?表达式2∶表达式3 功能: 先判断逻辑表达式1的值,若为true,则结果为表达式2的值,否则为表达式3的值。条件运算符相当于一个简单的if语句。举例如下: 01int i=10; 02System.out.println(i<5?"左岸": "右岸"); //控制台输出: 右岸 03System.out.println(i%2==1?"奇数": "偶数"); //控制台输出: 偶数 3.1.7表达式 表达式是用运算符将操作数(常量、变量和方法等)连接起来,有确定值,符合Java语法规则的式子。 3.1.8语句 Java语句是构成程序的基本单元,可以对计算机发出操作指令。一般情况下,Java语句要写到方法中,以“; ”结束。 常见的Java语句有以下几类。 (1) package、import语句。 (2) 赋值语句。 (3) 复合语句: { …; …; }。 (4) 流程控制语句: if…else,switch…case…defaut,for,while,do…while, break,continue。 (5) 方法调用语句: 例如System.out.println()。 32Java流 程控制 语句 3.2Java流程控制语句 3.2.1顺序结构 顺序结构是3种流程控制语句结构中最简单的一种,即语句按照书写的顺序依次执行。顺序结构流程图31所示。 3.2.2分支结构 分支结构又称为选择结构,它根据计算所得的判断条件表达式的值来判断应选择执行哪一个流程的分支。分支结构流程图32所示。 图31顺序结构 图32选择结构流程图 Java中提供的分支语句有if语句和switch语句。if语句可以判断布尔表达式,而switch只能判断表达式的内容。 1. if语句 if语句能根据条件从两个分支中选择一个执行。利用if语句的嵌套可以实现从多个分支中选择一个执行。 if语句的语法格式如下: if(条件表达式){ 语句1; … }[else{ 语句2; … }] 注意: (1) 条件表达式的值应为布尔类型。 (2) 建议语句块1、语句块2即使只有一句也用{}括起。 (3) 为防止出现错误,不推荐if嵌套。 (4) else子句为可选内容。 【示例程序34】计算BMI程序(BMI.java)。 功能描述: 从键盘输入身高h(m)和体重w(kg),计算体质指数BMI(Body Mass Index)。根据BMI输出体重状态(BMI<18.5: 偏廋; 18.5≤BMI<24: 正常; 24≤BMI<27: 偏胖; 27≤BMI<30: 肥胖; BMI≥30: 重度肥胖)。 01public class BMI { 02public static void main(String[] args) { 03//从键盘输入体重(kg)和身高(m) 04Scanner sc=new Scanner(System.in); 05double w=sc.nextDouble(); 06double h=sc.nextDouble(); 07//计算其BMI指数,根据BMI得出体重状态 08double bmi=w/(h*h); 09String result=""; 10if(bmi<18.5) { 11result="偏廋"; 12} 13if(bmi>=18.5 & bmi<24) { 14result="正常"; 15} 16if(bmi>=24 & bmi<27) { 17result="偏胖"; 18} 19if(bmi>=27 & bmi<30) { 20result="肥胖"; 21} 22if(bmi>=30) { 23result="重度肥胖"; 24} 25//输出BMI和体重状态。 26System.out.printf("BMI: %.2f,status: %s",bmi,result); 27} 28} 【拓展知识34】体重管理。1917年,毛泽东主席在《新青年》上发表《体育之研究》中提出: 欲文明其精神,先自野蛮其体魄。让我们每个人每天锻炼1小时,健康工作50年,幸福生活一辈子,进行有效的体重管理,建立一个健康的生活方式。 2. switch语句 switch语句用于多分支选择结构。 switch语句的语法格式如下: switch(表达式){ case 常量1∶语句1; break; case 常量2∶语句2; break; … case 常量n∶语句n; break; [default: 其他语句; break; ] } switch语句流程如图33所示。 图33switch语句流程图 注意: (1) 表达式必须是下述类型之一: int、byte、char、short、enum。JDK 1.7增加了String类型。 (2) 在case子句中给出的值必须是一个常量,且各case子句中的常量值各不相同。 (3) 如果break子句丢失,就会出现“穿越现象”(下面case条件不再判断,直接执行case后的语句)。 【示例程序35】根据年份和月份,输出该月的天数(Days.java)。 功能描述: 本程序演示了利用switch语句实现根据年份和月份,输出该月天数的功能。根据规定1、3、5、7、8、10、12月有31天,4、6、9、11月有30天,闰年的2月有29天,其他年份的2月只有28天。闰年指能被4整除不能被100整除; 或者能被400整除的年份。 01public class Days { 02public static void main(String[] args) { 03int year = 2000; 04int month = 2; 05int days = 0; 06switch (month) { 07case 1: 08case 3: 09case 5: 10case 7: 11case 8: 12case 10: 13case 12: 14days=31; 15break; 16case 4: 17case 6: 18case 9: 19case 11: 20days=30; 21break; 22case 2: 23if ((year%4==0&&year%100!=0)||year%400==0){ 24days=29; 25} else { 26days=28; 27} 28break; 29} 30System.out.printf("%d年%d月有%d天!",year,month,days); 31} 32} 【拓展知识35】公历(阳历)是以地球绕太阳公转的运动周期为基础而制定的历法。地球自转一圈为一天(24小时),地球绕太阳一圈定为一年365天5小时48分46秒。一年12个月,其中1、3、5、7、8、10、12月为31天,4、6、9、11月为30天,闰年的2月为29天,其他年份为28天。平年365天,闰年366天。每四年一闰,每满百年少闰一次,到第400年再闰,即每400年中有97个闰年。 【拓展知识36】汉历(农历、阴历)是以月亮围绕地球转动的规律来制定的历法。汉历以月亮圆缺一次的时间定做一个月,共29.5天。为了计算方便,大月被定做30天,小月29天,一年12个月,大月小月交替排列,共354天。每过2~3年加一个闰月,使汉历与地球绕太阳公转相一致。 【示例程序36】根据年份和月份,输出该月的天数(Days2.java)。 功能描述: 本程序演示了利用switch最新语法对示例程序35进行的改写,语句相对简洁。 01public class Days2 { 02public static void main(String[] args) { 03int year = 2000; 04int month = 2; 05int days = 0; 06days=switch(month){ 07case 1,3,5,7,8,10,12->31; 08case 4,6,9,11->30; 09case 2-> ((year%4==0&&year%100!=0)||year%400== 100)?29: 28; 11default->0; 12}; 13System.out.printf("%d年%d月有%s天!",year,month,days); 14} 15} 3.2.3循环结构 循环结构是指在一定条件下反复执行同一段语句的流程结构。 1. for语句 一般用于已知循环次数的情况下。for语句是当型循环,特点: 先判断,后执行; 循环体执行次数≥0; 当循环条件为真时执行。 for语句的语法格式如下: for(设定循环变量初值; 循环条件; 修改循环变量表达式){ 循环体代码 } for语句示意图如图34所示。 图34for语句示意图 2. while语句 while语句用于已知循环条件的情况。while语句也是当型循环,特点: 先判断,后执行; 循环体执行次数≥0; 当循环条件为真时执行。while语句的语法格式如下: [循环前初始化语句] while(循环条件){ 循环体 [修改循环变量的表达式] } 用for语句和while语句实现无限循环示例代码如下: 01//用for语句实现无限循环 02for(; ; ){ 03} 04//用while语句实现无限循环 05while(true){ 06} 3. do while语句 do…while语句是直到型循环,do…while语句的特点: 先执行,后判断; 循环体执行次数>=1; 当循环条件为真时执行。do…while语句的语法格式如下: [循环前初始化语句] do{ 循环体 [修改循环变量表达式] } while(循环条件) 当型循环流程图如图35所示,直到型循环流程图如图36所示。 图35当型循环流程图 图36直到型循环流程图 【示例程序37】3种方式求1+2+…+100的和(ForWhileTest.java)。 功能描述: 本程序用for、while和do…while 3种方式来求1+2+…+100的和(累加求和)。 01public class ForWhileTest { 02public static void main(String[] args) { 03//用for语句求1+2+…+100的和 04int sum=0; 05for(int i=1; i<=100; i++){ 06sum+=i; 07} 08//用while语句求1+2+…+100的和 09sum=0; 10int i=1; 11while(i<=100){ 12sum+=i; 13i++; 14} 15//用do…while语句求1+2+…+100的和 16sum=0; 17i=0; 18do{ 19sum+=i; 20i++; 21} while(i<=100); 22} 23} 【拓展知识37】求和求积。在计算机程序中累加求和,累加器(变量)先清0; 累乘求积,累乘器(变量)先置1。 33九九乘 法表和 卡拉OK 【示例程序38】在控制台输出九九乘法表(Table99.java)。 功能描述: 本程序在控制台利用双重循环输出九九乘法表,其中外层变量i从1循环到9,内层变量j从1循环到i。 01public class Table99{ 02public static void main(String[] args) { 03//九九乘法表 04for(int i=1; i<=9; i++){ 05for(int j=1; j<=i; j++){ 06//System.out.printf("%d*%d=%2d\t",i,j,i*j); 07System.out.print(i+"*"+j+"="+i*j+"\t"); 08} 09System.out.println(); 10} 11} 12} 【示例程序39】卡拉OK程序(OK.java)。 功能描述: 卡拉OK比赛,有N个评委为歌手打分,评分规则: 去掉一个最高分和一个最低分,然后取平均值作为歌手的最终得分。 编程提示: 从键盘输入第一个评委的分数score,既作为最高分max,又作为最低分min,sum保存总分。循环输入其他评委的打分: 和max比较,如果比max大,则赋值给max; 和min比较,如果比min小,则赋值给min; 累加求和存入sum。 01public class OK { 02public static void main(String[] args) { 03final int N=5; 04Scanner sc=new Scanner(System.in); 05System.out.print("请输入第1个选手的成绩: "); 06double score=sc.nextDouble(); 07double max=score; 08double min=score; 09double sum=score; 10for(int i=2; i<=N; i++) { 11System.out.print("请输入第"+i+"个选手的成绩: "); 12score=sc.nextDouble(); 13sum=sum+score; 14if(score>max) { 15max=score; 16} 17if(score0; i--){ 21if(i==(num-1)){ 22da[i]=money-da[i-1]; 23}else{ 24da[i]=da[i]-da[i-1]; 25} 26} 27return da; 28} 29public static void main(String[] args) { 30Scanner sc=new Scanner(System.in); 31System.out.print("请输入红包的金额(元)和个数,用空格分开: "); 32double money=sc.nextDouble(); 33int num=sc.nextInt(); 34int[] ia=createRedEnvelope((int)money*100,num); 35double[] da=new double[num]; 36for(int i=0; i