在还没有知道Java 选择结构的时候,我们编写的程序总是从程序入口开始,顺序执行每一条语句直到执行完左后一条语句结束。但是生活中经常需要进行条件判断,根据判断结果决定是否做一件事情,这就需要选择语句。
选择结构用于判断给定的条件,然后根据判断的结果来控制程序的流程。
主要的选择结构有: if 选择结构 和 switch 多选择结构。有如下结构:
语法结构
if(布尔表达式){
语句块
}
if语句对布尔表达式进行一次判定,若判定为真,则执行}{}中的语句块,否则跳过该语句块。流程图:
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 + "分");
新手雷区
语法结构:
if(布尔表达式){
语句块1
}else{
语句块2
}
当布尔表达式为真时,执行语句块1,否则,执行语句块2。也就是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 = Math.PI * Math.pow(r, 2);
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("周长大于面积");
}
}
}
public class Test3 {
public static void main(String[] args) {
int a=2;
int b=3;
if (a<b) {
System.out.println(a);
} else {
System.out.println(b);
}
}
}
public class Test4 {
public static void main(String[] args) {
int a=2;
int b=3;
System.out.println((a<b)?a:b);
}
}
语法结构
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 部分,流程图:
【示例3-5】if-esle if-else 多选择结构
public class Test5 {
public static void main(String[] args) {
int age = (int) (100 * Math.random());
System.out.print("年龄是" + age + ", 属于");
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("老寿星, 古来稀!");
}
}
}
语法结构:
switch (表达式) {
case 值1:
语句序列1;
[break];
case 值2:
语句序列2;
[break];
… … … … …
[default:
默认语句;]
}
switch 语句 会根据表达式的值从相匹配的case 标签处开始执行,一直执行到 break 语句处或者是switch语句的末尾。如果表达式的值与任一case 不匹配,则进入default 语句(如果存在default 语句的情况)。
根据表达式值的不同可以执行许多不同的操作。switch语句中 case 标签在JDK 1.5 之前必须是整数(long类型除外)或者枚举,不能是字符串,在JDK 1.7 之后允许使用字符串(String)。
大家要注意的是,当布尔表达式是等追判断的情况,可以使用 if -else if -else 多选择结构或者switch结构,如果布尔表达式区间判断的情况,则只能使用if-else if-else 多选择结构。
switch 多选择结构的流程图:
public class Test6 {
public static void main(String[] args) {
char c = 'a';
int rand = (int) (26 * Math.random());
char c2 = (char) (c + rand);
System.out.print(c2 + ": ");
switch (c2) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
System.out.println("元音");
break;
case 'y':
case 'w':
System.out.println("半元音");
break;
default:
System.out.println("辅音");
}
}
}
循环结构分为两个大类,一类是当型,一类是直到型。
当型
当布尔表达式条件为true时,反复执行某语句,但布尔表达式的值为false 时才停止循环,比如:while 与 for 循环
直到型
先执行某语句,再判断布尔表达式,如果为true,再执行某语句,如此反复,直到布尔表达式条件为false 时才停止循环,比如do-while 循环。
语法结构:
while (布尔表达式) {
循环体;
}
在循环刚开始的,会计算一次“布尔表达式”的值,若条件为真,执行循环体。而对于后来每一次额外的循环,都会在开始前重新计算一次。
语句中应有使循环趋向结束的语句,否则会出现无限循环——“死”循环。
while 循环结构流程图:
【示例3-7】 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);
}
}
语法结构:
do {
循环体;
} while(布尔表达式) ;
do-while 循环结构会先执行循环体,然后在判断布尔表达式的值,若条件为真,执行循环体他,条件为假时结束循环。do-while 循环体至少执行一次。do-while 循环流程图:
【示例3-8】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);
}
}
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 循环在第一次反复之前要进行初始化,即执行初始化表达式;随后,对布尔表达式进行判定,若判定结果为true,则执行循环体,否则,终止循环;最后在每一次反复的时候,进行某种形式的“步进”,即执行迭代因子。
for循环在执行条件判定之后,先执行的循环部分,在执行步进。
for循环结构的流程图:
【示例3-10】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();
}
}
Java 里能用到逗号运算符的地方屈指可数,其中一处就是for 循环的控制表达式。在控制表达式的初始化和步进控制部分,我们可以使用一系列由逗号分隔的表达式,而且哪些表达式会独立执行。
【示例3-11】逗号运算符
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);
}
}
}
初始化部分、条件判断部分和迭代因子可以为空语句,但必须以“;”分开:
【示例3-12】无限循环
public class Test12 {
public static void main(String[] args) {
for ( ; ; ) { // 无限循环: 相当于 while(true)
System.out.println("北京尚学堂");
}
}
}
编译器将 while(true)与 for(;;)看做同一回事,都指的是无限循环。
在 for 语句的初始化部分声明的变量,其作用域为整个for 循环体,不能在循环体外部使用该变量:
【示例3-13】初始化变量的作用域
在一个循环语句内部再嵌套一个或多个循环,称为嵌套循环。while、do-while 与 for 循环可以任意嵌套多层。
【示例3-14】嵌套循环
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 控制循环的流程。break 用于强行退出循环,不执行循环中剩余的语句。
【示例3-16】 break 语句
在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 语句用在循环体重,用于终止某次循环过程,即跳过 循环体重尚未执行的语句,接着进行下一次是否执行循环判定。
注意事项
【示例3-17】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();
}
}
}
}
goto 关键字很早就在程序设计语言中出现。尽管 goto 仍是Java 的一个保留关键字,但并未在Java 语言中得到正式使用;Java没有goto语句。然而,在break 和 continue 这两个关键字的身上,我们仍然能看出一些 goto 的影子——带变迁的 break 和 continue。
“标签” 是指后面很一个冒号的标识符,例如:“label:”。 对Java 来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于break 和continue 关键字通常只中断当前循环,但若随同标签使用,它们就会中断在存在标签的地方。
在 goto 有害论中,最有问题的就是标签,而非 goto,随着标签在一个程序里数量的增多,产生错误的机会也越来越多。但Java 变迁不会造成这方面的问题,因为它们的活动场所已被限死,不可通过特别的方式到处传递程序的控制权。由此也引出了一个有趣的问题:通过限制语句的能力,反而能使一项语言特性更加有用。
【示例3-18】带标签 vreak 和 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 + " ");
}
}
}
语句块(有时叫做符合语句),使用花括号括起来的任意数量的简单Java 语句。块确定了局部变量的作用于。块中的程序代码,作为一个整体,是要被一起执行的。块可以被嵌套在另一个块中,但是不能在两个嵌套的块内声明同名变量。语句块可以使用外部的变量,而外部不能使用语句块中定义的变量,因为语句块中定义的变量作用域只限于语句块。
【示例3-19】语句块
public class Test19 {
public static void main(String[] args) {
int n;
int a;
{
int k;
int n; //编译错误:不能重复定义变量n
} //变量k的作用域到此为止
}
}
方法就是一段用来完成特定功能的代码片段,类似于其他语言的函数。
方法用定义该类或该类的实例的行为特征和功能实现。方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。
[修饰符1 修饰符2 …] 返回值类型 方法名(形式参数列表){
Java语句;… … …
}
对象名.方法名称(实参列表)
方法的详细说明
【示例 3-20】方法的声明及调用
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("北京尚学堂...");
}
}
注意事项
基本类型传递的是该数据值的 copy 值。
引用类型传递的是该对象引用的 copy 值,但指向的是同一个对象。
方法的重载是指一个类中可以定义多个方法名相同,但单数不同的方法。调用时,会根据不同的参数自动匹配对应的方法。
雷区
重载的方法,实际是完全不同的方法,只是名称相同而已!
构成方法重载的条件:
如:
int a(String str){}与 void a(String str){}
如:
int a(String str){}与int a(String s){}
【示例3-21】方法重载
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 int add(int n2, int n1) {
double sum = n1 + n2;
return sum;
}
}
递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己。
利用递归可以用简单的程序解决一些父爱的问题。比如:斐波那契数列的计算、汉诺塔、快排等问题。
递归结构包括两个部分:
【示例3-22】递归:计算 n!
public class Test22 {
public static void main(String[] args) {
long d1 = System.currentTimeMillis();
System.out.printf("%d阶乘的结果:%s%n", 10, factorial(10));
long d2 = System.currentTimeMillis();
System.out.printf("递归费时:%s%n", d2-d1); //耗时:32ms
}
/** 求阶乘的方法*/
static long factorial(int n){
if(n==1){//递归头
return 1;
}else{//递归体
return n*factorial(n-1);//n! = n * (n-1)!
}
}
}
简单的程序是递归的有点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要谨慎。
用普通循环快得多:
【示例3-23】使用循环求 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("普通循环费时:%s%n", d4 - d3);
}
}
注意事项
任何能用递归解决的问题也能使用迭代解决。当递归方法可以更加自然地反映问题,并且易于理解和调试,并且不强调效率问题时,可以采用递归;
在要求高性能的情况下尽量避免使用递归,递归调用既花时间又耗内存。
if 单选择结构 if-else 双选择结构 if-else if-else 多选择结构
switch 多选择机构
循环结构:当型:while 与 for 直到型: do-while