控制语句
控制语句:把语句组合成能完成一定功能的小逻辑模块。它分为三类:顺序、选择和循环。
学会控制语句,是真正跨入编程界的“门槛”,是成为“程序猿”的“门票”。
- “顺序结构”代表“先执行a,再执行b”的逻辑。比如,先找个女朋友,再给女朋友打电话;先订婚,再结婚;
- “条件判断结构”代表“如果…,则…”的逻辑。比如,如果女朋友来电,则迅速接电话;如果看到红灯,则停车;
- “循环结构”代表“如果…,则重复执行…”的逻辑。比如,如果没打通女朋友电话,则再继续打一次; 如果没找到喜欢的人,则再继续找。
很神奇的是,三种流程控制语句就能表示所有的事情!不信,你可以试试拆分你遇到的各种事情。实际上,任何软件和程序,小到一个练习,大到一个操作系统,本质上都是由“变量、选择语句、循环语句”组成。
实时效果反馈
1. 如下哪个不是控制语句的基本结构:
A 顺序结构
B 选择结构
C 随机结构
D 循环结构
答案
1=>C
条件判断结构(选择结构)
条件判断结构有:if
结构和switch
结构。而if结构又可以分为if单分支结构、if-else
双分支结构、if-else if-else
多分支结构。
if单分支结构
语法结构:
if(布尔表达式){ 语句块 }
新手雷区
- 如果if语句不写{},则只能作用于后面的第一条语句。
- 强烈建议,任何时候都写上{},即使里面只有一句话!
【示例】if单分支结构(掷骰子游戏)
Math类的使用
Math.random()
该方法用于产生0到1区间的double类型的随机数,但是不包括1。
int i = (int) (6 * Math.**random**()); //
产生:[0,5]
之间的随机整数
public class Test1 { public static void main(String[ ] args) { //通过掷三个骰子看看今天的手气如何? int i = (int)(6 * Math.random()) + 1;//通过Math.random()产生随机数 int j = (int)(6 * Math.random()) + 1; int k = (int)(6 * Math.random()) + 1; int count = i + j + k; //如果三个骰子之和大于15,则手气不错 if(count > 15) { System.out.println("今天手气不错"); } //如果三个骰子之和在10到15之间,则手气一般 if(count >= 10 && count <= 15) { //错误写法:10<=count<=15 System.out.println("今天手气很一般"); } //如果三个骰子之和小于10,则手气不怎么样 if(count < 10) { System.out.println("今天手气不怎么样"); } System.out.println("得了" + count + "分"); } }
执行结果如图所示:
实时效果反馈
1. 如下代码的执行结果是:
int score = 80; if(score >= 60) { System.out.print("成绩不错!"); } if(score >80) System.out.print("成绩优秀!"); System.out.print("继续加油!");
A 成绩不错!成绩优秀!继续加油!
B 成绩不错!
C 成绩不错!成绩优秀!
D 成绩不错!继续加油!
答案
1=>D
if-else双分支结构
语法结构:
if(布尔表达式){ 语句块1 }else{ 语句块2 }
当布尔表达式为真时,执行语句块1,否则,执行语句块2。也就是else部分。
【示例】if-else双分支结构
public class Test2 { public static void main(String[ ] args) { //随机产生一个[0.0, 4.0)区间的半径,并根据半径求圆的面积和周长 double r = 4 * Math.random(); //Math.pow(r, 2)求半径r的平方 double area = 3.14* r*r; double circle = 2 * Math.PI * r; System.out.println("半径为: " + r); System.out.println("面积为: " + area); System.out.println("周长为: " + circle); //若面积>=周长,输出"面积大于等于周长",否则,输出周长大于面积 if(area >= circle) { System.out.println("面积大于等于周长"); } else { System.out.println("周长大于面积"); } } }
执行结果如图所示:
条件运算符有时候可用于代替if-else。
【示例】if-else
与条件运算符的比较:使用if-else
int a = 3; int b = 4; int c2 = 0; if(a<b){ c2 = b; }else{ c2 = a; } System.out.println(c2);
【示例】if-else与条件运算符的比较:使用条件运算符
int a = 3; int b = 4; int c = a<b?b:a; //返回a和b的最大值 System.out.println(c);
实时效果反馈
1. 如下代码执行后,变量c2
的值是:
int a = 3; int b = 4; int c2 = 0; if(a<b){ c2 = b; }else{ c2 = a; } System.out.println(c2);
A 0
B 3
C 2
D 4
答案
1=>D
if-else if-else多分支结构
语法结构:
if(布尔表达式1) { 语句块1; } else if(布尔表达式2) { 语句块2; }…… else if(布尔表达式n){ 语句块n; } else { 语句块n+1; }
当布尔表达式1为真时,执行语句块1;否则,判断布尔表达式2,当布尔表达式2为真时,执行语句块2;否则,继续判断布尔表达式3······;如果1~n个布尔表达式均判定为假时,则执行语句块n+1,也就是else部分。
【示例】if-else if-else多分支结构
public class Test5 { public static void main(String[ ] args) { int age = (int) (100 * Math.random()); System.out.print("年龄是" + age + ", 属于"); //15岁以下儿童;15-24青年;25-44中年;45-64中老年;65-84老年;85以上老寿星 if (age < 15) { System.out.println("儿童, 喜欢玩!"); } else if (age < 25) { System.out.println("青年, 要学习!"); } else if (age < 45) { System.out.println("中年, 要工作!"); } else if (age < 65) { System.out.println("中老年, 要补钙!"); } else if (age < 85) { System.out.println("老年, 多运动!"); } else { System.out.println("老寿星, 古来稀!"); } } }
执行结果如图所示:
实时效果反馈
1. 如下代码,执行结果是:
int a = 70; if (a < 30) { System.out.println("成绩太差!"); } else if (age < 60) { System.out.println("不及格!"); } else if (age < 70) { System.out.println("及格!"); } else if (age < 90) { System.out.println("良好!"); } else { System.out.println("优秀!"); }
A 成绩太差!
B 及格!
C 良好!
D 优秀!
答案
1=>C
switch语句
switch多分支结构(多值情况)
语法结构:
switch (表达式) { case 值1: 语句块1; [break]; case 值2: 语句块2; [break]; … … … … … default: 默认语句块; }
switch
会根据表达式的值从相匹配的case
标签处开始执行,一直执行到break
处或者是switch
的末尾。如果表达式的值与任一case
值不匹配,则进入default
语句。switch
中表达式的值,是int
(byte
、short
、char
也可,long
不行)、枚举,字符串。
练习一
//grade表示大学年级 int grade = 1; if(grade==1) { System.out.println("大学一年级,可以放松一下,学着谈谈恋爱"); }else if(grade==2){ System.out.println("大学二年级,少玩点游戏,不空虚,不慌嘛?"); }else if(grade==3) { System.out.println("大学三年级,专业课开始了,好好学,找份好工作"); }else{ System.out.println("大四了,要毕业了。因为跟着尚学堂学习,好工作搞定!"); } switch (grade){ case 1: System.out.println("大学一年级"); break; case 2: System.out.println("大学二年级"); break; case 3: System.out.println("大学三年级"); break; default: System.out.println("大四了,要毕业了"); break; }
switch接收字符串(JDK7新特性,掌握)
【示例】JDK7.0之后可以直接使用字符串。
String str = "audi"; switch (str){ case "audi": System.out.println("我买了个奥迪车"); break; case "benz": System.out.println("我买了个奔驰车"); break; default: System.out.println("比亚迪,挺好!"); }
实时效果反馈
1. 关于switch语句的代码,执行结果是:
String str = "a"; switch (str){ case "a": System.out.print("a"); case "A": System.out.print("A"); break; default: System.out.print("*"); }
case "a"
后面没有
break
语句,程序会继续执行下一个
case
。
A a
B aA
C A
D aA*
答案
1=>B
循环结构(while)
循环结构分两大类,一类是当型,一类是直到型。
- 当型:
当布尔表达式条件为true时,反复执行某语句,当布尔表达式的值为false时才停止循环,比如:while与for循环。
- 直到型:
先执行某语句, 再判断布尔表达式,如果为true,再执行某语句,如此反复,直到布尔表达式条件为false时才停止循环,比如do-while循环。
while循环
语法结构:
while (布尔表达式) { 循环体; }
- 在循环刚开始时,会计算一次“布尔表达式”的值,若条件为真,执行循环体。而对于后来每一次额外的循环,都会在开始前重新计算一次。
- 语句中应有使循环趋向于结束的语句,否则会出现无限循环–––"死"循环。
【示例】while循环结构:求1到100之间的累加和
public class Test7 { public static void main(String[ ] args) { int i = 0; int sum = 0; // 1+2+3+…+100=? while (i <= 100) { sum += i;//相当于sum = sum+i; i++; } System.out.println("Sum= " + sum); } }
执行结果如图所示:
实时效果反馈
1. 如下代码是计算1到50的累加和,空白处应该填写:
int i = 0; int sum = 0; while (i <= 50) { _________ ; i++; } System.out.println("Sum= " + sum);
A sum++
B sum += i
C i +=sum
D sum = ++i
答案
1=>B
do-while循环
语法结构:
do { 循环体; } while(布尔表达式) ;
do-while
循环结构会先执行循环体,然后再判断布尔表达式的值,若条件为真,执行循环体,当条件为假时结束循环。do-while
循环的循环体至少执行一次。
【示例】do-while循环结构:求1-100之间的累加和
public class Test8 { public static void main(String[ ] args) { int i = 0; int sum = 0; do { sum += i; // sum = sum + i i++; } while (i <= 100); //此处的;不能省略 System.out.println("Sum= " + sum); } }
执行结果如图3-16所示:
【示例】while与do-while的区别
public class Test9 { public static void main(String[ ] args) { //while循环:先判断再执行 int a = 0; while (a < 0) { System.out.println(a); a++; } System.out.println("-----"); //do-while循环:先执行再判断 a = 0; do { System.out.println(a); a++; } while (a < 0); } }
运行结构,可以看出do-while总是保证循环体至少会被执行一次!
循环结构(for)
语法结构:
for (初始表达式; 布尔表达式; 迭代因子) { 循环体; }
- 初始化部分设置:循环变量的初值
- 条件判断部分为:布尔表达式
- 迭代因子:控制循环变量的增减
for循环在执行条件判定后,先执行的循环体部分,再执行步进。
【示例】for循环
public class Test10 { public static void main(String args[ ]) { int sum = 0; //1.求1-100之间的累加和 for (int i = 0; i <= 100; i++) { sum += i; } System.out.println("Sum= " + sum); //2.循环输出9-1之间的数 for(int i=9;i>0;i--){ System.out.print(i+"、"); } System.out.println(); //3.输出90-1之间能被3整除的数 for(int i=90;i>0;i-=3){ System.out.print(i+"、"); } System.out.println(); } }
执行结果如图所示:
【示例】逗号运算符
public class Test11 { public static void main(String[ ] args) { for(int i = 1, j = i + 10; i < 5; i++, j = i * 2) { System.out.println("i= " + i + " j= " + j); } } }
执行结果如图所示:
【示例】无限循环
public class Test12 { public static void main(String[ ] args) { for ( ; ; ) { // 无限循环: 相当于 while(true) System.out.println("北京尚学堂"); } } }
编译器将while(true)
与for(;;)
看作同一回事,都指的是无限循环。
【示例】初始化变量的作用域
public class Test13 { public static void main(String[] args) { for(int i = 1; i < 10; i++) { System.out.println(i+" "); } System.out.println(i);//编译错误,无法访问在for循环中定义的变量i } }
实时效果反馈
1. 使用for循环,计算1到100的累加和,空白处应该填写:
//求1-100之间的累加和 int sum = 0; for (_____; _____; ____) { sum += i; }
A int i=0
i<=100
i++
B i=0
i<=100
i++
C int i=0
i++
i<=100
D i=0
i++
i<=100
答案
1=>A
嵌套循环
循环语句内部,再写一个或多个循环,称为嵌套循环。一般工作中多见的就是两层。
【示例】嵌套循环
public class Test14 { public static void main(String args[ ]) { for (int i=1; i <=5; i++) { for(int j=1; j<=5; j++){ System.out.print(i+" "); } System.out.println(); } } }
执行结果如图所示:
【示例3-15】使用嵌套循环实现九九乘法表
public class Test15 { public static void main(String args[ ]) { for (int i = 1; i < 10; i++) { // i是一个乘数 for (int j = 1; j <= i; j++) { // j是另一个乘数 System.out.print(j + "*" + i + "=" + (i * j < 10 ? (" " + i * j) : i * j) + " "); } System.out.println(); } } }
执行结果如图所示:
break语句和continue语句
-
break
用于强行退出整个循环 -
continue
用于结束本次循环,继续下一次
【示例】break语句
//产生100以内的随机数,直到随机数为88时终止循环 public class Test16 { public static void main(String[ ] args) { int total = 0;//定义计数器 System.out.println("Begin"); while (true) { total++;//每循环一次计数器加1 int i = (int) Math.round(100 * Math.random()); //当i等于88时,退出循环 if (i == 88) { break; } } //输出循环的次数 System.out.println("Game over, used " + total + " times."); } }
执行结果如图所示:
continue语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。
【示例】continue语句
//把100~150之间不能被3整除的数输出,并且每行输出5个 public class Test17 { public static void main(String[ ] args) { int count = 0;//定义计数器 for (int i = 100; i < 150; i++) { //如果是3的倍数,则跳过本次循环,继续进行下一次循环 if (i % 3 == 0){ continue; } //否则(不是3的倍数),输出该数 System.out.print(i + "、"); count++;//每输出一个数,计数器加1 //根据计数器判断每行是否已经输出了5个数 if (count % 5 == 0) { System.out.println(); } } } }
执行结果如图所示:
带标签的continue语句
“标签”是指后面跟一个冒号的标识符,例如:label:
。对Java来说唯一用到标签的地方是在循环语句之前。
goto有害
论中,最有问题的就是标签,而非goto
,随着标签在一个程序里数量的增多,产生错误的机会也越来越多。但Java标签不会造成这方面的问题,因为它们的活动场所已被限死,不可通过特别的方式到处传递程序的控制权。
【示例】带标签的continue
//控制嵌套循环跳转(打印101-150之间所有的质数) public class Test18 { public static void main(String args[ ]) { outer: for (int i = 101; i < 150; i++) { for (int j = 2; j < i / 2; j++) { if (i % j == 0){ continue outer; //符合某条件,跳到外部循环继续 } } System.out.print(i + " "); } } }
执行结果如图所示:
实时效果反馈
1. 如下关于break、continue的说法,错误的是:
A break
用于强行退出整个循环
B continue
用于结束本次循环,继续下一次
C break
用于强行退出switch语句
D continue
用于结束整个循环,继续循环后面的代码
答案
1=>D
方法
语句块
语句块(也叫复合语句)。语句块中定义的变量只能用于自己,外部不能使用。
语句块可以使用外部的变量,而外部不能使用语句块的变量;
【示例】语句块
public class Test19 { public static void main(String[ ] args) { int n; int a; { int k; int n; //编译错误:不能重复定义变量n } //变量k的作用域到此为止 } }
方法
- 方法(method):一段用来完成特定功能的代码片段,类似于其它语言的函数(function)。
- 方法用于定义该类或该类的实例的行为特征和功能实现。
- 面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。
- 面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
方法声明格式:
[修饰符1 修饰符2 …] 返回值类型 方法名(形式参数列表){ Java语句;… … … }
方法的调用方式:
普通方法 | 对象名*.*方法名(实参列表) |
---|---|
静态方法 | 类名.方法名(实参列表) |
方法的详细说明
-
形式参数:在方法声明时用于接收外界传入的数据。(方法定义时)
-
实参:调用方法时实际传给方法的数据。 (方法调用时)
-
返回值:执行完毕后,返还给调用它的环境的数据。
-
返回值类型:事先约定的返回值的数据类型,如无返回值,则为void。
【示例】方法的声明及调用
public class Test20 { /** main方法:程序的入口 */ public static void main(String[ ] args) { int num1 = 10; int num2 = 20; //调用求和的方法:将num1与num2的值传给add方法中的n1与n2 // 求完和后将结果返回,用sum接收结果 int sum = add(num1, num2); System.out.println("sum = " + sum);//输出:sum = 30 //调用打印的方法:该方法没有返回值 print(); } /** 求和的方法 */ public static int add(int n1, int n2) { int sum = n1 + n2; return sum;//使用return返回计算的结果 } /** 打印的方法 */ public static void print() { System.out.println("北京尚学堂..."); } }
执行结果如图所示:
注意事项
-
实参必须和形参列表匹配。
-
return:终止方法运行,并返回的数据。
-
Java中传递参数,遵循值传递的原则(传递的都是数据的副本):
- 基本类型传递的是该数据值的copy值。
- 引用类型传递的是该对象引用的copy值,但指向的是同一个对象。
实时效果反馈
1. 如下关于java中方法的定义,错误的是:
A return:终止方法运行,并返回的数据
B Java中传递参数,遵循值传递的原则(传递的都是数据的副本)
C 形式参数:在方法调用时用于接收外界传入的数据。(方法调用时)
D 面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
答案
1=>C
方法的重载(overload)
重载:一个类中可以定义多个名称相同,但形式参数列表不同的方法。
新手雷区
重载的方法,实际是完全不同的方法,只是名称相同而已!
构成方法重载的条件:
-
形参列表不同的含义:形参类型、形参个数、形参顺序不同
-
只有返回值不同不构成方法的重载
如:
int a(String str){}
与void a(String str){}
不构成方法重载 -
只有形参的名称不同,不构成方法的重载
如:
int a(String str){}
与int a(String s){}
不构成方法重载
【示例】方法重载
public class Test21 {
public static void main(String[ ] args) {
System.out.println(add(3, 5));// 8
System.out.println(add(3, 5, 10));// 18
System.out.println(add(3.0, 5));// 8.0
System.out.println(add(3, 5.0));// 8.0
// 我们已经见过的方法的重载
System.out.println();// 0个参数
System.out.println(1);// 参数是1个int
System.out.println(3.0);// 参数是1个double
}
/** 求和的方法 */
public static int add(int n1, int n2) {
int sum = n1 + n2;
return sum;
}
// 方法名相同,参数个数不同,构成重载
public static int add(int n1, int n2, int n3) {
int sum = n1 + n2 + n3;
return sum;
}
// 方法名相同,参数类型不同,构成重载
public static double add(double n1, int n2) {
double sum = n1 + n2;
return sum;
}
// 方法名相同,参数顺序不同,构成重载
public static double add(int n1, double n2) {
double sum = n1 + n2;
return sum;
}
//编译错误:只有返回值不同,不构成方法的重载
public static double add(int n1, int n2) {
double sum = n1 + n2;
return sum;
}
//编译错误:只有参数名称不同,不构成方法的重载
public static double add(int n2, int n1) {
double sum = n1 + n2;
return sum;
}
}
课堂练习
1、 定义一个方法处理公司的迟到问题:
(1) 输入:迟到时间,月薪。
(2) 处理逻辑:
① 迟到1-10分钟,警告。
② 迟到11-20分钟,罚款100元。
③ 迟到21分钟-30分钟,罚款200元。
④ 迟到30分钟以上,扣除半日工资。
⑤ 迟到1小时以上,按照旷工计算,扣除3日工资。
(3) 输出:罚款金额
public class TestMethod2 {
/**
* (1)输入参数:迟到时间,月薪。
* (2)处理逻辑:
* ①迟到1-10分钟,警告。
* ②迟到11-20分钟,罚款100元。
* ③迟到21分钟-30分钟,罚款200元。
* ④迟到30分钟以上,扣除半日工资。
* ⑤迟到1小时以上,按照旷工计算,扣除3日工资。
* (3)输出罚款金额
*/
public static int late(int lateMinute,double salary){
int fakuan = 0; //罚款
if(lateMinute<11){
System.out.println("警告!!不能迟到!!");
}else if(lateMinute<21){
fakuan = 100;
}else if(lateMinute<31){
fakuan = 200;
}else if(lateMinute<61){
fakuan = (int)(salary/(21.75*2)); //21.75指的是:月平均工作日
}else{
fakuan = (int)(salary*3/(21.75));
}
System.out.println("迟到时间:"+lateMinute+",罚款:"+fakuan);
return fakuan;
}
public static void main(String[] args) {
late(45,42000);
}
}
实时效果反馈
1. 如下关于方法的重载,说法错误的是:
A 重载的方法,实际是完全相同的方法,而且名称相同
B 形参列表不同的含义:形参类型、形参个数、形参顺序不同
C 只有返回值不同不构成方法的重载
D int a(String str){}
与int a(String s){}
不构成方法重载
答案
1=>A
递归结构
- 递归是一种常见的算法思路,在很多算法中都会用到。比如:深度优先搜索(DFS:Depth First Search)等。
- 递归的基本思想就是“自己调用自己”。
递归结构包括两个部分:
- 定义递归头。 解决:什么时候不调用自身方法。如果没有头,将陷入死循环,也就是递归的结束条件。
- 递归体。 解决:什么时候需要调用自身方法。
【示例】使用递归求n!
public class Test22 { public static void main(String[ ] args) { long d1 = System.currentTimeMillis(); factorial(10); long d2 = System.currentTimeMillis(); System.out.printf("递归费时:"+(d2-d1)); //耗时:32ms } /** 求阶乘的方法*/ static long factorial(int n){ if(n==1){//递归头 return 1; }else{//递归体 return n*factorial(n-1);//n! = n * (n-1)! } } }
执行结果如图所示:
执行结果如图所示:
递归的缺陷
算法简单是递归的优点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要慎重。
比如上面的递归耗时558ms(看电脑配置)。但是用普通循环的话快得多,如下所示。
【示例】使用循环求n!
public class Test23 { public static void main(String[ ] args) { long d3 = System.currentTimeMillis(); int a = 10; int result = 1; while (a > 1) { result *= a * (a - 1); a -= 2; } long d4 = System.currentTimeMillis(); System.out.println(result); System.out.printf("普通循环费时:"+(d4 - d3)); } }
执行结果如图3-29所示:
实时效果反馈
1. 如下关于递归结构的说法,错误的是:
A 深度优先搜索(DFS:Depth First Search)就使用了递归
B 递归结构其实就是:方法自己调用自己
C 递归调用会占用大量的系统堆栈,但是内存消耗很少,可以忽略不计
D 递归必须确定什么时候不调用自身方法,否则将陷入死循环。
答案
1=>C