本工具使用的开源库

本工具代码中捆绑了 1 个开源库。

Java 速查 — 简明参考

Java 17+ 语法、OOP、集合与最常用标准库速查手册,覆盖约 80% 日常场景。

J

Java Java 17 (LTS)

JDK (OpenJDK / Oracle) · OOP · 泛型 · 函数式 · 静态 · 强类型 · 名义类型

学习路径

先跑通「Hello World 与构建环境」(javac/java 或 Maven)→ 熟悉变量、类型与流程控制 → 用集合与 Stream API 处理数据 → 深入面向对象(继承/接口/泛型)→ 掌握异常处理与 lambda → 最后按需查文件、网络、时间与构建调试。FAQ 节适合回头避坑。

1.Hello World 与构建环境

从零编写、编译并运行一个 Java 程序:main 方法、javac/java、包与构建工具。

最小程序

每个 Java 程序都从 main 方法开始:public static void main(String[] args)。System.out.println 输出一行。

1
2
3
4
5
6
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, world!");
}
}
// 文件名必须与 public 类同名:Hello.java

编译与运行

javac 把 .java 编译成 .class 字节码,java 启动 JVM 运行。javap 反编译查看字节码。

1
2
3
4
// $ javac Hello.java # 生成 Hello.class
// $ java Hello # 运行(不含 .class 后缀)
// $ javap -c Hello # 反编译查看字节码
// $ java -version # 查看 JDK 版本

命令行参数

main(String[] args) 接收命令行参数,args[0] 起为用户参数。args.length 判断数量。

1
2
3
4
5
6
7
8
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("No args");
return;
}
System.out.println("Hello, " + args[0] + "!");
}
// $ java Hello Nick

退出码

System.exit(code) 终止 JVM 并返回退出码,0 成功、非 0 失败。非 main 线程仍在运行时可强制退出。

1
2
3
4
5
if (error) {
System.err.println("failed");
System.exit(1); // 非 0 表示失败
}
// 正常结束不调用也会返回 0

包声明

package 声明所在包,对应目录结构,用全限定名区分类型。缺省为匿名包。

1
2
3
4
5
package com.example.app;
// 文件放在 com/example/app/ 目录下
public class Main {
// 全限定名:com.example.app.Main
}

import 导入

import 导入其他包的类或静态成员,省去全限定名。java.lang 默认导入无需声明。

1
2
3
4
5
import java.util.List;
import java.util.ArrayList;
import static java.lang.Math.PI; // 静态导入
import java.util.*; // 通配导入(不推荐滥用)
List<String> list = new ArrayList<>();

类与文件名

public 类名必须与文件名一致。一个 .java 文件可含多个非 public 类,但只能有一个 public 类。

1
2
3
4
5
// FileName.java 中:
public class FileName { // 与文件名同名
public static void main(String[] args) { }
}
class Helper { } // 非 public,可同文件

Maven / Gradle

大型项目用构建工具:Maven(pom.xml)与 Gradle(build.gradle)管理依赖、编译、测试与打包。

1
2
3
4
5
6
7
8
// pom.xml (Maven)
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
// $ mvn compile / mvn test / mvn package
// $ gradle build / gradle run

2.变量与常量

变量声明、类型推断、final 常量、作用域与类型转换。

变量声明

声明 = 类型 + 名称 + 可选初始化。Java 局部变量必须先初始化才能用,字段有默认值。

1
2
3
4
5
6
int count = 42;
double price = 19.99;
String name = "Nick";
boolean ok = true;
char c = 'A';
// 同时声明多个:int a = 1, b = 2;

var 类型推断

var(Java 10+)由编译器推断类型,仅限局部变量。类型不明确时显式声明更清晰。

1
2
3
4
5
var count = 42; // int
var name = "Nick"; // String
var list = new ArrayList<String>(); // ArrayList<String>
// var 不能用于字段、方法参数、返回值
// var 只是推断,运行时类型不变

final 常量

final 局部变量只能赋值一次;final 字段必须在声明或构造函数初始化;static final 是常量。

1
2
3
4
5
6
final int MAX = 100; // 只读局部变量
class Config {
final String name = "app"; // final 字段
static final double PI = 3.14; // 常量
}
// 再赋值 final 变量会编译错误

作用域

块作用域:大括号内的变量只在块内可见。嵌套块可遮蔽外层同名变量(不建议)。

1
2
3
4
5
6
7
int x = 1;
{
int y = 2; // 仅本块可见
System.out.println(x + y);
}
// y 在块外不可访问
// System.out.println(y); // 编译错误

命名规范

类名 UpperCamelCase、变量/方法 lowerCamelCase、常量 UPPER_SNAKE_CASE、包名全小写。

1
2
3
4
5
class UserProfile { }
int userId = 1;
String getUserName() { return "nick"; }
static final int MAX_RETRY = 3;
package com.example.app;

类型转换

隐式转换:小范围→大范围(int→long→double)。显式强转可能丢失精度(截断)。

1
2
3
4
5
6
int i = 42;
long l = i; // 隐式:int → long
int back = (int) l; // 显式强转
long big = 3000000000L;
int overflow = (int) big; // 溢出:截断为负值
String s = String.valueOf(i); // 数字 → 字符串

null 与 NPE

null 是引用类型的空值。对 null 调方法/取字段抛 NullPointerException(NPE)。Objects 工具可安全处理。

1
2
3
4
5
6
7
8
String s = null;
// s.length() // NPE!
if (s != null) {
System.out.println(s.length());
}
// Java 8+ 用 Optional 表达可空:
Optional<String> opt = Optional.ofNullable(s);
opt.ifPresent(System.out::println);

字面量

整数字面量可用 _ 分隔(可读性)、0x 十六进制、0b 二进制。long 加 L、float 加 f。

1
2
3
4
5
6
7
int million = 1_000_000; // 下划线分隔
int hex = 0xFF; // 255
int bin = 0b1101; // 13
long big = 42L;
float f = 3.14f;
double d = 3.14;
char ch = '\u0041'; // 'A'

3.数据类型

基本类型与包装类、字符串、数组、枚举、泛型与 record。

基本类型

Java 8 种基本类型:byte/short/int/long(整数)、float/double(浮点)、char、boolean。值直接存储。

1
2
3
4
5
6
7
8
byte b = 1;
short s = 2;
int i = 42;
long l = 42L;
float f = 3.14f;
double d = 3.14;
char c = 'A';
boolean flag = true;

包装类

Integer/Double/Boolean 等包装类把基本类型装进对象。自动装箱拆箱(boxing/unboxing)在必要时发生。

1
2
3
4
5
Integer num = 42; // 自动装箱 int → Integer
int value = num; // 自动拆箱
Integer parsed = Integer.parseInt("42");
// 装箱比较注意:
// new Integer(42) == new Integer(42) 为 false

字符串

String 是不可变字符序列,equals 比内容、== 比引用。每次修改字符串生成新对象。

1
2
3
4
5
String s = "hello";
String t = new String("hello");
boolean eq = s.equals(t); // true(内容)
boolean sameRef = s == t; // false(引用)
String upper = s.toUpperCase(); // "HELLO"

数组

数组定长、下标访问、length 属性。int[] 声明、new int[n] 创建、{} 初始化。

1
2
3
4
5
6
int[] arr = new int[5]; // 默认 0
int[] nums = { 1, 2, 3 };
int first = nums[0];
nums[2] = 99;
int len = nums.length; // 3(属性,非方法)
int[][] grid = new int[3][3]; // 二维

枚举

enum 定义常量集合,可带字段与方法。switch 可直接用枚举值。编译期类型安全。

1
2
3
4
5
6
enum Color { RED, GREEN, BLUE }
Color c = Color.RED;
String name = c.name(); // "RED"
int ord = c.ordinal(); // 0(声明顺序)
Color parsed = Color.valueOf("BLUE");
// enum 可带构造与字段:enum Status { OK(200), ERR(500); ... }

泛型

泛型把类型参数化:List<T>、Map<K,V>。编译期类型检查,运行时擦除(type erasure)。

1
2
3
4
5
6
7
List<String> names = new ArrayList<>();
Map<String, Integer> ages = new HashMap<>();
// 泛型方法:
static <T> T first(List<T> list) {
return list.get(0);
}
// 通配符:List<? extends Number>

record 类型

record(Java 14+/16 正式)一行声明不可变数据传输类:自动生成构造函数、equals/hashCode/toString。

1
2
3
4
5
record Person(String name, int age) { }
var p = new Person("Nick", 30);
String n = p.name(); // 访问器(非 getName)
String s = p.toString(); // Person[name=Nick, age=30]
// 不可变:字段隐式 final

自动装箱陷阱

装箱拆箱在 == / 运算中暗藏坑:-128~127 的 Integer 有缓存池,之外 == 比较可能 false。

1
2
3
4
5
Integer a = 100, b = 100;
boolean same = a == b; // true(缓存池)
Integer c = 200, d = 200;
boolean diff = c == d; // false!应使用 equals
boolean ok = c.equals(d); // true

Object 根类

所有类隐式继承 Object:toString、equals、hashCode、clone、finalize。重写 equals 必须重写 hashCode。

1
2
3
4
5
6
7
8
9
10
11
class User {
private String name;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User u)) return false;
return name.equals(u.name);
}
@Override
public int hashCode() { return name.hashCode(); }
}

4.引用与内存

引用语义、null、对象拷贝、堆栈、GC 与字符串池。

引用语义

Java 没有指针,对象变量保存的是引用(类似指针)。赋值共享同一对象,修改彼此可见。

1
2
3
4
5
StringBuilder a = new StringBuilder("a");
StringBuilder b = a; // 引用拷贝,共享对象
b.append("b");
System.out.println(a); // "ab"(a 也变了)
// 基本类型是值拷贝:int x = y 互不影响

null 引用

null 表示引用不指向任何对象。比较 null 用 == / !=,传参可能产生 NPE。防御性判空。

1
2
3
4
5
6
String s = getMaybe();
if (s != null) {
System.out.println(s.length());
}
// Objects.requireNonNull(s, "s must not be null");
// Objects.toString(s, ""); 安全转换

Object 方法

toString 描述对象、equals 内容比较、hashCode 哈希。默认实现是引用比较,内容语义需重写。

1
2
3
4
5
6
7
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
@Override
public String toString() { return "(" + x + "," + y + ")"; }
}
System.out.println(new Point(1, 2)); // "(1,2)"

堆与栈

对象分配在堆(GC 管理)、局部变量与引用在栈。new 的对象引用存储在栈、对象体在堆。

1
2
3
4
void method() {
int x = 1; // 栈:基本类型
Person p = new Person(); // p 引用在栈,对象在堆
} // 方法结束,栈释放,对象等 GC

GC 垃圾回收

JVM 自动回收无引用对象,无需手动 free。System.gc() 只是建议,不保证立即执行。

1
2
3
4
5
// 不再引用对象即可被回收
Person p = new Person();
p = null; // 原对象失去引用,等待 GC
// System.gc(); // 仅建议,生产环境不调用
// 分代回收:新生代 / 老年代

拷贝语义

数组/对象赋值是引用共享。需要独立副本用 clone、Arrays.copyOf 或手动复制。深拷贝需逐层复制。

1
2
3
4
5
6
int[] a = { 1, 2, 3 };
int[] shallow = a; // 共享
int[] copy = a.clone(); // 独立副本
copy[0] = 99;
System.out.println(a[0]); // 1(copy 独立)
// 对象数组 clone 是浅拷贝,元素仍共享

字符串常量池

字面量字符串缓存在常量池:相同字面量共享。new String() 创建新对象不进池。intern() 手动入池。

1
2
3
4
5
6
String a = "hello";
String b = "hello";
boolean same = a == b; // true(池中同一对象)
String c = new String("hello");
boolean diff = a == c; // false(堆上新对象)
String d = c.intern(); // 入池后 a == d 为 true

finalize 与 Cleaner

finalize 在 GC 前调用但时机不保证(已废弃)。释放外部资源用 AutoCloseable + try-with-resources。

1
2
3
4
5
6
7
@Override
protected void finalize() { } // 已废弃,不要依赖
// 正确做法:
class Conn implements AutoCloseable {
public void close() { /* 释放资源 */ }
}
try (var c = new Conn()) { } // 自动 close

5.流程控制

if/else、switch、for/while 循环、break/continue 与三元。

if / else

if/else 按条件分支。条件必须是布尔表达式。可用 else-if 链多分支。

1
2
3
4
5
6
7
8
9
10
int score = 85;
String grade;
if (score >= 90) {
grade = "A";
} else if (score >= 60) {
grade = "B";
} else {
grade = "F";
}
System.out.println(grade);

switch 表达式

switch(Java 14+)可用 -> 表达式返回值和 case 合并。传统 switch 语句也可用箭头语法。

1
2
3
4
5
6
7
8
int day = 3;
String name = switch (day) {
case 1 -> "Monday";
case 2, 3 -> "Tue/Wed"; // 多值合并
default -> "other";
};
// 传统 switch 也能用 -> :
// switch (day) { case 1 -> System.out.println("Mon"); }

for 循环

经典 for:初始化、条件、步进。需要下标或逆序时用。遍历集合优先增强 for。

1
2
3
4
5
6
for (int i = 0; i < 10; i++) {
System.out.println(i); // 0..9
}
for (int i = arr.length - 1; i >= 0; i--) {
System.out.println(arr[i]); // 逆序
}

增强 for

for-each 遍历数组和 Iterable 集合,无需下标。不可修改集合结构(会抛异常)。

1
2
3
4
5
for (String name : names) {
System.out.println(name);
}
for (int n : nums) { /* 只读遍历 */ }
// 需要下标/删除时用 Iterator 或普通 for

while / do-while

while 先判断后执行;do-while 至少执行一次。循环次数未知的场景(读流、轮询)用 while。

1
2
3
4
5
6
int i = 0;
while (i < 5) { i++; } // 先判断
int x = 0;
do {
x++; // 至少执行一次
} while (x < 3);

break 与 continue

continue 跳本轮、break 退出循环、带标签 break/continue 控制嵌套循环。

1
2
3
4
5
6
7
8
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) continue; // 跳过本轮
if (i == 2) break outer; // 跳出全部
}
}
// 标签作用域:outer: 标记外层循环

三元运算符

cond ? a : b 一行表达 if/else。两侧类型需兼容。嵌套三元可读性差,避免。

1
2
3
4
5
int age = 20;
String type = age >= 18 ? "adult" : "minor";
// 等价:
String t2;
if (age >= 18) t2 = "adult"; else t2 = "minor";

return 与提前返回

return 结束方法并返回值(void 直接 return;)。提前返回让方法更易读(guard clause)。

1
2
3
4
5
6
boolean isValid(String s) {
if (s == null || s.isEmpty()) return false;
if (s.length() > 10) return false;
return true;
}
// 提前返回避免深层嵌套

6.方法与 Lambda

方法签名、参数、重载、递归、Lambda 与函数式编程。

方法定义

方法 = 访问修饰符 + 返回类型 + 名称 + 参数表 + 主体。void 表示无返回值。

1
2
3
4
5
6
7
public int add(int a, int b) {
return a + b;
}
public void say(String msg) {
System.out.println(msg); // void 无返回
}
static int timesTwo(int x) { return x * 2; }

参数传递

Java 一律按值传递:基本类型传副本,引用类型传引用的副本(改成员影响原对象,改引用不影响)。

1
2
3
4
5
6
void set(int x) { x = 99; } // 基本类型副本
void mutate(List<String> l) { l.add("x"); }
int n = 1;
set(n); // n 仍是 1
var list = new ArrayList<String>();
mutate(list); // list 被添加了 "x"

可变参数

... 可变参数接收任意数量同类型参数,本质是数组。必须放参数表最后,只能有一个。

1
2
3
4
5
6
7
8
int sum(int... nums) {
int total = 0;
for (int n : nums) total += n;
return total;
}
int s = sum(1, 2, 3); // 6
int s2 = sum(); // 0(空数组)
int s3 = sum(new int[]{1,2}); // 也可传数组

方法重载

同名方法不同参数列表(数量/类型)为重载。按实参类型选最匹配版本。返回类型不参与区分。

1
2
3
4
5
int parse(String s) { return Integer.parseInt(s); }
int parse(int x) { return x; } // 类型不同
int parse(String s, int radix) { // 数量不同
return Integer.parseInt(s, radix);
}

递归

方法调用自身为递归。必须要有基线条件(base case)否则栈溢出。

1
2
3
4
5
int factorial(int n) {
if (n <= 1) return 1; // 基线条件
return n * factorial(n - 1);
}
// 深递归注意栈溢出:每层调用占用调用栈

Lambda 表达式

Lambda = 参数 -> 表达式,是函数式接口的实例。可省略类型推断与单参数括号。

1
2
3
4
5
6
7
8
Runnable r = () -> System.out.println("run");
BiFunction<Integer, Integer, Integer> add =
(a, b) -> a + b;
// 单参数可去括号:x -> x * 2
// 多行体用花括号 + return:
Comparator<Integer> c = (x, y) -> {
return x.compareTo(y);
};

方法引用

:: 方法引用是 Lambda 的简写:ClassName::staticMethod、obj::instanceMethod、ClassName::new。

1
2
3
4
5
6
7
List<String> names = List.of("b", "a");
names.stream().sorted(String::compareTo);
// Class::staticMethod
names.forEach(System.out::println);
// obj::instanceMethod
names.forEach(s -> System.out.println(s));
// Class::new 构造器引用:Supplier<Person> s = Person::new;

函数式接口

只有一个抽象方法的接口可用 Lambda。常见:Runnable、Function、Consumer、Predicate、Supplier。

1
2
3
4
5
Predicate<Integer> isEven = n -> n % 2 == 0;
Function<String, Integer> len = String::length;
Consumer<String> print = System.out::println;
Supplier<Double> rand = Math::random;
// @FunctionalInterface 注解声明函数式接口

Stream API

Stream 链式处理集合:filter 过滤、map 转换、collect 收集。惰性、不修改原集合。

1
2
3
4
5
6
7
List<Integer> nums = List.of(1, 2, 3, 4);
var evens = nums.stream()
.filter(n -> n % 2 == 0) // [2, 4]
.map(n -> n * 10) // [20, 40]
.toList(); // Java 16+
// 聚合:sum/max/count/anyMatch/allMatch
int sum = nums.stream().mapToInt(Integer::intValue).sum();

7.字符串

字符串字面量、拼接、StringBuilder、常用方法与格式化。

字符串字面量

双引号字符串、\n 转义、文本块(Java 15+)用三个双引号保留多行格式。

1
2
3
4
5
6
7
8
String s = "Line\nTab\tindent";
String path = "C:\\Program Files\\";
// 文本块(Java 15+):三个双引号包裹多行文本,自动缩进
// String html = """
// <div>
// <p>Hello</p>
// </div>
// """.stripIndent();

拼接

+ 拼接字符串,字符串与其他类型拼接自动转字符串。少量拼接足够,循环内用 StringBuilder。

1
2
3
4
5
String a = "foo" + "bar"; // "foobar"
int age = 30;
String msg = "Age: " + age; // 自动转字符串
String joined = String.join(", ", "a", "b", "c");
// 循环拼接请用 StringBuilder(见 mem 节)

常用方法

常用方法:length 取长度、substring 截取、indexOf 查找、replace 替换、split 分割、trim 去除空白与大小写转换。

1
2
3
4
5
6
7
String s = " Hello, World ";
int len = s.length(); // 14
char c = s.charAt(0); // ' '
String sub = s.substring(7, 12); // "World"
boolean has = s.contains("World"); // true
String rep = s.replace("World", "Java");
String[] words = s.trim().split(",");

StringBuilder

大量拼接用 StringBuilder(非线程安全)或 StringBuffer(线程安全):append 后 toString。

1
2
3
4
5
6
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; i++) {
sb.append(i).append(",");
}
String result = sb.toString(); // 一次性转换
// 可变:删除/插入可用 sb.deleteCharAt / sb.insert

格式化

String.format 格式化:%d 整数、%s 字符串、%.2f 小数、%n 换行。可带宽度与标志。

1
2
3
4
5
double d = 1234.567;
String s = String.format("%.2f", d); // "1234.57"
String t = String.format("%d%%", 50); // "50%"
String pad = String.format("%5d", 42); // " 42"
String n = String.format("%,d", 1234567); // "1,234,567"

正则匹配

String.matches 整体匹配、replaceAll 按正则替换、split 按正则分割。含正则模式时注意转义。

1
2
3
boolean m = "123-456".matches("\\d{3}-\\d{3}"); // true
String masked = "123456".replaceAll("(\\d{3})(\\d+)", "$1***");
String[] parts = "a,b;c".split("[,;]"); // [a, b, c]

字符处理

charAt 取字符、Character.isDigit/isLetter/isWhitespace 判断类别。字符串不可变,字符可遍历。

1
2
3
4
5
6
String s = "Java 17";
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (Character.isUpperCase(c)) { }
}
// 也可:for (char c : s.toCharArray())

字符串转换

Integer.parseInt 字符串转数字,valueOf/toString 数字转字符串。解析失败抛 NumberFormatException。

1
2
3
4
5
6
7
int n = Integer.parseInt("42");
long l = Long.parseLong("42");
double d = Double.parseDouble("3.14");
String s = String.valueOf(42);
String hex = Integer.toHexString(255); // "ff"
// 安全解析:
// try { ... } catch (NumberFormatException e) { }

8.集合与 Stream

List/Set/Map/Queue 常用实现、遍历、Stream 与排序。

List 列表

List 有序集合,ArrayList 数组实现(查快)、LinkedList 链表实现(插删快)。List.of 不可变列表。

1
2
3
4
5
6
7
8
List<String> list = new ArrayList<>();
list.add("a");
list.add(0, "first"); // 指定位置插入
String x = list.get(1);
list.remove("a");
int size = list.size();
boolean has = list.contains("b");
var fixed = List.of("a", "b"); // 不可变

Set 集合

Set 去重集合:HashSet 无序 O(1)、LinkedHashSet 保插入序、TreeSet 排序。add 重复返回 false。

1
2
3
4
5
6
Set<Integer> set = new HashSet<>();
set.add(1);
boolean added = set.add(1); // false(已存在)
boolean has = set.contains(1);
var ordered = new LinkedHashSet<String>(); // 保序
var sorted = new TreeSet<Integer>(); // 升序

Map 映射

Map 键值映射:HashMap O(1)、LinkedHashMap 保插入序、TreeMap 按键排序。getOrDefault 安全取值。

1
2
3
4
5
6
7
8
Map<String, Integer> map = new HashMap<>();
map.put("Nick", 30);
Integer age = map.get("Nick");
int safe = map.getOrDefault("X", 0);
map.computeIfAbsent("k", k -> 1); // 缺省则计算
for (Map.Entry<String, Integer> e : map.entrySet()) {
System.out.println(e.getKey() + "=" + e.getValue());
}

Queue / Deque

Queue 先进先出(offer/poll/peek),Deque 双端队列(addFirst/addLast)。ArrayDeque 比 LinkedList 快。

1
2
3
4
5
6
7
Queue<Integer> q = new ArrayDeque<>();
q.offer(1); q.offer(2);
int head = q.peek(); // 1(不删)
int out = q.poll(); // 1(取出)
Deque<Integer> d = new ArrayDeque<>();
d.addFirst(1); d.addLast(2);
int first = d.pollFirst();

遍历与迭代

增强 for / Iterator / forEach 遍历集合。遍历时删除需用 Iterator.remove 或收集后删。

1
2
3
4
5
6
7
8
for (String s : list) { }
list.forEach(System.out::println);
// 遍历删除:
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().isEmpty()) it.remove();
}
// 不能用增强 for 直接 remove(会抛异常)

排序

List.sort 或 Collections.sort 排序,Comparator 定制顺序,comparing 链式组合比较器。

1
2
3
4
5
6
7
List<Integer> nums = new ArrayList<>(List.of(3, 1, 2));
nums.sort(null); // 自然升序
nums.sort(Comparator.reverseOrder()); // 降序
// 定制对象排序:
people.sort(Comparator
.comparing(Person::age)
.thenComparing(Person::name));

Stream 链式操作

filter/map/sorted/distinct/limit 链式,terminal 操作触发执行。collect 收集回集合。

1
2
3
4
5
6
7
8
var result = people.stream()
.filter(p -> p.age() >= 18)
.sorted(Comparator.comparing(Person::age).reversed())
.map(Person::name)
.distinct()
.limit(10)
.toList();
// 惰性:没有 terminal 操作不会真正执行

分组与聚合

Collectors.groupingBy 按键分组、partitioningBy 布尔分区、summarizingInt 统计。

1
2
3
4
5
6
7
Map<String, List<Order>> byRegion = orders.stream()
.collect(Collectors.groupingBy(Order::region));
Map<Boolean, List<Integer>> part = nums.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// 统计:Collectors.summingInt / averagingInt
int total = orders.stream()
.collect(Collectors.summingInt(Order::amount));

不可变集合

List.of/Set.of/Map.of 创建不可变集合。Collections.unmodifiableList 包装只读视图。

1
2
3
4
5
var fixed = List.of(1, 2, 3); // 不可变
// fixed.add(4); // UnsupportedOperationException
var mutable = new ArrayList<>(fixed); // 可变副本
var view = Collections.unmodifiableList(mutable);
// 包装后任何修改都抛异常

9.内存与性能

GC、弱引用、内存泄漏、字符串拼接性能与缓冲区。

字符串拼接性能

循环内 + 拼接会反复创建 String(不可变)。用 StringBuilder 一次组装,性能显著提升。

1
2
3
4
5
6
7
// 慢:循环拼接创建大量中间对象
String s = "";
for (int i = 0; i < 1000; i++) s += i;
// 快:StringBuilder 原地追加
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) sb.append(i);
String r = sb.toString();

弱引用与软引用

WeakReference 不阻止 GC(缓存用),SoftReference 内存不足才回收,PhantomReference 回收后清理。

1
2
3
4
5
WeakReference<BigObj> weak = new WeakReference<>(new BigObj());
System.gc();
BigObj obj = weak.get(); // 可能已为 null
// 软引用:内存不足才回收(适合图片缓存)
SoftReference<Image> soft = new SoftReference<>(image);

内存泄漏

常见泄漏:静态集合持有对象、未关闭资源、监听器未注销、ThreadLocal 不清理。避免长生命周期持有短对象。

1
2
3
4
5
// 静态 Map 一直增长 = 泄漏
static Map<String, Session> sessions = new HashMap<>();
// 用完须移除:sessions.remove(id)
// 或改用 WeakHashMap<String, Session>
// 资源用完关闭:try (var c = open()) { }

ThreadLocal

ThreadLocal 每线程独立变量副本。Web 容器线程池复用线程,用完不 remove 会跨请求泄漏。

1
2
3
4
5
private static final ThreadLocal<SimpleDateFormat> fmt =
ThreadLocal.withInitial(SimpleDateFormat::new);
String d = fmt.get().format(date);
// 关键:用完必须 remove(),尤其线程池环境
// fmt.remove(); 否则线程复用导致对象泄漏

ByteBuffer

ByteBuffer 直接内存(allocateDirect)减少 GC 压力,适合 NIO 与大数据传输。需手动管理。

1
2
3
4
5
6
7
ByteBuffer buf = ByteBuffer.allocate(1024); // 堆
ByteBuffer direct = ByteBuffer.allocateDirect(1024); // 直接内存
buf.putInt(42).putDouble(3.14);
buf.flip(); // 切换到读模式
int i = buf.getInt();
double d = buf.getDouble();
buf.clear(); // 复用缓冲

OutOfMemoryError

OOM 表示内存耗尽。Heap Space 堆满、Metaspace 类过多、Direct buffer 过多。调整 JVM 参数或找泄漏。

1
2
3
4
5
// JVM 参数:
// $ java -Xms512m -Xmx2g -XX:+HeapDumpOnOutOfMemoryError App
// -Xmx 最大堆、-Xms 初始堆
// 堆溢出转储:生成 .hprof 用工具分析
// 排查:找集合无界增长 / 静态持有 / 资源泄漏

数组拷贝

System.arraycopy 高效拷贝数组区间。Arrays.copyOf 复制并可能扩容。循环手动拷贝慢。

1
2
3
4
5
int[] src = { 1, 2, 3, 4 };
int[] dst = new int[4];
System.arraycopy(src, 1, dst, 0, 2); // [2, 3, 0, 0]
int[] copy = Arrays.copyOf(src, src.length);
int[] grown = Arrays.copyOf(src, 6); // 扩容补 0

对象池复用

频繁创建大对象会加大 GC 压力。用对象池缓存可复用实例,但需注意线程安全与归还路径。

1
2
3
4
5
6
7
8
9
10
11
// 简易对象池:避免频繁 new 大对象
final class ConnectionPool {
private final ArrayDeque<Connection> idle = new ArrayDeque<>();
synchronized Connection acquire() {
return idle.isEmpty() ? new Connection() : idle.poll();
}
synchronized void release(Connection c) {
idle.push(c); // 归还复用
}
}
// 池越大内存占用越高,需与复用收益权衡

10.面向对象

类、封装、继承、多态、抽象类、接口与访问修饰符。

类与对象

class 定义数据类型:字段存状态、方法定义行为、构造函数初始化、new 创建实例。

1
2
3
4
5
6
7
8
9
10
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String greet() { return "Hi, " + name; }
}
var p = new Person("Nick", 30);

封装

字段私有(private)、方法公开(public)控制访问。getter/setter 加校验逻辑,保护不变式。

1
2
3
4
5
6
7
8
public class Account {
private double balance;
public double getBalance() { return balance; }
public void deposit(double amount) {
if (amount <= 0) throw new IllegalArgumentException();
balance += amount;
}
}

继承

extends 继承基类。Java 单继承。super 调用父类构造/方法,子类是父类的一种(is-a)。

1
2
3
4
5
6
7
8
9
public class Animal {
public void speak() { System.out.println("..."); }
}
public class Dog extends Animal {
@Override
public void speak() { System.out.println("Woof"); }
}
Animal a = new Dog();
a.speak(); // "Woof"(多态)

多态

父类/接口引用指向子类实例,调用虚方法按实际类型分派。@Override 标注重写。

1
2
3
4
5
6
7
8
9
10
11
public class Shape {
public double area() { return 0; }
}
public class Circle extends Shape {
private final double r;
public Circle(double r) { this.r = r; }
@Override
public double area() { return Math.PI * r * r; }
}
Shape s = new Circle(2);
double a = s.area(); // 12.57(按实际类型)

抽象类

abstract 类不能实例化,可含 abstract 方法(子类必须实现)。用于模板基类共享状态。

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class Shape {
public abstract double area(); // 无实现
public void describe() {
System.out.println("Area: " + area());
}
}
public class Square extends Shape {
private final double side;
public Square(double s) { this.side = s; }
@Override
public double area() { return side * side; }
}

接口

interface 定义契约:方法默认 public abstract。Java 8+ 有 default/static 方法。可多实现。

1
2
3
4
5
6
7
8
9
10
11
public interface Logger {
void log(String msg); // 抽象方法
default void warn(String m) { // default 实现
log("[WARN] " + m);
}
}
public class ConsoleLogger implements Logger {
@Override
public void log(String msg) { System.out.println(msg); }
}
Logger l = new ConsoleLogger(); // 面向接口

访问修饰符

public 全公开、protected 包内+子类、缺省 包内、private 本类。类成员默认包内可见。

1
2
3
4
5
6
7
public class Demo {
public int pub; // 任意
protected int prot; // 包内 + 子类
int def; // 包内
private int priv; // 仅本类
}
// 顶级类只能是 public 或包内可见(缺省)

static 成员

static 字段/方法属于类而非实例。static 方法不能访问实例成员。静态代码块初始化。

1
2
3
4
5
6
7
8
public class Counter {
private static int count; // 类级
public static void inc() { count++; }
public static int get() { return count; }
}
Counter.inc(); // 通过类访问
Counter.inc();
System.out.println(Counter.get()); // 2

内部类与匿名类

内部类、匿名类、lambda 简化回调。静态内部类不持有外部实例,避免内存泄漏。

1
2
3
4
5
6
7
8
// 匿名类(Java 8 前写法):
Runnable r = new Runnable() {
@Override public void run() { }
};
// 更推荐 Lambda:
Runnable r2 = () -> System.out.println("hi");
// 静态内部类:static class Builder { }
// 内部类自动持有外部实例引用(注意泄漏)

11.异常处理

try/catch/finally、异常层级、checked/unchecked、try-with-resources 与自定义异常。

try / catch / finally

try 放可能出错的代码,catch 捕获处理,finally 无论成败都执行(清理资源)。异常向上传播。

1
2
3
4
5
6
7
try {
int n = Integer.parseInt("abc");
} catch (NumberFormatException e) {
System.out.println("bad number: " + e.getMessage());
} finally {
System.out.println("cleanup"); // 总会执行
}

多 catch

多个 catch 按类型匹配,具体异常在前、泛异常在后。multi-catch 用 | 合并无关类型。

1
2
3
4
5
6
7
8
9
try {
process();
} catch (FileNotFoundException e) {
// 具体异常
} catch (IOException e) {
// 泛异常
} catch (IllegalArgumentException | IllegalStateException e) {
// multi-catch:| 合并(无继承关系)
}

抛出与重抛

throw new 抛异常。catch 里 throw;(Java 7+)原样重抛保留堆栈。方法声明 throws 标记可能抛出的检查异常。

1
2
3
4
5
6
7
throw new IllegalArgumentException("invalid value");
public void read() throws IOException {
throw new IOException("io error");
}
try { read(); } catch (IOException e) {
throw new RuntimeException(e); // 包装重抛
}

自定义异常

自定义异常继承 Exception(检查)或 RuntimeException(非检查)。惯例类名以 Exception 结尾。

1
2
3
4
5
6
7
8
9
public class ConfigException extends RuntimeException {
public ConfigException(String message) {
super(message);
}
public ConfigException(String message, Throwable cause) {
super(message, cause);
}
}
throw new ConfigException("bad config");

检查与非检查异常

检查异常(IOException 等)必须处理或声明;非检查异常(RuntimeException 子类)编译期不强制。

1
2
3
4
5
6
7
// 检查异常:必须 catch 或 throws
public void read() throws IOException {
Files.readString(Path.of("a.txt"));
}
// 非检查异常:可不处理
int x = Integer.parseInt("abc"); // 编译通过,运行抛异常
// 自定义异常常用 RuntimeException 子类

try-with-resources

try (资源) { } 自动调用 AutoCloseable 的 close(),异常时也会关。替代手动 finally 关闭。

1
2
3
4
5
try (var reader = new BufferedReader(
Files.newBufferedReader(Path.of("a.txt")))) {
String line = reader.readLine();
} // 自动关闭 reader
// 多个资源:try (var a = ...; var b = ...) { }

异常的代价

异常捕获开销高(填堆栈),不做流程控制。可预判错误用返回值/判空/optional。

1
2
3
4
5
6
// 慢:用异常做流程控制
boolean ok1;
try { Integer.parseInt(s); ok1 = true; }
catch (NumberFormatException e) { ok1 = false; }
// 快:先判空/正则再解析
boolean ok2 = s != null && s.matches("\\d+");

日志与异常

异常要记录(含堆栈),别 System.out 打印后继续。日志框架(SLF4J+Logback)分级输出。

1
2
3
4
5
6
7
// SLF4J + Logback:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger log =
LoggerFactory.getLogger(App.class);
// 记录异常堆栈:
// log.error("failed", e); // e 保留堆栈

12.文件与 IO

Files/Path 现代 IO、Scanner、Reader/Writer 与流式读取。

Files 读写

java.nio.file.Files 现代 API:readString/writeString、readAllLines、copy/move/delete。

1
2
3
4
5
String content = Files.readString(Path.of("in.txt"));
Files.writeString(Path.of("out.txt"), content);
List<String> lines = Files.readAllLines(Path.of("in.txt"));
Files.copy(Path.of("a"), Path.of("b"), StandardCopyOption.REPLACE_EXISTING);
Files.deleteIfExists(Path.of("tmp"));

Scanner 输入

Scanner 读控制台或文件:nextLine 读行、nextInt/nextDouble 读类型。hasNext 判断还有输入。

1
2
3
4
5
6
7
Scanner sc = new Scanner(System.in);
System.out.print("Name: ");
String name = sc.nextLine();
int age = sc.nextInt();
// 从文件读:
var file = new Scanner(Path.of("a.txt"));
while (file.hasNextLine()) System.out.println(file.nextLine());

Reader / Writer

字符流读写文本:BufferedReader 逐行、BufferedWriter 写、try-with-resources 自动关闭。

1
2
3
4
5
6
7
8
9
10
try (var reader = new BufferedReader(
new FileReader("a.txt"));
var writer = new BufferedWriter(
new FileWriter("b.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} // 自动关闭两个流

字节流

InputStream/OutputStream 读写字节:FileInputStream、BufferedInputStream 缓冲。数据搬运用 transferTo。

1
2
3
4
5
6
7
8
9
try (var in = new FileInputStream("a.bin");
var out = new FileOutputStream("b.bin")) {
byte[] buf = new byte[8192];
int read;
while ((read = in.read(buf)) != -1) {
out.write(buf, 0, read);
}
}
// 简单版:Files.copy(Path.of("a"), Path.of("b"))

控制台 IO

System.out 输出、System.in 输入、System.err 错误输出。printf 格式化输出。

1
2
3
4
5
System.out.print("No newline");
System.out.println("With newline");
System.out.printf("%s is %d years old%n", "Nick", 30);
System.err.println("Error message"); // 标准错误
// 格式化:%n 换行(跨平台),不用 \n

Path 路径

Path 表示路径,resolve 拼接、getFileName、toAbsolutePath、exists、Files.walk 递归遍历。

1
2
3
4
5
6
7
8
9
Path dir = Path.of("data");
Path file = dir.resolve("a.txt"); // data/a.txt
String name = file.getFileName().toString();
boolean exists = Files.exists(file);
// 递归列出:
try (var stream = Files.walk(dir)) {
stream.filter(p -> p.toString().endsWith(".txt"))
.forEach(System.out::println);
}

序列化

ObjectOutputStream/ObjectInputStream 序列化对象(需 implements Serializable)。JSON 更常用。

1
2
3
4
5
6
7
8
9
10
class User implements Serializable {
private static final long serialVersionUID = 1L;
String name;
}
// 写入:
try (var out = new ObjectOutputStream(
new FileOutputStream("u.bin"))) {
out.writeObject(new User());
}
// 读取:ObjectInputStream in = ...; User u = (User) in.readObject();

临时文件

Files.createTempFile 在系统临时目录创建文件,用完须 Files.deleteIfExists 清理,避免残留堆积。

1
2
3
4
5
6
Path tmp = Files.createTempFile("app-", ".log");
System.out.println(tmp); // 系统临时目录
Files.writeString(tmp, "temp data");
// 用完即删,避免残留:
Files.deleteIfExists(tmp);
// 临时目录:Files.createTempDirectory("app");

13.常见陷阱(FAQ)

Java 开发者最常踩的坑:== vs equals、装箱缓存、并发、异常与集合修改。

== vs equals

字符串/对象比较内容必须用 equals,== 比较引用。字面量缓存池让 == 偶然成立,勿依赖。

1
2
3
4
5
6
7
// BAD:== 比较引用,字面量池掩盖问题
String a = "nick";
String b = new String("nick");
boolean bad = a == b; // false
// GOOD:equals 比较内容
boolean good = a.equals(b); // true

Integer 缓存池

自动装箱的 -128~127 有缓存,== 比较可能为 true;范围外为 false。对象比较一律 equals。

1
2
3
4
5
6
7
// BAD:用 == 比较装箱值
Integer a = 200, b = 200;
boolean bad = a == b; // false
// GOOD:equals 比较
Integer c = 200, d = 200;
boolean good = c.equals(d); // true

判空与 NPE

对 null 调方法必 NPE。可空值用 Optional 或提前判空,别到处 try-catch NPE。

1
2
3
4
5
6
7
8
// BAD:不做判空
String s = find();
System.out.println(s.length()); // 可能 NPE
// GOOD:判空或 Optional
String t = find();
System.out.println(t == null ? 0 : t.length());
// 或 Optional.ofNullable(find()).map(String::length).orElse(0)

异常吞掉

catch 后不处理就是吞异常,排查困难。至少打日志或原样重抛(包装到合适异常)。

1
2
3
4
5
6
7
8
// BAD:静默吞掉
catch (IOException e) { }
// GOOD:记录或抛给上层
catch (IOException e) {
log.error("read failed", e);
throw new RuntimeException("read failed", e);
}

equals 与 hashCode

重写 equals 必须重写 hashCode,否则放 HashSet/HashMap 行为错误(同一逻辑对象算不同 key)。

1
2
3
4
5
6
7
8
// BAD:只重写 equals
class A { public boolean equals(Object o) { ... } } // hashCode 不一致
// GOOD:两者同时重写
class B {
public boolean equals(Object o) { /* 相同逻辑 */ }
public int hashCode() { return Objects.hash(fields); }
}

遍历时删除

增强 for 里 List.remove 抛 ConcurrentModificationException。用 Iterator.remove 或收集后删。

1
2
3
4
5
6
7
8
9
10
// BAD:增强 for 中删除
for (String s : list) {
if (s.isEmpty()) list.remove(s); // 抛异常
}
// GOOD:Iterator 删除
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().isEmpty()) it.remove();
}

日期可变性

旧 Date/Calendar 可变易出错。统一用 java.time(LocalDate/LocalDateTime)不可变安全。

1
2
3
4
5
6
7
// BAD:可变 Date
Date d = new Date();
d.setTime(0); // 状态被外部修改
// GOOD:java.time 不可变
LocalDate today = LocalDate.now();
LocalDate next = today.plusDays(1); // 返回新对象

线程安全

HashMap/ArrayList 非线程安全,多线程写入出问题。用 ConcurrentHashMap 或同步包装。

1
2
3
4
5
6
7
8
// BAD:多线程写 HashMap
Map<String, Integer> map = new HashMap<>();
// 并发 put 可能损坏内部结构
// GOOD:并发集合
Map<String, Integer> safe = new ConcurrentHashMap<>();
safe.put("k", 1);
// 或 Collections.synchronizedMap(new HashMap<>())

Stream 空值

Stream 里元素为 null 时 filter/map 可能 NPE。先用 filter(Objects::nonNull) 过滤。

1
2
3
4
5
6
7
8
// BAD:null 元素进 map
list.stream().map(String::toUpperCase) // 有 null 则 NPE
// GOOD:先过滤 null
list.stream()
.filter(Objects::nonNull)
.map(String::toUpperCase)
.toList();

循环拼接

循环内 += 拼接产生大量中间 String,性能差。用 StringBuilder。

1
2
3
4
5
6
7
8
// BAD:循环拼接
String s = "";
for (int i = 0; i < 10000; i++) s += i;
// GOOD:StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) sb.append(i);
String r = sb.toString();

14.并发与线程

Thread、synchronized、线程池 ExecutorService 与 CompletableFuture。

Thread 线程

Thread 或 Runnable 创建线程,start 启动、join 等待、sleep 暂停。大多场景线程池更合适。

1
2
3
4
5
6
7
8
9
10
// 方式一:继承 Thread
Thread t = new Thread(() -> {
System.out.println("worker");
});
// 方式二:Runnable
Runnable task = () -> System.out.println("run");
Thread t2 = new Thread(task);
t2.start(); // 启动
// t2.join(); // 等待完成
// Thread.sleep(100); // 暂停毫秒

synchronized

synchronized 加锁保证互斥。锁实例方法锁 this、静态方法锁 Class、代码块可锁任意对象。

1
2
3
4
5
6
7
8
private int count;
public synchronized void increment() { count++; }
// 等价:synchronized(this) { count++; }
// 静态同步:
public static synchronized void inc2() { }
// 锁对象通常用专用 final 字段:
private final Object lock = new Object();
synchronized (lock) { /* 临界区 */ }

volatile

volatile 保证可见性(线程间立即可见),但不保证原子性。状态标志用 volatile,计数用 AtomicInteger。

1
2
3
4
5
6
7
private volatile boolean running = true;
// 其他线程读取立即可见
// 但 volatile 不解决原子操作:
// volatile int n; n++; // 非原子!
// 用 AtomicInteger:
AtomicInteger counter = new AtomicInteger();
counter.incrementAndGet();

线程池

ExecutorService 管理线程复用。newFixedThreadPool/newCachedThreadPool,用完 shutdown。

1
2
3
4
5
6
7
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
pool.submit(() -> System.out.println(Thread.currentThread().getName()));
}
pool.shutdown(); // 不再接收新任务
// pool.awaitTermination(5, TimeUnit.SECONDS);
// 推荐:Executors.newVirtualThreadPerTaskExecutor()(Java 21)

Future 与 Callable

Callable 有返回值的任务,Future.get 阻塞取结果。FutureTask 可手动控制。

1
2
3
4
5
6
7
8
ExecutorService pool = Executors.newFixedThreadPool(2);
Future<Integer> f = pool.submit(() -> {
return compute(); // Callable 有返回值
});
int result = f.get(); // 阻塞等待结果
// f.get(5, TimeUnit.SECONDS); // 超时
// f.cancel(true); // 取消
pool.shutdown();

CompletableFuture

异步编排:thenApply 转换、thenCombine 合并、allOf 等待多个。回调链式不阻塞主线程。

1
2
3
4
5
6
7
8
CompletableFuture.supplyAsync(() -> fetch())
.thenApply(data -> parse(data))
.thenAccept(result -> System.out.println(result))
.exceptionally(ex -> { log(ex); return null; });
// 等待多个:
var all = CompletableFuture.allOf(f1, f2);
all.join();
// join() 阻塞等待,结果经 get() 取出

并发集合

ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue 线程安全。替代手动加锁的普通集合。

1
2
3
4
5
6
7
Map<String, Integer> map = new ConcurrentHashMap<>();
map.put("k", 1);
map.compute("k", (k, v) -> v == null ? 1 : v + 1);
Queue<Task> q = new ArrayBlockingQueue<>(100);
q.offer(task); // 满则返回 false
Task t = q.poll(); // 空则返回 null
// CopyOnWriteArrayList 读多写少场景

Lock 接口

ReentrantLock 比 synchronized 灵活:可超时、可中断、可公平。Lock 必须手动 unlock(finally)。

1
2
3
4
5
6
7
8
9
10
11
private final ReentrantLock lock = new ReentrantLock();
void work() {
lock.lock();
try {
/* 临界区 */
} finally {
lock.unlock(); // 必须 finally 释放
}
}
// lock.tryLock(1, TimeUnit.SECONDS) 带超时
// ReadWriteLock 读写分离提升并发

15.网络与 HTTP

HttpClient、URL 请求、JSON 序列化与 WebSocket。

HttpClient 基础

java.net.http.HttpClient(Java 11+)发 HTTP 请求。send 同步、sendAsync 异步。构建器配置。

1
2
3
4
5
6
7
8
HttpClient client = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://example.com"))
.build();
HttpResponse<String> resp =
client.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.statusCode());
System.out.println(resp.body());

POST 与 JSON

POST 请求带 JSON body。Jackson 或 Gson 序列化反序列化 JSON。

1
2
3
4
5
6
7
8
9
String json = "{\"name\":\"Nick\"}";
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
// 解析:Jackson ObjectMapper om = new ObjectMapper();
// User u = om.readValue(resp.body(), User.class);

异步请求

sendAsync 返回 CompletableFuture,不阻塞调用线程。多请求并发 thenApply 处理。

1
2
3
4
5
6
7
HttpClient client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder(URI.create("https://example.com")).build();
client.sendAsync(req, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body)
.thenAccept(System.out::println)
.join(); // 阻塞等待(演示)
// 真正异步环境不调 join

URL 与 URLConnection

旧 API URL/HttpURLConnection 简单请求。多数场景用 HttpClient 更简洁。查询参数需 URLEncoder 编码。

1
2
3
4
5
6
7
URL url = new URL("https://example.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
int code = conn.getResponseCode();
String body = new String(conn.getInputStream().readAllBytes());
// 参数编码:
String q = URLEncoder.encode("中文", StandardCharsets.UTF_8);

JSON 解析

Jackson/Gson 主流 JSON 库。@JsonProperty 映射字段名,Java 17 新特性可结合 record。

1
2
3
4
5
6
7
8
9
// Gson:
Gson gson = new Gson();
User u = gson.fromJson(json, User.class);
String out = gson.toJson(u);
// Jackson:
ObjectMapper om = new ObjectMapper();
User u2 = om.readValue(json, User.class);
String s2 = om.writeValueAsString(u2);
// Jackson 依赖 pom 添加

调用 Web API

组合:构建请求 → 检查状态码 → 反序列化。处理 404/500 等非成功状态。

1
2
3
4
5
6
7
8
9
10
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.github.com/users/" + login))
.header("User-Agent", "MyApp")
.GET().build();
var resp = client.send(req, BodyHandlers.ofString());
if (resp.statusCode() == 200) {
var user = om.readValue(resp.body(), User.class);
} else {
System.err.println("status: " + resp.statusCode());
}

WebSocket

java.net.http.WebSocket 双向长连接。Listener 回调接收消息,onOpen/onText 处理。

1
2
3
4
5
6
7
8
9
10
11
var ws = HttpClient.newHttpClient()
.newWebSocketBuilder()
.buildAsync(URI.create("wss://example.com/ws"),
new WebSocket.Listener() {
public void onOpen(WebSocket webSocket) {
webSocket.sendText("hello", true);
}
// onText/onError/onClose 回调
})
.join();
ws.sendText("msg", true);

超时与重试

HttpRequest.timeout 设置请求超时;HttpClient.connectTimeout 连接超时。超时抛 HttpTimeoutException。

1
2
3
4
5
6
7
8
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
var req = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(10))
.build();
// 超时抛 HttpTimeoutException,需捕获处理
// 简单重试:循环 catch 后重试(加退避)

16.日期与时间

java.time 本地日期时间、Instant、Duration/Period 与格式化。

LocalDate

LocalDate 日期(无时间):now、of、plusDays/minusMonths。不可变线程安全。

1
2
3
4
5
6
7
LocalDate today = LocalDate.now();
LocalDate date = LocalDate.of(2024, 1, 1);
LocalDate next = date.plusDays(7);
LocalDate prev = date.minusMonths(1);
int year = date.getYear();
boolean leap = date.isLeapYear();
DayOfWeek dow = date.getDayOfWeek();

LocalTime

LocalTime 时间(无日期):now、of、plusMinutes。与 LocalDate 组合 LocalDateTime。

1
2
3
4
5
6
LocalTime time = LocalTime.now();
LocalTime t = LocalTime.of(14, 30, 0);
LocalTime later = t.plusHours(1);
int hour = t.getHour();
// 与日期组合:
LocalDateTime dt = LocalDateTime.of(date, t);

Instant 时间戳

Instant 是 UTC 时间点(epoch 秒/纳秒),跨时区存储用。系统计时用 System.currentTimeMillis。

1
2
3
4
5
6
7
Instant now = Instant.now();
long epochMilli = now.toEpochMilli();
Instant fromEpoch = Instant.ofEpochMilli(1_700_000_000_000L);
// 从系统时间:
long nowMs = System.currentTimeMillis();
// Instant 与 LocalDateTime 互转需时区:
LocalDateTime ldt = LocalDateTime.ofInstant(now, ZoneId.systemDefault());

Duration 与 Period

Duration 精确到纳秒的时间间隔(秒级)、Period 以年月日为单位的日期间隔。

1
2
3
4
5
6
Duration d = Duration.ofMinutes(90);
long seconds = d.toSeconds(); // 5400
Duration between = Duration.between(t1, t2);
Period p = Period.of(1, 2, 3); // 1年2月3天
Period since = Period.between(birthday, today);
int years = since.getYears();

格式化与解析

DateTimeFormatter 格式化和解析。预置 ISO 格式,自定义 pattern(yyyy-MM-dd HH:mm:ss)。

1
2
3
4
5
6
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
String s = LocalDateTime.now().format(fmt);
LocalDateTime parsed = LocalDateTime.parse("2024-01-01 12:30", fmt);
// 预置格式:
String iso = LocalDate.now().format(DateTimeFormatter.ISO_DATE);
// 解析注意异常:DateTimeParseException

时区

ZonedDateTime 带时区、ZoneOffset 固定偏移。ZoneId.systemDefault 本机时区。存储用 UTC。

1
2
3
4
5
6
7
ZoneId shanghai = ZoneId.of("Asia/Shanghai");
ZonedDateTime zdt = ZonedDateTime.now(shanghai);
Instant utc = zdt.toInstant();
ZonedDateTime back = utc.atZone(ZoneId.of("UTC"));
// 固定偏移:
ZoneOffset offset = ZoneOffset.ofHours(8);
OffsetDateTime odt = OffsetDateTime.of(ldt, offset);

旧 API 转换

java.util.Date/Calendar 与新 API 转换。Date 可变、易错,新代码用 java.time。

1
2
3
4
5
6
7
8
// Date → Instant:
Instant inst = new Date().toInstant();
// Instant → Date:
Date d = Date.from(Instant.now());
// 转换:
LocalDateTime ldt = LocalDateTime.ofInstant(
inst, ZoneId.systemDefault());
// Calendar 早已过时,避免使用

时间戳工具

System.currentTimeMillis 毫秒、nanoTime 纳秒差(仅测间隔,不能对时钟)。时间戳与格式化互转。

1
2
3
4
5
6
7
long start = System.nanoTime();
compute();
long elapsedNs = System.nanoTime() - start; // 间隔
// 毫秒时间戳 → LocalDateTime:
LocalDateTime t = LocalDateTime.ofInstant(
Instant.ofEpochMilli(System.currentTimeMillis()),
ZoneId.systemDefault());

17.进程与系统

ProcessBuilder 启动进程、系统属性、环境变量与运行时信息。

ProcessBuilder

ProcessBuilder 启动外部程序。参数用列表避免注入。redirectErrorStream 合并输出。

1
2
3
4
5
6
ProcessBuilder pb = new ProcessBuilder("git", "log", "-1");
pb.redirectErrorStream(true); // 合并错误输出
Process proc = pb.start();
String out = new String(proc.getInputStream().readAllBytes());
int code = proc.waitFor(); // 等待退出
System.out.println(out);

系统属性

System.getProperty 读 JVM 系统属性:user.home、java.version、os.name。setProperty 设置。

1
2
3
4
5
6
String home = System.getProperty("user.home");
String ver = System.getProperty("java.version");
String os = System.getProperty("os.name");
System.setProperty("my.prop", "value");
// 全部:System.getProperties().forEach(...)
// JVM 参数:-Dmy.prop=value

环境变量

System.getenv 读环境变量(不可修改),getenv() 全量。区分系统属性与环境变量。

1
2
3
4
5
6
String path = System.getenv("PATH");
String home = System.getenv("HOME");
Map<String, String> all = System.getenv();
// 环境变量只读;
// 系统属性用 -D 或 System.setProperty
// 读取配置建议两者都查:getenv 优先或属性优先

Runtime 信息

Runtime 提供 JVM 信息:availableProcessors、maxMemory、totalMemory。gc 建议回收。

1
2
3
4
5
6
7
Runtime rt = Runtime.getRuntime();
int cores = rt.availableProcessors();
long maxMem = rt.maxMemory(); // 最大堆
long used = rt.totalMemory() - rt.freeMemory();
// rt.gc() 建议回收(生产别用)
System.out.printf("cores=%d used=%dMB%n",
cores, used / 1024 / 1024);

命令行参数解析

main args 解析选项。简单场景手工循环,复杂用 picocli/JCommander 库。

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
String file = null;
boolean verbose = false;
for (String a : args) {
if (a.equals("-v")) verbose = true;
else if (a.equals("-f")) { /* 取下一个 */ }
else file = a;
}
}
// 复杂 CLI 用 picocli:@Command/@Option 注解

关闭钩子

Runtime.addShutdownHook 注册 JVM 退出时的清理。System.exit 触发。避免在钩子做耗时操作。

1
2
3
4
5
6
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("shutting down...");
saveState();
}));
// JVM 正常退出/System.exit 时执行
// 钩子线程并发,别做重活

工作目录

user.dir 系统属性是进程当前工作目录。ProcessBuilder.directory 可指定子进程的起始目录。

1
2
3
4
5
6
7
String cwd = System.getProperty("user.dir");
System.out.println(cwd); // 当前工作目录
// 子进程起始目录(改到其他目录执行):
Path target = Path.of(System.getProperty("user.home"), "work");
ProcessBuilder pb = new ProcessBuilder("git", "status");
pb.directory(target.toFile());
Process p = pb.start();

系统平台信息

系统属性提供平台信息:os.name 操作系统、os.arch 架构、file.separator 路径分隔符、line.separator 换行。

1
2
3
4
5
6
String os = System.getProperty("os.name"); // Windows 11 / Linux
String arch = System.getProperty("os.arch"); // amd64
String sep = System.getProperty("file.separator"); // \ 或 /
String eol = System.getProperty("line.separator"); // 换行符
String pathSep = System.getProperty("path.separator"); // ; 或 :
System.out.println(os + " " + arch);

18.正则表达式

Pattern/Matcher、匹配、捕获组、替换与常用模式。

Pattern 与 Matcher

Pattern.compile 编译正则(缓存复用),matcher 匹配文本。find/matches/lookingAt 三种匹配。

1
2
3
4
5
6
Pattern p = Pattern.compile("\\d{3}-\\d{4}");
Matcher m = p.matcher("call 123-4567");
boolean found = m.find(); // 部分匹配
Matcher m2 = p.matcher("123-4567");
boolean full = m2.matches(); // 整体匹配
// 直接判断:"123-4567".matches("\\d{3}-\\d{4}")

捕获组

圆括号捕获子串,group(1) 取第 1 组、命名组 (?<name>...) 用 group("name")。find 循环取全部。

1
2
3
4
5
6
7
8
9
Pattern p = Pattern.compile("(\\d{4})-(\\d{2})");
Matcher m = p.matcher("date: 2024-01");
if (m.find()) {
String year = m.group(1); // 2024
String month = m.group(2); // 01
}
// 命名组:
Pattern p2 = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})");
// m.group("year") 取 2024

查找全部

find 循环或 matcher.results 拿全部匹配。替换全部用 replaceAll(含 $1 组引用)。

1
2
3
4
5
6
7
8
Matcher m = Pattern.compile("\\w+").matcher(text);
while (m.find()) {
System.out.println(m.group()); // 每个单词
}
// Java 9+:
m.results().forEach(r -> System.out.println(r.group()));
// 替换:
String masked = "123456".replaceAll("(\\d{3})(\\d+)", "$1***");

替换

replaceAll/replaceFirst 按正则替换,$1/$2 引用组。Matcher.appendReplacement 逐段处理。

1
2
3
4
5
6
7
8
9
String s = "a1b2c3";
String all = s.replaceAll("\\d", "#"); // a#b#c#
String first = s.replaceFirst("\\d", "#"); // a#b2c3
// 回调替换:
Matcher m = Pattern.compile("\\d").matcher(s);
StringBuffer sb = new StringBuffer();
while (m.find()) m.appendReplacement(sb, "{" + m.group() + "}");
m.appendTail(sb);
// sb = "a{1}b{2}c{3}"

Pattern 标志

Pattern.CASE_INSENSITIVE 忽略大小写、MULTILINE 让 ^$ 匹配行、DOTALL 让 . 匹配换行。

1
2
3
4
5
6
Pattern p = Pattern.compile("^java",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
Matcher m = p.matcher("Java\nnot java\n");
// 内联标志:
Pattern p2 = Pattern.compile("(?i)java"); // 忽略大小写
// (?s) DOTALL、(?m) MULTILINE

常用模式

邮箱、URL、IP、手机号的常用正则。生产级校验(真邮箱格式)用专门库。

1
2
3
4
5
String email = "^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$";
String ip = "^((?:\\d{1,3}\\.){3}\\d{1,3})$";
String phone = "^1[3-9]\\d{9}$"; // 中国大陆手机
boolean ok = input.matches(phone);
// 注意:\\d 在 Java 字符串里是正则 \d

量词与锚点

* 零或多次、+ 一次或多次、? 零或一次、{n,m} 指定次数。^ 行首、$ 行尾、\\b 词边界。

1
2
3
4
5
6
"\\d+" // 一个或多个数字
"colou?r" // color 或 colour
"\\d{2,4}" // 2 到 4 位数字
"^start" // 行首 start
"end$" // 行尾 end
"\\bword\\b" // 独立单词 word

前瞻与后顾

前瞻 (?=...) 检查后文、负向前瞻 (?!...) 排除后文、后顾 (?<=...) 检查前文,均不消费字符。

1
2
3
4
5
6
7
Pattern p = Pattern.compile("\\d+(?=\\s*元)"); // 后接元才匹配
Matcher m = p.matcher("价格 100 元");
if (m.find()) System.out.println(m.group()); // 100
// 负向前瞻:数字后不接字母
Pattern p2 = Pattern.compile("\\d+(?!\\p{L})");
// 后顾需固定长度:
Pattern p3 = Pattern.compile("(?<=价格为)\\d+");

19.构建与调试

Maven/Gradle、javac/jar、JUnit 与 JVM 调试。

Maven 生命周期

mvn compile/test/package/install 生命周期阶段。target 目录输出 class 与 jar。

1
2
3
4
5
6
// $ mvn clean compile # 清理并编译
// $ mvn test # 运行测试
// $ mvn package # 打包 jar
// $ mvn install # 装到本地仓库
// $ mvn dependency:tree # 查看依赖树
// 输出在 target/ 目录

pom.xml

pom.xml 定义项目:groupId/artifactId/version 坐标、dependencies 依赖、properties 属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>app</artifactId>
<version>1.0.0</version>
<properties><maven.compiler.release>17</maven.compiler.release></properties>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
</project>

Gradle

Gradle 基于 Groovy/Kotlin DSL。task 定义构建步骤,依赖由中央仓库解析。

1
2
3
4
5
6
7
8
9
// build.gradle:
plugins { id 'java' }
repositories { mavenCentral() }
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
}
test { useJUnitPlatform() }
// $ gradle build / gradle test / gradle run

JUnit 测试

JUnit 5:@Test 测试方法、@BeforeEach 初始化、assert* 断言。mvn test 运行。

1
2
3
4
5
6
7
8
9
10
11
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void add_returns_sum() {
var calc = new Calculator();
assertEquals(5, calc.add(2, 3));
assertTrue(calc.add(1, 1) == 2);
}
}
// 断言失败会明确报告期望与实际

jar 打包

jar 命令打包 class。可执行 jar 需要 Main-Class 清单。java -jar 运行。

1
2
3
4
5
// $ jar cf app.jar com/ # 打包目录
// $ jar cfe app.jar Main com/ # 指定主类
// $ java -jar app.jar # 运行
// 查看内容:jar tf app.jar
// MANIFEST.MF 里 Main-Class: Main

JVM 参数

-Xmx 最大堆、-Xms 初始堆、-XX:+PrintGCDetails GC 日志、-D 系统属性。

1
2
3
4
5
// $ java -Xms256m -Xmx2g -jar app.jar
// $ java -Dserver.port=8080 -jar app.jar
// $ java -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError app
// 查看默认值:java -XX:+PrintFlagsFinal -version
// 调试:java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

日志配置

SLF4J 门面 + Logback 实现。logback.xml 配置级别与输出。生产别用 System.out。

1
2
3
4
5
6
7
8
9
10
11
// logback.xml:
<configuration>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender" />
</configuration>
// 代码:
private static final Logger log =
LoggerFactory.getLogger(App.class);
log.info("user {} logged in", id);

依赖管理

Maven 依赖按 scope 分类:compile/test/runtime。exclusions 排除传递依赖,版本冲突用 dependencyManagement 统一。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.2</version>
<scope>test</scope> <!-- 仅测试期可用 -->
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
<exclusions> <!-- 排除传递依赖 -->
<exclusion>
<groupId>org.checkerframework</groupId>
<artifactId>checker-qual</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
// 版本冲突统一在 dependencyManagement 中声明版本

官方链接

直达官方文档与资源。

关于本速查

本页是 Java 17(LTS)的自包含速查手册,覆盖语言核心、JDK 常用类库与构建生态在真实项目中约 80% 的常见用法。内容偏向现代惯用法:var 局部变量推断、switch 表达式、文本块、记录类(record)、密封类(sealed)、Stream 流式 API。Java 由 Sun Microsystems 于 1995 年发布,以「一次编写、到处运行」的 JVM 生态著称,是企业后端、Android 开发与大数据领域使用最广的语言之一。 19 个章节各自聚焦一个主题:基础语法、变量、类型与引用、控制流、函数、字符串、集合框架(List/Set/Map)、内存管理(GC)、面向对象、异常处理、输入输出、常见误区、并发、网络、时间、进程、正则与构建工具(Maven/Gradle)。每个小节都配有「概念介绍 + 可直接复制的代码片段」。 所有代码与文字均在浏览器本地渲染,无任何数据离开你的设备。权威参考见 Oracle 官方 Java 文档。

版本 2.1.0