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 8 | 2014 | Lambda、Stream(划时代) |
| Java 11 | 2018 | LTS,var 局部变量 |
| Java 17 | 2021 | LTS,密封类 |
| Java 21 | 2023 | LTS,虚拟线程 |
谁在用 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 | 虚拟机,运行字节码 |
| JRE | Java 运行环境 = JVM + 核心类库(能跑) |
| JDK | Java 开发工具包 = 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 兼容) |
| Temurin | Eclipse 基金会发行(推荐) |
| 包管理器 | 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; // ❌ 报错
基本数据类型
| 类型 | 大小 | 范围/示例 |
|---|---|---|
byte | 1 字节 | -128 ~ 127 |
short | 2 字节 | -32768 ~ 32767 |
int | 4 字节 | ±21 亿 |
long | 8 字节 | 极大(加 L) |
float | 4 字节 | 小数(加 F) |
double | 8 字节 | 小数(默认) |
char | 2 字节 | 单个字符 'A' |
boolean | 1 位 | 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 种基本类型
| 类型 | 默认值 | 大小 | 范围 |
|---|---|---|---|
byte | 0 | 1 字节 | -128 ~ 127 |
short | 0 | 2 字节 | -32768 ~ 32767 |
int | 0 | 4 字节 | ±21 亿 |
long | 0L | 8 字节 | 极大 |
float | 0.0f | 4 字节 | 约 ±3.4e38 |
double | 0.0d | 8 字节 | 约 ±1.7e308 |
char | ' ' | 2 字节 | 0 ~ 65535 |
boolean | false | 1 位 | 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/false | null |
| 赋值 | 复制值 | 复制引用 |
金额计算避免 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 不可变,每次拼接都创建新对象。大量拼接时用可变字符串类,性能差距巨大。
两者对比
| 对比项 | StringBuffer | StringBuilder |
|---|---|---|
| 线程安全 | ✅(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 Animal | Car 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 多态:同一方法调用,根据对象类型表现出不同行为——面向对象三大特性之一。
多态的前提
- 继承(或实现接口)
- 方法重写
- 父类引用指向子类对象
基本多态
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 class | interface |
| 多继承 | 单继承 | 可实现多个 |
| 具体方法 | 可以有 | 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.nio | NIO(非阻塞 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
| 对比项 | ArrayList | LinkedList |
|---|---|---|
| 底层 | 动态数组 | 双向链表 |
| 随机访问 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.com | 465/587 |
| 163 邮箱 | smtp.163.com | 465 |
| Gmail | smtp.gmail.com | 465/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 / Gson | JSON 序列化 |
| Apache Commons | 通用工具(StringUtils 等) |
| Guava | Google 工具库 |
| 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 能省大量造轮子时间,但要选主流维护的库。