入门
Hello World
每个 Java 程序从 main() 开始。文件名必须与公共类名匹配(Main.java → Main.class)。javac 编译为字节码(.class),java 在 JVM 上运行它。System.out.println 打印到 stdout;printf 支持格式说明符(%s、%d、%f、%n 表示换行)。
// Main.java
public class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
System.out.printf("Name: %s, Age: %d%n", "Alice", 30);
}
}
// Compile: javac Main.java -> Main.class
// Run: java Main
// Package: java -cp . com.example.Main
// Every Java program needs:
// 1. A class (public class, file name must match)
// 2. A main method: public static void main(String[] args)变量与基本类型
Java 有 8 种基本类型(int、long、double、float、boolean、char、byte、short)和引用类型(String、数组、对象)。使用 'final' 表示常量。'var'(Java 10+)在编译时推断类型——用于类型明显的局部变量。数字中的下划线(100_000)提高可读性。
// Primitive types (8 total)
int age = 30; // 32-bit integer
long bigNum = 100_000L; // 64-bit integer
double price = 19.99; // 64-bit float (default for decimals)
float pi = 3.14f; // 32-bit float
boolean active = true; // true/false
char grade = 'A'; // 16-bit Unicode character
byte b = 127; // 8-bit signed
short s = 32767; // 16-bit signed
// Reference types
String name = "Alice"; // Object (not primitive)
int[] nums = {1, 2, 3}; // Array object
// Constants
final double PI = 3.14159; // can't be reassigned
// var (Java 10+, local type inference)
var count = 42; // inferred as int
var list = new ArrayList<String>(); // inferred as ArrayList<String>包装类与装箱
包装类(Integer、Double、Boolean 等)是基本类型的对象版本。自动装箱/拆箱自动转换。Integer 缓存 -128 到 127 的值,所以 == 对小数字有效但对大数字失败——始终使用 .equals()。集合需要包装类(不能持有基本类型)。
// Wrapper classes (Object versions of primitives)
Integer wrapped = Integer.valueOf(42); // explicit
Integer auto = 42; // autoboxing
int unboxed = auto; // unboxing
// Useful methods
int max = Integer.MAX_VALUE; // 2147483647
String bin = Integer.toBinaryString(42);
int parsed = Integer.parseInt("42");
String s = String.valueOf(42);
// Other wrappers: Double, Boolean, Character, Long, Float
Double d = 3.14;
Boolean b = Boolean.TRUE;
Character c = 'A';
// Be careful with == on wrappers
Integer a = 127, b2 = 127; // a == b2: true (cached)
Integer x = 128, y = 128; // x == y: false (not cached)
// Always use .equals() for Integer comparison包与导入
包组织类并防止命名冲突。约定:反向域名(com.example.app)。导入特定类或使用通配符(*)。静态导入引入常量和方法(Math.PI、Math.sqrt)。完全限定名称无需导入即可工作但冗长。java.lang 包自动导入。
// Package declaration (must be first line)
package com.example.app;
// Import specific class
import java.util.List;
import java.util.ArrayList;
// Import all classes in a package
import java.util.*;
// Static import (for static members)
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
// Usage
double area = PI * 5 * 5;
double root = sqrt(16);
// Fully qualified name (no import needed)
java.time.LocalDate today = java.time.LocalDate.now();
// Package naming convention: reverse domain
// com.google.gson, org.apache.commons, io.netty.channel输入与输出
System.out(stdout)、System.err(stderr)、System.in(stdin)。Scanner 是读取控制台输入的最简单方式——它解析 token(nextInt、nextDouble、nextLine)。始终关闭 Scanner 以释放资源。命令行参数在 args[] 中(args[0] 是第一个参数,不像 C 中的程序名)。
import java.util.Scanner;
// Console output
System.out.println("Hello"); // with newline
System.out.print("No newline"); // without newline
System.out.printf("Pi: %.2f%n", 3.14159); // formatted
// Console input with Scanner
Scanner scanner = new Scanner(System.in);
System.out.print("Enter name: ");
String name = scanner.nextLine();
System.out.print("Enter age: ");
int age = scanner.nextInt();
System.out.printf("Hi %s, age %d%n", name, age);
scanner.close(); // always close
// Command-line arguments
// java Main arg1 arg2
// args[0] = "arg1", args[1] = "arg2"字符串与格式化
字符串方法
字符串是不可变的——方法返回新字符串。始终使用 .equals() 进行内容比较(== 比较引用)。compareTo() 返回负数/零/正数用于排序(适用于排序)。split() 返回 String[]。对于可变字符串,使用 StringBuilder。
String s = "Hello, World";
// Length and access
int len = s.length(); // 12
char c = s.charAt(0); // 'H'
// Comparison
s.equals("Hello, World"); // true (content comparison)
s.equalsIgnoreCase("hello, world"); // true
s.compareTo("Apple"); // positive (s > "Apple")
"abc".compareTo("abd"); // negative
// Search
s.indexOf("World"); // 7 (-1 if not found)
s.lastIndexOf("l"); // 10
s.contains("World"); // true
s.startsWith("Hello"); // true
s.endsWith("World"); // true
// Extract
s.substring(7); // "World"
s.substring(0, 5); // "Hello"
// Transform
s.toUpperCase(); // "HELLO, WORLD"
s.toLowerCase(); // "hello, world"
s.replace("o", "0"); // "Hell0, W0rld"
s.trim(); // remove whitespace
s.split(", "); // ["Hello", "World"]StringBuilder 与拼接
使用 + 的字符串拼接每次创建新 String(在循环中低效)。StringBuilder 是可变的,适用于增量构建字符串。StringBuffer 是线程安全版本(很少需要)。String.join() 用分隔符组合。Java 11+ 添加 repeat() 用于字符串乘法。
// String concatenation (creates new String each time)
String s = "Hello" + ", " + "World";
String formatted = String.format("%s is %d", "Alice", 30);
// StringBuilder (mutable, efficient for many concatenations)
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(", ");
sb.append("World");
sb.insert(5, " there");
sb.delete(5, 11);
sb.reverse();
String result = sb.toString();
// StringBuffer (thread-safe, slower than StringBuilder)
StringBuffer sbf = new StringBuffer("thread-safe");
// Join strings
String joined = String.join(", ", "a", "b", "c"); // "a, b, c"
// Repeat (Java 11+)
String repeated = "ab".repeat(3); // "ababab"字符串格式化
printf/format 使用 C 风格格式说明符:%d(int)、%f(float)、%s(string)、%c(char)、%b(boolean)、%x(hex)。宽度(%5d)、左对齐(%-5d)、零填充(%05d)、精度(%.2f)。%n 是平台换行符。文本块(Java 15+)用三引号启用多行字符串而无需转义。
// printf / format specifiers
System.out.printf("Int: %d%n", 42);
System.out.printf("Float: %.2f%n", 3.14159); // 3.14
System.out.printf("String: %s%n", "hello");
System.out.printf("Char: %c%n", 'A');
System.out.printf("Bool: %b%n", true);
System.out.printf("Hex: %x%n", 255); // ff
System.out.printf("Octal: %o%n", 8); // 10
// Width and padding
System.out.printf("[%5d]%n", 42); // [ 42]
System.out.printf("[%-5d]%n", 42); // [42 ]
System.out.printf("[%05d]%n", 42); // [00042]
System.out.printf("[%8.2f]%n", 3.14); // [ 3.14]
// String.format returns a String
String s = String.format("Name: %s, Age: %d", "Alice", 30);
// Text blocks (Java 15+)
String json = """
{
"name": "Alice",
"age": 30
}
""";正则表达式
Java 正则使用 Pattern(编译)和 Matcher(应用于输入)。String 方法(matches、split、replaceAll)是方便的快捷方式。Java 字符串字面量中反斜杠必须加倍(\\d 表示 \d)。组用括号捕获,在替换中引用为 $1、$2。如果重复使用,始终编译模式一次。
import java.util.regex.*;
// String methods
"hello123".matches("[a-z]+\d+"); // true
"a,b,c".split(","); // ["a", "b", "c"]
"hello".replaceAll("l", "L"); // "heLLo"
// Pattern and Matcher
Pattern p = Pattern.compile("\\d{4}-\\d{2}-\\d{2}");
Matcher m = p.matcher("Date: 2024-01-15");
if (m.find()) {
System.out.println(m.group()); // "2024-01-15"
}
// Find all matches
while (m.find()) {
System.out.println(m.group());
}
// Replace with regex
String result = "2024-01-15".replaceAll(
"(\\d{4})-(\\d{2})-(\\d{2})",
"$3/$2/$1"); // "15/01/2024"
// Common patterns
String email = "^[\\w.]+@[\\w.]+\\.\\w+$";
String phone = "^\\d{3}-\\d{4}$";数字与数学
Math 类提供静态数学函数。Math.random() 返回 0.0-1.0。要更多控制,使用 java.util.Random(可设种子)或 java.security.SecureRandom(加密)。Integer/Double 有静态实用方法。注意浮点精度——财务计算使用 BigDecimal。
// Math class
double sqrt = Math.sqrt(16); // 4.0
double pow = Math.pow(2, 10); // 1024.0
int abs = Math.abs(-5); // 5
int max = Math.max(3, 7); // 7
int min = Math.min(3, 7); // 3
double rounded = Math.round(3.7); // 4
double ceil = Math.ceil(3.1); // 4.0
double floor = Math.floor(3.9); // 3.0
double random = Math.random(); // 0.0 to 1.0
// Constants
double pi = Math.PI; // 3.14159...
double e = Math.E; // 2.71828...
// Integer/Long methods
int sum = Integer.sum(3, 4); // 7
int max2 = Integer.max(3, 7); // 7
// Rounding modes
double r = Math.round(3.5); // 4 (round half up)
double r2 = Math.floor(3.5 + 0.5); // alternative
// Random (java.util.Random)
import java.util.Random;
Random rand = new Random();
int n = rand.nextInt(100); // 0-99
double d = rand.nextDouble(); // 0.0-1.0
boolean b = rand.nextBoolean();控制流
If / Else
Java if/else 工作方式类似 C/C++。条件必须是布尔值——没有像 JavaScript 那样的 truthy/falsy(0 和非空字符串不是 truthy)。三元运算符(cond ? a : b)是表达式,不是语句。即使单行主体也使用花括号(代码风格最佳实践)。
int score = 85;
if (score >= 90) {
System.out.println("A");
} else if (score >= 80) {
System.out.println("B");
} else if (score >= 70) {
System.out.println("C");
} else {
System.out.println("F");
}
// Ternary operator
String grade = score >= 60 ? "Pass" : "Fail";
// Nested if
if (score >= 60) {
if (score >= 90) {
System.out.println("Excellent");
}
}
// Note: conditions must be boolean (no truthy/falsy)
// if (score) { } // Error: int is not booleanSwitch 与表达式
传统 switch 有贯穿(使用 break)。Java 14+ switch 表达式(->)不贯穿且可以返回值。使用逗号分隔多个 case 标签(case 1, 2, 3)。'yield' 从复杂块返回值。Switch 表达式是穷尽的——枚举需要 default 或所有 case。
// Traditional switch (fall-through)
int day = 3;
switch (day) {
case 1:
System.out.println("Mon");
break;
case 2:
case 3:
case 4:
System.out.println("Midweek");
break;
case 6:
case 7:
System.out.println("Weekend");
break;
default:
System.out.println("Invalid");
}
// Switch expression (Java 14+, no fall-through)
String type = switch (day) {
case 1, 2, 3, 4, 5 -> "Weekday";
case 6, 7 -> "Weekend";
default -> "Invalid";
};
// Switch with yield (for complex blocks)
int result = switch (day) {
case 1, 2, 3, 4, 5 -> {
int hours = 8;
yield hours * 5;
}
case 6, 7 -> 0;
default -> -1;
};循环
Java 有 for、while 和 do-while 循环。增强 for(for-each)适用于数组和任何 Iterable。break 退出循环;continue 跳到下一次迭代。对于集合,优先使用 for-each 或流而非索引循环。do-while 至少运行一次(很少使用)。
// For loop
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
// Enhanced for (for-each)
int[] nums = {1, 2, 3, 4, 5};
for (int n : nums) {
System.out.println(n);
}
List<String> names = List.of("Alice", "Bob");
for (String name : names) {
System.out.println(name);
}
// While loop
int count = 0;
while (count < 3) {
System.out.println(count);
count++;
}
// Do-while (runs at least once)
int i = 0;
do {
System.out.println(i);
i++;
} while (i < 3);
// Break and continue
for (int j = 0; j < 10; j++) {
if (j == 5) break; // exit loop
if (j % 2 == 0) continue; // skip iteration
System.out.println(j);
}标签 Break 与 Continue
标签(outer:)允许从嵌套循环中 break/continue 外层循环。这很少需要——提取到带 return 的方法通常更干净。标签放在循环之前,后跟冒号。break label 退出标记的循环;continue label 跳到其下一次迭代。
// Labels for breaking out of nested loops
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (i == 1 && j == 1) {
break outer; // exits both loops
}
System.out.println(i + "," + j);
}
}
// Labeled continue
outer:
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (j == 1) {
continue outer; // skip to next i
}
System.out.println(i + "," + j);
}
}
// Alternative: extract to method and use return
void findPair(int[][] matrix, int target) {
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] == target) return; // exit method
}
}
}数组
数组有固定长度(动态使用 ArrayList)。Arrays.sort() 原地排序。Arrays.toString() 给出可读表示。Arrays.copyOf() 创建新长度副本。对于多维数组,每行可以有不同长度(锯齿数组)。使用 Arrays 进行数组实用方法。
// Declare and initialize
int[] nums = {1, 2, 3, 4, 5};
int[] empty = new int[5]; // [0, 0, 0, 0, 0]
String[] names = new String[3]; // [null, null, null]
// Access and modify
nums[0] = 10;
int first = nums[0]; // 10
int length = nums.length; // 5
// Multidimensional
int[][] matrix = {{1, 2}, {3, 4}};
int val = matrix[0][1]; // 2
// Arrays utility class
import java.util.Arrays;
int[] sorted = {3, 1, 2};
Arrays.sort(sorted); // [1, 2, 3]
int[] copy = Arrays.copyOf(nums, 3);
String str = Arrays.toString(nums); // "[10, 2, 3, 4, 5]"
boolean eq = Arrays.equals(nums, copy);
// Fill
int[] filled = new int[5];
Arrays.fill(filled, 42); // [42, 42, 42, 42, 42]
// Binary search (sorted array only)
int idx = Arrays.binarySearch(sorted, 2); // index of 2方法与函数
方法定义
Java 方法始终在类内。'static' 意味着方法属于类(无需实例调用)。返回类型(int、String、void)在名称之前声明。参数有类型。Java 没有默认参数值——改用方法重载。
public class Calculator {
// Method with return type
public static int add(int a, int b) {
return a + b;
}
// Void method (no return)
public static void printResult(int result) {
System.out.println("Result: " + result);
}
// Method with default (no overloading needed)
public static String greet(String name, String greeting) {
return greeting + ", " + name + "!";
}
public static void main(String[] args) {
int sum = add(3, 4);
printResult(sum);
String msg = greet("Alice", "Hello");
System.out.println(msg);
}
}方法重载
方法重载允许同名但不同参数列表(类型、数量或顺序)的多个方法。Java 在编译时根据参数类型解析重载。重载常用于构造函数和实用方法。它与重写不同(涉及继承和运行时分发)。
public class MathUtils {
// Overloaded methods (same name, different params)
public static int add(int a, int b) {
return a + b;
}
public static double add(double a, double b) {
return a + b;
}
public static int add(int a, int b, int c) {
return a + b + c;
}
public static String add(String a, String b) {
return a + b;
}
}
// Java picks the most specific match
MathUtils.add(1, 2); // int version -> 3
MathUtils.add(1.5, 2.5); // double version -> 4.0
MathUtils.add(1, 2, 3); // 3-param version -> 6
MathUtils.add("Hello", "!"); // String version -> "Hello!"可变参数与按值传递
可变参数(Type... name)允许可变参数,作为数组接收。Java 始终按值传递:基本类型被复制,对象引用被复制(但指向相同对象)。所以在方法内修改参数不影响调用者的变量,但修改它指向的对象则会影响。
// Varargs: variable number of arguments
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 (empty array)
int[] arr = {1, 2, 3};
sum(arr); // 6 (pass array to varargs)
// Java is ALWAYS pass-by-value
public static void modify(int x) {
x = 100; // doesn't affect the caller's variable
}
int n = 5;
modify(n);
System.out.println(n); // still 5
// For objects, the reference is passed by value
public static void addItem(List<String> list) {
list.add("new"); // modifies the same list object
}递归
递归是方法调用自身。始终有基本情况以停止。Java 不优化尾递归(不像某些语言),所以深递归可能导致 StackOverflowError。对于性能关键或深递归,转换为迭代。记忆化(缓存结果)可以加速递归解决方案如 Fibonacci。
// Factorial
public static int factorial(int n) {
if (n <= 1) return 1; // base case
return n * factorial(n - 1); // recursive case
}
// factorial(5) = 5 * 4 * 3 * 2 * 1 = 120
// Fibonacci
public static int fib(int n) {
if (n < 2) return n;
return fib(n - 1) + fib(n - 2);
}
// Tail recursion (Java doesn't optimize this)
public static int factorialTail(int n, int acc) {
if (n <= 1) return acc;
return factorialTail(n - 1, n * acc);
}
// Call: factorialTail(5, 1)
// Be careful: deep recursion causes StackOverflowError
// For deep recursion, use iteration or a loop insteadLambda 表达式
Lambda(Java 8+)是匿名函数。类型是函数式接口(一个抽象方法)。常见:Function<T,R>(输入→输出)、Predicate<T>(布尔测试)、Consumer<T>(消费,无返回)、Supplier<T>(生产,无输入)。方法引用(String::length)是调用单个方法的 lambda 简写。
import java.util.function.*;
// Lambda syntax: (params) -> expression
Function<Integer, Integer> square = x -> x * x;
Function<String, Integer> length = s -> s.length();
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
// With type annotations
BinaryOperator<Integer> multiply = (Integer a, Integer b) -> a * b;
// Multi-line lambda
Function<String, String> process = s -> {
String upper = s.toUpperCase();
return upper.substring(0, 3);
};
// Predicate (boolean test)
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<String> isEmpty = String::isEmpty; // method reference
// Consumer (no return)
Consumer<String> printer = s -> System.out.println(s);
Consumer<String> printer2 = System.out::println; // method reference
// Supplier (no input, produces value)
Supplier<Double> random = () -> Math.random();
// Usage
int result = square.apply(5); // 25
boolean even = isEven.test(4); // true
printer.accept("Hello"); // prints "Hello"类与 OOP
类与构造函数
类是对象的模板。字段持有状态,方法定义行为。构造函数初始化新对象(使用 'this' 区分字段和参数)。@Override 表示方法重写超类方法(toString 来自 Object)。封装:私有字段,公共 getter/setter。
public class Person {
// Fields (instance variables)
private String name;
private int age;
// Constructor
public Person(String name, int age) {
this.name = name; // 'this' refers to the current instance
this.age = age;
}
// Methods
public String getName() { return name; }
public int getAge() { return age; }
public void setAge(int age) {
if (age >= 0) this.age = age;
}
public String greet() {
return "Hi, I'm " + name;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
// Usage
Person p = new Person("Alice", 30);
System.out.println(p.getName()); // "Alice"
System.out.println(p); // uses toString()访问修饰符与 Static
访问修饰符:public(到处)、private(仅类)、protected(类 + 子类 + 包)、default/package-private(同包)。静态成员属于类而非实例——在所有对象间共享。静态初始化器在类加载时运行一次。常量(static final)、实用方法和计数器使用 static。
public class BankAccount {
// Access modifiers:
public String owner; // accessible everywhere
private double balance; // class only
protected String type; // class + subclasses + same package
String id; // package-private (default)
// Static field (shared by all instances)
private static int accountCount = 0;
// Static constant
public static final double MIN_BALANCE = 100.0;
// Static method (call without instance)
public static int getAccountCount() {
return accountCount;
}
// Static initializer (runs once when class loads)
static {
System.out.println("BankAccount class loaded");
}
public BankAccount(String owner) {
this.owner = owner;
this.balance = MIN_BALANCE;
accountCount++; // increment shared counter
}
}
int count = BankAccount.getAccountCount(); // static method call继承与 super
Java 使用 'extends' 进行类继承(仅单继承)。super() 调用父构造函数(必须是第一行)。@Override 表示方法重写(运行时多态)。Dog IS-A Animal。'is-a' 关系使用继承;代码重用使用组合(has-a)。Java 17+ 支持密封类以限制继承。
// Parent class
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
System.out.println("Animal constructor");
}
public void eat() {
System.out.println(name + " is eating");
}
}
// Child class (extends)
class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // must be first line — call parent constructor
this.breed = breed;
}
// Override parent method
@Override
public void eat() {
super.eat(); // call parent's eat()
System.out.println(name + " the " + breed + " eats dog food");
}
public void bark() {
System.out.println("Woof!");
}
}
Dog dog = new Dog("Rex", "Labrador");
dog.eat(); // calls Dog's eat()
dog.bark(); // Dog-specific method抽象类与接口
抽象类不能被实例化,可以同时有抽象(无主体)和具体方法。接口定义契约——所有方法默认是 public abstract。Java 8+ 允许接口中的默认方法(有主体)和静态方法。一个类扩展一个抽象类但可以实现多个接口。共享代码使用抽象类,契约使用接口。
// Abstract class (can't be instantiated)
abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// Abstract method (must be implemented by subclasses)
public abstract double area();
// Concrete method (inherited)
public String describe() {
return color + " " + this.getClass().getSimpleName();
}
}
// Interface (pure contract, Java 8+ can have default methods)
interface Drawable {
void draw(); // abstract by default
// Default method (Java 8+)
default void drawTwice() {
draw();
draw();
}
// Static method in interface
static Drawable empty() {
return () -> System.out.println("nothing");
}
}
// A class can extend one class and implement multiple interfaces
class Circle extends Shape implements Drawable {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public void draw() {
System.out.println("Drawing " + describe());
}
}多态与转型
多态:父引用可以持有子对象。方法调用分发到实际对象的实现(运行时多态)。向下转型前使用 instanceof 以避免 ClassCastException。Java 16+ 模式匹配(instanceof Circle c)结合检查和转型。一起重写 equals() 和 hashCode() 以在集合中正确行为。
// Polymorphism: one interface, many forms
Shape s1 = new Circle("red", 5);
Shape s2 = new Square("blue", 3);
// Calls the overridden method (runtime dispatch)
System.out.println(s1.area()); // Circle's area
System.out.println(s2.area()); // Square's area
// instanceof check
if (s1 instanceof Circle) {
Circle c = (Circle) s1; // downcast
System.out.println("Radius: " + c.radius);
}
// Pattern matching (Java 16+)
if (s1 instanceof Circle c) {
System.out.println("Radius: " + c.radius); // c is already cast
}
// Upcasting (automatic)
Circle circle = new Circle("green", 2);
Shape shape = circle; // upcast (no explicit cast needed)
// Object class methods (all classes inherit from Object)
circle.equals(circle); // reference equality by default
circle.hashCode(); // hash code
circle.getClass(); // Class<Circle>
circle.toString(); // string representationRecord 与枚举
Record(Java 16+)是不可变数据类——编译器生成构造函数、getter、equals、hashCode 和 toString。用于 DTO 和值对象。枚举是类型安全的常量,可以有字段、方法和构造函数。枚举实现 Comparable 并有 values() 和 valueOf() 方法。两者都是现代 Java 的基础。
// Record (Java 16+): concise data class
public record Point(int x, int y) {}
// Equivalent to a class with:
// - final fields x, y
// - constructor
// - getters x(), y()
// - equals, hashCode, toString
Point p = new Point(3, 4);
System.out.println(p.x()); // 3
System.out.println(p.y()); // 4
System.out.println(p); // Point[x=3, y=4]
// Compact constructor (validation)
public record Age(int value) {
public Age {
if (value < 0 || value > 150) {
throw new IllegalArgumentException("Invalid age");
}
}
}
// Enum (named constants)
public enum Direction {
UP, DOWN, LEFT, RIGHT;
public Direction opposite() {
return switch (this) {
case UP -> DOWN;
case DOWN -> UP;
case LEFT -> RIGHT;
case RIGHT -> LEFT;
};
}
}
Direction d = Direction.UP;
Direction opp = d.opposite(); // DOWN集合与泛型
List(ArrayList 与 LinkedList)
ArrayList 由数组支持(快速 get/set,中间 insert/delete 慢)。LinkedList 由双向链表支持(两端快速 insert/delete,随机访问慢)。List.of() 创建不可变列表。大多数情况使用 ArrayList;仅频繁端操作使用 LinkedList。两者都实现 List 接口。
import java.util.*;
// ArrayList (fast random access, slow insert/delete in middle)
List<String> list = new ArrayList<>();
list.add("Alice");
list.add("Bob");
list.add(0, "Carol"); // insert at index
list.set(1, "Dave"); // replace at index
String name = list.get(0); // "Carol"
list.remove(0); // remove by index
list.remove("Dave"); // remove by value
int size = list.size(); // 1
boolean has = list.contains("Bob");
// LinkedList (fast insert/delete at ends)
LinkedList<Integer> linked = new LinkedList<>();
linked.addFirst(1);
linked.addLast(2);
linked.removeFirst();
linked.peek(); // see first element
// Immutable list (Java 9+)
List<String> immutable = List.of("a", "b", "c");
// immutable.add("d"); // UnsupportedOperationException
// Iterate
for (String s : list) {
System.out.println(s);
}
list.forEach(System.out::println); // method referenceSet(HashSet 与 TreeSet)
Set 存储唯一元素。HashSet 最快但无序。TreeSet 保持元素排序(自然顺序或 Comparator)。LinkedHashSet 维护插入顺序。集合操作:addAll(并集)、retainAll(交集)、removeAll(差集)。对于 HashSet 中的自定义对象,重写 equals() 和 hashCode()。
import java.util.*;
// HashSet (fast, unordered)
Set<String> set = new HashSet<>();
set.add("apple");
set.add("banana");
set.add("apple"); // duplicate ignored
System.out.println(set.size()); // 2
System.out.println(set.contains("apple")); // true
set.remove("banana");
// TreeSet (sorted, slower)
Set<Integer> sorted = new TreeSet<>();
sorted.add(3);
sorted.add(1);
sorted.add(2);
System.out.println(sorted); // [1, 2, 3]
// LinkedHashSet (maintains insertion order)
Set<String> ordered = new LinkedHashSet<>();
ordered.add("c");
ordered.add("a");
ordered.add("b");
System.out.println(ordered); // [c, a, b]
// Set operations
Set<Integer> a = new HashSet<>(Set.of(1, 2, 3));
Set<Integer> b = new HashSet<>(Set.of(2, 3, 4));
a.addAll(b); // union: [1, 2, 3, 4]
a.retainAll(b); // intersection: [2, 3]
a.removeAll(b); // difference: [1]
// Immutable set
Set<String> immutable = Set.of("x", "y", "z");Map(HashMap 与 TreeMap)
Map 存储键值对。HashMap 最快(无序)。TreeMap 按键排序。LinkedHashMap 维护插入顺序。getOrDefault 避免 null 检查。compute/merge 用于更新值很强大。对于自定义键,重写 equals() 和 hashCode()。Map.of() 创建不可变映射(Java 9+)。
import java.util.*;
// HashMap (fast, unordered)
Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 30);
ages.put("Bob", 25);
ages.put("Alice", 31); // overwrite
// Access
int age = ages.get("Alice"); // 31
int defaultAge = ages.getOrDefault("Eve", 0); // 0
// Check
boolean has = ages.containsKey("Alice");
boolean hasVal = ages.containsValue(25);
// Remove
ages.remove("Bob");
// Iterate
for (Map.Entry<String, Integer> entry : ages.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
ages.forEach((key, val) -> System.out.println(key + "=" + val));
// Compute (Java 8+)
ages.compute("Alice", (k, v) -> v + 1); // increment
ages.putIfAbsent("Carol", 28);
ages.merge("Alice", 1, Integer::sum); // add 1
// TreeMap (sorted by keys)
Map<String, Integer> sorted = new TreeMap<>();
// LinkedHashMap (maintains insertion order)
Map<String, Integer> ordered = new LinkedHashMap<>();Queue 与 Deque
Queue 是 FIFO(先进先出)。Deque 是双端的(可以从两端添加/删除)。PriorityQueue 按自然顺序或 Comparator 排序元素(默认最小堆)。对于栈,使用 ArrayDeque(push/pop)而不是遗留 Stack 类。ArrayDeque 在队列/deque 操作上比 LinkedList 快。
import java.util.*;
// Queue (FIFO)
Queue<String> queue = new LinkedList<>();
queue.add("first"); // throws if full (capacity-restricted)
queue.offer("second"); // returns false if full
String head = queue.peek(); // see head (null if empty)
String removed = queue.poll(); // remove and return head
// Deque (double-ended)
Deque<Integer> deque = new ArrayDeque<>();
deque.addFirst(1);
deque.addLast(2);
deque.peekFirst(); // 1
deque.peekLast(); // 2
deque.pollFirst(); // 1
deque.pollLast(); // 2
// PriorityQueue (min-heap by default)
PriorityQueue<Integer> pq = new PriorityQueue<>();
pq.add(3);
pq.add(1);
pq.add(2);
System.out.println(pq.poll()); // 1 (smallest first)
// Max-heap (reverse order)
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
maxHeap.add(1);
maxHeap.add(3);
System.out.println(maxHeap.poll()); // 3 (largest first)
// Stack (legacy, prefer Deque)
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1); // add to front
stack.push(2);
stack.pop(); // 2 (remove from front)泛型
泛型启用类型安全的集合和类。<T> 是类型参数。有界类型(<T extends Comparable<T>>)限制为具有特定行为的类型。通配符:?(任何)、? extends T(协变,只读)、? super T(逆变,只写)。泛型使用类型擦除——类型在编译时检查,运行时擦除。
// Generic class
public class Box<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
Box<String> stringBox = new Box<>();
stringBox.set("hello");
String s = stringBox.get();
Box<Integer> intBox = new Box<>();
intBox.set(42);
// Generic method
public static <T> T firstOf(List<T> list) {
return list.get(0);
}
String first = firstOf(List.of("a", "b"));
// Bounded type parameter
public static <T extends Comparable<T>> T max(List<T> list) {
T result = list.get(0);
for (T item : list) {
if (item.compareTo(result) > 0) {
result = item;
}
}
return result;
}
// Wildcards
void process(List<?> list) { } // any type
void processNums(List<? extends Number> list) { } // Number or subclass
void addNums(List<? super Integer> list) { } // Integer or superclass迭代器与 Comparable
Iterator 允许在迭代期间安全删除(it.remove())。ListIterator 添加双向遍历和 set/add。Comparable 定义自然顺序(compareTo)。Comparator 定义自定义顺序(comparing、comparingInt、reversed、thenComparing)。使用 Comparator.comparing() 进行流式排序。Collections.sort() 使用自然顺序。
import java.util.*;
// Iterator
List<String> list = List.of("a", "b", "c");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
// it.remove(); // safe removal during iteration
}
// ListIterator (bidirectional)
ListIterator<String> lit = list.listIterator();
while (lit.hasNext()) {
lit.set(lit.next().toUpperCase()); // replace
}
// Comparable (natural ordering)
public class Person implements Comparable<Person> {
String name;
int age;
@Override
public int compareTo(Person other) {
return Integer.compare(this.age, other.age);
}
}
// Comparator (custom ordering)
Comparator<Person> byName = Comparator.comparing(p -> p.name);
Comparator<Person> byAgeDesc = Comparator.comparingInt((Person p) -> p.age).reversed();
List<Person> people = new ArrayList<>();
people.sort(byName);
people.sort(byAgeDesc);
Collections.sort(people); // uses Comparable流与函数式
Stream 基础
Stream(Java 8+)提供声明式数据处理。用 .stream()(集合)或 Stream.of() 创建。中间操作(filter、map、sorted)是惰性的——它们仅在调用终端操作(collect、reduce、count、forEach)时执行。toList()(Java 16+)是 collect(Collectors.toList()) 的简洁替代。
import java.util.*;
import java.util.stream.*;
List<Integer> nums = List.of(1, 2, 3, 4, 5, 6);
// Filter and collect
List<Integer> evens = nums.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList()); // [2, 4, 6]
// Map (transform)
List<String> doubled = nums.stream()
.map(n -> "num" + n)
.collect(Collectors.toList());
// Reduce
int sum = nums.stream().reduce(0, Integer::sum); // 21
int product = nums.stream().reduce(1, (a, b) -> a * b);
// Count
long count = nums.stream().filter(n -> n > 3).count(); // 3
// Find
Optional<Integer> first = nums.stream().filter(n -> n > 3).findFirst();
boolean anyMatch = nums.stream().anyMatch(n -> n > 5);
boolean allMatch = nums.stream().allMatch(n -> n > 0);
// ForEach
nums.stream().forEach(System.out::println);
// toList() shortcut (Java 16+)
List<Integer> result = nums.stream().filter(n -> n > 3).toList();Stream 操作
sorted() 排序元素(自然或用 Comparator)。distinct() 移除重复。limit(n)/skip(n) 分页。flatMap 展平嵌套流——一对多转换的关键。peek() 用于调试(副作用)。groupingBy 创建按键分组元素的映射。Stream 是惰性的——操作链式高效。
List<String> names = List.of("Alice", "Bob", "Charlie", "David");
// Sorted
List<String> sorted = names.stream()
.sorted()
.toList();
// Sorted by length
List<String> byLength = names.stream()
.sorted(Comparator.comparing(String::length))
.toList();
// Distinct
List<Integer> distinct = List.of(1, 2, 2, 3, 3, 3).stream()
.distinct()
.toList(); // [1, 2, 3]
// Limit and Skip
List<Integer> limited = nums.stream()
.skip(2) // skip first 2
.limit(3) // take next 3
.toList();
// FlatMap (flatten nested structures)
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3, 4));
List<Integer> flat = nested.stream()
.flatMap(List::stream)
.toList(); // [1, 2, 3, 4]
// Peek (debug, side-effect)
nums.stream()
.peek(n -> System.out.println("before: " + n))
.filter(n -> n > 2)
.peek(n -> System.out.println("after: " + n))
.toList();
// Grouping
Map<Integer, List<String>> byLength = names.stream()
.collect(Collectors.groupingBy(String::length));Collector 与归约
Collector 提供丰富的归约操作:joining(连接字符串)、groupingBy(按键分组)、partitioningBy(按布尔拆分)、toMap(创建映射)、summarizingInt(统计:count、sum、min、max、average)。Collector 可以组合(groupingBy 带下游 collector)。这些用声明式单行代码替换冗长的循环。
import java.util.stream.*;
List<Person> people = List.of(
new Person("Alice", 30),
new Person("Bob", 25),
new Person("Charlie", 35)
);
// Join strings
String joined = people.stream()
.map(Person::getName)
.collect(Collectors.joining(", ")); // "Alice, Bob, Charlie"
// Group by
Map<Integer, List<Person>> byAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge));
// Partition (boolean)
Map<Boolean, List<Person>> partition = people.stream()
.collect(Collectors.partitioningBy(p -> p.getAge() > 28));
// Count by group
Map<Integer, Long> countByAge = people.stream()
.collect(Collectors.groupingBy(Person::getAge, Collectors.counting()));
// Summarizing
IntSummaryStatistics stats = people.stream()
.collect(Collectors.summarizingInt(Person::getAge));
System.out.println(stats.getAverage()); // 30.0
System.out.println(stats.getMax()); // 35
// To map
Map<String, Integer> nameToAge = people.stream()
.collect(Collectors.toMap(Person::getName, Person::getAge));
// Reducing
int totalAge = people.stream()
.collect(Collectors.reducing(0, Person::getAge, Integer::sum));Optional
Optional<T> 是一个可能包含也可能不包含值的容器。它强制显式处理缺失——不再有 NullPointerException。非 null 值使用 of(),可能 null 使用 ofNullable()。用 map/flatMap/filter 链式。永远不要在没有 isPresent() 的情况下使用 get()——优先使用 orElse/orElseThrow。Optional 设计用于返回类型,不是字段。
import java.util.Optional;
// Creating Optional
Optional<String> present = Optional.of("hello");
Optional<String> empty = Optional.empty();
Optional<String> nullable = Optional.ofNullable(null); // empty if null
// Checking
present.isPresent(); // true
empty.isEmpty(); // true (Java 11+)
// Getting values
String val = present.get(); // throws if empty (avoid!)
String safe = present.orElse("default");
String computed = present.orElseGet(() -> computeDefault());
String orThrow = present.orElseThrow(() -> new RuntimeException("missing"));
// Transform (map/flatMap)
Optional<Integer> length = present.map(String::length); // Optional[5]
Optional<String> upper = present.map(s -> s.toUpperCase());
// Filter
Optional<String> filtered = present.filter(s -> s.length() > 3);
// ifPresent
present.ifPresent(s -> System.out.println(s));
present.ifPresentOrElse(
s -> System.out.println("Got: " + s),
() -> System.out.println("Empty")
);
// Chaining (avoid null checks)
String result = getUser(1)
.map(User::getProfile)
.map(Profile::getEmail)
.orElse("no email");函数式接口
函数式接口恰好有一个抽象方法(可以有多个默认方法)。@FunctionalInterface 是可选的但记录意图。Java 在 java.util.function 中提供许多:Function、Predicate、Consumer、Supplier,加上 Bi- 和基本类型变体。尽可能使用这些而不是创建自定义接口。它们启用 lambda 表达式和方法引用。
import java.util.function.*;
// Built-in functional interfaces
Function<String, Integer> strToInt = Integer::parseInt;
BiFunction<String, String, String> concat = String::concat;
Predicate<String> isEmpty = String::isEmpty;
BiPredicate<String, String> contains = String::contains;
Consumer<String> printer = System.out::println;
BiConsumer<String, Integer> printPair = (s, i) -> System.out.println(s + ":" + i);
Supplier<List<String>> listFactory = ArrayList::new;
// Primitive specializations
IntFunction<String> intToStr = String::valueOf;
ToIntFunction<String> length = String::length;
IntPredicate isPositive = n -> n > 0;
IntConsumer intPrinter = System.out::println;
IntSupplier randomInt = () -> (int)(Math.random() * 100);
// Binary operators
BinaryOperator<Integer> max = Integer::max;
IntBinaryOperator sum = Integer::sum;
// Unary operators
UnaryOperator<String> trim = String::trim;
IntUnaryOperator negate = n -> -n;
// Custom functional interface
@FunctionalInterface
interface StringProcessor {
String process(String input);
// Can have default methods
default StringProcessor andThen(StringProcessor after) {
return input -> after.process(process(input));
}
}异常与 I/O
Try / Catch / Finally
try/catch/finally 处理异常。finally 始终运行(用于清理)。多捕获(catch A | B)一起处理多个异常。Try-with-resources 自动关闭任何 AutoCloseable(文件、连接、流)——优先于手动 finally 清理。资源按声明相反顺序关闭。
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("Math error: " + e.getMessage());
} catch (Exception e) {
System.out.println("General error: " + e);
} finally {
// Always runs (even if return/throw in try/catch)
System.out.println("Cleanup");
}
// Multi-catch (Java 7+)
try {
riskyOperation();
} catch (IOException | SQLException e) {
// Handle both exceptions the same way
log.error(e);
}
// Try-with-resources (auto-close, Java 7+)
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"));
PrintWriter pw = new PrintWriter("output.txt")) {
String line = br.readLine();
pw.println(line);
} catch (IOException e) {
e.printStackTrace();
}
// br and pw are auto-closed (in reverse order)受检与非受检
受检异常(extends Exception)必须用 'throws' 声明或捕获——编译器强制执行。用于可恢复条件(文件未找到、网络错误)。非受检异常(extends RuntimeException)不需要声明——用于编程错误(null 指针、无效参数)。争论:受检异常强制处理但可能使代码混乱;许多框架优先使用非受检。
// Checked exceptions (must be declared or caught)
public void readFile(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
// IOException is checked — compiler enforces handling
}
// Unchecked exceptions (RuntimeException, no need to declare)
public int divide(int a, int b) {
if (b == 0) {
throw new IllegalArgumentException("Divisor cannot be zero");
// RuntimeException — no 'throws' needed
}
return a / b;
}
// Common checked exceptions:
// IOException, SQLException, ClassNotFoundException
// Common unchecked exceptions:
// NullPointerException, IllegalArgumentException,
// IndexOutOfBoundsException, ArithmeticException,
// ClassCastException, IllegalStateException
// Custom checked exception
class DataException extends Exception {
public DataException(String msg) { super(msg); }
}
// Custom unchecked exception
class ValidationException extends RuntimeException {
public ValidationException(String msg) { super(msg); }
}文件 I/O(NIO.2)
NIO.2(java.nio.file)是现代文件 API。Files.readString/writeString(Java 11+)对文本很方便。Files.lines() 返回惰性 Stream——对大文件高效(必须用 try-with-resources 关闭)。Path.of() 替代旧 File 类。Files.createDirectories() 创建完整路径。始终处理 IOException。
import java.nio.file.*;
import java.io.*;
// Read entire file (small files)
List<String> lines = Files.readAllLines(Path.of("input.txt"));
String content = Files.readString(Path.of("config.json")); // Java 11+
byte[] bytes = Files.readAllBytes(Path.of("image.png"));
// Write file
Files.writeString(Path.of("output.txt"), "Hello, World!");
Files.write(Path.of("data.bin"), bytes);
// Append
Files.writeString(Path.of("log.txt"), "entry\n",
StandardOpenOption.APPEND, StandardOpenOption.CREATE);
// Stream lines (large files, lazy)
try (Stream<String> lineStream = Files.lines(Path.of("large.txt"))) {
lineStream.filter(l -> l.contains("ERROR"))
.forEach(System.out::println);
}
// Copy, move, delete
Files.copy(Path.of("src.txt"), Path.of("dest.txt"));
Files.move(Path.of("old.txt"), Path.of("new.txt"));
Files.delete(Path.of("temp.txt"));
// Check existence
boolean exists = Files.exists(Path.of("file.txt"));
// Create directories
Files.createDirectories(Path.of("a/b/c"));Reader 与 Writer(文本)
BufferedReader/Writer 对文本 I/O 高效(缓冲减少系统调用)。PrintWriter 提供 printf 风格格式化。Scanner 解析输入(nextInt、nextDouble、nextLine)。InputStreamReader 将字节流桥接到字符流(为非 UTF-8 指定字符集)。始终使用 try-with-resources 确保流关闭。
import java.io.*;
import java.nio.file.*;
// BufferedReader (efficient text reading)
try (BufferedReader br = Files.newBufferedReader(Path.of("input.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
// BufferedWriter (efficient text writing)
try (BufferedWriter bw = Files.newBufferedWriter(Path.of("output.txt"))) {
bw.write("First line");
bw.newLine();
bw.write("Second line");
}
// PrintWriter (convenient formatting)
try (PrintWriter pw = new PrintWriter("output.txt")) {
pw.println("Hello");
pw.printf("Name: %s, Age: %d%n", "Alice", 30);
}
// Scanner (parsing input)
try (Scanner sc = new Scanner(Path.of("data.txt"))) {
while (sc.hasNextLine()) {
String line = sc.nextLine();
// Parse tokens
Scanner lineSc = new Scanner(line);
if (lineSc.hasNextInt()) {
int n = lineSc.nextInt();
}
}
}
// InputStreamReader (bytes to chars, e.g., from InputStream)
Reader reader = new InputStreamReader(System.in);日期与时间(java.time)
java.time(Java 8+)是现代日期/时间 API,替代旧 Date/Calendar。LocalDate(仅日期)、LocalTime(仅时间)、LocalDateTime(两者)、ZonedDateTime(带时区)。都是不可变且线程安全的。日期差异使用 Period,时间差异使用 Duration。DateTimeFormatter 用于解析/格式化。Instant 用于机器时间戳(UTC)。
import java.time.*;
import java.time.format.*;
import java.time.temporal.*;
// Current date/time
LocalDate today = LocalDate.now(); // 2024-01-15
LocalTime now = LocalTime.now(); // 14:30:45.123
LocalDateTime dt = LocalDateTime.now(); // both
ZonedDateTime zdt = ZonedDateTime.now(); // with timezone
// Create specific
LocalDate date = LocalDate.of(2024, 1, 15);
LocalTime time = LocalTime.of(14, 30, 0);
LocalDateTime specific = LocalDateTime.of(2024, 1, 15, 14, 30);
// Parsing and formatting
LocalDate parsed = LocalDate.parse("2024-01-15");
String formatted = date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"));
// "15/01/2024"
// Manipulation (immutable, returns new)
LocalDate tomorrow = today.plusDays(1);
LocalDate lastMonth = today.minusMonths(1);
LocalDate nextYear = today.plusYears(1);
// Period (date-based)
Period age = Period.between(LocalDate.of(1990, 1, 1), today);
System.out.println(age.getYears()); // 34
// Duration (time-based)
Duration dur = Duration.between(time, LocalTime.now());
System.out.println(dur.toMinutes());
// Instant (machine time, UTC)
Instant instant = Instant.now();
Instant epoch = Instant.ofEpochSecond(0);并发基础
Java 并发:Thread(低级)、ExecutorService(线程池——优先)、CompletableFuture(异步组合,类似 Promise)。parallelStream() 使用 ForkJoinPool 进行并行处理。synchronized 块保护共享状态。原子变量(AtomicInteger 等)提供无锁线程安全操作。对于复杂并发,使用 java.util.concurrent 集合(ConcurrentHashMap、BlockingQueue)。
import java.util.concurrent.*;
// Create a thread
Thread thread = new Thread(() -> {
System.out.println("Running in: " + Thread.currentThread().getName());
});
thread.start();
thread.join(); // wait for completion
// ExecutorService (thread pool)
ExecutorService executor = Executors.newFixedThreadPool(4);
Future<Integer> future = executor.submit(() -> {
Thread.sleep(1000);
return 42;
});
Integer result = future.get(); // blocks until done
executor.shutdown();
// CompletableFuture (async, Java 8+)
CompletableFuture<String> cf = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenApply(String::toUpperCase);
String asyncResult = cf.join(); // "HELLO WORLD"
// Parallel stream
List<Integer> nums = List.of(1, 2, 3, 4, 5);
int sum = nums.parallelStream().mapToInt(Integer::intValue).sum();
// Synchronized
synchronized (this) {
// only one thread at a time
}
// Atomic variables
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();
counter.compareAndSet(0, 1);Lambda 表达式
Lambda 语法基础
Lambda(Java 8+)是函数式接口的简洁实现。语法:(params) -> expression 或 (params) -> { statements; }。编译器从目标类型推断参数类型。单参数 lambda 可以省略括号;零参数需要空括号。Lambda 启用函数式编程,是 Stream API 的支柱。
// Anonymous class (verbose, pre-Java 8)
Runnable r1 = new Runnable() {
public void run() {
System.out.println("Old way");
}
};
// Lambda expression (Java 8+)
Runnable r2 = () -> System.out.println("Lambda");
// With parameters and body
Comparator<Integer> cmp = (a, b) -> {
int diff = a - b;
return diff;
};
// Type inference (omit types)
Comparator<Integer> cmp2 = (a, b) -> a - b;
// Single param, no parentheses needed
Consumer<String> printer = s -> System.out.println(s);
// Zero params need empty parens
Runnable noop = () -> {};函数式接口
函数式接口恰好有一个抽象方法(SAM 类型)。@FunctionalInterface 注解使编译器强制执行此规则。Lambda 只能针对函数式接口。默认和静态方法允许,不破坏单方法规则。这是使 lambda 在 Java 类型系统中工作的基础。
// Functional interface: exactly one abstract method
@FunctionalInterface
interface MathOperation {
int operate(int a, int b);
}
// Implement with lambda
MathOperation add = (a, b) -> a + b;
MathOperation mul = (a, b) -> a * b;
MathOperation max = (a, b) -> Math.max(a, b);
int result = add.operate(3, 4); // 7
int m = mul.operate(3, 4); // 12
// @FunctionalInterface is optional but recommended
// It prevents accidentally adding a second abstract method
// Default and static methods don't count toward the limit
interface StringProcessor {
String process(String s);
default StringProcessor andThen(StringProcessor next) {
return s -> next.process(this.process(s));
}
}内置函数式接口
java.util.function 提供约 40 个现成的函数式接口,所以你很少自己编写。核心四个:Function(转换)、Predicate(测试)、Consumer(消费)、Supplier(生产)。Bi- 变体接受两个参数。基本类型变体(IntFunction、ToIntFunction 等)避免自动装箱开销。使用这些而不是创建自定义接口。
import java.util.function.*;
// Function<T,R>: input -> output
Function<String, Integer> len = String::length;
int n = len.apply("hello"); // 5
// Predicate<T>: input -> boolean (for filtering)
Predicate<String> isEmpty = String::isEmpty;
boolean e = isEmpty.test(""); // true
// Consumer<T>: input -> void (side effects)
Consumer<String> print = System.out::println;
print.accept("hi");
// Supplier<T>: no input -> output (factories, lazy)
Supplier<Double> random = Math::random;
double r = random.get();
// BiFunction<T,U,R>: two inputs -> output
BiFunction<String, Integer, String> repeat =
(s, i) -> s.repeat(i);
// Primitives variants avoid boxing
IntFunction<String> f = i -> "n=" + i;
IntPredicate positive = i -> i > 0;
ToIntFunction<String> length = String::length;
IntBinaryOperator sum = (a, b) -> a + b;方法引用
方法引用(::)是只调用单个方法的 lambda 简写。四种:静态(Class::static)、绑定实例(obj::method)、未绑定实例(Class::method——第一个参数成为接收者)和构造函数(Class::new)。当 lambda 只转发到一个方法时使用它们——更易读。否则坚持使用显式 lambda。
import java.util.*;
List<String> names = List.of("alice", "bob", "charlie");
// Lambda form
names.forEach(s -> System.out.println(s));
// Method reference (shorthand)
names.forEach(System.out::println);
// Four kinds of method references:
// 1. Static method: ClassName::staticMethod
names.stream().map(String::toUpperCase);
// 2. Instance method of particular object: instance::method
var printer = System.out;
names.forEach(printer::println);
// 3. Instance method of arbitrary object: ClassName::instanceMethod
List<String> upper = names.stream()
.map(String::toUpperCase)
.toList();
// 4. Constructor: ClassName::new
Supplier<ArrayList<String>> factory = ArrayList::new;
ArrayList<String> list = factory.get();捕获变量(事实上 final)
Lambda 可以捕获局部变量,但它们必须是 final 或'事实上 final'(从未重新赋值)。这是因为 lambda 可能比栈帧活得更久。要解决此问题,使用单元素数组或 AtomicInteger/holder 对象。实例和静态字段没有此限制。lambda 内的 'this' 指向封闭类实例,不是 lambda 本身。
import java.util.function.*;
int x = 10;
// Capturing a local variable — must be final or effectively final
Supplier<Integer> getter = () -> x * 2;
System.out.println(getter.get()); // 20
// x = 20; // ERROR: would break the lambda capture
// Local variables captured by lambdas must be final/effectively final
// Workaround: use an array or wrapper (mutable container)
int[] counter = {0};
Runnable inc = () -> counter[0]++;
inc.run();
inc.run();
System.out.println(counter[0]); // 2
// Instance/static fields CAN be modified (no restriction)
class Holder {
int value = 0;
Runnable bump = () -> value++; // OK, field access
}
// 'this' inside a lambda refers to the enclosing instance
class Outer {
String name = "Outer";
Runnable r = () -> System.out.println(this.name); // "Outer"
}构造函数引用
构造函数引用(ClassName::new)简洁地创建新实例。与 Collectors.toCollection() 一起选择结果类型,与数组创建(Type[]::new)一起,以及在工厂模式中。对于 record 和不可变对象,构造函数引用是构建副本的惯用方式。它们自然地与 Function/Supplier 目标配对。
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
// Supplier constructor reference
Supplier<StringBuilder> sbFactory = StringBuilder::new;
StringBuilder sb = sbFactory.get();
// Function constructor reference (with one arg)
Function<String, StringBuilder> sbFromString = StringBuilder::new;
StringBuilder named = sbFromString.apply("Hello");
// In streams: collect into a specific collection
List<String> names = List.of("a", "b", "c");
ArrayList<String> copy = names.stream()
.collect(Collectors.toCollection(ArrayList::new));
// Array constructor reference
IntFunction<String[]> arrayFactory = String[]::new;
String[] arr = arrayFactory.apply(5); // new String[5]
// Copying via constructor
record Point(int x, int y) {}
Function<Point, Point> copyCtor = Point::new;
Point p = copyCtor.apply(new Point(1, 2));Optional 与空安全
创建 Optional
Optional 是一个可能持有也可能不持有值的容器。无值使用 empty(),确定值非 null 时使用 of()(否则抛出 NPE),null 可能时使用 ofNullable()。Optional 强制调用者显式处理缺失情况。永远不要在期望 Optional 的地方返回 null——那违背了目的。
import java.util.Optional;
// empty() — no value
Optional<String> empty = Optional.empty();
// of() — value must be non-null (throws NPE if null)
Optional<String> present = Optional.of("hello");
// ofNullable() — accepts null safely
Optional<String> maybe = Optional.ofNullable(getName());
// From a stream that may produce 0 or 1 elements
Optional<Integer> first = List.of(1, 2, 3).stream().findFirst();
// Common helper pattern
public Optional<User> findUser(long id) {
User u = db.lookup(id);
return Optional.ofNullable(u);
}
String getName() { return Math.random() > 0.5 ? "Alice" : null; }
class User {}
class Db { User lookup(long id) { return null; } }
Db db = new Db();安全消费值
优先使用 ifPresent/ifPresentOrElse 而非 isPresent+get。orElse 返回常量默认值;orElseGet 接受 Supplier,所以默认值延迟计算(当默认值昂贵时重要)。orElseThrow 将缺失转换为异常。目标是永远不要盲目调用 .get()——那重新引入了 Optional 旨在消除的 NPE 风险。
import java.util.Optional;
Optional<String> name = Optional.of("Alice");
// isPresent / isEmpty (Java 11+)
if (name.isPresent()) {
System.out.println(name.get()); // "Alice"
}
// Avoid .get() without checking — throws NoSuchElementException
// ifPresent: run action only if value exists
name.ifPresent(System.out::println);
// ifPresentOrElse (Java 9+)
name.ifPresentOrElse(
System.out::println,
() -> System.out.println("No name")
);
// orElse: provide default
String s1 = name.orElse("Anonymous");
// orElseGet: lazy default (computed only if needed)
String s2 = name.orElseGet(() -> expensiveDefault());
// orElseThrow: throw if absent
String s3 = name.orElseThrow(() -> new IllegalStateException("missing"));
String expensiveDefault() { return "computed"; }用 map 与 flatMap 转换
map 转换包含的值(Optional<T> -> Optional<R>)。当映射函数本身返回 Optional 时使用 flatMap,防止嵌套 Optional。filter 仅在谓词匹配时保留值。链式 map/filter/flatMap 让你构建在第一个空值时短路的管道——比嵌套 null 检查干净得多。
import java.util.Optional;
Optional<String> name = Optional.of("Alice");
// map: transform the value if present
Optional<Integer> length = name.map(String::length); // Optional[5]
Optional<String> upper = name.map(String::toUpperCase);
// flatMap: when the transform itself returns Optional
// (avoids Optional<Optional<T>>)
public Optional<String> findEmail(long id) {
return Optional.ofNullable(db.get(id));
}
Optional<String> email = Optional.of(1L)
.flatMap(this::findEmail); // Optional<email> not Optional<Optional<email>>
// filter: keep only if predicate matches
Optional<Integer> adultAge = Optional.of(25)
.filter(a -> a >= 18); // Optional[25]
Optional<Integer> kid = Optional.of(10)
.filter(a -> a >= 18); // Optional.empty
// Chain transformations
String label = Optional.of("alice")
.map(String::strip)
.filter(s -> !s.isEmpty())
.map(s -> s.substring(0, 1).toUpperCase() + s.substring(1))
.orElse("unknown"); // "Alice"
class Db { String get(long id) { return "[email protected]"; } }
Db db = new Db();
Optional<String> findEmail(long id) { return Optional.ofNullable(db.get(id)); }要避免的反模式
Optional 设计用于返回类型,不是字段或参数。它不是 Serializable 的,作为字段会增加开销。不要在没有检查的情况下使用 .get(),不要使用 isPresent()+get()——那只是冗长的 null 检查。集合已经表达了空性,所以不要用 Optional 包装它们。使用 Optional 作为值可能缺失的返回类型信号。
import java.util.Optional;
// BAD: using Optional for fields (not serializable, wastes memory)
class Bad {
private Optional<String> name; // DON'T
}
// GOOD: use plain field, return Optional from accessor
class Good {
private String name;
public Optional<String> getName() { return Optional.ofNullable(name); }
}
// BAD: Optional as method parameter (clutters API)
public void process(Optional<String> input) {} // DON'T
// GOOD: method overloading or nullable param
public void process(String input) {}
public void process() { process(null); }
// BAD: .get() without check
String x = findName().get(); // throws if empty
// BAD: .isPresent() + .get() — defeats the purpose
Optional<String> opt = findName();
if (opt.isPresent()) {
use(opt.get()); // just use ifPresent or map instead
}
// BAD: returning Optional from collections
public Optional<Item> find(...) {
// Collections already express emptiness — return empty List, not Optional<List>
return Optional.ofNullable(items);
}Optional 与 Stream
Optional.stream()(Java 9+)产生 0 或 1 个元素的 Stream,让你优雅地从流中 flatMap Optional。这是在流处理期间跳过缺失值的最干净方式。它避免了冗长的 filter(isPresent).map(get) 模式并保持管道声明式。
import java.util.*;
import java.util.stream.*;
// stream() on Optional: 0 or 1 element stream
Optional<String> opt = Optional.of("hi");
opt.stream().forEach(System.out::println);
// Useful: flatMap Optional out of a stream
class User {
String email; // may be null
User(String e) { email = e; }
Optional<String> getEmail() { return Optional.ofNullable(email); }
}
List<User> users = List.of(
new User("[email protected]"),
new User(null),
new User("[email protected]")
);
// Extract emails, skipping nulls — clean with Optional::stream
List<String> emails = users.stream()
.flatMap(u -> u.getEmail().stream())
.toList(); // [[email protected], [email protected]]
// Without Optional::stream you'd need filter+map
List<String> emails2 = users.stream()
.map(User::getEmail)
.filter(Optional::isPresent)
.map(Optional::get)
.toList();Stream API 深入
创建 Stream
Stream 可以从集合、数组或静态工厂创建。iterate() 和 generate() 产生无限流——始终后跟 limit()。带谓词的 iterate(Java 9+)比裸 iterate 更安全。IntStream/LongStream/DoubleStream 避免数字工作的装箱。Stream 是一次性的:一旦终端操作运行,流就被消费。
import java.util.*;
import java.util.stream.*;
// From collections
Stream<String> s1 = List.of("a", "b").stream();
Stream<String> s2 = Set.of("x").stream();
// From arrays
int[] nums = {1, 2, 3};
IntStream s3 = Arrays.stream(nums);
Stream<String> s4 = Arrays.stream(new String[]{"a", "b"});
// Static factory methods
Stream<Integer> s5 = Stream.of(1, 2, 3);
Stream<Integer> s6 = Stream.empty();
Stream<Integer> s7 = Stream.iterate(1, n -> n * 2); // infinite
Stream<Integer> s8 = Stream.iterate(1, n -> n < 100, n -> n + 1); // bounded (Java 9+)
Stream<Double> s9 = Stream.generate(Math::random); // infinite
Stream<String> s10 = Stream.ofNullable(null); // 0 or 1 element (Java 9+)
// From functions (infinite, must limit)
List<Integer> powers = Stream.iterate(1, n -> n * 2)
.limit(10)
.toList();
// Numeric ranges
IntStream range = IntStream.range(0, 5); // 0,1,2,3,4
IntStream closed = IntStream.rangeClosed(1, 5); // 1,2,3,4,5中间操作
中间操作是惰性的——它们在调用终端操作之前不运行。filter 保留元素,map 1:1 转换,flatMap 1:多 转换。distinct/sorted/limit/skip 是有状态的。takeWhile/dropWhile(Java 9+)在第一个不匹配元素处停止(不像 filter 扫描所有)。peek 用于调试,不用于生产中的副作用。
import java.util.*;
import java.util.stream.*;
List<Integer> nums = List.of(3, 1, 4, 1, 5, 9, 2, 6, 5);
// filter: keep matching
List<Integer> evens = nums.stream()
.filter(n -> n % 2 == 0).toList();
// map: transform
List<String> labels = nums.stream()
.map(n -> "n=" + n).toList();
// flatMap: one-to-many
List<Integer> expanded = List.of(List.of(1, 2), List.of(3))
.stream().flatMap(List::stream).toList(); // [1,2,3]
// distinct: remove duplicates
List<Integer> uniq = nums.stream().distinct().toList();
// sorted
List<Integer> asc = nums.stream().sorted().toList();
List<Integer> desc = nums.stream().sorted(Comparator.reverseOrder()).toList();
// peek: inspect (mainly for debugging)
nums.stream().peek(n -> System.out.println("seen " + n)).count();
// limit / skip
List<Integer> first3 = nums.stream().limit(3).toList();
List<Integer> after2 = nums.stream().skip(2).toList();
// takeWhile / dropWhile (Java 9+)
List<Integer> lt5 = nums.stream().takeWhile(n -> n < 5).toList();Collector:分组与分区
Collectors.groupingBy 是 Java 流的 SQL GROUP BY。分类器函数定义键;可选的下游 collector 处理每个组(counting、summing、mapping 等)。partitioningBy 是带布尔谓词的特殊情况(恰好两个桶)。传递 TreeMap supplier 获得排序键。这些强大地组合——你可以构建多级分组。
import java.util.*;
import java.util.stream.*;
record Person(String name, String city, int age) {}
List<Person> people = List.of(
new Person("Alice", "NYC", 30),
new Person("Bob", "LA", 25),
new Person("Carol", "NYC", 35),
new Person("Dave", "LA", 40)
);
// groupingBy: Map<key, List<item>>
Map<String, List<Person>> byCity = people.stream()
.collect(Collectors.groupingBy(Person::city));
// {NYC=[Alice,Carol], LA=[Bob,Dave]}
// groupingBy with downstream collector
Map<String, Long> countByCity = people.stream()
.collect(Collectors.groupingBy(Person::city, Collectors.counting()));
Map<String, Integer> sumAgeByCity = people.stream()
.collect(Collectors.groupingBy(Person::city,
Collectors.summingInt(Person::age)));
Map<String, List<String>> namesByCity = people.stream()
.collect(Collectors.groupingBy(Person::city,
Collectors.mapping(Person::name, Collectors.toList())));
// partitioningBy: Map<Boolean, List> (2 buckets)
Map<Boolean, List<Person>> byAge = people.stream()
.collect(Collectors.partitioningBy(p -> p.age() >= 30));
// groupingBy with TreeMap for sorted keys
Map<String, List<Person>> sorted = people.stream()
.collect(Collectors.groupingBy(Person::city, TreeMap::new, Collectors.toList()));归约与统计
reduce 将所有元素合并为单个值——为空流安全提供 identity。summaryStatistics 一次通过给出 count/sum/min/max/avg。Collectors.joining 用于构建分隔字符串很方便。teeing(Java 12+)并行运行两个 collector 并合并结果——当你需要一次通过中的两个聚合(如 min 和 max)时有用。
import java.util.*;
import java.util.stream.*;
List<Integer> nums = List.of(1, 2, 3, 4, 5);
// reduce: combine all into one
int sum = nums.stream().reduce(0, Integer::sum); // 15
Optional<Integer> product = nums.stream().reduce((a, b) -> a * b); // Optional[120]
// Built-in summary collectors
IntSummaryStatistics stats = nums.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
// stats.getCount(), getSum(), getMin(), getMax(), getAverage()
// Common terminal collectors
long count = nums.stream().collect(Collectors.counting());
double avg = nums.stream().collect(Collectors.averagingInt(Integer::intValue));
int max = nums.stream().collect(Collectors.maxBy(Comparator.naturalOrder())).orElse(0);
// join strings
String joined = List.of("a", "b", "c").stream()
.collect(Collectors.joining(", ", "[", "]")); // "[a, b, c]"
// teeing (Java 12+): two collectors, merge results
record Range(int min, int max) {}
Range range = nums.stream().collect(Collectors.teeing(
Collectors.minBy(Comparator.naturalOrder()),
Collectors.maxBy(Comparator.naturalOrder()),
(mn, mx) -> new Range(mn.orElse(0), mx.orElse(0))
));数字流
IntStream/LongStream/DoubleStream 是避免自动装箱开销的基本特化——数字工作使用它们。mapToInt/mapToLong/mapToDouble 将对象流转换为基本流;boxed() 回去。基本流有专门的终端操作(sum、average、max)返回 OptionalInt/Double 以处理空流。适用于性能敏感的数字管道。
import java.util.stream.*;
import java.util.*;
// IntStream / LongStream / DoubleStream avoid boxing
IntStream range = IntStream.rangeClosed(1, 100);
int sum = range.sum(); // 5050
double avg = IntStream.of(1, 2, 3).average().orElse(0);
// mapToInt / mapToLong / mapToDouble from object stream
int totalAge = people().stream().mapToInt(Person::age).sum();
// boxed: convert primitive stream back to object stream
List<Integer> list = IntStream.range(0, 5).boxed().toList();
// mapToObj: primitive -> objects
List<String> labels = IntStream.range(1, 4)
.mapToObj(i -> "item-" + i).toList();
// asLongStream / asDoubleStream
LongStream longs = IntStream.range(0, 5).asLongStream();
// Common numeric operations
int max = IntStream.of(3, 1, 4, 1, 5).max().orElse(Integer.MIN_VALUE);
boolean anyEven = IntStream.of(1, 3, 5).anyMatch(n -> n % 2 == 0);
// Iterate to build numeric sequences
List<Integer> fib = Stream.iterate(new int[]{0, 1}, a -> new int[]{a[1], a[0] + a[1]})
.limit(10).mapToInt(a -> a[0]).boxed().toList();
List<Person> people() { return List.of(new Person("a", "c", 30)); }
record Person(String name, String city, int age) {}并行流
parallelStream 跨公共 ForkJoinPool(大小为 CPU 核心数)拆分工作。仅对大数据集的 CPU 密集型、无状态、顺序无关操作使用——小数据时开销超过收益。避免共享可变状态(导致竞争)。并行流中的 I/O 阻塞共享池——为阻塞工作使用自定义 ForkJoinPool。在假设并行更快之前测量。
import java.util.*;
import java.util.stream.*;
// parallelStream: uses common ForkJoinPool
long sum = List.of(1, 2, 3, 4, 5).parallelStream()
.mapToInt(Integer::intValue).sum();
// Convert sequential to parallel
long count = IntStream.range(0, 1_000_000).parallel()
.filter(n -> n % 2 == 0).count();
// Order may differ — use forEachOrdered if order matters
List.of(1, 2, 3, 4).parallelStream()
.forEachOrdered(System.out::println);
// Collecting preserves encounter order (but work is parallel)
List<Integer> doubled = IntStream.range(0, 1000).parallel()
.map(n -> n * 2).boxed().toList();
// Custom thread pool (avoid blocking the common pool)
import java.util.concurrent.ForkJoinPool;
ForkJoinPool pool = new ForkJoinPool(8);
int result = pool.submit(() ->
IntStream.range(0, 1000).parallel().sum()
).get();
// WHEN to use parallel: large dataset, CPU-heavy per element,
// order-independent, stateless operations
// WHEN NOT: small data, I/O-bound, shared mutable state, ordered ops泛型深入
泛型类与方法
泛型启用类型安全的可重用代码。类声明类型参数(<T>);方法也可以(返回类型前的 <T>)。菱形运算符 <> 在构造时推断类型。泛型在编译时检查——它们通过早期捕获类型错误而不是运行时通过 ClassCastException 使集合和 API 更安全。
// Generic class
public class Box<T> {
private T value;
public void set(T v) { value = v; }
public T get() { return value; }
}
Box<String> strBox = new Box<>();
strBox.set("hello");
String s = strBox.get(); // no cast needed
// Multiple type parameters
public class Pair<K, V> {
private final K key;
private final V value;
public Pair(K k, V v) { key = k; value = v; }
public K key() { return key; }
public V value() { return value; }
}
Pair<String, Integer> p = new Pair<>("age", 30);
// Generic method (independent of class type params)
public static <T> T first(List<T> list) {
return list.get(0);
}
// Generic method with multiple type params
public static <K, V> Map<K, V> zip(List<K> keys, List<V> values) {
Map<K, V> m = new HashMap<>();
for (int i = 0; i < keys.size(); i++) m.put(keys.get(i), values.get(i));
return m;
}有界类型参数
有界类型参数(<T extends Bound>)限制可以使用的类型并让你调用 bound 的方法。<T extends Number> 意味着 T 必须是 Number 或子类型。多个 bound 使用 &——最多一个类(必须在前),其余接口。Bound 对于编写需要特定能力(可比性、数字操作)的算法很重要。
// Upper bound: T must be a subtype of Number
public static <T extends Number> double sum(List<T> nums) {
double total = 0;
for (Number n : nums) total += n.doubleValue();
return total;
}
sum(List.of(1, 2, 3)); // Integer is a Number
sum(List.of(1.0, 2.5)); // Double is a Number
// sum(List.of("a")); // compile error
// Multiple bounds: T must extend all (first is class, rest interfaces)
interface Comparable<T> { int compareTo(T o); }
interface Serializable {}
public static <T extends Number & Comparable<T> & Serializable>
T max(List<T> list) {
T best = list.get(0);
for (T t : list) if (t.compareTo(best) > 0) best = t;
return best;
}
// Bound lets you call methods of the bound
public static <T extends CharSequence> int totalLength(List<T> items) {
int len = 0;
for (CharSequence c : items) len += c.length(); // can call .length()
return len;
}通配符:?、extends、super
通配符使泛型类型灵活。? extends T(协变)让你读 T 但不能写——用于生产者。? super T(逆变)让你写 T 但只能读 Object——用于消费者。PECS 规则(Producer Extends, Consumer Super)指导使用哪个。copy(dest, src) 是经典示例:dest 是消费者(super),src 是生产者(extends)。
import java.util.*;
// ? (unbounded wildcard) — any type
void printAll(List<?> list) {
for (Object o : list) System.out.println(o);
}
// ? extends T (upper-bounded / covariant) — producer (PECS: Producer Extends)
double sum(List<? extends Number> nums) {
double total = 0;
for (Number n : nums) total += n.doubleValue();
return total;
}
sum(List.of(1, 2, 3)); // List<Integer> OK
sum(List.of(1.0, 2.0)); // List<Double> OK
// nums.add(5); // ERROR: can't add (don't know exact type)
// ? super T (lower-bounded / contravariant) — consumer (PECS: Consumer Super)
void addNumbers(List<? super Integer> list) {
list.add(1); list.add(2); list.add(3); // OK to add Integer
}
addNumbers(new ArrayList<Number>()); // OK
addNumbers(new ArrayList<Object>()); // OK
// Number n = list.get(0); // only safe to read as Object
// PECS rule: Producer Extends, Consumer Super
// If you read from a collection, use ? extends T
// If you write to a collection, use ? super T
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (T t : src) dest.add(t);
}类型擦除
Java 泛型使用类型擦除——泛型类型仅在编译时存在;运行时,List<String> 和 List<Integer> 都只是 List。这启用向后兼容但有局限:你不能 new T()、创建泛型数组、使用 instanceof 与泛型或有相同擦除签名的重载方法。当未检查转换将错误类型放入泛型时发生堆污染,将错误延迟到运行时。
import java.util.*;
// At runtime, generic types are erased to their bounds (or Object)
// List<String>, List<Integer>, List<?> all become List at runtime
List<String> strings = new ArrayList<>();
List<Integer> ints = new ArrayList<>();
// Runtime: both are just ArrayList
// You CANNOT do these due to erasure:
// new T() — can't instantiate type param
// new T[] — can't create generic array
// instanceof List<String> — only instanceof List (raw)
// class MyException<T> extends Exception — can't extend Throwable generically
// static T field — no static generic fields
// Erasure means overloads clash:
// void process(List<String> list) {}
// void process(List<Integer> list) {} // ERROR: same erasure
// Checking types at runtime requires Class<T>
public static <T> List<T> filter(List<?> items, Class<T> type) {
List<T> result = new ArrayList<>();
for (Object o : items) {
if (type.isInstance(o)) result.add(type.cast(o));
}
return result;
}
// Heap pollution: when unchecked warnings lead to runtime ClassCastException
List<String> polluted = (List<String>)(List) List.of(1, 2); // unchecked
// String s = polluted.get(0); // ClassCastException at runtime泛型方法与推断
类型推断让编译器从上下文(参数和目标类型)确定类型参数,所以你很少显式编写它们。菱形运算符 <> 是构造函数的推断。目标类型使用变量的期望类型。仅当推断无法解决歧义时使用显式类型见证(Class.<T>method())。推断使泛型代码读起来和非泛型代码一样干净。
import java.util.*;
// Type inference: compiler figures out T from arguments
public static <T> T pick(T a, T b) { return Math.random() > 0.5 ? a : b; }
String s = pick("hello", "world"); // T inferred as String
Number n = pick(1, 2.0); // T inferred as Number (common supertype)
// Target typing: inference uses the expected type
List<String> list = Collections.emptyList(); // T inferred from target
// Inference with method chains
List<Integer> nums = List.of(1, 2, 3);
String joined = nums.stream()
.map(Object::toString) // Stream<String>
.collect(Collectors.joining(",")); // inferred
// Explicit type witness (rarely needed)
Collections.<String>emptyList();
// Generic constructor
class Holder<T> {
private T value;
<U extends T> Holder(U init) { value = init; } // constructor type param
T get() { return value; }
}
Holder<Number> h = new Holder<>(42); // U=Integer, T=Number
import java.util.stream.Collectors;
class Math { static double random() { return 0.5; } }泛型接口与模式
泛型接口(如 Repository<T,ID>)定义可重用契约。自引用 bound 模式(class X implements Comparable<X>)确保 compareTo 只接受相同类型。类型令牌模式(使用 Class<T> 作为键)绕过擦除以在异构容器中提供运行时类型安全。这些模式是 Spring Data 等框架的支柱。
// Generic interface
interface Repository<T, ID> {
Optional<T> findById(ID id);
List<T> findAll();
void save(T entity);
}
// Implement with concrete types
class UserRepository implements Repository<User, Long> {
public Optional<User> findById(Long id) { /* ... */ return Optional.empty(); }
public List<User> findAll() { return List.of(); }
public void save(User entity) {}
}
// Generic interface with self-referencing bound (Comparable pattern)
interface Comparable<T> {
int compareTo(T other);
}
class Temperature implements Comparable<Temperature> {
private final double celsius;
Temperature(double c) { celsius = c; }
public int compareTo(Temperature other) {
return Double.compare(celsius, other.celsius);
}
}
// Generic builder pattern
class Builder<T> {
private T value;
public Builder<T> set(T v) { value = v; return this; }
public T build() { return value; }
}
// Type token pattern for runtime type safety
class TypeSafeMap {
private final Map<Class<?>, Object> map = new HashMap<>();
public <T> void put(Class<T> type, T value) { map.put(type, value); }
public <T> T get(Class<T> type) { return type.cast(map.get(type)); }
}
record User(String name) {}
import java.util.Optional;注解
内置注解
Java 的内置注解:@Override(捕获重写中的拼写错误——始终使用它)、@Deprecated(信号 API 不应使用,带 since/forRemoval 元数据)、@SuppressWarnings(静默特定警告——窄范围使用)、@FunctionalInterface(强制 SAM 规则)。这些是提高编译时安全性和文档的日常注解。
import java.util.*;
// @Override: declares intent to override (compiler checks)
class Animal {
public void sound() { System.out.println("..."); }
}
class Dog extends Animal {
@Override
public void sound() { System.out.println("Woof"); }
}
// @Deprecated: marks API as outdated
class OldApi {
@Deprecated(since = "1.5", forRemoval = true)
public void legacy() {}
}
// @SuppressWarnings: silence compiler warnings
@SuppressWarnings("unchecked")
List<String> list = (List<String>) new ArrayList();
// @FunctionalInterface: enforces single abstract method
@FunctionalInterface
interface Op { int apply(int a, int b); }
// Common warning keys: unchecked, deprecation, rawtypes, null
// Java 17+ sealed-related
@Deprecated
class ToRemove {}自定义注解
自定义注解用 @interface 声明。成员看起来像方法但它们是注解属性——可以有默认值。使用 @Target 限制其应用位置(TYPE、METHOD、FIELD 等)和 @Retention 控制可用性。标记注解(无成员)只是标记元素。注解本身不携带行为——处理器(反射、注解工具)读取它们并行动。
import java.lang.annotation.*;
// Define an annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
String value() default "";
long timeout() default 0L;
}
// Use it
class MyTests {
@Test
public void quickCheck() {}
@Test(timeout = 5000)
public void slowCheck() {}
@Test("custom-name")
public void named() {}
}
// Annotation with default values
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface Entity {
String table() default "";
String[] columns() default {};
}
@Entity(table = "users", columns = {"id", "name"})
class User {}
// Marker annotation (no members)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Service {}保留与目标
@Retention 控制注解存活多久:SOURCE(仅编译时,如 @Override)、CLASS(在字节码中但运行时不可见——默认)、RUNTIME(通过反射访问)。@Target 限制注解可以出现的位置。Java 8+ 添加了 TYPE_USE 和 TYPE_PARAMETER,让你注解泛型和转换(List<@NonNull String>)。仅在需要反射访问时选择 RUNTIME。
import java.lang.annotation.*;
// RetentionPolicy.SOURCE: discarded by compiler (e.g., @Override)
@Retention(RetentionPolicy.SOURCE)
@interface CompileOnly {}
// RetentionPolicy.CLASS: kept in .class but not loaded (default)
@Retention(RetentionPolicy.CLASS)
@interface BytecodeOnly {}
// RetentionPolicy.RUNTIME: available via reflection at runtime
@Retention(RetentionPolicy.RUNTIME)
@interface RuntimeVisible {}
// ElementType targets
@Target(ElementType.TYPE) // classes, interfaces, enums
@interface ForType {}
@Target(ElementType.METHOD)
@interface ForMethod {}
@Target(ElementType.FIELD)
@interface ForField {}
@Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
@interface ForTypeUse {}
// Java 8+ type-use annotations (annotate any type occurrence)
@RuntimeVisible String[] names; // example usage
List<@RuntimeVisible String> typed;通过反射读取注解
具有 RUNTIME 保留的注解可以通过反射读取:isAnnotationPresent() 检查存在,getAnnotation() 检索它。这就是框架(Spring、JUnit、JAX-RS)声明式连接行为的方式——你标记方法/类,框架扫描并分发。编译时注解处理(注解处理器)是无运行时反射成本的代码生成替代方案。
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Route {
String path();
String method() default "GET";
}
class Api {
@Route(path = "/users", method = "GET")
public void listUsers() {}
@Route(path = "/users", method = "POST")
public void createUser() {}
}
// Scan methods for @Route at runtime
for (Method m : Api.class.getDeclaredMethods()) {
if (m.isAnnotationPresent(Route.class)) {
Route r = m.getAnnotation(Route.class);
System.out.println(r.method() + " " + r.path() + " -> " + m.getName());
}
}
// Output:
// GET /users -> listUsers
// POST /users -> createUser
// Reading annotations on a class
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Table { String name(); }
@Table(name = "orders")
class Order {}
Table t = Order.class.getAnnotation(Table.class);
System.out.println(t.name()); // "orders"可重复与元注解
@Repeatable(Java 8+)让你通过定义容器注解多次应用相同注解。@Inherited 使注解传播到子类(仅适用于类级注解)。@Documented 在 Javadoc 中包含注解。带 ANNOTATION_TYPE 的 @Target 创建元注解(注解其他注解的注解)——这就是 Spring 构建可组合注解原型如 @RestController = @Controller + @ResponseBody 的方式。
import java.lang.annotation.*;
// Repeatable: allow same annotation multiple times
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Schedule {
String cron();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface Schedules {
Schedule[] value(); // container annotation
}
// Make Schedule repeatable
@Repeatable(Schedules.class)
@interface Schedule2 {
String cron();
}
// Now you can repeat it (Java 8+)
class Job {
@Schedule2(cron = "0 0 * * *")
@Schedule2(cron = "0 30 * * *")
public void run() {}
}
// Meta-annotations: annotations on annotations
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE) // can only annotate other annotations
@interface TestCategory {}
@TestCategory
@Retention(RetentionPolicy.RUNTIME)
@interface UnitTest {}
// Inherited: subclass inherits the annotation
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Persistent {}
@Persistent class Base {}
class Child extends Base {} // Child also has @Persistent
// Documented: appears in Javadoc
@Documented
@interface PublicApi {}反射
Class 对象与获取类
每个加载的类型都有唯一的 Class 对象——反射的入口点。通过类字面量(Type.class)、instance.getClass() 或 Class.forName()(动态加载,抛出 ClassNotFoundException)获取。Class 对象暴露名称、修饰符、超类、接口和类型检查(isInterface、isArray、isEnum、isRecord)。isAssignableFrom 检查多态关系。
import java.lang.reflect.*;
// Three ways to get a Class object
Class<String> c1 = String.class; // class literal
Class<?> c2 = "hello".getClass(); // from instance
Class<?> c3 = Class.forName("java.lang.String"); // by name (throws checked)
// Basic introspection
Class<?> c = String.class;
System.out.println(c.getName()); // "java.lang.String"
System.out.println(c.getSimpleName()); // "String"
System.out.println(c.getPackage()); // "package java.lang"
System.out.println(c.getSuperclass()); // "class java.lang.Object"
System.out.println(Modifier.toString(c.getModifiers())); // "public final"
// Check type relationships
System.out.println(c.isInterface()); // false
System.out.println(c.isArray()); // false
System.out.println(c.isEnum()); // false
System.out.println(c.isRecord()); // false (Java 16+)
System.out.println(CharSequence.class.isAssignableFrom(c)); // true
// Primitive class objects
Class<?> intClass = int.class;
Class<?> intArrayClass = int[].class;
System.out.println(intClass.isPrimitive()); // true检查字段、方法、构造函数
getDeclaredFields/Methods/Constructors 返回此类中声明的所有成员(包括私有)。getFields/getMethods 仅返回公共成员但包括继承的。要查找特定成员,使用 getDeclaredField(name) 或 getDeclaredMethod(name, paramTypes...)——需要参数类型来消除重载歧义。反射绕过访问控制,除非你调用 setAccessible(true)。
import java.lang.reflect.*;
import java.util.*;
class Sample {
public String name;
private int count;
public Sample() {}
public Sample(String n) { name = n; }
private void secret() {}
public int compute(int x) { return x * 2; }
}
Class<?> c = Sample.class;
// Fields: getDeclaredFields includes private; getFields only public
for (Field f : c.getDeclaredFields()) {
System.out.println(f.getName() + " : " + f.getType().getSimpleName()
+ " (" + Modifier.toString(f.getModifiers()) + ")");
}
// Methods
for (Method m : c.getDeclaredMethods()) {
System.out.println(m.getName()
+ " params=" + Arrays.toString(m.getParameterTypes())
+ " returns=" + m.getReturnType().getSimpleName());
}
// Constructors
for (Constructor<?> ctor : c.getConstructors()) {
System.out.println("ctor params=" + Arrays.toString(ctor.getParameterTypes()));
}
// Lookup specific member
Field nameField = c.getDeclaredField("name");
Method compute = c.getDeclaredMethod("compute", int.class);
Constructor<?> ctor = c.getDeclaredConstructor(String.class);调用方法与创建实例
Method.invoke(obj, args...) 反射式调用方法——始终返回 Object,所以转换结果。setAccessible(true) 绕过 Java 访问检查(私有成员变得可达;可能需要模块上的 --add-opens)。Constructor.newInstance() 创建对象——new 的反射等价物。Array.newInstance 创建运行时已知组件类型的数组。反射比直接调用慢且绕过编译时安全。
import java.lang.reflect.*;
class Greeter {
public String greet(String name) { return "Hello, " + name; }
private String secret() { return "hidden"; }
}
Object obj = new Greeter();
Class<?> c = obj.getClass();
// Invoke public method
Method greet = c.getMethod("greet", String.class);
String result = (String) greet.invoke(obj, "Alice"); // "Hello, Alice"
// Invoke private method
Method sec = c.getDeclaredMethod("secret");
sec.setAccessible(true); // bypass access check
String s = (String) sec.invoke(obj); // "hidden"
// Create instances via constructor
Constructor<?> noArg = c.getConstructor();
Object o1 = noArg.newInstance();
// With args
class Person {
String name;
public Person(String n) { name = n; }
public String toString() { return "Person(" + name + ")"; }
}
Constructor<?> ctor = Person.class.getConstructor(String.class);
Object p = ctor.newInstance("Bob");
System.out.println(p); // "Person(Bob)"
// Array creation via reflection
Object strArray = Array.newInstance(String.class, 5);
Array.set(strArray, 0, "first");
String v = (String) Array.get(strArray, 0);读取与修改字段
Field.get(instance) 读取字段值;Field.set(instance, value) 写入。对于基本类型,使用类型特定的访问器(getInt/setInt)以避免装箱。静态字段接受 null 作为实例参数。私有字段需要 setAccessible(true)。反射字段访问是序列化库(Jackson、Gson)和 ORM 框架通用读/写对象状态的方式。