Java基础语法记录(第二期):流程控制+数组+字符串实战
本文承接上一期基础语法,聚焦Java流程控制、数组、字符串三大核心知识点,结合实战代码、底层原理解析,搭配AI辅助学习技巧,全程干货无冗余,适配掘金/CSDN技术博客排版,可直接复制到md文档,夯实Java基础进阶能力。
一、Java流程控制:程序执行的“导航仪”
流程控制是Java程序执行的核心逻辑,用于控制代码的执行顺序,主要分为分支结构和循环结构两大类,合理使用流程控制可让代码逻辑清晰、高效简洁,也是后续复杂业务逻辑编写的基础。
1.1 分支结构:根据条件执行不同代码
分支结构核心是“判断条件,执行对应逻辑”,Java中常用的分支结构有if-else、if-else if-else、switch-case三种,适用场景各有差异,需根据实际需求选择。
1.1.1 if-else 结构(最常用)
if-else是最基础的分支结构,核心逻辑:判断条件是否成立,成立则执行if块代码,否则执行else块代码(else可选)。
语法格式:
if (条件表达式) {
// 条件成立时执行的代码块
} else {
// 条件不成立时执行的代码块(可选)
}
核心注意点:
-
条件表达式必须是布尔类型(
true/false),不能是其他类型(如int、String); -
若代码块只有一行,可省略大括号,但建议始终保留(避免逻辑错乱);
-
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 (表达式) {
case 值1:
// 表达式等于值1时执行的代码
break; // 跳出switch,避免穿透
case 值2:
// 表达式等于值2时执行的代码
break;
...
default:
// 表达式不匹配任何case时执行的代码(可选)
break;
}
核心注意点:
-
JDK 7及以上,
switch表达式支持byte、short、int、char、String、枚举类型; -
case后的值必须是常量(不能是变量),且不能重复; -
必须加
break,否则会发生“case穿透”(执行当前case后,继续执行后续所有case,直到遇到break); -
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-else | 2个分支、条件复杂(如范围判断) | 灵活,可处理任意布尔条件 | 多分支时代码冗长,可读性差 |
| if-else if-else | 3个及以上分支、条件有范围 | 逻辑清晰,适配范围判断 | 分支过多时,代码冗余 |
| switch-case | 3个及以上分支、多值匹配(固定值) | 代码简洁,可读性强 | 不支持范围判断,必须加break |
1.2 循环结构:重复执行指定代码
循环结构用于“重复执行一段代码”,直到满足终止条件,Java中常用的循环结构有for、while、do-while三种,核心区别在于循环条件的判断时机和适用场景。
1.2.1 for 循环(最常用,固定次数循环)
for循环适用于已知循环次数的场景,语法结构清晰,将循环初始化、条件判断、迭代操作集中在一起,便于维护。
语法格式:
for (循环初始化; 循环条件; 迭代操作) {
// 循环体:需要重复执行的代码
}
执行流程:
-
执行循环初始化(仅执行1次);
-
判断循环条件,若成立则执行循环体;
-
执行迭代操作(更新循环变量);
-
重复步骤2-3,直到循环条件不成立,退出循环。
核心注意点:
-
循环初始化、循环条件、迭代操作均可省略,但分号不能省略(省略后会变成无限循环);
-
循环变量的作用域仅限于for循环内部,外部无法访问;
-
可嵌套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 (循环条件) {
// 循环体
// 迭代操作(必须有,否则会无限循环)
}
核心注意点:
-
循环初始化必须在while循环外部,否则每次循环都会重新初始化,导致逻辑错误;
-
循环体中必须有迭代操作,否则会出现无限循环(死循环);
-
循环条件必须是布尔类型,成立则执行循环体,不成立则退出循环。
实战案例:打印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
break和continue用于控制循环的执行流程,二者作用不同,需注意区分:
| 关键字 | 作用 | 适用场景 |
|---|---|---|
| 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 数组的核心特性与注意事项
-
数组是引用数据类型,存储在堆内存中,数组名存储的是数组的内存地址;
-
数组一旦创建,长度固定,无法动态修改(若需动态扩容,需使用集合框架,后续讲解);
-
数组索引从0开始,最大索引为“数组长度-1”,超出索引范围会报
ArrayIndexOutOfBoundsException(数组索引越界异常); -
数组可以嵌套(二维数组、三维数组),二维数组本质是“数组的数组”;
-
数组的
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先检查常量池中是否存在该字符串,若存在则直接复用,若不存在则创建新的字符串常量放入常量池;
-
因此,
str1和str2指向的是常量池中同一个字符串,str1 == str2结果为true(==比较的是内存地址)。
3.1.2 new 关键字创建(不推荐,创建新对象)
String str3 = new String("Java学习");
String str4 = new String("Java学习");
内存原理:
- 通过
new关键字创建字符串时,JVM会在堆内存中创建一个新的String对象,同时检查常量池:
-
若常量池中不存在该字符串,会先在常量池中创建字符串常量;
-
再将堆内存中对象的引用指向常量池中的字符串;
- 因此,
str3和str4指向的是堆内存中两个不同的对象,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是私有属性,外部无法直接访问和修改。
注意:我们平时使用的replace、substring等方法,并不是修改原字符串,而是创建一个新的字符串对象,原字符串内容不变。
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
}
}
不可变性的优势:
-
安全:避免字符串被意外修改,适合作为常量、密码等敏感数据;
-
高效:字符串常量池可复用相同的字符串,减少内存占用;
-
线程安全:不可变对象天生线程安全,无需同步处理。
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流程控制、数组、字符串三大核心知识点,结合实战代码、底层