Java

技术栈
后端框架
javajvmspring-bootobject-orientedmultithreadingenterprise

概览

Java 概述

Java 是 Sun Microsystems 于 1995 年发布的面向对象编程语言,由 James Gosling 主导设计。现由 Oracle 维护。口号 "Write Once, Run Anywhere"。

解决什么问题

  • 提供跨平台的标准化企业级开发平台,屏蔽底层操作系统差异
  • 通过 JVM 虚拟机实现字节码跨平台执行
  • 丰富的企业级生态(Spring、Jakarta EE、微服务)支撑大规模分布式系统
  • 严格类型系统和编译时检查,适合大型团队协作

关键特性

  • JVM 虚拟机:一次编译,到处运行;JIT/AOT 编译优化运行时性能
  • 强类型 + 泛型:编译期类型安全,泛型擦除向后兼容
  • 自动内存管理(GC):多种垃圾回收器(G1、ZGC、Shenandoah)
  • 虚拟线程(Project Loom):JDK 21+ 中的轻量级协程
  • 模式匹配instanceof 模式、switch 表达式、Record 类型
  • 密封类:限制继承层次,增强类型安全
  • 庞大生态:Spring Boot、Maven/Gradle、Apache 项目族
  • LTS 版本:JDK 17、21 提供长期支持

安装

Java 安装指南

1. 环境准备

要求 说明
操作系统 Windows 10+ / macOS 12+ / Linux (Ubuntu 20.04+, Debian 11+, CentOS 8+)
硬件 至少 4GB 内存(IDE 建议 8GB+),2GB 磁盘空间
IDE(推荐) IntelliJ IDEA Community / Eclipse / VS Code + Extension Pack for Java

2. 安装命令

Windows

# 方式一:winget(推荐)
winget install Oracle.JDK.21
winget install EclipseAdoptium.Temurin.21.JDK

# 方式二:手动
# 访问 https://adoptium.net 下载 Temurin JDK .msi

# 验证
java --version
javac --version

macOS

# Homebrew
brew install openjdk@21

# 设置 JAVA_HOME
echo 'export JAVA_HOME="/opt/homebrew/opt/openjdk@21"' >;> ~/.zshrc
echo 'export PATH="$JAVA_HOME/bin:$PATH"' >;> ~/.zshrc
source ~/.zshrc

Linux (Ubuntu/Debian)

# 方式一:apt
sudo apt update
sudo apt install -y openjdk-21-jdk

# 方式二:SDKMAN!(推荐,支持多版本管理)
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 21.0.5-tem

使用 SDKMAN! 管理多版本(全平台推荐)

# 安装 SDKMAN!
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"

# 安装 JDK
sdk list java              # 查看可用版本
sdk install java 21.0.5-tem
sdk install java 17.0.13-tem
sdk default java 21.0.5-tem
sdk use java 17.0.13-tem   # 临时切换

验证安装

java --version    # 例如 openjdk 21.0.5
javac --version   # 例如 javac 21.0.5
echo $JAVA_HOME   # /path/to/jdk

3. 常见安装问题

JAVA_HOME 未设置或错误

# 查找 Java 安装路径
which java              # 如 /usr/bin/java
readlink -f $(which java)  # 找到实际路径

# 检查正确路径
/usr/libexec/java_home -V   # macOS
update-alternatives --list java  # Linux

多个 JDK 版本冲突

# SDKMAN! 轻松解决
sdk use java 21.0.5-tem   # 仅当前 Shell
sdk default java 21.0.5-tem  # 全局默认

# update-alternatives(Linux)
sudo update-alternatives --config java

Maven/Gradle 找不到 JDK

确保 JAVA_HOME 指向 JDK 根目录(不是 bin/ 子目录):

# ✅ 正确
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk

# ❌ 错误
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk/bin

国内 Maven 镜像加速

~/.m2/settings.xml 中:

<mirrors>
  <mirror>
    <id>aliyun</id>
    <mirrorOf>central</mirrorOf>
    <url>https://maven.aliyun.com/repository/central</url>
  </mirror>
</mirrors>

示例

Java Hello World — Spring Boot REST API

目标

用 Spring Boot 3 创建一个 RESTful Web 服务,返回 JSON 格式的 "Hello World"。

完整代码

// HelloWorldApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.Instant;
import java.util.Map;

@SpringBootApplication
public class HelloWorldApplication {
    public static void main(String[] args) {
        SpringApplication.run(HelloWorldApplication.class, args);
    }
}

@RestController
class HelloController {

    @GetMapping("/")
    public Map<String, Object> hello() {
        return Map.of(
            "message", "Hello World",
            "timestamp", Instant.now().getEpochSecond()
        );
    }

    @GetMapping("/health")
    public Map<String, String> health() {
        return Map.of("status", "ok");
    }
}

运行步骤

方式一:Spring Initializr (推荐)

# 1. 访问 https://start.spring.io
# 2. 选 Maven / Java / Spring Boot 3.4+ / Java 21
# 3. 添加依赖: Spring Web
# 4. 下载解压,将上述代码复制到 src/main/java/com/example/demo/

# 5. 运行
./mvnw spring-boot:run     # macOS/Linux
mvnw.cmd spring-boot:run   # Windows

方式二:Maven 手动创建

<!-- pom.xml -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.4.0</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

测试

curl http://localhost:8080/
# 输出: {"message":"Hello World","timestamp":1717000000}

curl http://localhost:8080/health
# 输出: {"status":"ok"}

预期输出

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot :: (v3.4.0)
...
Tomcat started on port 8080

要点说明

  • @SpringBootApplication 组合了 @Configuration@EnableAutoConfiguration@ComponentScan
  • @RestController = @Controller + @ResponseBody(返回 JSON 而非视图)
  • Map.of() 是 JDK 9+ 工厂方法,创建不可变 Map
  • Spring Boot 内嵌 Tomcat,无需部署 war 包

Java Spring Boot — 用户管理 CRUD API

目标

使用 Spring Boot 3 + Spring Data JPA + H2 构建完整的用户管理 REST API。

完整代码

//─── User.java (Entity) ───
package com.example.demo.entity;

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false, unique = true)
    private String email;

    public User() {}
    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    // getters & setters ...
    public Long getId() { return id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

//─── UserRepository.java ───
package com.example.demo.repository;

import com.example.demo.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository<User, Long> {
}

//─── UserController.java ───
package com.example.demo.controller;

import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserRepository repo;

    public UserController(UserRepository repo) {
        this.repo = repo;
    }

    @GetMapping
    public List<User> getAll() {
        return repo.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getById(@PathVariable Long id) {
        return repo.findById(id)
            .map(ResponseEntity::ok)
            .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public ResponseEntity<User> create(@RequestBody User user) {
        User saved = repo.save(user);
        return ResponseEntity
            .created(java.net.URI.create("/api/users/" + saved.getId()))
            .body(saved);
    }

    @PutMapping("/{id}")
    public ResponseEntity<User> update(@PathVariable Long id, @RequestBody User user) {
        return repo.findById(id).map(existing -> {
            existing.setName(user.getName());
            existing.setEmail(user.getEmail());
            return ResponseEntity.ok(repo.save(existing));
        }).orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        if (!repo.existsById(id)) return ResponseEntity.notFound().build();
        repo.deleteById(id);
        return ResponseEntity.noContent().build();
    }
}
# application.properties
spring.datasource.url=jdbc:h2:mem:userdb
spring.h2.console.enabled=true
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
<!-- pom.xml 附加依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>runtime</scope>
</dependency>

测试

curl http://localhost:8080/api/users

curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice","email":"alice@example.com"}'

curl -X PUT http://localhost:8080/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice Updated","email":"alice@example.com"}'

curl -X DELETE http://localhost:8080/api/users/1

要点说明

  • JpaRepository<User, Long> 自动提供 findAll()findById()save()deleteById() 等方法
  • 构造函数注入优于 @Autowired 字段注入
  • H2 内存数据库适合开发测试,生产需切换 MySQL/PostgreSQL
  • ResponseEntity 提供完整的 HTTP 响应控制(状态码、头、体)

教程

Java 入门篇:Spring Boot 3 全栈 Web 开发

背景

Spring Boot 是 Java 生态中最流行的微服务框架,用"约定大于配置"的理念大幅简化了 Spring 应用的搭建。本教程带你构建一个完整的用户管理系统。

核心概念

1. 控制反转(IoC)和依赖注入(DI)

Spring 容器管理 Bean 的生命周期和依赖关系。用 @Autowired(或构造函数注入)声明依赖,Spring 自动装配。

// 推荐:构造函数注入
@RestController
public class UserController {
    private final UserService service;
    public UserController(UserService service) {
        this.service = service;  // Spring 自动注入
    }
}

2. Spring MVC 请求流程

Client → DispatcherServlet → HandlerMapping
       → Controller → Service → Repository → DB
       → 返回 → ViewResolver / HttpMessageConverter

3. 常用注解速查

注解 作用
@SpringBootApplication 组合了 @Configuration@EnableAutoConfiguration@ComponentScan
@RestController 组合了 @Controller + @ResponseBody,返回 JSON/文本
@GetMapping("/path") 映射 GET 请求
@PathVariable 提取 URL 路径参数
@RequestParam 提取查询参数
@RequestBody 提取 JSON 请求体
@Service 标记业务逻辑层组件
@Repository 标记数据访问层组件
@Transactional 声明式事务管理

分步操作

第一步:创建项目

# 用 Spring Initializr: https://start.spring.io
# 依赖选: Spring Web, Spring Data JPA, H2 Database, Lombok, Validation

第二步:三层架构

controller/   ← 接收请求,返回响应
service/      ← 业务逻辑
repository/   ← 数据访问(JPA)
entity/       ← 实体类
dto/          ← 数据传输对象

第三步:实现 Service 层

@Service
@Transactional
public class UserService {
    private final UserRepository repo;
    public UserService(UserRepository repo) { this.repo = repo; }

    public List<UserDTO> findAll() {
        return repo.findAll().stream()
            .map(UserDTO::fromEntity)
            .toList();
    }
}

第四步:异常处理

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity<?> handleNotFound(EntityNotFoundException ex) {
        return ResponseEntity.status(404).body(Map.of("error", ex.getMessage()));
    }
}

第五步:集成 Swagger/OpenAPI

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.6.0</version>
</dependency>

访问 http://localhost:8080/swagger-ui.html 查看 API 文档。

思考题

  1. @Component@Service@Repository 有什么区别?能否互换?
  2. @Transactional 的传播行为(Propagation)有哪些?REQUIRED vs REQUIRES_NEW 的区别?
  3. Spring Boot 自动配置原理是什么?@ConditionalOnClass 如何工作?

Java 进阶篇:虚拟线程与并发编程

背景

JDK 21 引入了虚拟线程(Virtual Threads,Project Loom),彻底改变了 Java 的并发模型。与传统平台线程不同,虚拟线程由 JVM 管理,轻量到可以创建百万个。

核心概念

1. 虚拟线程 vs 平台线程

// 传统平台线程 — 重量级(约 1MB 栈内存/线程)
Thread.ofPlatform().start(() -> {
    System.out.println("Platform thread: " + Thread.currentThread());
});

// 虚拟线程 — 轻量级(可创建百万个)
Thread.ofVirtual().start(() -> {
    System.out.println("Virtual thread: " + Thread.currentThread());
});

2. 使用 ExecutorService

// 为每个任务创建一个虚拟线程
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    var futures = IntStream.range(0, 1000)
        .mapToObj(i -> executor.submit(() -> {
            Thread.sleep(Duration.ofMillis(100)); // 虚拟线程不阻塞
            return "Task " + i + " done";
        }))
        .toList();

    futures.forEach(f -> {
        try { System.out.println(f.get()); }
        catch (Exception e) { e.printStackTrace(); }
    });
}
// 1000 个虚拟线程,0.1 秒全部完成!

3. Spring Boot 集成虚拟线程

# application.properties
spring.threads.virtual.enabled=true  # Spring Boot 3.2+
@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
    return protocolHandler -> protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
}

4. CompletableFuture 进阶

// 组合多个异步调用
CompletableFuture<String> userFuture = 
    CompletableFuture.supplyAsync(() -> fetchUser(id), virtualExecutor);
CompletableFuture<Integer> orderFuture = 
    CompletableFuture.supplyAsync(() -> fetchOrders(id), virtualExecutor);

String result = CompletableFuture
    .allOf(userFuture, orderFuture)
    .thenApply(v -> String.format(
        "用户: %s, 订单数: %d",
        userFuture.join(), orderFuture.join()
    ))
    .exceptionally(ex -> "Error: " + ex.getMessage())
    .join();

5. 线程安全的集合

// ❌ 线程不安全
Map<String, Integer> map = new HashMap<>();

// ✅ ConcurrentHashMap
Map<String, Integer> safeMap = new ConcurrentHashMap<>();

// ✅ CopyOnWriteArrayList — 读多写少场景
List<String> list = new CopyOnWriteArrayList<>();

// ✅ ConcurrentLinkedQueue — 高并发队列
Queue<String> queue = new ConcurrentLinkedQueue<>();

虚拟线程最佳实践

场景 建议
I/O 密集型(HTTP/DB 调用) 虚拟线程极佳
CPU 密集型 用平台线程 + ForkJoinPool
使用 synchronized 会 pin 住虚拟线程,优先用 ReentrantLock
ThreadLocal 避免大量使用,改用 ScopedValue(JDK 21 preview)
连接池 虚拟线程减少连接池压力,但数据库连接池仍需限制

思考题

  1. 虚拟线程为什么不适合 CPU 密集型任务?在什么条件下会 pin 住底层平台线程?
  2. ReentrantLocksynchronized 在虚拟线程下的行为有什么区别?
  3. 如果数据库连接池只有 10 个连接,启动 10000 个虚拟线程同时查询会怎样?

参考资料

  1. [1] Oracle. The Java Language Specification (Java SE 21 Edition). 2023.
  2. [2] Joshua Bloch. Effective Java (3rd Edition). 2018.
  3. [3] Brian Goetz. Java Concurrency in Practice. 2006.
  4. [4] Zhou Zhiming. Understanding the JVM (3rd Edition). 2019.
  5. [5] Spring Team. Spring Boot Reference Documentation. 2024.
  6. [6] Oracle. Java 平台官方文档. 2024.
  7. [7] Joshua Bloch. Effective Java, 3rd Edition. 2018.
  8. [8] VMware / Broadcom. Spring Boot 官方参考文档. 2024.
  9. [9] Brian Goetz, Tim Peierls. Java Concurrency in Practice. 2006.