Java 学习第 3 节:流程控制、数组与方法


文章目录


前言

在前面的学习中,我们已经接触了 Java 的变量、数据类型、运算符等基础知识。

本节将继续学习 Java 中非常重要的三部分内容:

  • 流程控制语句ifswitchforwhile
  • 数组:一维数组、二维数组以及数组的常见操作
  • 方法:方法的定义、参数传递以及方法重载

这些知识是后续学习面向对象、集合、算法以及实际项目开发的重要基础。


一、流程控制语句

程序默认按照代码从上到下依次执行。

但在实际开发中,我们经常需要:

  • 根据不同条件执行不同代码
  • 重复执行某段代码

因此,Java 提供了分支语句和循环语句。


1. switch 分支语句

switch 语句适合处理一个变量存在多个固定取值的情况。

基本格式如下:

switch (表达式) {
    case1:
        执行语句1;
        break;

    case2:
        执行语句2;
        break;

    default:
        默认执行语句;
        break;
}

执行流程

程序会将 switch 中表达式的结果依次与每个 case 后面的值进行匹配:

  1. 如果匹配成功,则从对应的 case 开始执行。
  2. 遇到 break 时,立即结束整个 switch
  3. 如果所有 case 都没有匹配成功,则执行 default
  4. default 可以省略。

例如:

import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        int data = sc.nextInt();

        switch (data) {
            case 1:
                System.out.println("星期一");
                break;

            case 2:
                System.out.println("星期二");
                break;

            case 3:
                System.out.println("星期三");
                break;

            case 4:
                System.out.println("星期四");
                break;

            case 5:
                System.out.println("星期五");
                break;

            case 6:
                System.out.println("星期六");
                break;

            case 7:
                System.out.println("星期日");
                break;

            default:
                System.out.println("输入错误");
                break;
        }

        sc.close();
    }
}

switch 穿透现象

需要特别注意:

case 可以理解为程序执行的入口,而 break 可以理解为当前分支的出口。

如果匹配到某个 case 后没有遇到 break,程序会继续向下执行后面的代码,这种现象称为:

case 穿透

例如:

int data = 1;

switch (data) {
    case 1:
        System.out.println("A");

    case 2:
        System.out.println("B");

    case 3:
        System.out.println("C");
        break;
}

运行结果:

A
B
C

原因是:

  • data 匹配 case 1
  • 执行 "A"
  • 没有遇到 break
  • 继续执行 case 2
  • 继续执行 case 3
  • 最后遇到 break

因此,在普通分支判断中,通常需要注意是否应该添加 break


2. if 分支语句

if 的意思是“如果”。

它用于根据一个布尔条件决定是否执行某段代码。

Java 中常见的 if 语句有三种格式。


2.1 单分支 if

格式:

if (布尔表达式) {
    执行语句;
}

执行流程:

  1. 判断 if 后面的布尔表达式。
  2. 如果结果为 true,执行大括号中的代码。
  3. 如果结果为 false,跳过 if 代码块。

例如:

int age = 20;

if (age >= 18) {
    System.out.println("已经成年");
}

2.2 双分支 if…else

格式:

if (布尔表达式) {
    执行语句1;
} else {
    执行语句2;
}

执行流程:

  1. 判断 if 后面的条件。
  2. 如果结果为 true,执行语句 1。
  3. 如果结果为 false,执行语句 2。

例如:

int age = 16;

if (age >= 18) {
    System.out.println("已经成年");
} else {
    System.out.println("尚未成年");
}

2.3 多分支 if…else if…else

格式:

if (条件1) {
    执行语句1;
} else if (条件2) {
    执行语句2;
} else if (条件3) {
    执行语句3;
} else {
    执行语句n;
}

执行流程:

从上到下依次判断:

  1. 如果条件 1 为 true,执行语句 1,然后结束整个分支结构。
  2. 否则继续判断条件 2。
  3. 哪个条件最先为 true,就执行对应代码。
  4. 如果前面的所有条件都为 false,则执行最后的 else

例如:

int score = 85;

if (score >= 90) {
    System.out.println("优秀");
} else if (score >= 80) {
    System.out.println("良好");
} else if (score >= 60) {
    System.out.println("及格");
} else {
    System.out.println("不及格");
}

运行结果:

良好

二、循环语句

循环语句用于重复执行某段代码。

Java 中常见的循环包括:

  • for
  • while
  • do...while

本节重点介绍 forwhile


1. for 循环

基本格式:

for (初始化语句; 条件判断; 步进表达式) {
    循环体;
}

例如:

for (int i = 0; i < 10; i++) {
    System.out.println(i);
}

运行结果:

0
1
2
3
4
5
6
7
8
9

执行流程

初始化
  ↓
判断条件
  ↓ true
执行循环体
  ↓
执行步进表达式
  ↓
重新判断条件

具体步骤:

  1. 执行初始化语句,例如 int i = 0
  2. 判断循环条件,例如 i < 10
  3. 如果条件为 true,执行循环体
  4. 执行步进表达式,例如 i++
  5. 再次判断条件
  6. 重复以上过程
  7. 当条件为 false 时结束循环

需要注意:

初始化语句通常只执行一次。


2. while 循环

基本格式:

初始化语句;

while (条件判断) {
    循环体;
    步进表达式;
}

例如:

public class Demo {
    public static void main(String[] args) {
        int i = 0;

        while (i < 3) {
            System.out.println(i);
            i++;
        }
    }
}

运行结果:

0
1
2

执行流程

  1. 初始化变量
  2. 判断 while 条件
  3. 如果条件为 true,执行循环体
  4. 修改循环变量
  5. 再次判断
  6. 当条件为 false 时结束循环

从逻辑上看,下面两个循环基本等价:

for (int i = 0; i < 3; i++) {
    System.out.println(i);
}

与:

int i = 0;

while (i < 3) {
    System.out.println(i);
    i++;
}

for 和 while 的简单区别

当循环次数比较明确时,通常使用:

for

当循环次数不明确,只知道循环结束条件时,通常使用:

while

例如:

while (用户没有退出程序) {
    继续运行;
}

三、Scanner 键盘输入

在正式介绍数组之前,先简单了解 Java 中的键盘输入。

Java 可以使用 Scanner 类接收用户从键盘输入的数据。


1. Scanner 的基本使用

第一步:导包

import java.util.Scanner;

第二步:创建 Scanner 对象

Scanner sc = new Scanner(System.in);

第三步:调用方法读取输入

读取整数:

int data = sc.nextInt();

读取字符串:

String str = sc.next();

完整示例:

import java.util.Scanner;

public class Demo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // 录入一个整数
        int data1 = sc.nextInt();
        System.out.println(data1);

        // 录入一个字符串
        String data2 = sc.next();
        System.out.println(data2);

        sc.close();
    }
}

2. 常见输入方法

方法 作用
nextInt() 读取 int 类型整数
nextDouble() 读取 double 类型小数
next() 读取一个字符串,以空白字符为分隔
nextLine() 读取一整行字符串

例如:

String name = sc.next();

如果输入:

Zhang San

next() 通常只读取:

Zhang

而:

String name = sc.nextLine();

可以读取整行内容:

Zhang San

四、数组

1. 数组简介

数组可以理解为:

一个用于存储多个相同类型数据的容器。

例如,现在需要存储 5 个学生的成绩。

如果不用数组:

int score1 = 90;
int score2 = 80;
int score3 = 70;
int score4 = 95;
int score5 = 85;

使用数组:

int[] scores = {90, 80, 70, 95, 85};

显然更加简洁。


2. 数组的特点

2.1 数组属于引用类型

例如:

int[] arr = new int[3];

变量 arr 保存的不是数组中的某个具体整数,而是对数组对象的引用。


2.2 一个数组可以存储多个数据

例如:

int[] arr = {1, 2, 3, 4, 5};

2.3 数组长度固定

数组一旦创建,其长度不能改变。

例如:

int[] arr = new int[3];

这个数组的长度永远是:

3

注意:

数组中的元素值可以修改,但是数组本身的长度不能修改


2.4 数组中的元素类型统一

例如:

int[] arr = {1, 2, 3};

这个数组只能保存 int 类型的数据。

下面的写法错误:

int[] arr = {1, "hello", 3};

因为 "hello"String 类型。


五、数组的定义

1. 动态初始化

格式:

数据类型[] 数组名 = new 数据类型[数组长度];

例如:

int[] arr1 = new int[3];
String[] arr2 = new String[3];

表示:

int[] arr1 = new int[3];

创建一个能够保存 3 个 int 元素的数组。

另一种语法也合法:

int arr1[] = new int[3];

不过在 Java 中更加推荐:

int[] arr1 = new int[3];

因为这种写法更清晰地表示:

arr1 是一个 int[] 类型的变量。


2. 完整静态初始化

格式:

数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3};

例如:

int[] arr = new int[]{1, 2, 3, 4, 5};

3. 简化静态初始化

格式:

数据类型[] 数组名 = {元素1, 元素2, 元素3};

例如:

int[] arr1 = {1, 2, 3, 4, 5};

String[] arr2 = {"a", "b", "c"};

这是静态初始化中非常常见的写法。

完整示例:

public class Demo {
    public static void main(String[] args) {
        // 动态初始化
        int[] arr1 = new int[3];
        String[] arr2 = new String[3];

        // 静态初始化
        int[] arr3 = {1, 2, 3, 4, 5};
        String[] arr4 = {"a", "b", "c"};
    }
}

六、数组的基本操作

1. 获取数组长度

通过:

数组名.length

获取数组长度。

例如:

int[] arr = {1, 2, 3};

System.out.println(arr.length);

运行结果:

3

注意:

数组的 length 是一个属性,因此:

arr.length

后面没有括号。


2. 数组索引

索引表示元素在数组中的位置。

需要牢记:

Java 数组的索引从 0 开始。

例如:

String[] arr = {"a", "b", "c"};

对应关系:

索引 元素
0 "a"
1 "b"
2 "c"

因此:

arr[0]

表示:

"a"

而:

arr[2]

表示:

"c"

3. 存储和修改元素

可以根据索引给数组元素赋值:

int[] arr = new int[3];

arr[0] = 1;
arr[1] = 2;
arr[2] = 3;

也可以修改已经存在的元素:

arr[1] = 100;

此时数组变成:

[1, 100, 3]

4. 获取数组元素

通过:

数组名[索引]

获取元素。

例如:

System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);

5. 遍历数组

遍历数组是指:

将数组中的所有元素依次访问一次。

可以使用 for 循环:

int[] arr = {10, 20, 30, 40};

for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

执行过程:

i = 0 → arr[0]
i = 1 → arr[1]
i = 2 → arr[2]
i = 3 → arr[3]

当:

i = 4

条件:

i < arr.length

即:

4 < 4

结果为 false,循环结束。


6. 数组索引越界

例如:

int[] arr = {1, 2, 3};

System.out.println(arr[3]);

这是错误的。

因为数组长度为 3,合法索引只有:

0
1
2

访问 arr[3] 会发生数组索引越界异常。

因此,一个长度为 n 的数组,其合法索引范围是:

0 ~ n - 1

七、二维数组

1. 二维数组简介

如果一维数组可以理解为“一行数据”,那么二维数组可以理解为:

由多个一维数组组成的数组。

例如:

int[][] arr = {
    {1, 2, 3},
    {4, 5, 6}
};

可以理解为:

1 2 3
4 5 6

其中:

arr[0]

表示第一行:

1 2 3

而:

arr[1]

表示第二行:

4 5 6

2. 二维数组索引

例如:

arr[i][j]

其中:

  • i 表示第一个维度的索引,可以理解为行索引
  • j 表示第二个维度的索引,可以理解为列索引

例如:

arr[1][2]

表示:

  • 1 + 1 = 2
  • 2 + 1 = 3

也就是:

第 2 行第 3 列

需要特别注意:

arr[3][5]

表示的是:

第 4 行第 6 列

但这并不意味着数组本身“有 3 行 5 列”。

35 在这里是索引值,不是数组长度。


3. 动态初始化

格式:

数据类型[][] 数组名 = new 数据类型[m][n];

例如:

int[][] arr = new int[3][4];

表示:

  • 二维数组中有 3 个一维数组
  • 每个一维数组长度为 4

通常可以直观理解为:

3 行 4 列

其中:

m

表示外层数组长度。

n

表示每个内层一维数组的长度。


4. 静态初始化

完整格式:

int[][] arr = new int[][]{
    {1, 2},
    {3, 4},
    {5, 6}
};

5. 简化静态初始化

int[][] arr = {
    {1, 2},
    {3, 4},
    {5, 6}
};

6. 获取二维数组元素

例如:

int[][] arr = {
    {1, 2, 3},
    {4, 5, 6}
};

访问:

System.out.println(arr[0][0]);

结果:

1

访问:

System.out.println(arr[1][2]);

结果:

6

7. 遍历二维数组

使用两层 for 循环:

int[][] arr = {
    {1, 2, 3},
    {4, 5, 6}
};

for (int i = 0; i < arr.length; i++) {
    for (int j = 0; j < arr[i].length; j++) {
        System.out.println(arr[i][j]);
    }
}

其中:

arr.length

表示外层数组长度,也就是这里的“行数”。

arr[i].length

表示第 i 个一维数组的长度。

这种写法比直接写固定列数更加安全。


八、方法

1. 方法简介

在 C 或 C++ 中,如果要实现某个独立功能,例如:

  • 两数相加
  • 排序
  • 查找最大值

通常会定义一个函数。

Java 中也有类似概念,称为:

方法(Method)

方法可以理解为:

一段用于完成特定功能,并且可以被重复调用的代码。

例如:

public static void printHello() {
    System.out.println("Hello");
}

调用:

printHello();

2. 方法的一般定义格式

修饰符 返回值类型 方法名(参数列表) {
    方法体;
    return 返回结果;
}

例如:

public static int add(int a, int b) {
    int result = a + b;
    return result;
}

调用:

int result = add(10, 20);

System.out.println(result);

运行结果:

30

九、方法的四种常见形式

根据:

  • 是否有参数
  • 是否有返回值

可以将方法分为四种常见形式。


1. 无参无返回值

public static void printHello() {
    System.out.println("Hello");
}

特点:

  • 没有参数
  • 返回值类型为 void
  • 不返回结果

调用:

printHello();

2. 有参无返回值

public static void printHello(int a) {
    if (a == 1) {
        System.out.println("Hello");
    } else {
        System.out.println("Wrong");
    }
}

调用:

printHello(1);

3. 无参有返回值

public static int getZero() {
    return 0;
}

调用:

int result = getZero();

System.out.println(result);

4. 有参有返回值

public static String getMessage(int a) {
    if (a == 1) {
        return "Hello";
    } else {
        return "Wrong";
    }
}

调用:

String result = getMessage(1);

System.out.println(result);

运行结果:

Hello

十、方法参数传递

1. 形参与实参

例如:

public static void main(String[] args) {
    int x = 10;

    method(x);
}

public static void method(int a) {
    System.out.println(a);
}

调用方法时:

method(x);

这里的:

x

叫做:

实际参数,简称实参

而方法定义中的:

int a

叫做:

形式参数,简称形参


2. Java 参数传递的本质

需要特别注意:

Java 中的方法参数传递统一采用值传递。

这是一个非常重要的概念。

无论是:

  • 基本数据类型
  • 引用数据类型

本质上都是:

值传递

只是传递的“值的内容”不同。


3. 基本数据类型作为参数

对于基本数据类型,传递的是:

基本类型变量中保存的具体值的副本。

例如:

public class Demo {
    public static void main(String[] args) {
        int a = 1;
        int b = 10;

        method(a, b);

        System.out.println(a); // 1
        System.out.println(b); // 10
    }

    public static void method(int a, int b) {
        a += 1;
        b += 10;

        System.out.println(a); // 2
        System.out.println(b); // 20
    }
}

运行过程可以简单理解为:

调用:

method(a, b);

相当于把:

1
10

复制给方法中的形参。

也就是:

public static void method(int a, int b)

内部可以理解为获得了新的局部变量:

a = 1
b = 10

因此:

a += 1;
b += 10;

只改变方法内部的形参。

不会改变 main 方法中的原变量。

最终:

main 中的 a = 1
main 中的 b = 10

4. 引用类型作为参数

对于数组、对象等引用类型,传递的仍然是:

值的副本。

只不过这个值是:

引用值的副本。

例如:

public class Demo {
    public static void main(String[] args) {
        int[] arr = {1, 2};

        method(arr);

        System.out.println(arr[0]); // 2
        System.out.println(arr[1]); // 12
    }

    public static void method(int[] arr) {
        arr[0] += 1;
        arr[1] += 10;

        System.out.println(arr[0]); // 2
        System.out.println(arr[1]); // 12
    }
}

为什么方法内部修改数组后,原数组也变化了?

因为:

main 方法中的 arr

与:

method 方法中的 arr

虽然是两个不同的变量,但它们保存的引用值指向同一个数组对象。

可以简单理解为:

main 中的 arr ──────┐
                    ↓
                [1, 2]
                    ↑
method 中的 arr ────┘

所以:

arr[0] += 1;

修改的是同一个数组对象中的元素。

最终原数组也会发生变化。


5. 一个非常重要的区别

下面这种操作会影响原数组:

public static void method(int[] arr) {
    arr[0] = 100;
}

因为修改的是:

同一个数组对象内部的元素

但是下面这种情况不同:

public static void method(int[] arr) {
    arr = new int[]{100, 200};
}

这里只是让方法内部的形参 arr 指向一个新的数组。

不会改变外部变量原本保存的引用值。

例如:

public class Demo {
    public static void main(String[] args) {
        int[] arr = {1, 2};

        method(arr);

        System.out.println(arr[0]); // 1
        System.out.println(arr[1]); // 2
    }

    public static void method(int[] arr) {
        arr = new int[]{100, 200};
    }
}

最终原数组仍然是:

[1, 2]

因此,更准确的理解是:

Java 永远是值传递。引用类型传递的是引用值的副本,而不是“引用传递”。


十一、方法重载(Overload)

1. 什么是方法重载

在同一个类中:

方法名相同,但是参数列表不同的方法,称为方法重载。

例如:

public static int add(int a, int b) {
    return a + b;
}

public static double add(double a, double b) {
    return a + b;
}

这两个方法:

  • 方法名都叫 add
  • 参数类型不同

因此构成重载。


2. 参数列表不同的情况

2.1 参数个数不同

public static void method(int a) {
}

public static void method(int a, int b) {
}

构成重载。


2.2 参数类型不同

public static void method(int a) {
}

public static void method(double a) {
}

构成重载。


2.3 参数类型顺序不同

public static void method(int a, double b) {
}

public static void method(double a, int b) {
}

构成重载。


3. 与重载无关的因素

3.1 与参数名无关

下面两个方法:

public static void method(int a) {
}

public static void method(int b) {
}

不构成重载。

因为:

int a

和:

int b

参数类型完全相同。

参数名不同没有意义。

实际上,这样写会直接发生重复定义错误。


3.2 与返回值类型无关

下面两个方法:

public static int method(int a) {
    return 1;
}

public static double method(int a) {
    return 1.0;
}

不构成重载。

虽然返回值类型不同:

int
double

但是参数列表完全相同,因此 Java 无法仅根据返回值判断应该调用哪个方法。


4. 判断方法重载的核心

判断两个方法是否构成重载,主要看:

方法名是否相同
+
参数列表是否不同

参数列表不同包括:

1. 参数个数不同
2. 参数类型不同
3. 参数类型顺序不同

与下面这些内容无关:

1. 参数名称
2. 返回值类型

十二、本节总结

本节主要学习了 Java 中三个非常重要的知识模块。

1. 流程控制

分支语句:

if
switch

循环语句:

for
while

其中:

  • if 适合复杂条件判断
  • switch 适合多个固定值匹配
  • for 适合循环次数较明确的场景
  • while 适合循环次数不明确、主要依靠条件结束的场景

2. 数组

数组用于一次存储多个相同类型的数据。

常见定义:

int[] arr = new int[3];

或者:

int[] arr = {1, 2, 3};

访问元素:

arr[0]

获取长度:

arr.length

遍历数组:

for (int i = 0; i < arr.length; i++) {
    System.out.println(arr[i]);
}

3. 方法

方法用于封装一个独立功能。

基本格式:

public static 返回值类型 方法名(参数列表) {
    方法体;
}

Java 参数传递的本质:

始终是值传递

基本类型:

传递具体值的副本

引用类型:

传递引用值的副本

方法重载要求:

方法名相同,参数列表不同

掌握这些知识后,就已经可以开始编写结构更加完整、逻辑更加清晰的 Java 程序了。

Logo

CANN开发者社区旨在汇聚广大开发者,围绕CANN架构重构、算子开发、部署应用优化等核心方向,展开深度交流与思想碰撞,携手共同促进CANN开放生态突破!

更多推荐