Java 入门与核心基础

本文是一份 Java 入门与核心基础教程,覆盖 JDK 安装、开发环境搭建、基础语法、变量、运算符、流程控制、数组、方法、面向对象、String 基础与 StringBuilder 入门等内容。文章通过循序渐进的代码示例,帮助读者建立 Java 编程的完整基础认知。

第一章 开发环境搭建

1.1 初识 Java

Java 的故事

Java 是 1995 年诞生的高级编程语言,最大特点是 “一次编写,到处运行” 。你在一台电脑上写好的 Java 程序,放到另一台完全不同操作系统的电脑上也能直接运行,靠的就是 JVM(Java 虚拟机)。现在 Java 广泛应用于企业级应用、手机 App 开发、大数据等领域。

安装 JDK

JDK 是开发 Java 程序的工具箱。学习与企业开发建议安装 JDK 17 LTS(长期支持版本)。

下载渠道(任选其一):

Windows 安装示例:下载 jdk-17_windows-x64.msi.exe,双击安装并记住路径,例如 C:\Program Files\Java\jdk-17

常用 DOS 命令(Windows 必会)

我们先通过小黑窗(命令提示符)感受一下如何与电脑“对话”。 按下 Win + R,输入 cmd 回车,出现黑色窗口后尝试:

Bash
D:               # 切换到 D 盘
cd java_demo     # 进入 java_demo 文件夹
dir              # 查看当前文件夹里的内容
cd ..            # 回到上一级

第一个 Java 程序:HelloWorld

Step 1:写代码 在 D 盘新建文件夹 java_demo,里面新建一个 HelloWorld.java 文件(注意文件扩展名是 .java)。用记事本打开,输入以下代码:

Java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, 小白你好!");
    }
}

代码解释(小白第一眼不用完全理解,先照着写):

  • public class HelloWorld:定义了一个公开的类,类名 HelloWorld 必须和文件名一模一样。
  • public static void main(String[] args):这是程序的“入口”,Java 程序会从这里一句一句往下执行。
  • System.out.println(...):在屏幕上打印括号里的内容,运行后就能看到 “Hello, 小白你好!”。

Step 2:编译 在地址栏输入 cmd 打开命令行,执行:

Bash
javac HelloWorld.java

如果没有出现任何错误提示,文件夹里就会多出一个 HelloWorld.class 文件,这就是编译好的字节码。

Step 3:运行 继续输入:

Bash
java HelloWorld

屏幕上显示 Hello, 小白你好!,恭喜你,第一个程序成功运行!

常见错误处理

  • 提示 错误: 找不到文件:检查文件名后缀是否为 .java 以及是否打开了“隐藏已知文件类型的扩展名”。
  • 提示 错误: 需要 ';':Java 每条语句结尾必须写分号 ;,忘记会报错。
  • 提示 HelloWorld.java:3: 错误: 找不到符号:多是因为大小写写错,比如把 System 写成了 system

Java 程序执行原理(简要)

.java 源文件 --[编译(javac)]--> .class 字节码文件 --[运行在 JVM]--> 屏幕输出。 JVM 在不同系统上都是一个“中间翻译”,让同一份字节码到处都能跑。

1.2 配置环境变量与 IntelliJ IDEA

Path 环境变量

为什么我们能在任何文件夹下运行 javac?因为系统知道它在哪里。需要把 JDK 的 bin 目录告诉系统:

  1. 右键“此电脑” → 属性 → 高级系统设置 → 环境变量。
  2. 系统变量里新建 JAVA_HOME,变量值填 JDK 安装路径,比如 C:\Program Files\Java\jdk-17
  3. 在系统变量里找到 Path,编辑,新增一行 %JAVA_HOME%\bin
  4. 打开一个新的命令行,输入 javac -version,出现版本号就说明配置成功。

IntelliJ IDEA 工具

IDEA 是写 Java 的神器,免费社区版就够用。下载安装后,我们创建第一个工程:

  • 新建项目,选 Java,设置好 JDK 版本。
  • src 文件夹右键 → New → Java Class,取名 HelloIDEA
  • 在类里写 main 方法并运行:
Java
public class HelloIDEA {
    public static void main(String[] args) {
        System.out.println("IDEA 真好用!");
    }
}

点击行号左边的绿色三角,就能看到结果。 IDEA 小技巧:输入 psvm 回车立刻生成 main 方法;sout 回车立刻生成 System.out.println()


第二章 基础语法(第一行程序逻辑)

2.1 注释、关键字和字面量

注释:给人看的内容,程序不会执行。

Java
// 这是单行注释

/*
   这是多行注释
   可以写很多行
*/

/**
 * 文档注释,通常用在类和方法上
 */
public class Demo {
    public static void main(String[] args) {
        System.out.println("注释演示"); // 行尾注释
    }
}

关键字:Java 自己预留的特殊单词,比如 public, class, static, void,我们给变量起名不能和它们重名。

字面量:直接写出来的常量。

Java
System.out.println(10);        // 整数
System.out.println(3.14);      // 小数
System.out.println('A');       // 字符,单引号
System.out.println("Hello");   // 字符串,双引号
System.out.println(true);      // 布尔值

2.2 变量与数据类型

变量:存放数据的“小盒子”,里面的值可以变化。 定义格式:数据类型 变量名 = 初始值;

Java
public class VarDemo {
    public static void main(String[] args) {
        int age = 20;            // 整数
        double score = 88.5;     // 小数
        char gender = '男';      // 单个字符
        String name = "张三";    // 字符串(注意 String 大写)
        boolean flag = true;    // 布尔,只有 true 或 false

        System.out.println(age);
        System.out.println(name);
    }
}

使用变量注意事项

  • 不能重名,同一个大括号里只能定义一次同名变量。
  • 变量必须先赋值再使用,否则编译报错。
  • 起名要见名知意,用驼峰命名法:studentName, maxScore

基本数据类型(8 种)

  • 整数:byte (很小), short (较小), int (默认常用), long (大,加 L)
  • 小数:float (加 F), double (默认)
  • 字符:char
  • 布尔:boolean

代码演示:

Java
long bigNum = 10000000000L;  // long 类型加 L
float price = 9.9F;          // float 类型加 F

2.3 运算符

算术运算+ - * / %

Java
int a = 10, b = 3;
System.out.println(a + b);  // 13
System.out.println(a - b);  // 7
System.out.println(a * b);  // 30
System.out.println(a / b);  // 3 (整数除法,舍弃小数)
System.out.println(a % b);  // 1 (取余)

字符串拼接+ 一边是字符串,就会拼接。

Java
String name = "小明";
int age = 18;
System.out.println(name + "今年" + age + "岁"); // 小明今年18岁

自增自减++--

Java
int x = 5;
x++;           // x 变为 6(单独使用前后都行)
System.out.println(x); // 6

int y = 5;
int result = ++y; // 先加后用,result=6, y=6
int result2 = y++; // 先用后加,result2=6, y=7

赋值和关系运算符

Java
int num = 10;
num += 5;   // 等价 num = num + 5; 现在 num 是 15

boolean r1 = 10 == 10;  // true
boolean r2 = 10 != 5;   // true
boolean r3 = 3 > 5;     // false

逻辑运算符:拼接多个条件判断

Java
boolean b1 = (3 > 2) && (5 > 3); // true,短路与,两边都真才是真
boolean b2 = (3 > 2) || (1 > 5); // true,短路或,一边真就是真
boolean b3 = !true;              // false

三元运算符条件 ? 值1 : 值2

Java
int score = 75;
String result = score >= 60 ? "及格" : "不及格"; // 及格

类型转换

Java
// 自动转换:小范围 -> 大范围
double d = 10;       // int 自动变 double

// 强制转换:大范围 -> 小范围 (可能丢失精度)
int i = (int) 3.99;  // i 是 3,小数直接砍掉

2.4 Scanner 键盘录入

让程序能接收用户的输入。

Java
import java.util.Scanner;  // 1. 导包

public class ScannerDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in); // 2. 创建对象

        System.out.println("请输入你的名字:");
        String name = sc.next();             // 3. 接收字符串(不含空格)

        System.out.println("请输入你的年龄:");
        int age = sc.nextInt();              // 接收整数

        System.out.println("姓名:" + name + ",年龄:" + age);
    }
}

其他接收:nextDouble() 小数,nextLine() 整行字符串(会接收回车,需注意混用问题)。

2.5 方法(重要的打包工具)

方法就是把一段代码打包,起个名,以后直接调用。

Java
public class MethodDemo {
    public static void main(String[] args) {
        // 调用方法
        printHello();
        int sum = add(5, 3);
        System.out.println("和为:" + sum);
    }

    // 1. 无参数无返回值的方法
    public static void printHello() {
        System.out.println("Hello from method!");
    }

    // 2. 有参数有返回值的方法
    public static int add(int a, int b) {
        return a + b;
    }
}

方法重载:同一个类中,方法名相同,参数列表不同(个数、类型、顺序),与返回值无关。

Java
public static int add(int a, int b) { return a + b; }
public static double add(double a, double b) { return a + b; } // 重载
public static int add(int a, int b, int c) { return a + b + c; } // 也是重载

package 管理

把 Java 文件分文件夹存放,避免类名冲突。

Java
package com.itbaizhan.study;  // 必须在文件第一行

public class Student {
    // ...
}

文件夹结构对应:com/itbaizhan/study/Student.java


第三章 流程控制语句(让程序活起来)

3.1 if 分支:条件判断

Java
public class IfDemo {
    public static void main(String[] args) {
        int score = 85;
        // 格式1:单分支
        if (score >= 60) {
            System.out.println("及格了");
        }

        // 格式2:双分支
        if (score >= 60) {
            System.out.println("及格");
        } else {
            System.out.println("不及格");
        }

        // 格式3:多分支
        if (score >= 90) {
            System.out.println("优秀");
        } else if (score >= 80) {
            System.out.println("良好");
        } else if (score >= 60) {
            System.out.println("及格");
        } else {
            System.out.println("不及格");
        }
    }
}

注意事项:大括号里只有一行语句时,大括号可以省略,但不建议。

3.2 switch 分支:多值匹配

Java
public class SwitchDemo {
    public static void main(String[] args) {
        int day = 3;
        switch (day) {
            case 1:
                System.out.println("星期一");
                break;  // 别忘了 break,否则会穿透
            case 2:
                System.out.println("星期二");
                break;
            case 3:
                System.out.println("星期三");
                break;
            default:
                System.out.println("输入有误");
        }
    }
}

支持 byte, short, int, char, String, 枚举

Java 14+ 箭头写法(无需 break,多分支可合并):

Java
String week = switch (day) {
    case 1 -> "星期一";
    case 2 -> "星期二";
    case 3 -> "星期三";
    default -> "输入有误";
};
System.out.println(week);

3.3 for 循环:重复做事情

Java
// 打印 1 到 5
for (int i = 1; i <= 5; i++) {
    System.out.println("第" + i + "次循环");
}

练习:求 1-100 之间的偶数和

Java
int sum = 0;
for (int i = 1; i <= 100; i++) {
    if (i % 2 == 0) {
        sum += i;
    }
}
System.out.println("偶数和:" + sum); // 2550

嵌套循环:打印九九乘法表

Java
for (int i = 1; i <= 9; i++) {
    for (int j = 1; j <= i; j++) {
        System.out.print(j + "*" + i + "=" + (j*i) + "\t");
    }
    System.out.println(); // 换行
}

3.4 while 与 do-while 循环

Java
// while:先判断后执行
int a = 1;
while (a <= 5) {
    System.out.println(a);
    a++;
}

// do-while:先执行一次再判断(至少执行一次)
int b = 1;
do {
    System.out.println(b);
    b++;
} while (b <= 5);

3.5 break 和 continue

Java
// break: 结束整个循环
for (int i = 1; i <= 10; i++) {
    if (i == 5) break;
    System.out.print(i + " "); // 打印 1 2 3 4
}

// continue: 跳过本次循环,继续下一次
for (int i = 1; i <= 5; i++) {
    if (i == 3) continue;
    System.out.print(i + " "); // 打印 1 2 4 5
}

3.6 Random 随机数

Java
import java.util.Random;

public class RandomDemo {
    public static void main(String[] args) {
        Random r = new Random();
        int number = r.nextInt(100); // 0 到 99 的随机整数
        System.out.println(number);

        // 猜数字小游戏(简单版)
        int guessNum = r.nextInt(100) + 1; // 1-100
        // ... 可结合循环和扫描器完成猜数字
    }
}

第四章 数组(一堆数据的容器)

4.1 数组的定义与初始化

Java
// 静态初始化:已知元素
int[] arr1 = new int[]{10, 20, 30};
int[] arr2 = {40, 50, 60};      // 简写形式

String[] names = {"张三", "李四", "王五"};

4.2 访问元素和遍历

索引从 0 开始。

Java
int[] arr = {10, 20, 30};
System.out.println(arr[0]);     // 10
System.out.println(arr.length); // 3,数组长度

// 遍历
for (int i = 0; i < arr.length; i++) {
    System.out.println("第" + i + "个元素:" + arr[i]);
}

4.3 常见案例

求最大值

Java
int[] scores = {85, 92, 78, 99, 63};
int max = scores[0];
for (int i = 1; i < scores.length; i++) {
    if (scores[i] > max) {
        max = scores[i];
    }
}
System.out.println("最高分:" + max);

数组反转(头尾交换):

Java
int[] arr = {1, 2, 3, 4, 5};
for (int i = 0, j = arr.length - 1; i < j; i++, j--) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
// 打印反转后:5 4 3 2 1

4.4 动态初始化

Java
int[] arr = new int[5];   // 默认值都是 0
arr[0] = 10;
arr[1] = 20;
// ...

4.5 数组常见问题

  • 索引越界:arr[5] 当长度只有 5 时(最大索引 4),抛出 ArrayIndexOutOfBoundsException
  • 空指针:int[] arr = null; arr[0] = 10; 抛出 NullPointerException

4.6 二维数组(表格数据)

Java
int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};
// 遍历
for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        System.out.print(matrix[i][j] + " ");
    }
    System.out.println();
}

输出:

Text
1 2 3
4 5 6
7 8 9

第五章 面向对象基础(编程思维升级)

5.1 类与对象

是模板,对象是具体产品。

Java
// 学生类
public class Student {
    // 属性(成员变量)
    String name;
    int age;
    
    // 行为(成员方法)
    public void study() {
        System.out.println(name + "正在学习...");
    }
    
    public void doHomework() {
        System.out.println(name + "正在写作业,年龄:" + age);
    }
}

创建并使用对象:

Java
public class Test {
    public static void main(String[] args) {
        Student stu = new Student(); // 创建对象
        stu.name = "小明";           // 赋值属性
        stu.age = 18;
        stu.study();                 // 调用方法
        stu.doHomework();
    }
}

5.2 封装与 this

封装:用 private 隐藏属性,通过公共的 get/set 方法访问。

Java
public class Person {
    private String name; // 私有
    private int age;

    // setter 方法:设置值
    public void setName(String name) {
        this.name = name; // this 指当前对象
    }
    // getter 方法:获取值
    public String getName() {
        return name;
    }
    
    public void setAge(int age) {
        if (age >= 0 && age <= 150) {
            this.age = age;
        } else {
            System.out.println("年龄不合法");
        }
    }
    public int getAge() {
        return age;
    }
}

使用:

Java
Person p = new Person();
p.setName("小红");
p.setAge(20);
System.out.println(p.getName() + " " + p.getAge());

5.3 构造方法

构造方法是对象创建时自动调用的方法,用来初始化对象。

Java
public class Student {
    private String name;
    private int age;
    
    // 无参构造(若不写,系统默认给;一旦手写有参,建议补上无参)
    public Student() {}
    
    // 有参构造
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    // getter/setter 省略...
}

创建对象:

Java
Student s1 = new Student();              // 调用无参构造
Student s2 = new Student("小刚", 20);    // 调用有参构造

5.4 标准 JavaBean

一个标准的 JavaBean 满足:

  1. 类公开
  2. 属性私有
  3. 提供无参和全参构造
  4. 提供 getter 和 setter

可以按照这个模板写大多数实体类。

5.5 static 关键字

static 表示“属于类”,所有对象共享。

Java
public class Chinese {
    static String country = "中国"; // 共享属性
    String name;                   // 每个对象独有

    public Chinese(String name) {
        this.name = name;
    }
    
    public static void showCountry() {
        System.out.println("国籍:" + country);
        // 静态方法不能直接访问非静态成员 name
    }
}

使用:

Java
Chinese c1 = new Chinese("小明");
Chinese c2 = new Chinese("小红");
System.out.println(c1.country); // 中国
System.out.println(c2.country); // 中国
Chinese.country = "中华人民共和国"; // 推荐用类名访问
System.out.println(c1.country); // 中华人民共和国

5.6 工具类的制作(私有构造方法)

在日常开发中,我们经常需要编写一些只包含静态方法的工具类(例如 MathArrays 类)。与 C# 可以直接定义 public static class 不同,Java 不支持顶级的静态类。

为了防止其他开发者误用 new 关键字实例化这些仅仅提供静态方法的工具类,标准的做法是私有化构造方法

Java
public final class ArrayUtils {
    
    // 1. 私有化无参构造,隐藏实例化入口
    private ArrayUtils() {
        // 推荐做法:抛出异常,防止在类内部或通过反射机制被意外调用
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }

    // 2. 暴露静态方法
    public static int getMax(int[] arr) {
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if (arr[i] > max) max = arr[i];
        }
        return max;
    }
}
  • final 修饰类:防止该类被继承,从而避免子类在内部实例化或破坏设计。
  • private 构造方法:禁止外部直接 new ArrayUtils()

第六章 面向对象高级(能力进阶)

6.1 继承

继承就是子承父业,减少重复代码,使用 extends

Java
// 父类
public class Animal {
    String name;
    public void eat() {
        System.out.println(name + "吃东西");
    }
}

// 子类
public class Dog extends Animal {
    public void bark() {
        System.out.println(name + "汪汪叫");
    }
}

测试:

Java
Dog dog = new Dog();
dog.name = "旺财";
dog.eat();  // 来自父类
dog.bark(); // 自己独有

方法重写:子类觉得父类方法不满足需求,就重写一个。

Java
public class Cat extends Animal {
    @Override   // 注解,检查是否正确重写
    public void eat() {
        System.out.println(name + "优雅地吃鱼");
    }
}

super 关键字调用父类成员:

Java
public class Son extends Father {
    public void show() {
        super.show(); // 先调用父类的 show
        System.out.println("子类的扩展");
    }
}

继承中的构造方法:子类构造器第一行默认有 super() 调用父类无参构造。如果父类只有有参构造,子类必须手动 super(参数)

6.2 final 关键字

  • final class:不能被继承
  • final 方法:不能被重写
  • final 变量:变成常量,只能赋值一次(基本类型值不变,引用类型地址不变)

6.3 抽象类

抽象类不能实例化,专门当爹用,定义一些抽象方法让子类必须实现。

Java
public abstract class Shape {
    public abstract double area(); // 没有方法体
}

public class Circle extends Shape {
    double r;
    public Circle(double r) { this.r = r; }
    @Override
    public double area() {
        return Math.PI * r * r;
    }
}

6.4 接口

接口是一种行为规范:规定「能做什么」,由实现类决定「怎么做」。Java 8 起接口除抽象方法外,还可以包含 default 默认方法static 静态方法

Java
public interface Flyable {
    void fly(); // 默认 public abstract

    default void glide() {
        System.out.println("滑翔辅助");
    }

    static void showTip() {
        System.out.println("实现 fly() 即可");
    }
}

public class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("鸟儿扇动翅膀飞");
    }
}

public class Airplane implements Flyable {
    @Override
    public void fly() {
        System.out.println("飞机引擎驱动飞");
    }
}

一个类可以实现多个接口:class SuperMan implements Flyable, Swimable { }

6.5 多态

多态就是 父类引用指向子类对象

Java
Animal a = new Dog(); // 编译看左边(Animal),运行看右边(Dog)
a.eat();              // 调用的是 Dog 重写后的 eat
// a.bark();          // 编译错误,不能调用子类独有方法

转型

Java
if (a instanceof Dog) {
    Dog d = (Dog) a;    // 向下转型
    d.bark();
}

6.6 内部类与 Lambda 表达式

匿名内部类:因为没有名字,直接 new 接口/抽象类,重写方法。

Java
Flyable f = new Flyable() {
    @Override
    public void fly() {
        System.out.println("匿名内部类的方式飞");
    }
};
f.fly();

Lambda 表达式:简化匿名内部类,只适用于函数式接口(只有一个抽象方法的接口)。

Java
Flyable f2 = () -> { System.out.println("Lambda 方式飞"); };
f2.fly();
// 省略更多:若只有一行代码,大括号和分号也可省略
Flyable f3 = () -> System.out.println("极简飞");

第七章 String 与 StringBuilder

7.1 String 是什么

String 表示字符串,也就是一串字符。在 Java 里,字符串不是基本类型,而是一个类,位于 java.lang 包下,所以可以直接使用。

Java
String s1 = "hello";
String s2 = "Java";
String s3 = new String("world");

常见创建方式:

  • String s = "abc";:最常用,推荐写法。
  • new String("abc"):也能创建,但一般没必要。

String 的核心特点:不可变

字符串一旦创建,内容就不能被直接修改。看起来像“修改”,本质上是生成了新字符串对象。

Java
String name = "Tom";
name = name + " Lee";
System.out.println(name); // Tom Lee

上面不是把原来的 "Tom" 改掉了,而是让变量 name 指向了一个新字符串。

7.2 字符串常量池与创建差异

Java 为了节省内存,对字符串字面量做了复用,这个区域通常叫字符串常量池

Java
String a = "abc";
String b = "abc";
String c = new String("abc");

System.out.println(a == b); // true,指向常量池中同一个字符串对象
System.out.println(a == c); // false,c 是 new 出来的新对象
System.out.println(a.equals(c)); // true,内容一样

重点:

  • 直接写 "abc",会优先复用常量池中的对象。
  • new String("abc") 会在堆内存中再创建新对象。
  • == 在这里体现的是“是否为同一个对象”,不是“内容是否相同”。

7.3 String 常用方法

String 在业务开发中非常常见,下面这些方法最常用:

方法作用示例
length()获取长度"abc".length() 得到 3
charAt(i)获取指定位置字符"abc".charAt(1) 得到 'b'
equals(str)比较内容是否相同"abc".equals("abc")
equalsIgnoreCase(str)忽略大小写比较"java".equalsIgnoreCase("JAVA")
substring(begin, end)截取子串,左闭右开"abcdef".substring(1, 4) 得到 "bcd"
contains(str)是否包含子串"hello".contains("ll")
startsWith(str)是否以指定内容开头"java".startsWith("ja")
endsWith(str)是否以指定内容结尾"a.txt".endsWith(".txt")
replace(old, new)替换内容"2024-01".replace("-", "/")
split(regex)按规则拆分字符串"a,b,c".split(",")
trim()去掉首尾空白" hi ".trim()
toUpperCase()转大写"java".toUpperCase()
toLowerCase()转小写"JAVA".toLowerCase()

代码示例:

Java
public class StringMethodDemo {
    public static void main(String[] args) {
        String str = "  Hello Java  ";

        System.out.println(str.length());
        System.out.println(str.charAt(2));
        System.out.println(str.trim());
        System.out.println(str.contains("Java"));
        System.out.println(str.substring(2, 7));
        System.out.println(str.replace("Java", "JDK"));
    }
}

7.4 比较字符串时的常见坑

上面的方法表已经列出常用 API,这里专门补充初学者最容易踩坑的几个点。

1. == 比较地址,equals() 比较内容

Java
String s1 = "admin";
String s2 = new String("admin");

System.out.println(s1 == s2);      // false
System.out.println(s1.equals(s2)); // true

2. 注意空指针问题

如果变量可能为 null,直接调用 str.equals("abc") 可能报错。

Java
String role = null;
// System.out.println(role.equals("admin")); // 可能抛出空指针异常
System.out.println("admin".equals(role));    // 更安全

3. split 的参数是正则表达式

.| 这些字符在正则里有特殊含义,需要转义。

Java
String ip = "192.168.1.1";
String[] arr = ip.split("\\.");

7.5 为什么循环拼接字符串不推荐直接用 +

前面在运算符部分已经讲过 + 可以做字符串拼接,这里补充它在性能上的注意点。

少量拼接写 + 很方便,但在循环里频繁拼接会产生很多中间字符串对象,性能较差。

Java
String result = "";
for (int i = 0; i < 5; i++) {
    result = result + i;
}
System.out.println(result);

因为 String 不可变,每次 + 都可能创建新对象。循环次数一大,开销就明显了。

这类场景更适合使用 StringBuilder

7.6 StringBuilder 的作用

StringBuilder 是一个可变字符容器,适合频繁拼接、修改字符串内容的场景。

Java
StringBuilder sb = new StringBuilder();
sb.append("Java");
sb.append(" ");
sb.append("Study");

System.out.println(sb);            // Java Study
System.out.println(sb.toString()); // 转成 String

特点:

  • 内容可变,追加和修改效率比频繁 String + 更高。
  • 位于 java.lang 包下,也不需要手动导包。
  • 最终如果要得到字符串结果,调用 toString()

7.7 StringBuilder 常用方法

方法作用
append(data)在末尾追加内容
insert(index, data)在指定位置插入
delete(start, end)删除指定范围,左闭右开
replace(start, end, str)替换指定范围内容
reverse()反转字符顺序
length()获取当前字符个数
toString()转成 String

示例:

Java
public class StringBuilderDemo {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("abc");

        sb.append("123");
        sb.insert(3, "-");
        sb.replace(0, 3, "XYZ");

        System.out.println(sb);            // XYZ-123
        System.out.println(sb.reverse());  // 321-ZYX
        System.out.println(sb.reverse());  // XYZ-123
        System.out.println(sb.toString()); // XYZ-123
    }
}

7.8 String、StringBuilder、StringBuffer 的区别

类型是否可变线程安全适用场景
String不可变安全字符串常量、少量拼接、比较和传参
StringBuilder可变不保证线程安全单线程下频繁拼接,最常用
StringBuffer可变线程安全多线程下的字符串修改,现在线程内场景较少用

初学阶段先记住:

  • 普通字符串处理,使用 String
  • 循环拼接、批量拼接,优先使用 StringBuilder
  • StringBuffer 知道它是线程安全版本即可。

7.9 实战案例:把数组拼成字符串

需求:把 {"张三", "李四", "王五"} 拼成 [张三, 李四, 王五]

Java
public class JoinNamesDemo {
    public static void main(String[] args) {
        String[] names = {"张三", "李四", "王五"};
        StringBuilder sb = new StringBuilder("[");

        for (int i = 0; i < names.length; i++) {
            sb.append(names[i]);
            if (i != names.length - 1) {
                sb.append(", ");
            }
        }

        sb.append("]");
        System.out.println(sb.toString());
    }
}

这种“循环中持续追加”的题目,是 StringBuilder 最经典的使用场景。

这里故意没有直接使用 String.join(),是因为当前这篇文章的重点是让你先掌握 StringBuilder 的核心使用方式。等你进入后续的常用 API 和集合章节后,再学习更简洁的拼接写法会更自然。

7.10 本章先记住这几件事

  • String 是类,不是基本类型,而且内容一旦创建就不能直接修改。
  • 比较字符串内容用 equals(),不要用 ==
  • 变量可能为 null 时,推荐写 "固定值".equals(变量)
  • 循环里频繁拼接字符串,优先使用 StringBuilder
  • StringBuffer 先知道它是线程安全版本即可,初学阶段不用深挖。

如果你还想继续学习 String.join()StringJoinerString.format()formatted() 这类字符串处理 API,可以接着看《Java 常用 API 与字符串、时间处理》;如果想学习 Collectors.joining(),可以在集合文档的 Stream 小节继续往下看。