Java 教程

Java 是一门面向对象的、跨平台的编程语言,由 Sun Microsystems(现属 Oracle)于 1995 年发布。它以"一次编写,到处运行"著称,是企业级开发、Android 应用和大型系统的主流选择。

Java 的核心特性

特性说明
跨平台JVM 机制,一套代码多平台运行
面向对象封装、继承、多态三大特性
自动内存管理垃圾回收(GC)自动释放内存
健壮安全强类型、异常处理、沙箱机制
生态庞大Spring、Hadoop、Android 等

一个 Java 程序

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, CodeNest!");
    }
}

Java 的运行机制

Hello.java (源码)
   │ javac 编译
   ▼
Hello.class (字节码)
   │ java 运行(JVM 解释/即时编译)
   ▼
本机执行

Java 版本演进

版本年份亮点
Java 82014Lambda、Stream(划时代)
Java 112018LTS,var 局部变量
Java 172021LTS,密封类
Java 212023LTS,虚拟线程

谁在用 Java

  • 大型企业系统(银行、保险、电商)
  • Android 应用开发
  • 大数据(Hadoop、Spark)
  • Spring 全家桶生态
💡 提示

Java 20 多年长盛不衰,核心在于稳定 + 生态。学习时抓住面向对象和 JVM 机制两个重点。

Java 简介

Java 简介:Java 的历史、特点与基本概念。

Java 的诞生

  • 1991 年 James Gosling 团队开始研发(原名 Oak)
  • 1995 年正式发布,赶上互联网浪潮
  • 2010 年被 Oracle 收购
  • 2017 年起改为 6 个月一版的快速迭代

Java 的核心机制:JVM

JVM(Java Virtual Machine)是 Java 跨平台的基石:

源码(.java) → 字节码(.class) → 各平台 JVM → 运行

字节码与平台无关,JVM 与平台相关(Windows/Mac/Linux 各有 JVM)

JVM vs JRE vs JDK

概念说明
JVM虚拟机,运行字节码
JREJava 运行环境 = JVM + 核心类库(能跑)
JDKJava 开发工具包 = JRE + 编译器(javac) + 工具(能开发)

Java 的应用领域

  • 企业后端:Spring Boot 微服务
  • Android:移动应用
  • 大数据:Hadoop/Spark/Kafka
  • 金融系统:高可靠交易平台

Java 与 JVM 语言

JVM 上还运行着 Kotlin(Android 官方)、Scala、Groovy 等语言,它们共享 JVM 生态,与 Java 可互操作。

💡 提示

理解"源码 → 字节码 → JVM"这条链路,就理解了 Java"跨平台"和"稍慢于原生"的原因。

Java 开发环境配置

配置 Java 开发环境:安装 JDK、配置环境变量、验证安装。

第一步:安装 JDK

方式说明
Oracle JDK官方发行版(oracle.com)
OpenJDK开源版(推荐,与 Oracle JDK 兼容)
TemurinEclipse 基金会发行(推荐)
包管理器Homebrew(macOS)/ apt(Linux)
# macOS (Homebrew)
brew install openjdk@17

# Ubuntu/Debian
sudo apt install openjdk-17-jdk

# 验证
java -version
javac -version

第二步:配置环境变量

# macOS/Linux (~/.zshrc 或 ~/.bashrc)
export JAVA_HOME=/Library/Java/JavaVirtualMachines/openjdk-17.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH

# Windows 系统属性 → 环境变量
# JAVA_HOME=C:\Program Files\Java\jdk-17
# PATH 添加 %JAVA_HOME%in

第三步:编写第一个程序

// Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
    }
}
javac Hello.java    # 编译 → Hello.class
java Hello          # 运行(不带 .class 后缀)

第四步:安装 IDE

  • IntelliJ IDEA:Java 开发首选(社区版免费)
  • Eclipse:老牌 IDE
  • VS Code + Java 插件:轻量选择

常见问题

问题解决
javac 找不到确认 JAVA_HOME 和 PATH 配置正确
版本不对项目要求 Java 8 就用 8(java -version 检查)
中文乱码源码保存 UTF-8,编译加 -encoding UTF-8
💡 提示

新项目建议 JDK 17+(LTS 长期支持);历史项目可能锁 Java 8,注意版本兼容。

Java 基础语法

掌握 Java 基础语法:程序结构、变量、数据类型、运算符。

Java 程序结构

// 类名必须与文件名一致
public class Main {
    // main 是程序入口
    public static void main(String[] args) {
        System.out.println("Hello Java");
    }
}

变量与常量

// 变量
int age = 25;
double price = 99.5;
String name = "CodeNest";
boolean isActive = true;

// 常量(final)
final double PI = 3.14159;
// PI = 3.14;  // ❌ 报错

基本数据类型

类型大小范围/示例
byte1 字节-128 ~ 127
short2 字节-32768 ~ 32767
int4 字节±21 亿
long8 字节极大(加 L)
float4 字节小数(加 F)
double8 字节小数(默认)
char2 字节单个字符 'A'
boolean1 位true / false

字符串(引用类型)

String greeting = "你好";
String full = greeting + ",Java";   // 拼接
int len = full.length();
String upper = full.toUpperCase();

类型转换

// 自动转换(小→大)
int i = 100;
long l = i;          // ✅ 自动
double d = i;        // ✅ 自动

// 强制转换(大→小,可能丢失)
double x = 9.7;
int y = (int) x;     // 9(截断)
💡 提示

Java 是强类型语言:变量必须先声明类型,类型不符在编译期就报错——这保证了代码的可靠性。

Java 注释

Java 注释:代码中的说明文字,不参与编译执行。

三种注释

// 单行注释:用两个斜杠

/*
 * 多行注释
 * 可以写多行说明
 */

/**
 * 文档注释(Javadoc)
 * 可以被 javadoc 工具生成 API 文档
 */
public class Demo {
    /**
     * 计算两个数的和
     * @param a 第一个数
     * @param b 第二个数
     * @return 两数之和
     */
    public int add(int a, int b) {
        return a + b;
    }
}

Javadoc 常用标签

标签用途
@param方法参数说明
@return返回值说明
@throws可能抛出的异常
@author作者
@version版本
@deprecated标记废弃

生成文档

# 生成 API 文档到 doc 目录
javadoc -d doc -encoding UTF-8 -charset UTF-8 Demo.java

注释的最佳实践

  • 解释"为什么"而不是"是什么"
  • 复杂逻辑写清思路
  • 公共 API 用 Javadoc
  • 代码本身清晰时少写废话注释
💡 提示

好的代码"自解释",注释用于补充无法从代码看出的信息(设计意图、业务背景、注意事项)。

Java 对象和类

Java 对象和类:面向对象编程的核心概念。

什么是类和对象

  • 类(Class):模板/蓝图,描述对象的属性和行为
  • 对象(Object):类的具体实例

定义类

public class Student {
    // 属性(成员变量)
    String name;
    int age;

    // 方法(行为)
    void study() {
        System.out.println(name + " 正在学习");
    }

    void introduce() {
        System.out.println("我是 " + name + "," + age + " 岁");
    }
}

创建对象

public class Main {
    public static void main(String[] args) {
        // new 创建对象
        Student s1 = new Student();
        s1.name = "张三";
        s1.age = 20;
        s1.study();       // 张三 正在学习
        s1.introduce();   // 我是 张三,20 岁

        // 多个对象独立
        Student s2 = new Student();
        s2.name = "李四";
        s2.age = 22;
    }
}

访问修饰符

修饰符同类同包子类任意
private✅
默认✅✅
protected✅✅✅
public✅✅✅✅

this 关键字

public class Student {
    String name;

    void setName(String name) {
        this.name = name;   // this 指向当前对象
    }
}
💡 提示

面向对象的思想:把数据(属性)和行为(方法)封装在一起,通过对象交互完成功能,而不是全局函数操作全局数据。

Java 基本数据类型

Java 基本数据类型:8 种基本类型详解与包装类。

8 种基本类型

类型默认值大小范围
byte01 字节-128 ~ 127
short02 字节-32768 ~ 32767
int04 字节±21 亿
long0L8 字节极大
float0.0f4 字节约 ±3.4e38
double0.0d8 字节约 ±1.7e308
char''2 字节0 ~ 65535
booleanfalse1 位true/false

字面量写法

int a = 100;
long b = 10000000000L;    // long 加 L
float c = 3.14f;          // float 加 f
double d = 3.14159;
char ch = 'A';
boolean flag = true;

// 进制
int hex = 0xFF;        // 255(十六进制)
int bin = 0b1010;      // 10(二进制)
int oct = 017;         // 15(八进制)

// 下划线分隔(可读性)
int million = 1_000_000;

包装类(Wrapper)

// 基本类型 → 包装类
int → Integer      double → Double
long → Long        boolean → Boolean
char → Character   float → Float
byte → Byte        short → Short

// 自动装箱/拆箱
Integer num = 100;          // 自动装箱
int value = num;            // 自动拆箱

// 常用方法
Integer.parseInt("42");     // 字符串转 int
String.valueOf(42);         // int 转字符串
Integer.MAX_VALUE;          // 2147483647

基本类型 vs 引用类型

对比项基本类型引用类型
存储栈(直接存值)堆(存引用地址)
默认值0/falsenull
赋值复制值复制引用
💡 提示

金额计算避免 float/double(精度问题),用 BigDecimal;字符串比较用 equals() 而非 ==。

Java 变量类型

Java 变量类型:局部变量、实例变量、静态变量。

三种变量的区别

类型位置生命周期默认值
局部变量方法内方法调用期间无(必须初始化)
实例变量类内,方法外随对象有默认值
静态变量static 修饰随类(全局共享)有默认值

示例

public class Employee {
    // 实例变量(每个对象一份)
    String name;
    int age;

    // 静态变量(所有对象共享一份)
    static String company = "CodeNest";
    static int totalEmployees = 0;

    public Employee(String name, int age) {
        this.name = name;      // 局部变量 name 赋值给实例变量
        this.age = age;
        totalEmployees++;      // 访问静态变量
    }

    public void showInfo() {
        // 局部变量
        String info = name + " 在 " + company + " 工作";
        System.out.println(info);
    }

    public static void main(String[] args) {
        Employee e1 = new Employee("张三", 25);
        Employee e2 = new Employee("李四", 28);

        // 静态变量通过类名访问(推荐)
        System.out.println(Employee.company);       // CodeNest
        System.out.println(Employee.totalEmployees); // 2
    }
}

变量的作用域

public void method() {
    int a = 10;               // 方法作用域
    if (a > 0) {
        int b = 20;           // 代码块作用域
        System.out.println(a + b);
    }
    // System.out.println(b); // ❌ b 超出作用域
}
💡 提示

静态变量是"类级别的全局数据",谨慎使用(多线程共享需同步);实例变量承载对象状态;局部变量保持最小作用域。

Java 变量命名规则

Java 变量命名规则:命名规范与最佳实践。

命名规则(必须遵守)

  • 字母、数字、_、$ 组成
  • 不能以数字开头
  • 不能是关键字(class、if、int 等)
  • 区分大小写(name 与 Name 不同)

命名规范(约定俗成)

场景规范示例
类名大驼峰(PascalCase)StudentManager
方法名小驼峰(camelCase)getUserName()
变量名小驼峰userAge
常量全大写 + 下划线MAX_SIZE
包名全小写com.codenest

有效 vs 无效命名

// ✅ 有效
int age;
String userName;
final int MAX_AGE = 100;

// ❌ 无效
int 123abc;      // 数字开头
String class;    // 关键字
int my-name;     // 连字符

命名最佳实践

  • 名副其实:userName 而非 n
  • 避免缩写:getUserInfo 而非 getUI
  • 布尔变量用 is/has 开头:isActive、hasPermission
  • 集合加复数:userList、orderMap
💡 提示

命名是代码可读性的第一要素。IDE(IntelliJ)有自动重命名(Shift+F6),命名不满意随时改。

Java 修饰符

Java 修饰符:访问修饰符与非访问修饰符。

访问修饰符

修饰符作用
public所有类可访问
protected同包 + 子类可访问
默认(不写)同包可访问
private仅本类可访问

非访问修饰符

修饰符作用
static静态(属于类而非对象)
final不可变(类不可继承、方法不可重写、变量不可改)
abstract抽象(类不可实例化、方法无实现)
synchronized同步(线程安全)
transient序列化时忽略
volatile可见性(多线程)

示例

public class ModifierDemo {
    // private:封装
    private String secret = "隐藏数据";

    // static final:常量
    public static final int VERSION = 1;

    // public:对外方法
    public String getSecret() {
        return secret;
    }
}

// final 类不可继承
final class Constants {
    public static final double PI = 3.14159;
}

final 的三种用法

final class A {}        // 类不可继承
class B {
    final void method() {}  // 方法不可重写
    final int MAX = 10;     // 变量不可修改
}
💡 提示

设计原则:默认 private,需要暴露才 public;能 final 就 final(不可变性带来安全与性能)。

Java 运算符

Java 运算符:算术、关系、逻辑、位运算等。

算术运算符

int a = 10, b = 3;
a + b   // 13
a - b   // 7
a * b   // 30
a / b   // 3(整数除法,向下取整)
a % b   // 1(取余)
a++     // 后自增
++a     // 前自增
a += 5  // 复合赋值(a = a + 5)

关系运算符

a > b    // true
a < b    // false
a >= b   // true
a <= b   // false
a == b   // false(值比较;对象用 equals)
a != b   // true

逻辑运算符

boolean x = true, y = false;
x && y    // false(短路与)
x || y    // true(短路或)
!x        // false(非)

// 短路:&& 左边 false 不执行右边
int n = 5;
boolean r = (n > 10) && (n / 0 == 1);  // 不会除零异常

三元运算符

int age = 20;
String status = age >= 18 ? "成年" : "未成年";

位运算(较少用)

int a = 6;   // 110
a & 3        // 2 (010) 与
a | 3        // 7 (111) 或
a ^ 3        // 5 (101) 异或
~a           // 取反
a << 1       // 12 左移(乘2)
a >> 1       // 3 右移(除2)

字符串比较陷阱

String s1 = new String("hello");
String s2 = new String("hello");
s1 == s2       // false(引用比较!)
s1.equals(s2)  // true(内容比较,用这个!)
💡 提示

对象比较永远用 equals();字符串内容相等判断同理。== 只用于基本类型和引用身份。

Java 循环结构

Java 循环结构:for、while、do-while 与增强 for。

for 循环

for (int i = 0; i < 5; i++) {
    System.out.println(i);    // 0 1 2 3 4
}

// 嵌套
for (int i = 1; i <= 3; i++) {
    for (int j = 1; j <= 3; j++) {
        System.out.print(i + "*" + j + "=" + i*j + " ");
    }
    System.out.println();
}

while 循环

int count = 0;
while (count < 3) {
    System.out.println(count);
    count++;
}

do-while(至少执行一次)

int n = 0;
do {
    System.out.println("至少执行一次: " + n);
    n++;
} while (n < 2);

增强 for(遍历集合/数组)

int[] nums = {1, 2, 3, 4};
for (int num : nums) {
    System.out.println(num);
}

// 遍历集合
List<String> names = List.of("张三", "李四");
for (String name : names) {
    System.out.println(name);
}

break / continue

for (int i = 0; i < 10; i++) {
    if (i == 2) continue;      // 跳过 2
    if (i == 5) break;         // 到 5 终止
    System.out.print(i + " "); // 0 1 3 4
}

循环选择建议

  • 知道次数 → for
  • 不知道次数,条件控制 → while
  • 至少执行一次 → do-while
  • 遍历数组/集合 → 增强 for
💡 提示

Java 8+ 常用 Stream 替代循环(list.stream().filter(...).forEach(...)),但基础循环必须先掌握。

Java 条件语句

Java 条件语句:if、if-else、if-else-if。

if 语句

int score = 85;
if (score >= 60) {
    System.out.println("及格");
}

if-else

if (score >= 60) {
    System.out.println("及格");
} else {
    System.out.println("不及格");
}

if-else if-else(多分支)

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

嵌套 if

if (isLogin) {
    if (isAdmin) {
        System.out.println("管理员");
    } else {
        System.out.println("普通用户");
    }
} else {
    System.out.println("未登录");
}

三元运算符替代

// if-else
String result;
if (age >= 18) {
    result = "成年";
} else {
    result = "未成年";
}

// 等价三元
String result2 = age >= 18 ? "成年" : "未成年";

逻辑条件组合

if (age >= 18 && age <= 60) {
    System.out.println("工作年龄");
}

if (city.equals("北京") || city.equals("上海")) {
    System.out.println("一线城市");
}
💡 提示

条件分支多时考虑 switch、枚举或策略模式,避免"金字塔"嵌套过深。

Java switch case

Java switch case:多分支选择的优雅写法。

基本语法

int day = 3;
switch (day) {
    case 1:
        System.out.println("周一");
        break;
    case 2:
        System.out.println("周二");
        break;
    case 3:
        System.out.println("周三");
        break;
    default:
        System.out.println("其他");
        break;
}

break 的重要性

// 没有 break 会"穿透"执行后续 case
switch (day) {
    case 1:
        System.out.println("周一");
        // 无 break!继续执行
    case 2:
        System.out.println("周二");
        break;
}
// day=1 时输出:周一 周二

字符串 switch(Java 7+)

String fruit = "apple";
switch (fruit) {
    case "apple":
        System.out.println("苹果");
        break;
    case "banana":
        System.out.println("香蕉");
        break;
    default:
        System.out.println("未知水果");
}

合并多个 case

int month = 2;
switch (month) {
    case 1: case 3: case 5: case 7: case 8: case 10: case 12:
        System.out.println("31 天");
        break;
    case 4: case 6: case 9: case 11:
        System.out.println("30 天");
        break;
    case 2:
        System.out.println("28/29 天");
        break;
    default:
        System.out.println("无效月份");
}

switch 表达式(Java 14+)

// 新的箭头语法,无需 break
String result = switch (day) {
    case 1, 2, 3, 4, 5 -> "工作日";
    case 6, 7 -> "周末";
    default -> "无效";
};
System.out.println(result);
💡 提示

Java 14+ 推荐用箭头语法 switch 表达式:更简洁、无需 break、天然防穿透。

Java Number & Math 类

Java Number & Math 类:数值处理与数学运算。

Number 包装类

// 包装类提供常用方法
Integer.parseInt("42");         // 字符串 → int
Double.parseDouble("3.14");
Long.parseLong("1000");

String.valueOf(42);             // int → 字符串
Integer.toString(42);

Integer.valueOf(42);            // 返回 Integer 对象
Integer.max(3, 5);              // 5
Integer.min(3, 5);              // 3

// 进制转换
Integer.toHexString(255);       // "ff"
Integer.toBinaryString(10);     // "1010"

Math 类(静态方法)

Math.abs(-10);          // 10 绝对值
Math.max(3, 7);         // 7
Math.min(3, 7);         // 3
Math.round(3.6);        // 4 四舍五入
Math.floor(3.9);        // 3.0 向下取整
Math.ceil(3.1);         // 4.0 向上取整

Math.pow(2, 10);        // 1024.0 幂
Math.sqrt(16);          // 4.0 平方根
Math.random();          // [0,1) 随机数

// 三角
Math.sin(Math.PI / 2);  // 1.0
Math.cos(0);            // 1.0

// 对数
Math.log(Math.E);       // 1.0
Math.log10(100);        // 2.0

随机数

// 1-100 随机整数
int n = (int)(Math.random() * 100) + 1;

// 更灵活:Random 类
import java.util.Random;
Random rand = new Random();
rand.nextInt(100);      // 0-99
rand.nextInt(50, 100);  // 50-99(Java 17+)
rand.nextDouble();      // [0,1)

BigDecimal(金额)

import java.math.BigDecimal;

// ❌ 浮点精度问题
// System.out.println(0.1 + 0.2);  // 0.30000000000000004

// ✅ BigDecimal
BigDecimal a = new BigDecimal("0.1");
BigDecimal b = new BigDecimal("0.2");
BigDecimal sum = a.add(b);          // 0.3
System.out.println(sum);            // 0.3

a.multiply(b)
a.subtract(b)
a.divide(b, 2, BigDecimal.ROUND_HALF_UP)  // 除法指定精度
💡 提示

金额、金融计算必须用 BigDecimal(字符串构造);Math.random() 够用则不必引入 Random。

Java Character 类

Java Character 类:字符处理工具。

基本用法

Character ch = 'A';

// 判断
Character.isLetter('A');     // true 字母
Character.isDigit('5');      // true 数字
Character.isWhitespace(' '); // true 空白
Character.isUpperCase('A');  // true 大写
Character.isLowerCase('a');  // true 小写
Character.isLetterOrDigit('1');  // true 字母或数字

// 转换
Character.toUpperCase('a');  // 'A'
Character.toLowerCase('A');  // 'a'

// 其他
Character.isAlphabetic('中');   // true(Unicode 字母)
char[] chars = Character.toChars(128512);  // emoji 转 char 数组

char 与 int

// char 本质是 Unicode 码点
char a = 'A';
int code = a;             // 65
char b = (char)(a + 1);   // 'B'

// 遍历字母
for (char c = 'a'; c <= 'z'; c++) {
    System.out.print(c);  // abcdefg...
}

判断字符类型实战

// 统计字符串中的数字/字母/其他
public static void countChars(String s) {
    int digits = 0, letters = 0, others = 0;
    for (char c : s.toCharArray()) {
        if (Character.isDigit(c)) digits++;
        else if (Character.isLetter(c)) letters++;
        else others++;
    }
    System.out.println("数字:" + digits + " 字母:" + letters + " 其他:" + others);
}

countChars("abc123!@#");   // 数字:3 字母:3 其他:3
💡 提示

Character 的静态方法在表单校验、字符统计、数据清洗中高频使用,值得熟练掌握。

Java String 类

Java String 类:字符串是不可变对象,方法极为丰富。

创建字符串

String s1 = "hello";                 // 字面量(推荐)
String s2 = new String("hello");     // 构造(不推荐,浪费)
String s3 = String.valueOf(42);      // 数字转字符串

常用方法

String str = "Hello, World";

str.length();               // 12
str.charAt(0);              // 'H'
str.substring(0, 5);        // "Hello"
str.substring(7);           // "World"

str.toUpperCase();          // "HELLO, WORLD"
str.toLowerCase();          // "hello, world"
str.trim();                 // 去首尾空白

str.indexOf('o');           // 4
str.lastIndexOf('o');       // 8
str.contains("World");      // true
str.startsWith("Hello");    // true
str.endsWith("World");      // true

str.replace("World", "Java");  // "Hello, Java"
str.split(",");                 // ["Hello", " World"]
str.isEmpty();                  // false

字符串拼接

// + 拼接(循环内不推荐,用 StringBuilder)
String name = "张三";
String msg = "你好," + name + "!";

// 大量拼接用 StringBuilder(性能好)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
    sb.append(i).append(",");
}
String result = sb.toString();

格式化

// String.format
String s = String.format("姓名:%s,年龄:%d,分数:%.1f",
                          "张三", 25, 89.5);
// 姓名:张三,年龄:25,分数:89.5

// 常用占位符:%s 字符串 %d 整数 %f 小数 %n 换行

字符串池(不可变的好处)

// 字面量相同 → 指向同一对象
String a = "hello";
String b = "hello";
a == b          // true(同一池中对象)

// 但 new 出来的不同
String c = new String("hello");
a == c          // false
💡 提示

String 不可变 → 线程安全、可缓存;大量拼接用 StringBuilder;比较内容用 equals()。

Java StringBuffer

Java StringBuffer 与 StringBuilder:可变字符串。

为什么需要可变字符串

String 不可变,每次拼接都创建新对象。大量拼接时用可变字符串类,性能差距巨大。

两者对比

对比项StringBufferStringBuilder
线程安全✅(synchronized)❌
性能较慢更快
适用多线程共享单线程(推荐默认用)

常用方法

StringBuilder sb = new StringBuilder();

sb.append("Hello");        // 追加
sb.append(" ").append("World");  // 链式
sb.insert(5, ",");         // 插入 "Hello, World"
sb.replace(7, 12, "Java"); // 替换 "Hello, Java"
sb.delete(5, 6);           // 删除 "Hello Java"
sb.reverse();              // 反转 "avaJ olleH"

String result = sb.toString();   // 转 String
int len = sb.length();
sb.setLength(0);           // 清空

性能对比

// 慢:String +(每次创建新对象)
String s = "";
for (int i = 0; i < 10000; i++) {
    s += i;
}

// 快:StringBuilder(原地修改)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
    sb.append(i);
}
String result = sb.toString();

典型应用

// 构建 SQL(注意参数化)
StringBuilder sql = new StringBuilder("SELECT * FROM users WHERE 1=1");
if (name != null) sql.append(" AND name LIKE ?");
if (age > 0) sql.append(" AND age > ?");

// 构建 JSON/XML
StringBuilder json = new StringBuilder();
json.append("{").append(""name":"").append(name).append(""").append("}");
💡 提示

默认用 StringBuilder;只有多线程共享同一可变字符串时才用 StringBuffer。

Java 数组

Java 数组:固定长度的同类型元素集合。

创建数组

// 声明 + 分配空间
int[] nums = new int[5];       // [0,0,0,0,0]
String[] names = new String[3]; // [null,null,null]

// 直接初始化
int[] scores = {90, 85, 95};
String[] fruits = {"苹果", "香蕉"};

// 分步
int[] arr;
arr = new int[]{1, 2, 3};

访问与遍历

// 访问
scores[0];         // 90(索引从 0 开始)
scores.length;     // 3(长度)
scores[0] = 100;   // 修改

// 普通 for
for (int i = 0; i < scores.length; i++) {
    System.out.println(scores[i]);
}

// 增强 for
for (int score : scores) {
    System.out.println(score);
}

二维数组

int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
};

matrix[0][1];    // 2
matrix.length;   // 3(行数)
matrix[0].length;// 3(列数)

// 遍历
for (int[] row : matrix) {
    for (int val : row) {
        System.out.print(val + " ");
    }
    System.out.println();
}

数组工具类 Arrays

import java.util.Arrays;

Arrays.sort(scores);            // 排序
Arrays.toString(scores);        // "[90, 85, 95]"
Arrays.fill(arr, 0);            // 全部填充 0
Arrays.copyOf(arr, 5);          // 复制并扩容
Arrays.equals(a, b);            // 比较内容
Arrays.binarySearch(arr, 85);   // 二分查找(需先排序)
Arrays.asList("a", "b");        // 转 List

数组的局限性

  • 长度固定,不能动态增删
  • 需要动态集合时用 ArrayList
  • 数组是引用类型,默认值 null/0/false
💡 提示

动态大小用 ArrayList;固定大小、性能敏感用数组。两者可通过 toArray()/Arrays.asList() 互转。

Java 日期时间

Java 日期时间:java.time 包(Java 8+ 推荐)。

LocalDate / LocalTime / LocalDateTime

import java.time.*;

// 获取当前
LocalDate today = LocalDate.now();              // 2026-08-11
LocalTime now = LocalTime.now();                // 14:30:00
LocalDateTime now2 = LocalDateTime.now();       // 2026-08-11T14:30

// 指定
LocalDate date = LocalDate.of(2026, 8, 11);
LocalTime time = LocalTime.of(14, 30);

// 解析
LocalDate parsed = LocalDate.parse("2026-08-11");
LocalDateTime dt = LocalDateTime.parse("2026-08-11T14:30:00");

常用操作

// 加减
date.plusDays(7);
date.minusMonths(1);
date.plusYears(1);

// 比较
date.isAfter(other);
date.isBefore(other);
date.equals(other);

// 提取
date.getYear();      // 2026
date.getMonthValue();// 8
date.getDayOfMonth();// 11
date.getDayOfWeek(); // TUESDAY

格式化

import java.time.format.DateTimeFormatter;

DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String s = LocalDateTime.now().format(fmt);   // "2026-08-11 14:30:00"
LocalDateTime parsed = LocalDateTime.parse(s, fmt);

Period 与 Duration

// 日期差
LocalDate start = LocalDate.of(2026, 1, 1);
LocalDate end = LocalDate.of(2026, 8, 11);
Period p = Period.between(start, end);
p.getMonths();    // 7

// 时间差
Duration d = Duration.between(time1, time2);
d.toHours();      // 小时
d.toMinutes();    // 分钟

旧 API 转换

// Date ↔ LocalDateTime
Date old = new Date();
LocalDateTime ldt = old.toInstant()
    .atZone(ZoneId.systemDefault())
    .toLocalDateTime();
Date back = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
💡 提示

新代码一律用 java.time(LocalDate/LocalDateTime),线程安全且 API 清晰;Date/Calendar 已过时。

Java 正则表达式

Java 正则表达式:用模式匹配和操作文本。

基础使用

import java.util.regex.Pattern;
import java.util.regex.Matcher;

// matches:整体匹配
boolean isEmail = Pattern.matches(
    "^[\w.-]+@[\w-]+\.[\w.]+$", "user@example.com");

// 编译复用
Pattern p = Pattern.compile("\d+");
Matcher m = p.matcher("订单号 123 金额 456");

// find:查找
while (m.find()) {
    System.out.println(m.group());   // 123, 456
}

// matches vs find
// matches 要求整体匹配
// find 查找子串

常用正则模式

// 手机号
"^1[3-9]\d{9}$"

// 邮箱
"^[\w.-]+@[\w-]+\.[\w.]+$"

// 纯数字
"^\d+$"

// 中文
"^[\u4e00-\u9fa5]+$"

// 日期 yyyy-MM-dd
"^\d{4}-\d{2}-\d{2}$"

分组提取

Pattern p = Pattern.compile("(\d{4})-(\d{2})-(\d{2})");
Matcher m = p.matcher("日期 2026-08-11");
if (m.find()) {
    m.group(1);   // 2026
    m.group(2);   // 08
    m.group(3);   // 11
    m.group(0);   // 2026-08-11(整体)
}

替换与分割

// 替换
String cleaned = "a1b2c3".replaceAll("\d", "#");   // a#b#c#
String phone = "138-1234-5678".replaceAll("[^0-9]", "");  // 13812345678

// 分割
String[] parts = "a,b;c".split("[,;]");   // [a, b, c]

String 自带正则方法

"abc123".matches("\w+");       // true
"hello".replaceAll("l", "L");   // heLLo
"a-b-c".split("-");             // [a, b, c]
💡 提示

Java 字符串中的正则反斜杠需写 \\d;复杂正则先在 regex101 上验证再写入代码。

Java 方法

Java 方法:封装可复用代码块。

方法定义

// 访问修饰符 返回类型 方法名(参数) { 方法体 }
public int add(int a, int b) {
    return a + b;
}

// void:无返回值
public void sayHello(String name) {
    System.out.println("你好," + name);
}

方法重载(Overload)

// 同名方法,参数不同
public int add(int a, int b) { return a + b; }
public int add(int a, int b, int c) { return a + b + c; }
public double add(double a, double b) { return a + b; }

add(1, 2);        // 3
add(1, 2, 3);     // 6
add(1.5, 2.5);    // 4.0

参数传递

// 基本类型:传值(副本)
public static void change(int x) { x = 100; }
int n = 10;
change(n);
// n 仍是 10(基本类型按值传递)

// 引用类型:传引用(对象可被修改)
public static void changeName(Student s) { s.name = "新名字"; }
Student stu = new Student();
stu.name = "旧名字";
changeName(stu);
// stu.name 变为 "新名字"

可变参数

public static int sum(int... nums) {
    int total = 0;
    for (int n : nums) total += n;
    return total;
}

sum(1, 2, 3);        // 6
sum(1, 2, 3, 4, 5);  // 15
sum();               // 0

递归

// 阶乘
public static int factorial(int n) {
    if (n <= 1) return 1;          // 终止条件
    return n * factorial(n - 1);   // 递归调用
}

factorial(5);   // 120

方法签名

方法签名 = 方法名 + 参数列表(用于区分重载)。返回类型不属于签名。

💡 提示

方法设计原则:单一职责(一个方法只做一件事)、命名清晰、参数合理;递归务必有终止条件防栈溢出。

Java 构造方法

Java 构造方法:创建对象时初始化状态。

基本构造方法

public class Student {
    String name;
    int age;

    // 无参构造(不写也有默认的)
    public Student() {
        this.name = "未命名";
        this.age = 0;
    }

    // 带参构造
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 使用
Student s1 = new Student();                  // 无参
Student s2 = new Student("张三", 20);        // 带参

构造方法重载

public class Product {
    String name;
    double price;

    public Product() {
        this("默认商品", 0);    // this() 调用另一构造
    }

    public Product(String name) {
        this(name, 0);
    }

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
}

this 关键字

public Product(String name, double price) {
    this.name = name;       // this.name = 参数 name
    this.price = price;
}

默认构造方法

// 不写任何构造方法时,编译器提供默认无参构造
public class Demo {
    // 隐藏的:public Demo() {}
}

// 但一旦定义了带参构造,默认无参构造消失
// 需要无参构造时需显式写出

静态初始化块

public class Config {
    static String version;

    // 静态块:类加载时执行一次
    static {
        version = "1.0.0";
        System.out.println("静态块执行");
    }
}
💡 提示

构造方法负责"对象的合法初始状态":校验参数、设置默认值。常用 IDE 生成(IntelliJ 的 Alt+Insert)。

Java Stream、File、IO

Java Stream、File、IO:文件读写与输入输出。

File 类

import java.io.File;

File file = new File("/tmp/test.txt");

file.exists();          // 是否存在
file.isFile();          // 是否文件
file.isDirectory();     // 是否目录
file.length();          // 大小(字节)
file.getName();         // 文件名
file.getAbsolutePath(); // 绝对路径
file.createNewFile();   // 创建
file.delete();          // 删除
file.mkdirs();          // 创建目录(含父目录)

// 列出目录
File dir = new File("/tmp");
File[] files = dir.listFiles();
for (File f : files) {
    System.out.println(f.getName());
}

读文件

import java.nio.file.Files;
import java.nio.file.Path;

// Java 7+ NIO:最简洁
String content = Files.readString(Path.of("/tmp/test.txt"));
List<String> lines = Files.readAllLines(Path.of("/tmp/test.txt"));

// 传统 BufferedReader
try (BufferedReader br = new BufferedReader(
        new FileReader("/tmp/test.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}   // try-with-resources 自动关闭

写文件

import java.nio.file.Files;
import java.nio.file.Path;

// 最简单
Files.writeString(Path.of("/tmp/out.txt"), "内容");

// 追加
Files.writeString(Path.of("/tmp/out.txt"), "追加内容",
    StandardOpenOption.APPEND);

// 传统 BufferedWriter
try (BufferedWriter bw = new BufferedWriter(
        new FileWriter("/tmp/out.txt"))) {
    bw.write("第一行");
    bw.newLine();
    bw.write("第二行");
}   // 自动关闭

字节流(二进制)

// 复制文件
Files.copy(Path.of("a.jpg"), Path.of("b.jpg"));

// 读字节
byte[] data = Files.readAllBytes(Path.of("a.jpg"));
Files.write(Path.of("c.jpg"), data);

异常处理

try {
    Files.readString(Path.of("/不存在/文件.txt"));
} catch (IOException e) {
    System.err.println("读取失败:" + e.getMessage());
    e.printStackTrace();
}
💡 提示

现代 Java 推荐 java.nio.file.Files 一行读/写文件;try-with-resources 自动管理资源,不用手写 close。

Java 继承

Java 继承:子类复用父类的属性和方法,实现代码复用与层级关系。

基本继承

// 父类(基类)
class Animal {
    String name;

    void eat() {
        System.out.println(name + " 正在吃东西");
    }
}

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

// 使用
Dog dog = new Dog();
dog.name = "旺财";
dog.eat();    // 继承的方法
dog.bark();   // 自己的方法

super 关键字

class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    void speak() {
        System.out.println("动物叫");
    }
}

class Dog extends Animal {
    Dog(String name) {
        super(name);       // 调用父类构造方法(必须第一行)
    }

    @Override
    void speak() {
        super.speak();     // 调用父类方法
        System.out.println("汪汪!");
    }
}

方法重写(Override)

class Animal {
    void speak() { System.out.println("动物发出声音"); }
}

class Cat extends Animal {
    @Override          // 注解:声明这是重写
    void speak() {
        System.out.println("喵喵!");
    }
}

// 动态绑定:运行时决定调用哪个方法
Animal a = new Cat();
a.speak();   // 喵喵!(调用子类重写的方法)

继承的特性

  • Java 只支持单继承(一个类只能 extends 一个父类)
  • 所有类最终继承 Object
  • 构造方法不继承,子类需调用 super()
  • private 成员不继承

继承 vs 组合

对比项继承(is-a)组合(has-a)
关系Dog is a AnimalCar has a Engine
耦合强耦合低耦合
推荐度明确层级时用大多数场景推荐
💡 提示

设计原则:优先组合,慎用继承。继承用于"真正的 is-a 关系",层级别超过 2-3 层。

Java Override/Overload

Java Override / Overload:方法重写与方法重载的区别。

方法重写(Override)

子类重写父类的方法,方法签名相同,实现不同。

class Animal {
    void speak() { System.out.println("动物叫"); }
}

class Dog extends Animal {
    @Override
    void speak() { System.out.println("汪汪!"); }   // 重写
}

方法重载(Overload)

同一类中同名方法,参数不同(数量/类型/顺序)。

class Calculator {
    int add(int a, int b) { return a + b; }
    int add(int a, int b, int c) { return a + b + c; }   // 参数数量不同
    double add(double a, double b) { return a + b; }     // 参数类型不同
}

两者对比

对比项Override(重写)Overload(重载)
位置子类与父类同一类
方法签名完全相同必须不同
返回类型相同或协变可不同
注解@Override无
绑定运行时(动态)编译期(静态)
目的改变行为便捷调用

重写规则

  • 参数列表必须完全一致
  • 返回类型相同或为其子类型
  • 访问权限不能比父类更严格
  • 不能抛出更宽泛的异常

重载规则

  • 方法名相同
  • 参数列表不同(数量/类型/顺序)
  • 返回类型不影响重载判断
💡 提示

记忆口诀:重写是父子之间的"覆盖",重载是同类之间的"多版本"。

Java 多态

Java 多态:同一方法调用,根据对象类型表现出不同行为——面向对象三大特性之一。

多态的前提

  1. 继承(或实现接口)
  2. 方法重写
  3. 父类引用指向子类对象

基本多态

class Animal {
    void speak() { System.out.println("动物叫"); }
}

class Dog extends Animal {
    @Override void speak() { System.out.println("汪汪!"); }
}

class Cat extends Animal {
    @Override void speak() { System.out.println("喵喵!"); }
}

// 父类引用指向子类对象
Animal a1 = new Dog();
Animal a2 = new Cat();

a1.speak();   // 汪汪!(动态绑定)
a2.speak();   // 喵喵!

多态的好处

// 面向父类编程:新类型无需修改调用代码
public static void makeSound(Animal animal) {
    animal.speak();   // 传入什么就调用什么
}

makeSound(new Dog());    // 汪汪!
makeSound(new Cat());    // 喵喵!
// 新增 Bird 类也无需改这个方法

多态应用:数组/集合

Animal[] animals = {new Dog(), new Cat(), new Dog()};
for (Animal a : animals) {
    a.speak();
}
// 汪汪!喵喵!汪汪!

类型转换

// 向上转型(自动)
Animal a = new Dog();

// 向下转型(需强转 + 检查)
if (a instanceof Dog) {
    Dog dog = (Dog) a;
    dog.bark();
}

// instanceof 模式匹配(Java 16+)
if (a instanceof Dog dog) {
    dog.bark();   // 直接使用,无需再强转
}

多态的三种形式

  • 重写多态:继承 + 方法重写
  • 接口多态:实现接口 + 重写
  • 重载多态:编译期选择方法
💡 提示

多态是面向对象设计的核心:"对扩展开放,对修改关闭"(开闭原则)正是靠多态实现。

Java 抽象类

Java 抽象类:用 abstract 定义"骨架",子类实现细节。

抽象类与抽象方法

// 抽象类:不能被实例化
abstract class Shape {
    // 抽象方法:没有方法体,子类必须实现
    abstract double area();

    // 普通方法:可继承复用
    void describe() {
        System.out.println("面积:" + area());
    }
}

class Circle extends Shape {
    double radius;

    Circle(double r) { this.radius = r; }

    @Override
    double area() {
        return Math.PI * radius * radius;
    }
}

// 使用
// Shape s = new Shape();  // ❌ 抽象类不能实例化
Circle c = new Circle(5);
c.describe();   // 面积:78.5398...

抽象类的作用

  • 定义模板/契约:规定子类必须有哪些行为
  • 代码复用:公共字段和逻辑放抽象类
  • 强制规范:忘记实现抽象方法会编译报错

抽象方法规则

  • 抽象方法无方法体(只有签名)
  • 抽象方法必须在抽象类中
  • 子类必须实现所有抽象方法(除非子类也是抽象类)
  • 抽象类可以有构造方法(供子类调用)

抽象类 vs 接口

对比项抽象类接口(interface)
关键字abstract classinterface
多继承单继承可实现多个
具体方法可以有Java 8+ 默认方法
字段普通字段仅常量
语义is-a(同一类族)can-do(能力)
💡 提示

选择:有共享代码且是同一类族 → 抽象类;定义能力契约、需要多实现 → 接口。

Java 封装

Java 封装:隐藏内部实现,通过公共方法访问——面向对象三大特性之一。

封装的核心

  • 私有化字段(private)
  • 提供公共访问方法(getter/setter)
  • 控制数据访问(校验、只读等)

基本封装

public class BankAccount {
    // 私有字段:外部不能直接访问
    private String accountNo;
    private double balance;

    // 构造方法
    public BankAccount(String accountNo, double balance) {
        this.accountNo = accountNo;
        this.balance = balance;
    }

    // getter:读取(可只读)
    public double getBalance() {
        return balance;
    }

    public String getAccountNo() {
        return accountNo;
    }

    // 业务方法:带校验的修改
    public void deposit(double amount) {
        if (amount <= 0) {
            throw new IllegalArgumentException("存款金额必须为正");
        }
        balance += amount;
    }

    public boolean withdraw(double amount) {
        if (amount > balance) {
            return false;   // 余额不足
        }
        balance -= amount;
        return true;
    }
}

封装的优点

优点说明
安全性外部无法直接改内部状态
可控性通过方法加校验、日志
灵活性内部实现可改,接口不变
可维护性降低耦合,便于修改

IDE 生成 getter/setter

IntelliJ:Alt + Insert → Getter and Setter
Eclipse:右键 → Source → Generate Getters/Setters

JavaBean 规范

// JavaBean:private 字段 + public getter/setter
public class User {
    private String name;
    private int age;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}
💡 提示

封装是"黑盒"思想:只暴露必要接口,隐藏实现细节——这是 Java 应用框架(Spring 等)的基础约定。

Java 接口

Java 接口(Interface):定义能力契约,实现类必须遵循。

定义接口

// 接口:定义"能做什么"
public interface Payment {
    // 抽象方法(默认 public abstract)
    void pay(double amount);

    // Java 8+:默认方法(带实现)
    default void check() {
        System.out.println("检查支付状态");
    }

    // Java 8+:静态方法
    static String getType() {
        return "payment";
    }

    // 常量(默认 public static final)
    int MAX_AMOUNT = 100000;
}

实现接口

public class WechatPay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("微信支付 " + amount + " 元");
    }
}

public class Alipay implements Payment {
    @Override
    public void pay(double amount) {
        System.out.println("支付宝支付 " + amount + " 元");
    }
}

多接口实现

interface Flyable { void fly(); }
interface Swimable { void swim(); }

class Duck implements Flyable, Swimable {
    @Override public void fly() { System.out.println("鸭子飞"); }
    @Override public void swim() { System.out.println("鸭子游"); }
}

接口继承

interface Animal { void eat(); }
interface Dog extends Animal { void bark(); }

class Husky implements Dog {
    @Override public void eat() { /* ... */ }
    @Override public void bark() { /* ... */ }
}

接口多态

// 面向接口编程
public static void checkout(Payment payment, double amount) {
    payment.pay(amount);   // 传入什么实现就怎么付
}

checkout(new WechatPay(), 100);
checkout(new Alipay(), 200);

接口 vs 抽象类

对比项接口抽象类
实现数量可多个只能一个
方法实现默认方法(Java 8+)可有完整实现
字段仅常量普通字段
语义能力(can-do)族类(is-a)
💡 提示

面向接口编程是 Java 最重要的设计思想:依赖抽象而非具体实现,实现解耦与扩展。

Java 枚举

Java 枚举(Enum):定义一组固定的命名常量。

基本枚举

public enum Day {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
    FRIDAY, SATURDAY, SUNDAY
}

// 使用
Day today = Day.MONDAY;
System.out.println(today);        // MONDAY
Day[] all = Day.values();         // 所有值
Day d = Day.valueOf("MONDAY");    // 按名字获取
today.ordinal();                  // 索引 0

带字段的枚举

public enum Status {
    PENDING("待处理", 0),
    PROCESSING("处理中", 1),
    DONE("已完成", 2),
    FAILED("失败", 3);

    private final String label;
    private final int code;

    Status(String label, int code) {
        this.label = label;
        this.code = code;
    }

    public String getLabel() { return label; }
    public int getCode() { return code; }
}

// 使用
Status s = Status.DONE;
s.getLabel();    // 已完成
s.getCode();     // 2

带方法的枚举

public enum Operation {
    ADD { public double apply(double a, double b) { return a + b; } },
    SUBTRACT { public double apply(double a, double b) { return a - b; } },
    MULTIPLY { public double apply(double a, double b) { return a * b; } };

    public abstract double apply(double a, double b);
}

Operation.ADD.apply(3, 4);   // 7.0

switch 与枚举

switch (status) {
    case PENDING:
        System.out.println("等待处理");
        break;
    case DONE:
        System.out.println("已完成");
        break;
    default:
        System.out.println("其他状态");
}

枚举的优点

  • 类型安全:只能取定义的值
  • 可读性:语义明确
  • 内置方法:values()、valueOf()
  • 可带行为:字段、方法
💡 提示

状态码、类型、选项等固定取值集合,用枚举替代魔法数字/字符串,代码更安全可读。

Java 包(package)

Java 包(Package):组织和管理类的命名空间。

什么是包

包是类的容器,类似文件系统中的目录,用于:

  • 组织相关类
  • 避免命名冲突
  • 控制访问权限

定义包

// 文件第一行声明包
package com.codenest.utils;

public class StringUtils {
    // ...
}

// 目录结构
// src/com/codenest/utils/StringUtils.java

导入包

// 导入单个类
import java.util.List;
import java.util.ArrayList;

// 导入整个包(不推荐,不清晰)
import java.util.*;

// 静态导入
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;

// 使用
List<String> list = new ArrayList<>();
double area = PI * sqrt(4);

常用 JDK 包

包内容
java.lang核心类(String、Math、System)自动导入
java.util集合、日期、工具类
java.io输入输出
java.nioNIO(非阻塞 IO)
java.net网络编程
java.sql数据库操作

命名规范

域名的反序 + 模块名
com.codenest.utils
com.codenest.service
org.example.api

全小写,用 . 分隔层级

同包与跨包

// 同包:直接使用类名
// 跨包:import 或全限定名
com.codenest.utils.StringUtils.trim("  abc  ");   // 全限定名
💡 提示

IDE 会自动管理 import(IntelliJ 的 Alt+Enter);包结构反映模块划分,良好的包设计 = 良好的架构。

Java 反射

Java 反射(Reflection):运行时检查和操作类、方法、字段。

获取 Class 对象

// 方式一:类名.class
Class<?> c1 = String.class;

// 方式二:对象.getClass()
String s = "hello";
Class<?> c2 = s.getClass();

// 方式三:Class.forName(字符串)
Class<?> c3 = Class.forName("java.lang.String");

查看类信息

Class<?> clazz = User.class;

clazz.getName();           // 类全名
clazz.getSimpleName();     // 类名 User
clazz.getPackageName();    // 包名
clazz.getModifiers();      // 修饰符
clazz.getSuperclass();     // 父类
clazz.getInterfaces();     // 接口数组
clazz.getDeclaredFields(); // 字段数组
clazz.getDeclaredMethods();// 方法数组
clazz.getDeclaredConstructors(); // 构造方法

反射创建对象

Class<?> clazz = Class.forName("com.example.User");
User user = (User) clazz.getDeclaredConstructor().newInstance();

// 带参构造
User user2 = (User) clazz
    .getDeclaredConstructor(String.class, int.class)
    .newInstance("张三", 25);

反射操作字段和方法

Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.getDeclaredConstructor().newInstance();

// 字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);      // 突破 private
nameField.set(obj, "李四");
System.out.println(nameField.get(obj));

// 方法
Method method = clazz.getMethod("getName");
Object result = method.invoke(obj);   // 调用方法

反射的应用

场景说明
Spring 框架依赖注入、AOP 基于反射
ORM(MyBatis/Hibernate)实体映射
JSON 序列化Jackson/Gson 读写对象
动态代理JDK 动态代理

⚠️ 反射注意事项

  • 性能低于直接调用(有缓存可缓解)
  • 破坏封装(setAccessible(true) 需谨慎)
  • 异常较多(ClassNotFoundException 等)
💡 提示

反射是框架的基石,日常业务代码很少直接写反射——理解原理即可,用框架时知道"它在做什么"。

Java 数据结构

Java 数据结构:数组、链表、栈、队列、树等基础概念。

数据结构分类

结构特点Java 实现
数组连续内存,随机访问快int[]
链表非连续,插入删除快LinkedList
栈后进先出(LIFO)Stack / ArrayDeque
队列先进先出(FIFO)Queue / LinkedList
哈希表键值快速查找HashMap / HashSet
树层级关系TreeMap / TreeSet
图网络关系需自定义

数组 vs 链表

对比项数组链表
内存连续分散(节点+指针)
随机访问O(1) 快O(n) 慢
插入删除O(n) 慢O(1) 快(已知位置)
大小固定动态

栈(Stack)

// 后进先出
Deque<String> stack = new ArrayDeque<>();
stack.push("A");   // 入栈
stack.push("B");
stack.pop();       // 出栈 B
stack.peek();      // 查看栈顶 A(不移除)

// 应用:括号匹配、撤销操作

队列(Queue)

// 先进先出
Queue<String> queue = new LinkedList<>();
queue.offer("A");  // 入队
queue.offer("B");
queue.poll();      // 出队 A
queue.peek();      // 查看队头 B

// 应用:消息队列、任务调度

哈希表(HashMap)

Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.get("A");          // 1
map.containsKey("A");  // true
map.keySet();          // 键集合
map.values();          // 值集合
💡 提示

数据结构选择口诀:查多用哈希,有序用树,FIFO 用队列,LIFO 用栈。

Java 集合框架

Java 集合框架:List、Set、Map 三大接口及常用实现。

集合框架总览

Collection(单列集合)
├── List(有序可重复)
│   ├── ArrayList    数组实现,查快增删慢
│   └── LinkedList   链表实现,增删快查慢
├── Set(无序不可重复)
│   ├── HashSet      哈希实现,最快
│   ├── LinkedHashSet 保插入顺序
│   └── TreeSet      排序(红黑树)
└── Queue(队列)
    └── ArrayDeque / LinkedList

Map(键值对集合)
├── HashMap          哈希,最快(不保序)
├── LinkedHashMap    保插入顺序
├── TreeMap          按键排序
└── ConcurrentHashMap 线程安全

List 接口

List<String> list = new ArrayList<>();

list.add("A");           // 添加
list.add(0, "B");        // 指定位置
list.get(0);             // 获取
list.set(0, "C");        // 修改
list.remove(0);          // 按索引删除
list.remove("C");        // 按值删除
list.size();             // 大小
list.contains("A");      // 是否包含
list.isEmpty();          // 是否为空
list.indexOf("A");       // 索引

// 遍历
for (String s : list) { }
list.forEach(s -> System.out.println(s));

Set 接口

Set<Integer> set = new HashSet<>();
set.add(1);
set.add(1);          // 重复被忽略
set.size();          // 1
set.contains(1);     // true
set.remove(1);
set.addAll(List.of(2, 3, 4));

Map 接口

Map<String, Integer> map = new HashMap<>();
map.put("A", 1);
map.put("B", 2);
map.get("A");            // 1
map.getOrDefault("C", 0);// 0(不存在给默认)
map.putIfAbsent("A", 99);// 存在不覆盖
map.containsKey("A");    // true
map.remove("A");

// 遍历
for (Map.Entry<String, Integer> e : map.entrySet()) {
    System.out.println(e.getKey() + "=" + e.getValue());
}
map.forEach((k, v) -> System.out.println(k + "=" + v));

线程安全

// 单线程:HashMap/ArrayList(推荐,快)
// 多线程:
Map<String, Integer> safeMap = new ConcurrentHashMap<>();
List<String> safeList = Collections.synchronizedList(new ArrayList<>());
💡 提示

默认选择:List=ArrayList、Set=HashSet、Map=HashMap;需要排序才换 Tree 系列;多线程用 Concurrent 系列。

Java ArrayList

Java ArrayList:动态数组,最常用的 List 实现。

创建 ArrayList

import java.util.ArrayList;
import java.util.List;

List<String> list = new ArrayList<>();        // 空列表
List<Integer> nums = new ArrayList<>(List.of(1, 2, 3));  // 初始化

// 固定不可变列表(Java 9+)
List<String> fixed = List.of("a", "b");       // 不能 add/remove

常用方法

list.add("Java");             // 尾部添加
list.add(0, "First");         // 指定位置
list.addAll(other);           // 添加整个集合

list.get(0);                  // 获取
list.set(0, "New");           // 替换
list.remove(0);               // 删除索引
list.remove("Java");          // 删除对象

list.size();                  // 大小
list.isEmpty();               // 是否空
list.contains("Java");        // 包含
list.indexOf("Java");         // 索引

list.clear();                 // 清空

遍历方式

// 增强 for
for (String s : list) { }

// forEach + Lambda
list.forEach(s -> System.out.println(s));

// 传统 for(需要索引时)
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

// 迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    System.out.println(it.next());
}

Stream 操作

// 过滤
list.stream().filter(s -> s.startsWith("J")).toList();

// 映射
list.stream().map(String::toUpperCase).toList();

// 排序
list.stream().sorted().toList();

// 聚合
list.stream().count();
list.stream().max(String::compareTo);

ArrayList 的原理

  • 底层是 Object[] 数组
  • 默认容量 10,扩容为 1.5 倍
  • 随机访问 O(1),插入删除 O(n)
  • 预估大小可指定初始容量优化性能
// 预估 1000 条,指定容量减少扩容
List<String> big = new ArrayList<>(1000);
💡 提示

ArrayList 是 List 的"默认选择";只有大量头部插入/删除才考虑 LinkedList。

Java LinkedList

Java LinkedList:双向链表实现,适合频繁增删。

创建与基本操作

import java.util.LinkedList;

LinkedList<String> list = new LinkedList<>();
list.add("B");
list.addFirst("A");     // 头部添加
list.addLast("C");      // 尾部添加
list.getFirst();        // A
list.getLast();         // C
list.removeFirst();     // 移除头部
list.removeLast();      // 移除尾部

ArrayList vs LinkedList

对比项ArrayListLinkedList
底层动态数组双向链表
随机访问 get(i)O(1) 快O(n) 慢
头部增删O(n) 慢O(1) 快
内存紧凑每节点有指针开销
适用查询多频繁头尾增删

当作队列/栈用

// LinkedList 实现了 Queue 和 Deque 接口
Queue<String> queue = new LinkedList<>();
queue.offer("A");    // 入队
queue.poll();        // 出队

Deque<String> deque = new LinkedList<>();
deque.push("A");     // 栈:压入
deque.pop();         // 栈:弹出

遍历

// 所有 List 遍历方式通用
for (String s : list) { }
list.forEach(s -> System.out.println(s));

// 迭代器(增删安全)
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("A")) it.remove();   // 安全删除
}

性能注意

  • get(i) 需要遍历到位置,慢
  • 头尾操作极快(双指针)
  • 绝大多数场景 ArrayList 已够用
💡 提示

判断用哪个:随机访问多 → ArrayList;头尾增删频繁 → LinkedList。不确定就 ArrayList。

Java HashSet

Java HashSet:基于哈希表、元素不重复的集合。

基本用法

import java.util.HashSet;
import java.util.Set;

Set<String> set = new HashSet<>();

set.add("A");
set.add("B");
set.add("A");          // 重复忽略
set.size();            // 2
set.contains("A");     // true(O(1) 快)
set.remove("B");
set.isEmpty();

HashSet 的特点

特性说明
不重复基于 hashCode + equals
无序不保证插入顺序
快速查找/添加 O(1)
允许 null可存一个 null

去重应用

// 列表去重
List<Integer> nums = List.of(1, 2, 2, 3, 3, 3);
Set<Integer> unique = new HashSet<>(nums);
// [1, 2, 3]

// 交集/并集/差集
Set<Integer> a = new HashSet<>(List.of(1, 2, 3));
Set<Integer> b = new HashSet<>(List.of(2, 3, 4));

a.retainAll(b);        // 交集 [2, 3]
// a.addAll(b)         // 并集
// a.removeAll(b)      // 差集

LinkedHashSet / TreeSet

// 保插入顺序
Set<String> ordered = new LinkedHashSet<>();

// 排序
Set<Integer> sorted = new TreeSet<>();
sorted.add(3); sorted.add(1); sorted.add(2);
// [1, 2, 3](自动排序)

自定义对象的去重

// 需要重写 hashCode + equals
public class User {
    String name;
    int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;
        User u = (User) o;
        return age == u.age && name.equals(u.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
💡 提示

HashSet 去重依赖 hashCode() 和 equals();自定义类放 Set 前务必重写这两个方法(IDE 可生成)。

Java HashMap

Java HashMap:键值对存储,最常用的 Map 实现。

基本用法

import java.util.HashMap;
import java.util.Map;

Map<String, Integer> map = new HashMap<>();

map.put("张三", 90);       // 添加/更新
map.put("李四", 85);
map.get("张三");           // 90
map.getOrDefault("王五", 0);  // 0(默认值)
map.containsKey("张三");   // true
map.containsValue(90);     // true
map.size();                // 2
map.remove("李四");        // 删除

map.putIfAbsent("张三", 100);  // 已存在则不覆盖
map.replace("张三", 95);       // 覆盖(已存在才生效)

遍历 Map

// 方式一:entrySet(推荐)
for (Map.Entry<String, Integer> e : map.entrySet()) {
    System.out.println(e.getKey() + "=" + e.getValue());
}

// 方式二:forEach + Lambda
map.forEach((k, v) -> System.out.println(k + "=" + v));

// 方式三:只遍历键/值
for (String k : map.keySet()) { }
for (Integer v : map.values()) { }

统计频率(经典应用)

Map<String, Integer> count = new HashMap<>();
for (String word : words) {
    count.put(word, count.getOrDefault(word, 0) + 1);
}

// Java 8+ 简化
count.merge(word, 1, Integer::sum);

HashMap 原理

  • 数组 + 链表 + 红黑树(Java 8+)
  • 默认容量 16,负载因子 0.75
  • key 可为 null(存一个)
  • 查找 O(1),冲突 O(logn)

LinkedHashMap / TreeMap

// 保插入顺序
Map<String, Integer> ordered = new LinkedHashMap<>();

// 按键排序
Map<String, Integer> sorted = new TreeMap<>();

// 自定义排序
Map<String, Integer> custom = new TreeMap<>(
    Comparator.reverseOrder());
💡 提示

HashMap 是 Map 默认选择;多线程用 ConcurrentHashMap;需要有序用 LinkedHashMap/TreeMap。

Java Iterator

Java Iterator:统一遍历集合的迭代器接口。

基本使用

import java.util.Iterator;

List<String> list = new ArrayList<>(List.of("A", "B", "C"));
Iterator<String> it = list.iterator();

while (it.hasNext()) {       // 是否还有下一个
    String s = it.next();    // 取下一个
    System.out.println(s);
}

迭代时安全删除

// ❌ 增强 for 中删除 → ConcurrentModificationException
// for (String s : list) { if (s.equals("A")) list.remove(s); }

// ✅ 用迭代器删除
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("A")) {
        it.remove();   // 安全删除
    }
}

ListIterator(双向)

ListIterator<String> lit = list.listIterator();

lit.hasNext();       // 向后
lit.next();
lit.hasPrevious();   // 向前
lit.previous();
lit.set("New");      // 替换当前元素
lit.add("X");        // 当前位置插入

Iterable 与 for-each

// 所有集合都实现 Iterable,所以可用 for-each
// for-each 底层就是迭代器语法糖

for (String s : list) { }   // 等价于用 iterator

// 自定义类实现 Iterable
class MyCollection implements Iterable<String> {
    @Override
    public Iterator<String> iterator() {
        return myList.iterator();
    }
}

forEachRemaining

// 剩余元素批量处理
Iterator<Integer> it = nums.iterator();
it.next();                    // 消费一个
it.forEachRemaining(n -> System.out.println(n));  // 处理剩下的
💡 提示

需要遍历时删除/修改集合,务必用 Iterator 的 remove(),不要用集合自身的 remove()(会抛并发修改异常)。

Java Object

Java Object 类:所有类的根类,提供通用方法。

Object 的常用方法

方法用途
toString()返回字符串表示
equals(Object)比较对象是否相等
hashCode()哈希值(配合 equals)
getClass()获取 Class 对象
clone()克隆对象
notify()/wait()线程通信

重写 toString

public class User {
    String name;
    int age;

    @Override
    public String toString() {
        return "User{name='" + name + "', age=" + age + "}";
    }
}

System.out.println(user);
// User{name='张三', age=25}

equals 与 hashCode

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    User user = (User) o;
    return age == user.age && name.equals(user.name);
}

@Override
public int hashCode() {
    return Objects.hash(name, age);
}

// 规则:
// equals 相等 → hashCode 必须相等
// hashCode 相等 → equals 不一定相等(哈希冲突)
// 放进 HashSet/HashMap 必须重写两个

Objects 工具类

import java.util.Objects;

Objects.equals(a, b);        // 安全比较(处理 null)
Objects.hash(x, y, z);       // 生成哈希
Objects.requireNonNull(obj); // 非空检查,null 抛 NPE
Objects.isNull(obj);         // 是否为 null
Objects.nonNull(obj);        // 是否非 null

重写原则

  • 重写 equals 必须重写 hashCode
  • equals 比较"业务内容",不是引用
  • IDE 可自动生成(Alt+Insert)
💡 提示

几乎每个实体类都要重写 toString()(调试友好)和 equals/hashCode`(集合使用)——IDE 一键生成即可。

Java NIO Files

Java NIO Files:Java 7+ 的现代文件操作 API。

Path 与 Files

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

// Path 创建
Path p1 = Path.of("/tmp/test.txt");        // Java 11+
Path p2 = Paths.get("/tmp", "test.txt");   // 拼接
Path p3 = Path.of(".").toAbsolutePath();   // 绝对路径

// 常用方法
p1.getFileName();      // test.txt
p1.getParent();        // /tmp
p1.resolve("sub");     // /tmp/test.txt/sub
p1.getRoot();          // /
Files.exists(p1);      // 是否存在

读写文件(一行搞定)

// 读
String content = Files.readString(Path.of("data.txt"));
List<String> lines = Files.readAllLines(Path.of("data.txt"));
byte[] bytes = Files.readAllBytes(Path.of("img.png"));

// 写
Files.writeString(Path.of("out.txt"), "内容");
Files.writeString(Path.of("out.txt"), "追加", StandardOpenOption.APPEND);
Files.write(Path.of("out.bin"), bytes);

目录操作

// 创建目录
Files.createDirectories(Path.of("/tmp/a/b/c"));   // 含父目录

// 遍历目录
try (var stream = Files.list(Path.of("/tmp"))) {
    stream.forEach(System.out::println);
}

// 递归遍历
try (var stream = Files.walk(Path.of("/tmp"))) {
    stream.filter(Files::isRegularFile).forEach(System.out::println);
}

复制/移动/删除

Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING);
Files.delete(path);                  // 不存在抛异常
Files.deleteIfExists(path);          // 安全删除
Files.createTempFile("prefix", ".txt");  // 临时文件

文件属性

Files.size(path);           // 大小
Files.isRegularFile(path);  // 是否普通文件
Files.isDirectory(path);    // 是否目录
Files.getLastModifiedTime(path);  // 修改时间
Files.isReadable(path);     // 可读

异常处理

try {
    String s = Files.readString(Path.of("x.txt"));
} catch (IOException e) {
    System.err.println("IO 错误:" + e.getMessage());
}
💡 提示

NIO Files 是现代 Java 文件操作的首选:一行读写、类型安全、流式处理,替代了繁琐的 FileInputStream 时代。

Java 泛型

Java 泛型:类型参数化,编写通用且类型安全的代码。

为什么需要泛型

// ❌ 不使用泛型:Object 需强转,易 ClassCastException
List list = new ArrayList();
list.add("hello");
list.add(123);                 // 什么都能加
String s = (String) list.get(0);  // 强转

// ✅ 泛型:编译期类型检查
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123);   // ❌ 编译报错
String s = list.get(0);        // 无需强转

泛型类

public class Box<T> {
    private T value;

    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

// 使用
Box<String> strBox = new Box<>();
strBox.set("hello");
String s = strBox.get();

Box<Integer> intBox = new Box<>();
intBox.set(100);

泛型方法

public class Utils {
    // 泛型方法:<T> 在返回类型前
    public static <T> T lastElement(List<T> list) {
        return list.get(list.size() - 1);
    }

    public static <T> List<T> reverse(List<T> list) {
        List<T> result = new ArrayList<>(list);
        Collections.reverse(result);
        return result;
    }
}

String last = Utils.lastElement(List.of("a", "b"));   // "b"

泛型通配符

// ? 通配符:未知类型
public static void printList(List<?> list) {
    for (Object o : list) System.out.println(o);
}

// 上界:只读 Number 及其子类
public static double sum(List<? extends Number> list) {
    double s = 0;
    for (Number n : list) s += n.doubleValue();
    return s;
}
sum(List.of(1, 2, 3));        // ✅
sum(List.of(1.5, 2.5));       // ✅

// 下界:写入 Integer 及其父类
public static void addInts(List<? super Integer> list) {
    list.add(1);
    list.add(2);
}

泛型边界

// 限制类型:T 必须实现 Comparable
public static <T extends Comparable<T>> T max(T a, T b) {
    return a.compareTo(b) >= 0 ? a : b;
}

max("apple", "banana");    // "banana"
max(1, 2);                 // 2
💡 提示

泛型三件套:泛型类 <T>、泛型方法、通配符 ?。集合框架全是泛型,理解后代码更安全。

Java 序列化

Java 序列化:把对象转为字节流(存储/传输),反序列化还原。

实现 Serializable

import java.io.Serializable;

public class User implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    // transient 字段不序列化
    private transient String password;

    // getter/setter...
}

序列化与反序列化

import java.io.*;

// 序列化:对象 → 文件
try (ObjectOutputStream oos =
        new ObjectOutputStream(new FileOutputStream("user.ser"))) {
    oos.writeObject(user);
}

// 反序列化:文件 → 对象
try (ObjectInputStream ois =
        new ObjectInputStream(new FileInputStream("user.ser"))) {
    User u = (User) ois.readObject();
    System.out.println(u.getName());
}

serialVersionUID

// 序列化版本号:类结构变化时保持兼容
// 不写会按类结构自动生成(类改了会不兼容)
private static final long serialVersionUID = 1L;
// 修改类时手动调整版本号可控制兼容性

transient 关键字

// transient:该字段不参与序列化
// 适用:密码、缓存、派生数据
private transient String password;   // 序列化后为 null

现代替代:JSON 序列化

// 现在更常用 JSON 序列化(Jackson/Gson)
import com.fasterxml.jackson.databind.ObjectMapper;

ObjectMapper mapper = new ObjectMapper();

// 对象 → JSON 字符串
String json = mapper.writeValueAsString(user);

// JSON 字符串 → 对象
User u = mapper.readValue(json, User.class);

// 优势:跨语言、可读、灵活

序列化的用途

  • 对象持久化(存文件/数据库)
  • 网络传输(RPC、Socket)
  • 缓存(Redis 等)
  • 深拷贝
💡 提示

Web 开发中"序列化"多指 JSON 序列化(Jackson/Gson);Java 原生 Serializable 用于本地持久化和 RPC 场景。

Java 网络编程

Java 网络编程:Socket 通信、HTTP 请求。

TCP Socket 客户端

import java.net.Socket;
import java.io.*;

// 客户端
try (Socket socket = new Socket("127.0.0.1", 8080);
     PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
     BufferedReader in = new BufferedReader(
         new InputStreamReader(socket.getInputStream()))) {

    out.println("Hello Server");
    String response = in.readLine();
    System.out.println("服务器响应:" + response);
} catch (IOException e) {
    e.printStackTrace();
}

TCP Socket 服务端

import java.net.ServerSocket;

try (ServerSocket server = new ServerSocket(8080)) {
    System.out.println("等待客户端连接...");
    try (Socket socket = server.accept();     // 阻塞等待
         BufferedReader in = new BufferedReader(
             new InputStreamReader(socket.getInputStream()));
         PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {

        String msg = in.readLine();
        System.out.println("收到:" + msg);
        out.println("收到你的消息:" + msg);
    }
} catch (IOException e) {
    e.printStackTrace();
}

HTTP 请求(Java 11+ HttpClient)

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

HttpClient client = HttpClient.newHttpClient();

// GET
HttpRequest getReq = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .GET()
    .build();

HttpResponse<String> resp = client.send(getReq,
    HttpResponse.BodyHandlers.ofString());
System.out.println(resp.statusCode());   // 200
System.out.println(resp.body());

// POST(JSON)
HttpRequest postReq = HttpRequest.newBuilder()
    .uri(URI.create("https://api.example.com/users"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(
        "{"name":"张三"}"))
    .build();

URL 类(旧方式)

// 老 API(了解即可)
URL url = new URL("https://api.example.com/data");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
💡 提示

日常 HTTP 调用推荐 java.net.http.HttpClient(Java 11+)或第三方库(OkHttp、RestTemplate);Socket 用于自建 TCP 协议。

Java 发送邮件

Java 发送邮件:用 JavaMail 发送邮件。

依赖(Maven)

<dependency>
    <groupId>org.eclipse.angus</groupId>
    <artifactId>angus-mail</artifactId>
    <version>2.0.3</version>
</dependency>

发送邮件示例

import jakarta.mail.*;
import jakarta.mail.internet.*;
import java.util.Properties;

public class MailSender {
    public static void main(String[] args) throws MessagingException {
        // 1. 配置 SMTP
        Properties props = new Properties();
        props.put("mail.smtp.host", "smtp.example.com");
        props.put("mail.smtp.port", "465");
        props.put("mail.smtp.auth", "true");
        props.put("mail.smtp.ssl.enable", "true");

        // 2. 认证
        Session session = Session.getInstance(props,
            new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return new PasswordAuthentication(
                        "sender@example.com", "password");
                }
            });

        // 3. 构造邮件
        Message message = new MimeMessage(session);
        message.setFrom(new InternetAddress("sender@example.com"));
        message.setRecipients(Message.RecipientType.TO,
            InternetAddress.parse("receiver@example.com"));
        message.setSubject("邮件主题");
        message.setText("邮件正文内容");

        // 4. 发送
        Transport.send(message);
        System.out.println("邮件已发送");
    }
}

HTML 邮件

// 发送 HTML 内容
message.setContent(
    "<h1>欢迎注册</h1><p>点击<a href='https://example.com'>这里</a>激活</p>",
    "text/html; charset=utf-8");

带附件

// Multipart 邮件
MimeBodyPart textPart = new MimeBodyPart();
textPart.setText("请查收附件");

MimeBodyPart filePart = new MimeBodyPart();
filePart.attachFile(new File("/tmp/report.pdf"));

Multipart multipart = new MimeMultipart();
multipart.addBodyPart(textPart);
multipart.addBodyPart(filePart);

message.setContent(multipart);

常用邮箱 SMTP

邮箱SMTP 地址端口
QQ 邮箱smtp.qq.com465/587
163 邮箱smtp.163.com465
Gmailsmtp.gmail.com465/587
⚠️ 注意

邮箱通常需要开启 SMTP 服务并获取授权码(不是登录密码);密钥不要写死在代码里,用环境变量或配置中心。

Java 多线程编程

Java 多线程编程:并发执行任务的核心技术。

创建线程的三种方式

// 方式一:继承 Thread
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程运行:" + Thread.currentThread().getName());
    }
}
new MyThread().start();

// 方式二:实现 Runnable(推荐,避免单继承限制)
Runnable task = () -> System.out.println("Runnable 任务");
new Thread(task).start();

// 方式三:实现 Callable(有返回值)
Callable<Integer> callable = () -> 42;
FutureTask<Integer> ft = new FutureTask<>(callable);
new Thread(ft).start();
int result = ft.get();   // 42

线程池(推荐)

import java.util.concurrent.*;

// 固定大小线程池
ExecutorService pool = Executors.newFixedThreadPool(4);

// 提交任务
pool.submit(() -> System.out.println("任务1"));
Future<Integer> future = pool.submit(() -> 100);

// 关闭
pool.shutdown();            // 不再接收新任务
pool.awaitTermination(5, TimeUnit.SECONDS);

线程同步(synchronized)

class Counter {
    private int count = 0;

    // 同步方法:同一时间只有一个线程执行
    public synchronized void increment() {
        count++;
    }

    // 或同步代码块
    public void increment2() {
        synchronized (this) {
            count++;
        }
    }

    public int getCount() { return count; }
}

线程安全集合

// 并发集合
Map<String, Integer> map = new ConcurrentHashMap<>();
List<String> list = new CopyOnWriteArrayList<>();
Set<Integer> set = ConcurrentHashMap.newKeySet();

volatile 与原子类

// volatile:可见性(不保证原子性)
private volatile boolean running = true;

// 原子类:保证原子操作
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();    // 原子 +1
counter.get();                // 读取

注意事项

  • 优先线程池而非裸 new Thread
  • 共享可变数据必须同步
  • 避免死锁(锁顺序一致)
  • 用并发集合替代手动同步
💡 提示

现代 Java 并发推荐:ExecutorService 线程池 + 并发集合 + Lock/原子类;Java 21 的虚拟线程是未来方向。

Java Applet 基础

Java Applet 基础:历史遗留技术(了解即可)。

什么是 Applet

Applet 是嵌入网页运行的 Java 小程序(Java 1.0 ~ 1.8 时期的技术),通过浏览器 JVM 插件运行。由于安全与性能问题已被淘汰,现代浏览器不再支持。

Applet 生命周期

import java.applet.Applet;
import java.awt.Graphics;

// 老式 Applet(仅供理解历史)
public class HelloApplet extends Applet {
    @Override
    public void init() { /* 初始化 */ }

    @Override
    public void start() { /* 启动 */ }

    @Override
    public void paint(Graphics g) {
        g.drawString("Hello Applet", 20, 20);
    }

    @Override
    public void stop() { /* 停止 */ }

    @Override
    public void destroy() { /* 销毁 */ }
}

为什么被淘汰

问题说明
安全漏洞频繁曝出远程执行漏洞
体验差需安装 JRE 插件、启动慢
移动端不支持iOS/Android 无 Applet
HTML5 替代Canvas/WebGL 更强大

现代替代方案

  • JavaScript:浏览器原生
  • WebAssembly:高性能计算
  • Java Web Start:桌面应用启动(也已淘汰)
💡 提示

本页仅为知识补全。实际开发请忽略 Applet,用前端技术(HTML/JS/WebAssembly)实现浏览器端功能。

Java 文档注释

Java 文档注释:用 Javadoc 生成专业 API 文档。

Javadoc 基本写法

/**
 * 用户服务类,负责用户相关的业务操作。
 *
 * @author 老王
 * @version 1.0
 * @since 1.0
 */
public class UserService {

    /**
     * 根据 ID 查询用户。
     *
     * @param id 用户 ID,必须大于 0
     * @return 用户对象;不存在时返回 null
     * @throws IllegalArgumentException 当 id 小于等于 0 时
     */
    public User getUserById(int id) {
        if (id <= 0) {
            throw new IllegalArgumentException("ID 必须大于 0");
        }
        // 查询逻辑
        return null;
    }

    /**
     * 创建用户。
     *
     * @param name 用户名
     * @param age  年龄
     * @return 创建的用户
     * @see User
     */
    public User createUser(String name, int age) { return null; }
}

常用标签

标签用途
@param参数说明
@return返回值说明
@throws异常说明
@author作者
@version版本
@since从哪个版本开始
@see相关引用
@deprecated标记废弃
@link内联链接

生成文档

# 生成整个项目文档
javadoc -d docs -encoding UTF-8 -charset UTF-8 src/**/*.java

# Maven 项目
mvn javadoc:javadoc

# 生成后打开 docs/index.html 查看

最佳实践

  • 公共 API(public)必须写 Javadoc
  • 描述"做什么"和"约束",不重复代码
  • IDE 支持 /** 自动生成模板
  • 保持与实现同步更新
💡 提示

好的文档注释让团队协作顺畅:调用者无需读源码就能安全使用 API。Spring 等框架源码就是范例。

Java 实例

Java 实例:精选常见编程实例练习。

1. 判断闰年

public static boolean isLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
}

2. 斐波那契数列

public static long fib(int n) {
    if (n <= 1) return n;
    long a = 0, b = 1;
    for (int i = 2; i <= n; i++) {
        long tmp = a + b;
        a = b;
        b = tmp;
    }
    return b;
}

3. 字符串反转

public static String reverse(String s) {
    return new StringBuilder(s).reverse().toString();
}

4. 统计字符

public static Map<Character, Integer> countChars(String s) {
    Map<Character, Integer> map = new HashMap<>();
    for (char c : s.toCharArray()) {
        map.put(c, map.getOrDefault(c, 0) + 1);
    }
    return map;
}

5. 数组最大值

public static int max(int[] arr) {
    int max = arr[0];
    for (int n : arr) if (n > max) max = n;
    return max;
}

6. 素数判断

public static boolean isPrime(int n) {
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return false;
    }
    return true;
}

7. 冒泡排序

public static void bubbleSort(int[] arr) {
    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
            }
        }
    }
}

8. 二分查找

public static int binarySearch(int[] arr, int target) {
    int left = 0, right = arr.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == target) return mid;
        if (arr[mid] < target) left = mid + 1;
        else right = mid - 1;
    }
    return -1;
}
💡 提示

动手敲一遍这些实例,胜过看十遍理论——它们是算法与语法的综合练习。

Java 8 新特性

Java 8 新特性:Lambda、Stream、Optional 等划时代特性。

1. Lambda 表达式

// 传统匿名类
Runnable r1 = new Runnable() {
    public void run() { System.out.println("运行"); }
};

// Lambda 简写
Runnable r2 = () -> System.out.println("运行");

// 带参数
Comparator<Integer> cmp = (a, b) -> a - b;

2. 函数式接口

// 内置函数式接口
Function<String, Integer> f = s -> s.length();
Predicate<Integer> p = n -> n > 0;
Consumer<String> c = s -> System.out.println(s);
Supplier<String> sup = () -> "Hello";

f.apply("hello");     // 5
p.test(5);            // true

3. Stream API

List<Integer> nums = List.of(1, 2, 3, 4, 5, 6);

// 过滤偶数并平方
List<Integer> result = nums.stream()
    .filter(n -> n % 2 == 0)         // 过滤
    .map(n -> n * n)                 // 映射
    .sorted(Comparator.reverseOrder())// 排序
    .toList();                        // 收集(Java 16+)
// [36, 16, 4]

// 聚合
nums.stream().sum();
nums.stream().count();
nums.stream().max(Integer::compareTo);
nums.stream().reduce(0, Integer::sum);   // 累加

4. Optional

// 避免 NPE
Optional<String> name = Optional.ofNullable(user.getName());

name.isPresent();        // 是否有值
name.orElse("默认名");    // 无值给默认
name.orElseThrow(() -> new RuntimeException("名字缺失"));
name.ifPresent(n -> System.out.println(n));
name.map(String::toUpperCase).orElse("");  // 链式

5. 默认方法与静态方法

interface Greeting {
    void sayHello();

    // 默认方法:实现类可不覆盖
    default void sayBye() { System.out.println("再见"); }

    // 静态方法
    static String getGreeting() { return "你好"; }
}

6. 新日期 API

LocalDate date = LocalDate.now();
LocalDateTime dt = LocalDateTime.now();

7. 方法引用

// 类::方法
list.forEach(System.out::println);
list.stream().map(String::toUpperCase).toList();
list.stream().filter(Objects::nonNull).toList();
💡 提示

Java 8 是划时代版本:Lambda + Stream 让 Java 焕发新生。至今大量项目仍停留在 Java 8,务必掌握。

Java MySQL 连接

Java MySQL 连接:JDBC 操作 MySQL 数据库。

Maven 依赖

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.4.0</version>
</dependency>

JDBC 基础流程

import java.sql.*;

public class JdbcDemo {
    public static void main(String[] args) throws SQLException {
        // 1. 加载驱动(新版本可选)
        // Class.forName("com.mysql.cj.jdbc.Driver");

        // 2. 连接
        String url = "jdbc:mysql://127.0.0.1:3306/knowledge_site?useSSL=false&characterEncoding=utf8";
        String user = "root";
        String password = "password";

        try (Connection conn = DriverManager.getConnection(url, user, password)) {

            // 3. 创建语句
            try (PreparedStatement ps = conn.prepareStatement(
                    "SELECT id, title FROM tutorials WHERE cat_id = ?")) {

                // 4. 设置参数(防 SQL 注入)
                ps.setInt(1, 1);

                // 5. 执行查询
                try (ResultSet rs = ps.executeQuery()) {
                    // 6. 处理结果
                    while (rs.next()) {
                        int id = rs.getInt("id");
                        String title = rs.getString("title");
                        System.out.println(id + " - " + title);
                    }
                }
            }
        }
    }
}

增删改(executeUpdate)

// INSERT
try (Connection conn = getConnection();
     PreparedStatement ps = conn.prepareStatement(
         "INSERT INTO tutorials (title, cat_id) VALUES (?, ?)")) {

    ps.setString(1, "新教程");
    ps.setInt(2, 1);
    int rows = ps.executeUpdate();   // 影响行数
    System.out.println("插入 " + rows + " 行");
}

// UPDATE / DELETE 同理
// 事务:
conn.setAutoCommit(false);
try {
    // 多条 SQL...
    conn.commit();
} catch (SQLException e) {
    conn.rollback();
}

获取自增 ID

PreparedStatement ps = conn.prepareStatement(
    "INSERT INTO users (name) VALUES (?)",
    Statement.RETURN_GENERATED_KEYS);
ps.setString(1, "张三");
ps.executeUpdate();

ResultSet keys = ps.getGeneratedKeys();
if (keys.next()) {
    int newId = keys.getInt(1);   // 自增 ID
}

连接池(生产必须)

// HikariCP(最流行)
HikariConfig config = new HikariConfig();
config.setJdbcUrl(url);
config.setUsername(user);
config.setPassword(password);
config.setMaximumPoolSize(10);

HikariDataSource ds = new HikariDataSource(config);
Connection conn = ds.getConnection();   // 从池中取连接

更优选择:ORM

JDBC 是基础,日常开发更常用:
- MyBatis:SQL 与映射分离
- Spring Data JPA:全自动 ORM
- JdbcTemplate:Spring 封装的轻量 JDBC
💡 提示

JDBC 三件套:PreparedStatement(防注入)+ 连接池(性能)+ 事务管理(一致性)。生产别裸用 DriverManager。

Java 9 新特性

Java 9 新特性:模块化、私有接口方法等。

1. 模块系统(JPMS)

// module-info.java
module com.codenest.app {
    requires java.sql;
    exports com.codenest.app.service;
}

// 模块化:显式声明依赖和导出,强化封装
// 应用:JDK 自身被拆成模块(java.base 等)

2. 私有接口方法

interface Calculator {
    default int add(int a, int b) {
        return validate(a) + validate(b);
    }

    // 接口私有方法(Java 9+)
    private int validate(int n) {
        if (n < 0) throw new IllegalArgumentException();
        return n;
    }
}

3. List.of / Set.of / Map.of

// 不可变集合工厂
List<String> list = List.of("a", "b");
Set<Integer> set = Set.of(1, 2, 3);
Map<String, Integer> map = Map.of("A", 1, "B", 2);

// list.add("c")  // ❌ 不可变集合,添加抛异常

4. Optional 增强

Optional<String> opt = Optional.of("hello");

opt.ifPresentOrElse(
    s -> System.out.println(s),     // 有值
    () -> System.out.println("空")); // 无值

Optional<String> filtered = opt.stream().findFirst();  // 转 Stream

5. try-with-resources 增强

// 已有的 final/effectively final 变量可直接用
BufferedReader reader = new BufferedReader(new FileReader("a.txt"));
try (reader) {          // Java 9+ 不必再在括号里声明
    reader.readLine();
}

6. 其他特性

  • Stream 新增 takeWhile / dropWhile / ofNullable
  • JShell 交互式工具(java jshell)
  • G1 成为默认垃圾回收器
💡 提示

Java 9 之后 6 个月一版,每个版本都有小增强;LTS 版本(8/11/17/21)是企业采用重点。

Java 常用类库

Java 常用类库:开发中的高频工具类。

1. java.lang(自动导入)

String / StringBuilder / Math / System / Object
Integer / Double / Long / Boolean 等包装类
Thread / Runtime / Class

2. java.util(工具类)

// 集合
List / Set / Map / Queue
// 工具
Collections.shuffle(list);        // 洗牌
Collections.sort(list);           // 排序
Collections.reverse(list);        // 反转
Collections.max(list);            // 最大
Collections.unmodifiableList(list); // 只读视图

Arrays.sort(arr);                 // 数组排序
Arrays.toString(arr);             // 转字符串
Arrays.equals(a, b);              // 比较

// 日期
Date / Calendar / UUID.randomUUID()  // 唯一 ID

3. java.time(新日期)

LocalDate / LocalTime / LocalDateTime
DateTimeFormatter / Period / Duration
ZoneId / Instant

4. java.io / java.nio(IO)

Files / Path / BufferedReader
FileInputStream / FileOutputStream

5. 常用第三方库

库用途
Jackson / GsonJSON 序列化
Apache Commons通用工具(StringUtils 等)
GuavaGoogle 工具库
Hutool国产工具库(易用)
Lombok减少样板代码(@Data 等)
Log4j2 / Logback日志
JUnit 5单元测试

6. 字符串处理(Apache Commons / Hutool)

// Hutool
StringUtil.isEmpty(str);
StringUtil.isBlank(str);
StrUtil.sub(str, 0, 10);
StrUtil.concat(true, "a", "b");

// Apache Commons
StringUtils.isBlank(str);
StringUtils.join(list, ",");
StringUtils.abbreviate(str, 50);   // 截断加省略号
💡 提示

写代码前先想想"这个工具有没有现成的":Hutool/Commons 能省大量造轮子时间,但要选主流维护的库。

第 1 / 54 节