Java基础语法记录(第二期):流程控制+数组+字符串实战

5 阅读21分钟

Java基础语法记录(第二期):流程控制+数组+字符串实战

本文承接上一期基础语法,聚焦Java流程控制、数组、字符串三大核心知识点,结合实战代码、底层原理解析,搭配AI辅助学习技巧,全程干货无冗余,适配掘金/CSDN技术博客排版,可直接复制到md文档,夯实Java基础进阶能力。

一、Java流程控制:程序执行的“导航仪”

流程控制是Java程序执行的核心逻辑,用于控制代码的执行顺序,主要分为分支结构循环结构两大类,合理使用流程控制可让代码逻辑清晰、高效简洁,也是后续复杂业务逻辑编写的基础。

1.1 分支结构:根据条件执行不同代码

分支结构核心是“判断条件,执行对应逻辑”,Java中常用的分支结构有if-elseif-else if-elseswitch-case三种,适用场景各有差异,需根据实际需求选择。

1.1.1 if-else 结构(最常用)

if-else是最基础的分支结构,核心逻辑:判断条件是否成立,成立则执行if块代码,否则执行else块代码(else可选)。

语法格式:

if (条件表达式) {
    // 条件成立时执行的代码块
} else {
    // 条件不成立时执行的代码块(可选)
}

核心注意点:

  1. 条件表达式必须是布尔类型(true/false),不能是其他类型(如int、String);

  2. 若代码块只有一行,可省略大括号,但建议始终保留(避免逻辑错乱);

  3. if语句可以嵌套使用,嵌套层级建议不超过3层(提升代码可读性)。

实战案例:判断一个整数是正数、负数还是0

public class IfElseDemo {
    public static void main(String[] args) {
        int num = 15;
        
        if (num > 0) {
            System.out.println(num + "是正数");
        } else if (num < 0) {
            System.out.println(num + "是负数");
        } else {
            System.out.println(num + "是0");
        }
        
        // 嵌套if-else:判断一个数是否是偶数且大于10
        if (num > 10) {
            if (num % 2 == 0) {
                System.out.println(num + "是偶数且大于10");
            } else {
                System.out.println(num + "是奇数且大于10");
            }
        }
    }
}
1.1.2 switch-case 结构(多值匹配)

switch-case适用于“一个变量与多个值匹配”的场景,比if-else if-else更简洁,可读性更强,尤其适合多分支(3个及以上)的场景。

语法格式:

switch (表达式) {
    case1:
        // 表达式等于值1时执行的代码
        break; // 跳出switch,避免穿透
    case2:
        // 表达式等于值2时执行的代码
        break;
    ...
    default:
        // 表达式不匹配任何case时执行的代码(可选)
        break;
}

核心注意点:

  1. JDK 7及以上,switch表达式支持byteshortintcharString、枚举类型;

  2. case后的值必须是常量(不能是变量),且不能重复;

  3. 必须加break,否则会发生“case穿透”(执行当前case后,继续执行后续所有case,直到遇到break);

  4. default位置可任意(通常放在最后),无论放在哪里,都只会在所有case不匹配时执行。

实战案例:根据月份判断季节

public class SwitchCaseDemo {
    public static void main(String[] args) {
        int month = 8;
        String season;
        
        switch (month) {
            case 3:
            case 4:
            case 5:
                season = "春季";
                break;
            case 6:
            case 7:
            case 8:
                season = "夏季";
                break;
            case 9:
            case 10:
            case 11:
                season = "秋季";
                break;
            case 12:
            case 1:
            case 2:
                season = "冬季";
                break;
            default:
                season = "无效月份";
                break;
        }
        
        System.out.println(month + "月份属于" + season);
    }
}
1.1.3 分支结构对比与选择
结构适用场景优点缺点
if-else2个分支、条件复杂(如范围判断)灵活,可处理任意布尔条件多分支时代码冗长,可读性差
if-else if-else3个及以上分支、条件有范围逻辑清晰,适配范围判断分支过多时,代码冗余
switch-case3个及以上分支、多值匹配(固定值)代码简洁,可读性强不支持范围判断,必须加break

1.2 循环结构:重复执行指定代码

循环结构用于“重复执行一段代码”,直到满足终止条件,Java中常用的循环结构有forwhiledo-while三种,核心区别在于循环条件的判断时机和适用场景。

1.2.1 for 循环(最常用,固定次数循环)

for循环适用于已知循环次数的场景,语法结构清晰,将循环初始化、条件判断、迭代操作集中在一起,便于维护。

语法格式:

for (循环初始化; 循环条件; 迭代操作) {
    // 循环体:需要重复执行的代码
}

执行流程:

  1. 执行循环初始化(仅执行1次);

  2. 判断循环条件,若成立则执行循环体;

  3. 执行迭代操作(更新循环变量);

  4. 重复步骤2-3,直到循环条件不成立,退出循环。

核心注意点:

  1. 循环初始化、循环条件、迭代操作均可省略,但分号不能省略(省略后会变成无限循环);

  2. 循环变量的作用域仅限于for循环内部,外部无法访问;

  3. 可嵌套for循环(如二维数组遍历),但嵌套层级不宜过多(建议不超过3层)。

实战案例1:打印1-10的整数,计算1-10的和

public class ForLoopDemo {
    public static void main(String[] args) {
        // 打印1-10的整数
        for (int i = 1; i <= 10; i++) {
            System.out.print(i + " ");
        }
        System.out.println();
        
        // 计算1-10的和
        int sum = 0;
        for (int i = 1; i <= 10; i++) {
            sum += i; // 等价于 sum = sum + i
        }
        System.out.println("1-10的和:" + sum);
    }
}

实战案例2:嵌套for循环(打印99乘法表)

public class ForNestedDemo {
    public static void main(String[] args) {
        // 99乘法表:外层循环控制行数,内层循环控制列数
        for (int i = 1; i <= 9; i++) {
            for (int j = 1; j <= i; j++) {
                System.out.print(j + "×" + i + "=" + (i*j) + "\t");
            }
            System.out.println(); // 换行
        }
    }
}
1.2.2 while 循环(未知次数循环)

while循环适用于未知循环次数的场景,仅需判断循环条件,循环初始化在循环外部,迭代操作在循环体内部。

语法格式:

// 循环初始化(在while外部)
while (循环条件) {
    // 循环体
    // 迭代操作(必须有,否则会无限循环)
}

核心注意点:

  1. 循环初始化必须在while循环外部,否则每次循环都会重新初始化,导致逻辑错误;

  2. 循环体中必须有迭代操作,否则会出现无限循环(死循环);

  3. 循环条件必须是布尔类型,成立则执行循环体,不成立则退出循环。

实战案例:打印1-100中能被3整除的数

public class WhileLoopDemo {
    public static void main(String[] args) {
        int num = 1; // 循环初始化
        while (num<= 100) { // 循环条件
            if (num % 3 == 0) {
                System.out.print(num + " ");
            }
            num++; // 迭代操作(关键,避免死循环)
        }
    }
}
1.2.3 do-while 循环(至少执行一次循环)

do-while循环与while循环类似,核心区别:do-while先执行一次循环体,再判断循环条件,即无论条件是否成立,循环体至少执行一次。

语法格式:

// 循环初始化
do {
    // 循环体(至少执行一次)
    // 迭代操作
} while (循环条件);

适用场景:需要“先执行、后判断”的场景(如用户输入验证、菜单选择)。

实战案例:用户输入密码,直到输入正确为止

import java.util.Scanner;

public class DoWhileDemo {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String correctPwd = "123456";
        String inputPwd;
        
        do {
            System.out.print("请输入密码:");
            inputPwd = scanner.next();
            if (!inputPwd.equals(correctPwd)) {
                System.out.println("密码错误,请重新输入!");
            }
        } while (!inputPwd.equals(correctPwd));
        
        System.out.println("密码正确,登录成功!");
        scanner.close();
    }
}
1.2.4 循环控制:break 与 continue

breakcontinue用于控制循环的执行流程,二者作用不同,需注意区分:

关键字作用适用场景
break跳出当前循环(或switch-case),不再执行后续循环满足条件时,提前终止循环
continue跳过当前循环的剩余代码,直接进入下一次循环满足条件时,跳过本次循环,继续下一次
实战案例:区分break与continue的用法
public class BreakContinueDemo {
    public static void main(String[] args) {
        // break:打印1-10,遇到5则终止循环
        System.out.println("break示例:");
        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                break; // 跳出循环,后续代码不再执行
            }
            System.out.print(i + " ");
        }
        System.out.println();
        
        // continue:打印1-10,跳过5
        System.out.println("continue示例:");
        for (int i = 1; i <= 10; i++) {
            if (i == 5) {
                continue; // 跳过本次循环,进入下一次
            }
            System.out.print(i + " ");
        }
    }
}

1.3 流程控制实战:综合案例

结合分支+循环,实现“猜数字游戏”,巩固流程控制知识点:

import java.util.Scanner;
import java.util.Random;

public class GuessNumberGame {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Random random = new Random();
        int targetNum = random.nextInt(100) + 1; // 生成1-100的随机数
        int guessNum;
        int count = 0; // 记录猜测次数
        
        System.out.println("猜数字游戏开始!(1-100之间)");
        
        do {
            System.out.print("请输入你猜测的数字:");
            guessNum = scanner.nextInt();
            count++;
            
            if (guessNum > targetNum) {
                System.out.println("猜大了!再试试~");
            } else if (guessNum < targetNum) {
                System.out.println("猜小了!再试试~");
            } else {
                System.out.println("恭喜你,猜对了!");
                System.out.println("你一共猜了" + count + "次");
            }
        } while (guessNum != targetNum);
        
        scanner.close();
    }
}

二、Java数组:存储多个相同类型数据的容器

数组是Java中最基础的引用数据类型,用于存储多个相同类型的数据,数组一旦创建,长度固定,存储在堆内存中,通过索引(下标)访问元素,索引从0开始。

2.1 数组的定义与初始化

数组的定义有两种方式,初始化分为“静态初始化”和“动态初始化”,根据是否已知数组元素值选择对应方式。

2.1.1 数组的定义(两种方式)
// 方式1:数据类型[] 数组名;(推荐,更规范)
int[] arr1;
String[] arr2;

// 方式2:数据类型 数组名[];(兼容C/C++语法,不推荐)
int arr3[];
2.1.2 静态初始化(已知元素值)

静态初始化:创建数组时,直接指定数组元素的值,数组长度由元素个数自动决定,无需手动指定长度。

语法格式:

// 完整格式
数据类型[] 数组名 = new 数据类型[]{元素1, 元素2, 元素3, ...};

// 简化格式(推荐,更简洁)
数据类型[] 数组名 = {元素1, 元素2, 元素3, ...};

实战案例:静态初始化数组

public class ArrayStaticInit {
    public static void main(String[] args) {
        // 静态初始化int数组
        int[] arr1 = {1, 2, 3, 4, 5};
        // 静态初始化String数组
        String[] arr2 = {"Java", "Python", "C++"};
        // 静态初始化double数组
        double[] arr3 = new double[]{3.14, 1.68, 2.71};
        
        // 访问数组元素(索引从0开始)
        System.out.println("arr1的第一个元素:" + arr1[0]);
        System.out.println("arr2的第二个元素:" + arr2[1]);
        System.out.println("arr3的长度:" + arr3.length); // length属性获取数组长度
    }
}
2.1.3 动态初始化(未知元素值,已知长度)

动态初始化:创建数组时,只指定数组长度,元素值由系统赋予默认值,后续再手动赋值。

语法格式:

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

数组默认值规则:

  • 整数类型(byte、short、int、long):默认值0;

  • 浮点类型(float、double):默认值0.0;

  • 布尔类型(boolean):默认值false;

  • 引用类型(String、数组等):默认值null;

  • 字符类型(char):默认值'\u0000'(空字符)。

实战案例:动态初始化数组

public class ArrayDynamicInit {
    public static void main(String[] args) {
        // 动态初始化int数组,长度为5
        int[] arr1 = new int[5];
        // 动态初始化boolean数组,长度为3
        boolean[] arr2 = new boolean[3];
        
        // 手动给数组赋值
        arr1[0] = 10;
        arr1[1] = 20;
        arr1[2] = 30;
        
        // 遍历数组,查看元素值
        System.out.println("arr1的元素:");
        for (int i = 0; i < arr1.length; i++) {
            System.out.print(arr1[i] + " "); // 输出:10 20 30 0 0
        }
        System.out.println();
        
        System.out.println("arr2的元素:");
        for (int i = 0; i < arr2.length; i++) {
            System.out.print(arr2[i] + " "); // 输出:false false false
        }
    }
}

2.2 数组的遍历(核心操作)

数组遍历是指“依次访问数组中的每一个元素”,常用的遍历方式有两种:for循环遍历(最常用)、for-each增强for循环(JDK 5+)。

2.2.1 for 循环遍历(灵活,可操作索引)

适合需要操作数组索引的场景(如修改元素值、获取元素位置)。

实战案例:遍历数组,修改元素值,计算数组平均值

public class ArrayForTraversal {
    public static void main(String[] args) {
        int[] arr = {10, 20, 30, 40, 50};
        int sum = 0;
        
        // for循环遍历,修改元素值(每个元素加5)
        for (int i = 0; i < arr.length; i++) {
            arr[i] += 5;
            sum += arr[i];
            System.out.println("arr[" + i + "] = " + arr[i]);
        }
        
        // 计算平均值
        double avg = (double) sum / arr.length;
        System.out.println("数组平均值:" + avg);
    }
}
2.2.2 for-each 增强for循环(简洁,无需索引)

适合仅需“读取元素值”,无需操作索引的场景,语法简洁,避免索引越界问题。

语法格式:

for (数据类型 变量名 : 数组名) {
    // 变量名代表数组中的当前元素
}

实战案例:for-each遍历数组,查找最大值

public class ArrayForEachTraversal {
    public static void main(String[] args) {
        int[] arr = {15, 28, 9, 45, 32};
        int max = arr[0]; // 假设第一个元素是最大值
        
        // for-each遍历,查找最大值
        for (int num : arr) {
            if (num > max) {
                max = num;
            }
        }
        
        System.out.println("数组中的最大值:" + max);
    }
}

2.3 数组的核心特性与注意事项

  1. 数组是引用数据类型,存储在堆内存中,数组名存储的是数组的内存地址;

  2. 数组一旦创建,长度固定,无法动态修改(若需动态扩容,需使用集合框架,后续讲解);

  3. 数组索引从0开始,最大索引为“数组长度-1”,超出索引范围会报ArrayIndexOutOfBoundsException(数组索引越界异常);

  4. 数组可以嵌套(二维数组、三维数组),二维数组本质是“数组的数组”;

  5. 数组的length属性是“成员属性”,不是方法(无需加括号,如arr.length,而非arr.length())。

常见错误案例(避坑):

public class ArrayErrorDemo {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};
        
        // 错误1:索引越界(最大索引是2,访问索引3)
        // System.out.println(arr[3]); // 报ArrayIndexOutOfBoundsException
        
        // 错误2:修改数组长度(数组长度固定,无法修改)
        // arr.length = 5; // 报错,length是只读属性
        
        // 错误3:静态初始化时指定长度
        // int[] arr2 = new int[3]{1,2,3}; // 报错,静态初始化不能指定长度
    }
}

2.4 二维数组(进阶)

二维数组是“数组的数组”,用于存储表格形式的数据(行和列),定义和初始化与一维数组类似,常用for嵌套遍历。

2.4.1 二维数组的初始化
public class TwoDimensionalArray {
    public static void main(String[] args) {
        // 1. 静态初始化(已知元素值)
        int[][] arr1 = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
        
        // 2. 动态初始化(已知行数和列数)
        int[][] arr2 = new int[3][4]; // 3行4列,元素默认值0
        
        // 3. 动态初始化(已知行数,列数不固定)
        int[][] arr3 = new int[3][];
        arr3[0] = new int[2]; // 第一行2列
        arr3[1] = new int[3]; // 第二行3列
        arr3[2] = new int[4]; // 第三行4列
        
        // 遍历二维数组(for嵌套)
        System.out.println("arr1的元素:");
        for (int i = 0; i < arr1.length; i++) { // 外层循环控制行数
            for (int j = 0; j < arr1[i].length; j++) { // 内层循环控制列数
                System.out.print(arr1[i][j] + "\t");
            }
            System.out.println();
        }
    }
}

2.5 数组实战:常用操作

案例1:数组反转(将数组元素倒序排列)
public class ArrayReverse {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        int left = 0; // 左指针(起始索引)
        int right = arr.length - 1; // 右指针(结束索引)
        
        // 反转逻辑:左右指针交换元素,直到左指针 >= 右指针
        while (left < right) {
            // 交换元素
            int temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
            
            left++;
            right--;
        }
        
        // 遍历反转后的数组
        System.out.println("反转后的数组:");
        for (int num : arr) {
            System.out.print(num + " "); // 输出:5 4 3 2 1
        }
    }
}
案例2:数组排序(冒泡排序,基础排序算法)

冒泡排序核心逻辑:多次遍历数组,每次比较相邻两个元素,将较大的元素“冒泡”到数组末尾,重复直到数组有序。

public class BubbleSort {
    public static void main(String[] args) {
        int[] arr = {5, 2, 9, 1, 5, 6};
        
        // 冒泡排序:外层循环控制排序轮数(n个元素,需要n-1轮)
        for (int i = 0; i < arr.length - 1; i++) {
            // 内层循环控制每轮比较次数(每轮减少1次比较)
            for (int j = 0; j < arr.length - 1 - i; j++) {
                // 相邻元素比较,交换位置(从小到大排序)
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        
        // 遍历排序后的数组
        System.out.println("排序后的数组:");
        for (int num : arr) {
            System.out.print(num + " "); // 输出:1 2 5 5 6 9
        }
    }
}

三、Java字符串:文本处理的核心工具

字符串是Java中最常用的引用数据类型,用于存储文本数据,Java中字符串由String类实现,String类是不可变类(字符串一旦创建,内容无法修改),核心特点是安全、高效,提供了丰富的方法用于文本处理。

3.1 String 类的定义与初始化

字符串的初始化有两种方式:直接赋值(推荐)、通过new关键字创建,二者在内存中的存储方式不同,需重点区分。

3.1.1 直接赋值(推荐,复用常量池)
String str1 = "Java学习";
String str2 = "Java学习";

内存原理:

  • 字符串常量池(String Constant Pool):JVM专门用于存储字符串常量的区域,避免重复创建相同的字符串;

  • 直接赋值时,JVM先检查常量池中是否存在该字符串,若存在则直接复用,若不存在则创建新的字符串常量放入常量池;

  • 因此,str1str2指向的是常量池中同一个字符串,str1 == str2结果为true(==比较的是内存地址)。

3.1.2 new 关键字创建(不推荐,创建新对象)
String str3 = new String("Java学习");
String str4 = new String("Java学习");

内存原理:

  • 通过new关键字创建字符串时,JVM会在堆内存中创建一个新的String对象,同时检查常量池:
  1. 若常量池中不存在该字符串,会先在常量池中创建字符串常量;

  2. 再将堆内存中对象的引用指向常量池中的字符串;

  • 因此,str3str4指向的是堆内存中两个不同的对象,str3 == str4结果为false,但str3.equals(str4)结果为true(equals方法比较的是字符串内容)。

实战案例:对比两种初始化方式的区别

public class StringInit {
    public static void main(String[] args) {
        // 直接赋值
        String str1 = "Java学习";
        String str2 = "Java学习";
        
        // new关键字创建
        String str3 = new String("Java学习");
        String str4 = new String("Java学习");
        
        // == 比较内存地址
        System.out.println(str1 == str2); // true(复用常量池)
        System.out.println(str3 == str4); // false(两个不同对象)
        System.out.println(str1 == str3); // false(常量池 vs 堆内存)
        
        // equals 比较字符串内容(推荐用于字符串比较)
        System.out.println(str1.equals(str2)); // true
        System.out.println(str3.equals(str4)); // true
        System.out.println(str1.equals(str3)); // true
    }
}

3.2 String 类的核心方法(高频实用)

String类提供了大量方法用于字符串处理,以下是开发中最常用的方法,结合案例讲解,务必掌握。

3.2.1 字符串长度与空判断
  • int length():获取字符串的长度(字符个数);

  • boolean isEmpty():判断字符串是否为空(长度为0);

  • boolean isBlank():判断字符串是否为空或仅包含空白字符(JDK 11+,推荐)。

public class StringLength {
    public static void main(String[] args) {
        String str1 = "Java学习笔记";
        String str2 = ""; // 空字符串
        String str3 = "   "; // 空白字符
        
        System.out.println("str1长度:" + str1.length()); // 6
        System.out.println("str2是否为空:" + str2.isEmpty()); // true
        System.out.println("str3是否为空:" + str3.isEmpty()); // false
        System.out.println("str3是否为空白:" + str3.isBlank()); // true
    }
}
3.2.2 字符串拼接与替换
  • String concat(String str):拼接两个字符串(等价于+,但效率更高);

  • String replace(CharSequence target, CharSequence replacement):替换字符串中的指定内容;

  • String replaceAll(String regex, String replacement):根据正则表达式替换内容(更灵活)。

public class StringConcatReplace {
    public static void main(String[] args) {
        String str = "Java基础语法";
        
        // 拼接字符串
        String str1 = str.concat(",进阶学习");
        String str2 = str + ",进阶学习"; // 等价于concat,可读性更强
        System.out.println(str1); // Java基础语法,进阶学习
        System.out.println(str2); // Java基础语法,进阶学习
        
        // 替换字符串
        String str3 = str.replace("基础", "核心");
        System.out.println(str3); // Java核心语法
        
        // 替换所有符合正则的内容(替换所有数字)
        String str4 = "Java123基础456语法789";
        String str5 = str4.replaceAll("\\d+", ""); // \\d+ 匹配一个或多个数字
        System.out.println(str5); // Java基础语法
    }
}
3.2.3 字符串截取与分割
  • String substring(int beginIndex):从指定索引开始,截取到字符串末尾;

  • String substring(int beginIndex, int endIndex):从beginIndex截取到endIndex(左闭右开,不包含endIndex);

  • String[] split(String regex):根据指定分隔符分割字符串,返回字符串数组。

public class StringSubstringSplit {
    public static void main(String[] args) {
        String str = "Java-基础-语法-实战";
        
        // 截取字符串
        String str1 = str.substring(5); // 从索引5开始截取
        System.out.println(str1); // 基础-语法-实战
        
        String str2 = str.substring(5, 8); // 从5截取到8(不包含8)
        System.out.println(str2); // 基础
        
        // 分割字符串
        String[] strArr = str.split("-"); // 以"-"为分隔符
        System.out.println("分割后的数组:");
        for (String s : strArr) {
            System.out.print(s + " "); // Java 基础 语法 实战
        }
    }
}
3.2.4 字符串大小写转换与去空格
  • String toLowerCase():将字符串转为小写;

  • String toUpperCase():将字符串转为大写;

  • String trim():去除字符串首尾的空白字符(不包含中间的空白);

  • String strip():去除字符串首尾的空白字符(支持Unicode空白,JDK 11+,推荐)。

public class StringCaseTrim {
    public static void main(String[] args) {
        String str = "   Java Learning 笔记   ";
        
        // 大小写转换
        String str1 = str.toLowerCase();
        String str2 = str.toUpperCase();
        System.out.println(str1); //    java learning 笔记   
        System.out.println(str2); //    JAVA LEARNING 笔记   
        
        // 去空格
        String str3 = str.trim();
        String str4 = str.strip();
        System.out.println("trim去空格:" + str3); // Java Learning 笔记
        System.out.println("strip去空格:" + str4); // Java Learning 笔记
    }
}
3.2.5 字符串查找与包含
  • int indexOf(String str):查找指定字符串在当前字符串中第一次出现的索引,找不到返回-1;

  • int lastIndexOf(String str):查找指定字符串在当前字符串中最后一次出现的索引,找不到返回-1;

  • boolean contains(CharSequence s):判断当前字符串是否包含指定内容;

  • boolean startsWith(String prefix):判断当前字符串是否以指定前缀开头;

  • boolean endsWith(String suffix):判断当前字符串是否以指定后缀结尾。

public class StringSearch {
    public static void main(String[] args) {
        String str = "Java基础语法,Java进阶学习";
        
        // 查找索引
        int index1 = str.indexOf("Java");
        int index2 = str.lastIndexOf("Java");
        System.out.println("第一次出现Java的索引:" + index1); // 0
        System.out.println("最后一次出现Java的索引:" + index2); // 6
        
        // 包含、前缀、后缀判断
        boolean hasJava = str.contains("Java");
        boolean startWithJava = str.startsWith("Java");
        boolean endWithStudy = str.endsWith("学习");
        System.out.println("是否包含Java:" + hasJava); // true
        System.out.println("是否以Java开头:" + startWithJava); // true
        System.out.println("是否以学习结尾:" + endWithStudy); // true
    }
}

3.3 String 不可变性详解(核心考点)

String类是不可变类,核心原因:String类内部使用private final char[] value存储字符串内容,final修饰的数组无法修改引用,且value是私有属性,外部无法直接访问和修改。

注意:我们平时使用的replacesubstring等方法,并不是修改原字符串,而是创建一个新的字符串对象,原字符串内容不变。

public class StringImmutable {
    public static void main(String[] args) {
        String str = "Java";
        
        // 调用replace方法,原字符串不变,返回新对象
        String newStr = str.replace("Java", "JavaSE");
        
        System.out.println(str); // Java(原字符串未修改)
        System.out.println(newStr); // JavaSE(新字符串)
        
        // 验证:str和newStr是两个不同的对象
        System.out.println(str == newStr); // false
    }
}

不可变性的优势:

  1. 安全:避免字符串被意外修改,适合作为常量、密码等敏感数据;

  2. 高效:字符串常量池可复用相同的字符串,减少内存占用;

  3. 线程安全:不可变对象天生线程安全,无需同步处理。

3.4 字符串实战:综合案例

案例1:统计字符串中指定字符出现的次数
public class StringCountChar {
    public static void main(String[] args) {
        String str = "Java基础语法,Java进阶学习,Java实战案例";
        char target = 'J';
        int count = 0;
        
        // 遍历字符串,统计目标字符出现次数
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == target) { // charAt(i)获取指定索引的字符
                count++;
            }
        }
        
        System.out.println("字符'" + target + "'在字符串中出现了" + count + "次");
    }
}
案例2:判断字符串是否是回文(正读和反读一样,如“abba”)
public class StringPalindrome {
    public static void main(String[] args) {
        String str = "abba";
        boolean isPalindrome = true;
        
        // 回文判断:左右指针对比字符
        int left = 0;
        int right = str.length() - 1;
        
        while (left < right) {
            if (str.charAt(left) != str.charAt(right)) {
                isPalindrome = false;
                break;
            }
            left++;
            right--;
        }
        
        if (isPalindrome) {
            System.out.println(str + "是回文");
        } else {
            System.out.println(str + "不是回文");
        }
    }
}

四、AI辅助Java流程控制、数组、字符串学习

结合AI工具,可快速解决学习中的难点、排查错误、生成案例,提升学习效率,以下是高频实用场景:

4.1 AI排查代码错误

将报错代码复制给AI,快速定位问题并给出解决方案,例如:

问题:以下代码报错ArrayIndexOutOfBoundsException,帮忙分析原因并修改
public class ErrorDemo {
    public static void main(String[] args) {
        int[] arr = {1,2,3};
        for (int i = 0; i <= arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

AI会直接指出“索引越界”(循环条件i <= arr.length错误,应改为i < arr.length),并给出修改后的完整代码。

4.2 AI生成自定义案例

输入需求,让AI生成符合需求的代码案例,例如:

生成Java数组排序的案例,包含冒泡排序、选择排序两种方式,带详细注释

AI会生成完整的排序代码,并添加注释,帮助理解排序逻辑。

4.3 AI讲解底层原理

针对复杂知识点(如String不可变性、数组内存存储),输入:

讲解Java String类的不可变性,结合源码说明为什么字符串无法修改

AI会结合String类的源码(private final char[] value),详细解析不可变性的实现原理,比死记硬背更高效。

Java基础语法记录(第二期):流程控制+数组+字符串实战 > 本文承接上一期基础语法,聚焦Java流程控制、数组、字符串三大核心知识点,结合实战代码、底层