控制语句(二)

本文深入解析循环结构,涵盖while、do-while与for循环的特点、语法和应用实例,同时探讨了嵌套循环、break与continue语句的使用,以及递归结构的概念与优缺点。

二、循环结构

循环结构分两大类,一类是当型,一类是直到型。

当型:

  当布尔表达式条件为true时,反复执行某语句,当布尔表达式的值为false时才停止循环,比如:while与for循环。

直到型:

  先执行某语句, 再判断布尔表达式,如果为true,再执行某语句,如此反复,直到布尔表达式条件为false时才停止循环,比如do-while循环。

1、while循环

语法结构:

while (布尔表达式) {
    循环体;
}

  在循环刚开始时,会计算一次“布尔表达式”的值,若条件为真,执行循环体。而对于后来每一次额外的循环,都会在开始前重新计算一次。

  语句中应有使循环趋向于结束的语句,否则会出现无限循环–––"死"循环。

while循环结构流程图如图1.1所示。

在这里插入图片描述

图1-1 while流程图

【示例1-1】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-2 示例1-1运行效果图

2、do-while循环

语法结构:

do {
        循环体;
     } while(布尔表达式) ;

  do-while循环结构会先执行循环体,然后再判断布尔表达式的值,若条件为真,执行循环体,当条件为假时结束循环。do-while循环的循环体至少执行一次。do-while循环结构流程图如图2.1所示。

在这里插入图片描述

图2-1 do-while流程图

【示例2-1】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);
    }
}

在这里插入图片描述

图2-2 示例2-1运行效果图

【示例2-2】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);
    }
}

在这里插入图片描述

图2-3 示例2-2运行效果图

  • 从运行效图中可以看出do-while总是保证循环体至少会被执行一次!

3、for循环

语法结构:

for (初始表达式; 布尔表达式; 迭代因子) {
      循环体;
}

  for循环语句是支持迭代的一种通用结构,是最有效、最灵活的循环结构。for循环在第一次反复之前要进行初始化,即执行初始表达式;随后,对布尔表达式进行判定,若判定结果为true,则执行循环体,否则,终止循环;最后在每一次反复的时候,进行某种形式的“步进”,即执行迭代因子。

  • A. 初始化部分设置循环变量的初值

  • B. 条件判断部分为任意布尔表达式

  • C. 迭代因子控制循环变量的增减

for循环在执行条件判定后,先执行的循环体部分,再执行步进。

for循环结构的流程图如图3-1所示。
图3-1 for循环流程图

【示例3-1】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();
    }
}

在这里插入图片描述

图3-2 示例3-1运行效果图

  Java里能用到逗号运算符的地方屈指可数,其中一处就是for循环的控制表达式。在控制表达式的初始化和步进控制部分,我们可以使用一系列由逗号分隔的表达式,而且那些表达式均会独立执行。

【示例3-2】逗号运算符

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.png

图3-3 示例3-2运行效果图

1. 无论在初始化还是在步进部分,语句都是顺序执行的。

2. 尽管初始化部分可设置任意数量的定义,但都属于同一类型。

3. 约定:只在for语句的控制表达式中写入与循环变量初始化,条件判断和迭代因子相关的表达式。

初始化部分、条件判断部分和迭代因子可以为空语句,但必须以“;”分开,如示例3-12所示。

【示例3-3】无限循环

public class Test12 {
    public static void main(String[] args) { 
        for ( ; ; ) {    // 无限循环: 相当于 while(true)
            System.out.println("南京");
        }
    }
}

  编译器将while(true)与for(;;)看作同一回事,都指的是无限循环。

  在for语句的初始化部分声明的变量,其作用域为整个for循环体,不能在循环外部使用该变量。如示例3-13所示。

【示例3-4】初始化变量的作用域

在这里插入图片描述

4、嵌套循环

  在一个循环语句内部再嵌套一个或多个循环,称为嵌套循环。while、do-while与for循环可以任意嵌套多层。

【示例4-1】嵌套循环

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();
        }
    }
}

在这里插入图片描述

图4-1 示例4-1运行效果图

【示例4-2】使用嵌套循环实现九九乘法表

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();
        }
    }
}

在这里插入图片描述

图4-2 示例4-2运行效果图

5、break语句和continue语句

  在任何循环语句的主体部分,均可用break控制循环的流程。break用于强行退出循环,不执行循环中剩余的语句。

【示例5-1】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.");
    }
}

在这里插入图片描述

图5-1 示例5-1运行效果图

  continue 语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判定。

注意事项:

  1. continue用在while,do-while中,continue 语句立刻跳到循环首部,越过了当前循环的其余部分。

  2. continue用在for循环中,跳到for循环的迭代因子部分。

【示例5-2】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();
            }
        }
    }
}

在这里插入图片描述

图5-2 示例5-2运行效果图

6、带标签的break和continue

  goto关键字很早就在程序设计语言中出现。尽管goto仍是Java的一个保留字,但并未在Java语言中得到正式使用;Java没有goto语句。然而,在break和continue这两个关键字的身上,我们仍然能看出一些goto的影子—带标签的break和continue。

  “标签”是指后面跟一个冒号的标识符,例如:“label:”。对Java来说唯一用到标签的地方是在循环语句之前。而在循环之前设置标签的唯一理由是:我们希望在其中嵌套另一个循环,由于break和continue关键字通常只中断当前循环,但若随同标签使用,它们就会中断到存在标签的地方。

  在 “goto有害”论中,最有问题的就是标签,而非goto, 随着标签在一个程序里数量的增多,产生错误的机会也越来越多。 但Java标签不会造成这方面的问题,因为它们的活动场所已被限死,不可通过特别的方式到处传递程序的控制权。由此也引出了一个有趣的问题:通过限制语句的能力,反而能使一项语言特性更加有用。

【示例6-1】带标签break和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 + "  ");
        }
    }
}

在这里插入图片描述

图6-1 示例6-1运行效果图

三、语句块

  语句块(有时叫做复合语句),是用花括号扩起的任意数量的简单Java语句。块确定了局部变量的作用域。块中的程序代码,作为一个整体,是要被一起执行的。块可以被嵌套在另一个块中,但是不能在两个嵌套的块内声明同名的变量。语句块可以使用外部的变量,而外部不能使用语句块中定义的变量,因为语句块中定义的变量作用域只限于语句块。

【示例1-1】语句块

public class Test19 {
    public static void main(String[] args) {
        int n;
        int a;
        {
            int k;
            int n; //编译错误:不能重复定义变量n
        } //变量k的作用域到此为止
    }
}

四、方法

  方法就是一段用来完成特定功能的代码片段,类似于其它语言的函数。

  方法用于定义该类或该类的实例的行为特征和功能实现。 方法是类和对象行为特征的抽象。方法很类似于面向过程中的函数。面向过程中,函数是最基本单位,整个程序由一个个函数调用组成。面向对象中,整个程序的基本单位是类,方法是从属于类和对象的。

方法声明格式:

[修饰符1  修饰符2  …]   返回值类型    方法名(形式参数列表){
    Java语句;… … …
 }

方法的调用方式:

  对象名.方法名(实参列表)

方法的详细说明:

  1. 形式参数:在方法声明时用于接收外界传入的数据。

  2. 实参:调用方法时实际传给方法的数据。

  3. 返回值:方法在执行完毕后返还给调用它的环境的数据。

  4. 返回值类型:事先约定的返回值的数据类型,如无返回值,必须显示指定为为void。

【示例1-1】方法的声明及调用

   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("我是某某某...");
        }
    }

1.png

图1-1 示例1-1运行效果图

注意事项:

  1. 实参的数目、数据类型和次序必须和所调用的方法声明的形式参数列表匹配。

  2. return 语句终止方法的运行并指定要返回的数据。

  3. Java中进行方法调用中传递参数时,遵循值传递的原则(传递的都是数据的副本):

  4. 基本类型传递的是该数据值的copy值。

  5. 引用类型传递的是该对象引用的copy值,但指向的是同一个对象。

五、方法的重载(overload)

  方法的重载是指一个类中可以定义多个方法名相同,但参数不同的方法。 调用时,会根据不同的参数自动匹配对应的方法。

雷区:

重载的方法,实际是完全不同的方法,只是名称相同而已!

构成方法重载的条件:

1.不同的含义:形参类型、形参个数、形参顺序不同

2.只有返回值不同不构成方法的重载

如:

int a(String str){}与 void a(String str){}

不构成方法重载

3.只有形参的名称不同,不构成方法的重载

如:

int a(String str){}与int a(String s){}

不构成方法重载

【示例5-1】方法重载

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;
    }  
}

六、递归结构

  递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己。

  利用递归可以用简单的程序来解决一些复杂的问题。比如:斐波那契数列的计算、汉诺塔、快排等问题。

  递归结构包括两个部分:

  1.定义递归头。解答:什么时候不调用自身方法。如果没有头,将陷入死循环,也就是递归的结束条件。

  2.递归体。解答:什么时候需要调用自身方法。

【示例6-1】递归:计算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)!
        }
    }
}

1.png

图6-1示例6-1运行效果图

2.png

图6-2 递归原理分析图

递归的缺陷:

  简单的程序是递归的优点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要慎重。

  比如上面的递归耗时558ms。但是用普通循环的话快得多,如示例6-2所示。

【示例6-2】使用循环求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);
    }
}

在这里插入图片描述

图6-3 示例6-2运行效果图

注意事项:

  任何能用递归解决的问题也能使用迭代解决。当递归方法可以更加自然地反映问题,并且易于理解和调试,并且不强调效率问题时,可以采用递归;

  在要求高性能的情况下尽量避免使用递归,递归调用既花时间又耗内存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值