Java 程序三大结构

2021/01/07 Java基础知识 共 7289 字,约 21 分钟
闷骚的程序员
口诀:三大结构顺择循

1. 顺序结构

不用说,自上而下执行,遇到选择结构就判断,遇到循环结构就多绕几个圈。

2. 选择结构

Java中的判断语句有:

1. if(){}
2. if(){}else{}
3. if(){}else if(){}else if(){}else{}
4. if(){}else{if(){}else{}}
5. switch(表达式){//表达式支持8大基本类型、String和枚举
case 匹配值1:语句1; // case后边的语句也可以整体使用{}括起来
            break;
case 匹配值2:语句2;
            break;
…..
default:语句n;
}
区别:if语句主要用来对选项不多的情况进行单项选择,当选项很多的情况下使用switch  case语句去匹配更方便。不论是if还是switch case都只能选择一条case进行执行。

3. 循环结构

3.1 Java中循环语句有:

(1) while(){};
(2) do{}while();
(3) for(){};

区别:while循环和do while循环适用于不知道循环次数的情况,只知道循环结束条件,这两者的区别在于,do while循环无论如何都会执行一次;for循环适用于已知循环次数的循环。

3.2 常见的死循环语句:

(1) while(true){};
(2) do{}while(true);
(3) for(;;){};

4. 选择结构案例

Java中程序的三大结构:顺序、选择、循环。顺序结构不用说,基本任何语言都是从上到下,顺序执行的。
此处进行选择结构:if else 模型的编写。
if else 一:

package javase.demo02.sandajiegou;
import org.junit.Test;
public class Demo01XuanZeIfElse {
    
    // 案例一:java中if中的条件表达式(也叫做boolean表达式,是由关系运算符和逻辑运算符组成的)只能是个明确的boolean表达式,如果是null值将报空指针异常。
    @Test
    public void testIfElse() {
        int a = 3;
        if (a == 3) {
            System.out.println("a == 3");
        } else {
            System.out.println("a ! = 3");
        }
    }
    
    // 案例二:java中的if后面的boolean表达式直接就是一个字符串。
    // 可以看出,java是一门语法严谨的语言,即boolean表达式必须是个boolean表达式,不能直接是字符串。
    // 为什么要提这一茬呢?因为在Js或者shell这种脚本语言中,if或者switch等类似的()中是可以直接判断字符串的。
    @Test
    public void testIfElse2() {
        // String str = "my name is Eric";
        // if (str) {
        // System.out.println("我的名字是Eric");
        // }
    }
    
    // 案例三:如果if后面的boolean表达式是null值的话,将直接抛出空指针异常
    @Test
    public void testIfElse3(){
        Boolean flag = null;
        // flag作为boolean表达式,此处会直接拆箱,拆箱后尝试复制给基本类型boolean,发现拆箱后是null值,将null值直接赋值给boolean,直接抛出空指针
        if(flag){
            System.out.println("ture");
        }
    }
}

if else 二:

package javase.demo02.sandajiegou;

// Java中程序的三大结构:顺序、选择、循环。
// 选择结构:if else if 模型
public class Demo02XuanZeIfElseIf {
    public static void main(String args[]) {
        int a = 6;
        int b = 5;
        if (a == 3) {
            System.out.println("a == 3");
        } else if (b == 5) {
            System.out.println("b == 5");
        } else {
            System.out.println("没有匹配结果!!!");
        }
    }
}

switch case案例:

package javase.demo02.sandajiegou;
import javase.demo02.sandajiegou.enump.MyCommonEnum;
import javase.demo02.sandajiegou.enump.MyEnum;
import org.junit.Test;

// Java中程序的三大结构:顺序、选择、循环。
// 选择结构switch case模型。
// 表达式支持8大基本类型(包括8大包装类型)、String和枚举。
public class Demo03XuanZeSwitchCase {

    // case支持String。
    @Test
    public void testSwitchCaseForString() {
        int a = 10;
        int b = 34;
        //case中匹配包装类型
        Character flag = '*';
        switch (flag) {
            // case后边的语句也可以整体使用{}括起来
            case '+':
                System.out.println("a + b = " + (a + b));
                // 如果哪个分支不写break,一旦被匹配到,流程就会继续向下传递
                break;
            case '-':
                System.out.println("a - b = " + (a - b));
                // 如果后面的流程还是没有写break,则流程会一直向下传递,遇见break存在的case,执行后才会跳出,或者直接执行完了所有的case
                break;
            case '*':
                System.out.println("a * b = " + (a * b));
                break;
            case '/':
                System.out.println("a / b = " + (a / b));
                break;
            case '%':
                System.out.println("a % b = " + (a % b));
                break;
            default:
                System.out.println("没有匹配的case");
        }
    }

    // case支持Enum。
    // 枚举类型和switch case一起使用时一定不要限定枚举常量值的常量,即枚举 switch case 标签必须为枚举常量的非限定名称,不能加枚举常量所属的枚举类名。
    // 原因:
    // java的switch语法,是通过jvm的tableswitch和lookupswitch两个指令实现。
    // 简单说一下实现原理:
    // java编译器为switch语句编译成一个局部变量数组,每个case对应一个数组的索引,指令的执行是通过不同的数组索引找到不同的入口指令。
    // 所以原则上switch...case只能处理int型的变量。
    // enum能用在switch语句中,也是一个语法糖,我们知道所有枚举类的父类Enum中有一个private final int ordinal;
    // java编译器检测到switch语句中变量是一个枚举类,则会利用之前枚举类的ordinal属性,编译一个局部变量数组,后续在进行case分支比较的时候,就是简单通过tableswitch或lookupswitch指令来进行跳转。
    // 需要注意的一点:这个局部变量数组的构建过程是在编译器在编译阶段完成的。
    // 编译过程中的一旦判断switch语句中的变量是enum类型,便只需要以其ordinal属性为索引,通过tableswitch查找局部变量数组 (这在反编译后的7~12行字节码可以体现出)。
    // 在此时case语句不需要类限定前缀,完全是java编译器的限制。
    @Test
    public void testSwitchCaseForEnum() {
        MyEnum myEnum = MyEnum.FIVE;
        switch (myEnum) {
//            case MyEnum.ONE:
            case ONE:
                System.out.println("one");
                break;
//            case MyEnum.TWO:
            case TWO:
                System.out.println("two");
                break;
//            case MyEnum.THREE:
            case THREE:
                System.out.println("three");
                break;
//            case MyEnum.FOUR:
            case FOUR:
                System.out.println("four");
                break;
//            case MyEnum.FIVE:
            case FIVE:
                System.out.println("five");
                break;
            default:
                System.out.println("没有匹配");
                break;
        }
    }

    // case支持Enum。
    // 枚举作为内部类,和直接使用枚举一样,case后的枚举不能携带任何类路径。
    @Test
    public void testSwitchCaseForEnum2() {
        MyCommonEnum.MyEnum2 myCommonEnum =  MyCommonEnum.MyEnum2.FOUR2;
        switch (myCommonEnum) {
//            case MyCommonEnum.MyEnum2.ONE:
            case ONE2:
                System.out.println("one");
                break;
//            case MyCommonEnum.MyEnum2.TWO:
            case TWO2:
                System.out.println("two");
                break;
//            case MyCommonEnum.MyEnum2.THREE:
            case THREE2:
                System.out.println("three");
                break;
//            case MyCommonEnum.MyEnum2.FOUR:
            case FOUR2:
                System.out.println("four");
                break;
//            case MyCommonEnum.MyEnum2.FIVE:
            case FIVE2:
                System.out.println("five");
                break;
            default:
                System.out.println("没有匹配");
                break;
        }
    }
}

5. 循环结构案例

do while案例:

package javase.demo02.sandajiegou;

// 循环结构之do while。
public class Demo04XunHuanDoWhile {
    public static void main(String args[]) {
        int a = 1;
        int sum = 0;
        do {
            sum += a;
            a++;
        } while (a <= 10);
        System.out.println("1..>10 sum is:" + sum);
    }
}

while循环案例:

package javase.demo02.sandajiegou;

// 循环结构之while。
public class Demo05XunHuanWhile {
    public static void main(String args[]) {
        int a = 1;
        int sum = 0;
        while (a <= 10) {
            sum += a;
            a++;
        }
        System.out.println("1..>10 sum is:" + sum)
    }
}

for循环案例:

package javase.demo02.sandajiegou;

// 循环结构之for循环。
public class Demo06XunHuanFor {
    public static void main(String args[]) {
        int sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += i;
        }
        System.out.println("1..>10 sum is:" + sum);
    }
}

嵌套for循环案例:

package javase.demo02.sandajiegou;

// 嵌套for循环:输出乘法口诀。
// 注意:嵌套循环总是先遍历最里面的循环,从内到外依次遍历。即当最里面的循环整体循环完毕后,再从外边的循环继续开始遍历。
public class Demo06XunHuanFor02 {
    public static void main(String[] args) {
        for (int i = 1; i < 10; i++) {
            for (int j = 1; j < 10; j++) {
                // 先遍历里面的for
                System.out.print(j + "*" + i + "=" + (j * i) + " ");
            }
            System.out.println();
        }
    }
}

循环的中断:continue和break

package javase.demo02.sandajiegou;
import org.junit.Test;

// Java中循环一次中断和循环整体中断模型。
// break和continue。
public class Demo07XunHuanForBreakAndContinue {

    // continue表示:跳出当前循环,跳出后继续执行后面的循环。
    @Test
    public void testContinue() {
        for (int i = 0; i < 10; i++) {
            if (i == 3) {
                continue;// 当i=3时只是结束本次循环,而不是整个循环体,即当i=4时还是会进入当前层循环中。
            }
            System.out.println("i = " + i);
        }
    }

    // break表示:跳出当前整体循环,即跳出后这个循环体就结束了,不会再次进入循环进行遍历了。
    @Test
    public void testBreak() {
        for (int i = 0; i < 10; i++) {
            if (i == 3) {
                break;// 当前层循环整体中断
            }
            System.out.println("i = " + i);
        }
    }

    // break是跳出当前循环体。如果是多层嵌套循环的话,则break在哪一层循环体中便跳出哪一层循环体。
    @Test
    public void testBreak2() {
        for (int i = 1; i < 10; i++) {
            for (int j = 1; j < 10; j++) {
                if (j == 3) {
                    break;// 注意,所有j为3以后的值都不会输出。但是i的值是全部输出的。
                }
                // 先遍历里面的for
                System.out.print(j + "*" + i + "=" + (j * i) + " ");
            }
            System.out.println("                    " + i);
        }
    }
}

死循环案例:

package javase.demo02.sandajiegou;
import org.junit.Test;

// 死循环:三种循环结构实现的死循环没有任何区别;一般不会使用死循环的。
// 注意:在代码中一定要考虑如何避免死循环。
public class Demo08DieXunHuan {
    // 使用doWhile实现死循环
    @Test
    public void testDieByDoWhile() {
        do {
            System.out.println("do...while...");
        } while (true);
    }
    // 使用while实现死循环
    @Test
    public void testDieByWhile() {
        while (true) {
            System.out.println("while...");
        }
    }
    // 使用for实现死循环
    @Test
    public void testDieByFor() {
        for (; ; ) {
            System.out.println("for...");
        }
    }
}

6. null值在for循环和foreach中遍历集合时的特性

很多时候,使用for循环或者foreach,目的是为了遍历集合、数组等容器对象,进行迭代,操作里面的单个元素对象。
我们常常会纠结下面的问题:

  1. 当需要遍历的目标集合对象是null值的时候,for循环或者foreach怎么办?
  2. 当需要遍历的目标集合对象不是null值,但是是个对象,即size为0时,for循环或者foreach怎么办?
    //如果foreach传入的数组或者集合本身是null值,则会报空指针异常。
    @Test
    public void testForeach() {
    List&lt;Long> allList = null;
    //这种情况下要做空指针校验
    for (Long l : allList) {
        System.out.println(l);
    }
    }
    

    结论:
    当集合对象为null值时,如果通过for循环或者foreach去尝试遍历,将报空指针。

// 如果foreach传入的数组或者集合本身不是null,而只是没有任何东西,则foreach根本就不会进去,不会报空指针异常。
@Test
public void testForeach2() {
    List&lt;Long> allList = new ArrayList<>();
    if (allList.size() == 0) {
        System.out.println("为空的...");
    }
    for (Long list : allList) {
        if (allList.size() == 0) {
            System.out.println("根本就不会进来,所以该句话不会打印...");
        }
        System.out.println(list);
    }
}

结论:
当目标集合对象不为null,但是其容器为空时,此时for循环或者foreach压根就不会进去,相当于一个if的作用,阻止了进入for循环体。

理解for循环和foreach的本质:  
本质相同,都是设置循环变量i,通过循环条件控制和循环条件的变化确认是否执行for循环。   
foreach实际上是for循环的变种,底层一样,如果传入的是list或者数组等,会转换成int i的形式,通过list或者array的size或者length去做循环总数。  
这样一来,如果list或者array是null值的话,在.size()或者.length的时候将直接报错,空指针异常!  
实际上在mybatis或者其他使用类似于foreach的标签时,其原理是相同的,只要传入的那个集合对象不为null,foreach就不会报错;如果集合没有内容,foreach将不会进去。  

文档信息

Search

    Table of Contents