Modern Java cover image

目录

前言

像 Python、Golang、Rust、Zig、Kotlin 等一些编程语言拥有现代语法(尤其新兴编程语言), 它们去除繁琐和冗长,化繁为简,使用干净简洁直观优雅的语法来表达业务逻辑,灵活自如,拥有很强的表达表现能力。 它们符合直觉,符合人体工学,更加的语义化,朝着偏自然语言的方向演进,对人友好,易于阅读。 它们解决了一些传统语法受人诟病的地方。正因为它们拥有现代化的语法特性而被称为现代化的编程语言。

Java 被一些人认为是老语言(甚至极端的认为是过时了要被淘汰的语言),它的语法冗长繁琐而被人诟病, 让人喜欢不起来,一些人直接看不上,一些人转向了其它语言(如 Spring 之父 Rod Johnson 就对 Kotlin 大加赞赏, 相对于 Java 来说他更喜欢 Kotlin, 参见 Spring 之父:我不是 Java 的“黑粉”,但我也不想再碰它!这门语言拯救了我……), 比如 Golang、Kotlin 等。它的语法常常被用来和 Python 和 Golang 比,认为后者通过简单几句代码就 能表达的东西,而 Java 要写一大堆,非常的冗长和繁琐,不性感。

Java 在大家殷切的期盼下也逐渐加入了一些现代语法特性,旨在提高开发效率、代码可读性和程序性能, 虽然动作很慢,且得等呢。兼容性是一门工业级语言重要的要求, 为了兼容性,Java 不得不背上厚重的历史包袱,做不到像 Python3 那样壮士断腕。这时候,臃肿的 Java 就 混杂着过时传统语法和现代语法,如果没有最佳实践,就容易滥用,体会不到现代 Java,没有获得现代 Java 的精华。 在兼容性着方面,Golang 幸运得多,它工具链中的工具 gofix(go fix ./...)能安全稳定高效的批量更新代码库, 使得大多数的修改都能被快速应用更新,做到了与时俱进、常用常新,不用背上历史包袱, 参见 Google 的 Go:服务于软件工程的语言设计

备注

我使用的是 SDKMan 来管理 JDK 等 SDK 的安装升级卸载和多版本管理, 使用的 JDK 为 OpenJDK 24.0.1。

正文

Java 自从 Java 8 以来引入了大量现代语言特性,旨在提高开发效率、代码可读性和程序性能。 “现代”是一个相对概念,这里主要指 Java 8 及以后引入的,对编程范式有较大影响的特性。

1. Lambda 表达式 (Lambda Expressions) - Java 8/11

描述:

  • 允许将函数作为方法参数传递,或者将代码视为数据。简化了匿名内部类的写法,尤其在集合操作和事件处理中非常有用。
  • 允许在 Lambda 表达式的参数列表中使用 var,主要好处是可以向这些参数添加注解。(Java 11)

Demo:

import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import javax.annotation.Nonnull; // 假设有一个非空注解(这个注解不存在,代码不可运行,直接运行会报错,此处只为举例说明)

public class LambdaDemo {
    public static void main(String[] args) {
        var names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        // 旧的匿名内部类方式
        // names.sort(new Comparator<String>() {
        //     @Override
        //     public int compare(String a, String b) {
        //         return a.compareTo(b);
        //     }
        // });

        // Lambda 表达式方式
        names.sort((a, b) -> a.compareTo(b));
        // 或者更简洁的方法引用
        // names.sort(String::compareTo);

        names.forEach(name -> System.out.println(name));
        // 或者方法引用
        // names.forEach(System.out::println);

        // 使用 var 可以在参数上添加注解
        BiFunction<String, String, String> concat = (@Nonnull var s1, @Nonnull var s2) -> s1 + s2;
        System.out.println(concat.apply("Hello, ", "Java 11!"));
    }
}

2. Stream API - Java 8/16

描述:

  • 提供了一种声明式、函数式的方式来处理集合数据。支持串行和并行操作,可以进行过滤、映射、归约等复杂操作。
  • Java 16 增加了一个便捷的终端操作 Stream.toList() 方法,用于将流中的元素收集到一个不可修改的列表中。比 collect(Collectors.toList()) 更简洁。

Demo:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamApiDemo {
    public static void main(String[] args) {
        var numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // 筛选出偶数,将其平方,然后收集到一个新的 List 中
        var evenSquares = numbers.stream()     // 创建流
                .filter(n -> n % 2 == 0)       // 筛选偶数
                .map(n -> n * n)               // 计算平方
                .toList(); // 收集结果

        System.out.println("Original numbers: " + numbers);
        System.out.println("Squares of even numbers: " + evenSquares); // [4, 16, 36, 64, 100]

        // 计算所有数字的总和
        var sum = numbers.stream()
                .reduce(0, (a, b) -> a + b);
        // 或者更简洁
        // var sum = numbers.stream().mapToInt(Integer::intValue).sum();
        System.out.println("Sum of numbers: " + sum); // 55
    }
}

3. Optional 类 - Java 8

描述: 一个容器对象,可能包含也可能不包含非 null 值。用于优雅地处理可能为 null 的情况,避免 NullPointerException

Demo:

import java.util.Optional;

public class OptionalDemo {
    public static Optional<String> findUserById(String id) {
        if ("123".equals(id)) {
            return Optional.of("Alice");
        }
        return Optional.empty(); // 或者 Optional.ofNullable(null);
    }

    public static void main(String[] args) {
        Optional<String> userOpt = findUserById("123");

        // 安全地获取值
        var userName = userOpt.orElse("Unknown User");
        System.out.println("User (orElse): " + userName); // Alice

        userOpt.ifPresent(name -> System.out.println("User (ifPresent): " + name)); // User (ifPresent): Alice

        Optional<String> noUserOpt = findUserById("456");
        var anotherUser = noUserOpt.orElseGet(() -> "Default User From Supplier");
        System.out.println("User (orElseGet): " + anotherUser); // Default User From Supplier

        // 可能会抛出异常
        // var mustExistUser = noUserOpt.orElseThrow(() -> new RuntimeException("User not found!"));
    }
}

4. 接口的默认方法、静态方法和私有方法 - Java 8/9

描述:

  • 默认方法: 允许在接口中提供方法的默认实现,实现该接口的类无需强制实现这些方法,有助于接口的演进。
  • 静态方法: 允许在接口中定义静态方法,这些方法与接口本身相关联,而不是与实现类相关联。
  • 私有方法: 允许在接口中定义私有方法。这使得多个默认方法可以共享一段代码,而无需将该共享代码暴露给实现类。(Java 9)

Demo:

interface MyLogger {
    void log(String message); // 抽象方法

    // 默认方法
    default void logInfo(String message) {
        log("INFO: " + wrap(message));
    }


    // 静态方法
    static String getLoggerName() {
        return "MyCustomLogger";
    }

    // 私有方法,仅供接口内的默认方法使用
    private String wrap(String message) {
        return "[ " + message + " ]";
    }
}

class ConsoleLogger implements MyLogger {
    @Override
    public void log(String message) {
        System.out.println(message);
    }
}

class FileLogger implements MyLogger {
    @Override
    public void log(String message) {
        // 假设写入文件
        System.out.println("FILE_LOG: " + message);
    }

    // 可以选择覆盖默认方法
    @Override
    public void logInfo(String message) {
        log("FILE_INFO: " + message);
    }
}

public class InterfaceMethodsDemo {
    public static void main(String[] args) {
        System.out.println("Logger Name: " + MyLogger.getLoggerName());

        MyLogger console = new ConsoleLogger();
        console.log("An error occurred.");
        console.logInfo("System started."); // 使用默认方法

        MyLogger file = new FileLogger();
        file.log("Data saved.");
        file.logInfo("User logged in."); // 使用覆盖后的默认方法
    }
}

5. 局部变量类型推断 var (Local Variable Type Inference) - Java 10

描述: 允许在声明局部变量时使用 var 关键字,编译器会根据初始化表达式自动推断变量的类型。使代码更简洁。

Demo:

import java.util.ArrayList;
import java.util.HashMap;

public class VarDemo {
    public static void main(String[] args) {
        var message = "Hello, Java 10!"; // 推断为 String
        var count = 10;                  // 推断为 int
        var list = new ArrayList<String>(); // 推断为 ArrayList<String>
        var map = new HashMap<Integer, String>(); // 推断为 HashMap<Integer, String>

        list.add("Apple");
        list.add("Banana");

        System.out.println(message);
        System.out.println("Count: " + count);
        System.out.println("List: " + list);

        for (var item : list) { // 循环中也可以使用 var
            System.out.println(item.toUpperCase());
        }
        
        // var number; // 错误:var 必须在声明时初始化
        // var n = null; // 错误: 不能用 null 初始化 var
    }
}

6. Switch 表达式 (Switch Expressions) - Java 14 (标准版), Java 12/13 (预览)

描述: 扩展了 switch 语句,使其可以作为表达式返回值。支持 -> 箭头语法,减少了 break 的使用,并能保证穷尽性(配合 default 或覆盖所有情况)。

Demo:

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

    public static String getDayType(Day day) {
        // 旧的 switch 语句
        // var type;
        // switch (day) {
        //     case MONDAY:
        //     case TUESDAY:
        //     case WEDNESDAY:
        //     case THURSDAY:
        //     case FRIDAY:
        //         type = "Weekday";
        //         break;
        //     case SATURDAY:
        //     case SUNDAY:
        //         type = "Weekend";
        //         break;
        //     default:
        //         throw new IllegalArgumentException("Invalid day: " + day);
        // }
        // return type;

        // 新的 switch 表达式
        var type = switch (day) {
            case MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -> "Weekday";
            case SATURDAY, SUNDAY -> "Weekend";
            // default -> throw new IllegalArgumentException("Invalid day: " + day); // 如果Day枚举不完整,需要default
        }; // 注意这里有分号,因为是表达式赋值
        return type;
    }
    
    public static int getValue(String mode) {
        return switch (mode) {
            case "a", "b" -> 1;
            case "c" -> {
                System.out.println("Processing c...");
                yield 2; // yield 用于在多行代码块中返回值
            }
            default -> 0;
        };
    }

    public static void main(String[] args) {
        System.out.println("MONDAY is a " + getDayType(Day.MONDAY));   // Weekday
        System.out.println("SATURDAY is a " + getDayType(Day.SATURDAY)); // Weekend
        System.out.println("Value for 'c': " + getValue("c")); // Processing c... \n Value for 'c': 2
        System.out.println("Value for 'x': " + getValue("x")); // Value for 'x': 0
    }
}

7. 文本块 (Text Blocks) - Java 15 (标准版), Java 13/14 (预览)

描述: 简化了多行字符串字面量的创建,无需大量的转义字符和拼接操作。

Demo:

public class TextBlockDemo {
    public static void main(String[] args) {
        // 旧的方式
        var htmlOld = "<html>\n" +
                      "    <body>\n" +
                      "        <p>Hello, World</p>\n" +
                      "    </body>\n" +
                      "</html>";
        System.out.println("--- Old HTML ---");
        System.out.println(htmlOld);

        // 使用文本块
        var htmlNew = """
                      <html>
                          <body>
                              <p>Hello, World</p>
                          </body>
                      </html>
                      """; // 内容的缩进相对于结束的三个引号
        System.out.println("--- New HTML (Text Block) ---");
        System.out.println(htmlNew);

        var query = """
                    SELECT "EMP_ID", "LAST_NAME" FROM "EMPLOYEE_TB"
                    WHERE "CITY" = 'INDIANAPOLIS'
                    ORDER BY "EMP_ID", "LAST_NAME";
                    """;
        System.out.println("--- SQL Query ---");
        System.out.println(query);
    }
}

8. Record 类 (Records) - Java 16 (标准版), Java 14/15 (预览)

描述: 一种特殊的类,用于创建不可变的数据载体对象。编译器会自动生成构造函数、equals()hashCode()toString() 以及 getter 方法 (名为组件名,如 x() 而非 getX())。

Demo:

// 定义一个 Record
record Point(int x, int y) {} // 自动生成构造器、getter、equals、hashCode、toString

record User(String username, String email) {
    // 可以添加静态方法
    public static User createGuest() {
        return new User("guest", "guest@example.com");
    }

    // 可以添加实例方法
    public String greeting() {
        return "Hello, " + username;
    }

    // 可以添加紧凑构造函数进行参数校验或规范化
    public User {
        if (username == null || username.isBlank()) {
            throw new IllegalArgumentException("Username cannot be blank");
        }
        // username = username.trim(); // 规范化
    }
}

public class RecordDemo {
    public static void main(String[] args) {
        var p1 = new Point(10, 20);
        var p2 = new Point(10, 20);
        var p3 = new Point(30, 40);

        System.out.println("p1: " + p1); // Point[x=10, y=20]
        System.out.println("p1.x(): " + p1.x()); // 10 (注意 getter 方法名)
        System.out.println("p1.y(): " + p1.y()); // 20

        System.out.println("p1 equals p2: " + p1.equals(p2)); // true
        System.out.println("p1 equals p3: " + p1.equals(p3)); // false
        System.out.println("p1 hashCode: " + p1.hashCode());

        var user = new User("john.doe", "john.doe@example.com");
        System.out.println(user); // User[username=john.doe, email=john.doe@example.com]
        System.out.println(user.greeting());
        System.out.println(User.createGuest());
        
        // try {
        //     var blankUser = new User("", "test@test.com");
        // } catch (IllegalArgumentException e) {
        //     System.err.println(e.getMessage());
        // }
    }
}

9. instanceof 的模式匹配 (Pattern Matching for instanceof) - Java 16 (标准版), Java 14/15 (预览)

描述: 简化了 instanceof 操作和类型转换。如果在 instanceof 判断为 true 后,可以直接在同一个表达式中声明一个绑定变量,该变量是已转换类型,无需显式转换。

Demo:

public class PatternMatchingInstanceofDemo {
    public static void process(Object obj) {
        // 旧的方式
        // if (obj instanceof String) {
        //     String s = (String) obj;
        //     System.out.println("String length: " + s.length());
        // } else if (obj instanceof Integer) {
        //     Integer i = (Integer) obj;
        //     System.out.println("Integer value * 2: " + (i * 2));
        // }

        // 使用模式匹配
        if (obj instanceof String s) { // s 在此作用域内已是 String 类型
            System.out.println("String length: " + s.length());
        } else if (obj instanceof Integer i && i > 10) { // 可以在模式后添加条件
             System.out.println("Integer value > 10 and doubled: " + (i * 2));
        } else if (obj instanceof Double d) {
            System.out.println("Double value: " + d);
        } else {
            System.out.println("Unknown type");
        }
    }

    public static void main(String[] args) {
        process("Hello Java"); // String length: 11
        process(20);           // Integer value > 10 and doubled: 40
        process(5);            // Unknown type (falls through Integer condition)
        process(3.14);         // Double value: 3.14
        process(new Object()); // Unknown type
    }
}

10. 密封类和接口 (Sealed Classes and Interfaces) - Java 17 (标准版), Java 15/16 (预览)

描述: 允许类或接口的作者控制哪些其他类或接口可以扩展或实现它们。提供了更细粒度的继承控制。

Demo:

// 定义一个密封接口 Shape,只允许 Circle 和 Rectangle 实现它
sealed interface Shape permits Circle, Rectangle { // Square added for completeness
    double area();
}

// Circle 必须是 final, sealed, 或者 non-sealed
final class Circle implements Shape { // final: 不允许再被继承
    private final double radius;

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

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

    public double getRadius() { 
        return radius; 
    }
}

sealed class Rectangle implements Shape permits TransparentRectangle, Square { // sealed: 它的子类也需要声明
    protected final double length, width;

    public Rectangle(double length, double width) {
        this.length = length;
        this.width = width;
    }

    @Override 
    public double area() { 
        return length * width; 
    }

    public double getLength() { 
        return length; 
    }

    public double getWidth() { 
        return width; 
    }
}

non-sealed class Square extends Rectangle { // non-sealed: 允许任何类继承它
    public Square(double side) {
        super(side, side);
    }

    public double getSide() { 
        return length;
    }
}

final class TransparentRectangle extends Rectangle { // final
    private final int transparency;

    public TransparentRectangle(double length, double width, int transparency) {
        super(length, width);
        this.transparency = transparency;
    }

    public int getTransparency() { 
        return transparency; 
    }
}

// 如果 Shape 和其子类在同一个 .java 文件中,可以省略 permits 子句,编译器会自动推断。
// 如果不在同一个文件,则必须使用 permits。

public class SealedClassesDemo {
    public static void describeShape(Shape shape) {
        System.out.print("This shape is a ");
        if (shape instanceof Circle c) {
            System.out.println("Circle with radius " + c.getRadius() + " and area " + c.area());
        } else if (shape instanceof Square s) {
            System.out.println("Square with side " + s.getSide() + " and area " + s.area());
        } else if (shape instanceof Rectangle r) { // Must come after Square due to inheritance
            System.out.println("Rectangle with length " + r.getLength() + ", width " + r.getWidth() + " and area " + r.area());
        } else {
            System.out.println("Unknown shape with area " + shape.area());
        }
        // 在 Java 21 中,这里可以用模式匹配 switch 来完美处理
    }

    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape rectangle = new Rectangle(4, 6);
        Shape square = new Square(3);

        describeShape(circle);
        describeShape(rectangle);
        describeShape(square);
    }
}

11. switch 的模式匹配 (Pattern Matching for switch) - Java 21 (标准版), Java 17-20 (预览)

描述: 将模式匹配扩展到 switch 语句和表达式中,允许根据对象的类型和结构进行分支。

Demo:

public class SwitchPatternMatchingDemo {

    sealed interface Shape permits Circle, Rectangle {}

    final record Circle(double radius) implements Shape {}

    sealed static class Rectangle implements Shape permits TransparentRectangle, Square {
        final double length, width;

        Rectangle(double length, double width) { 
            this.length = length; 
            this.width = width; 
        }
    }

    non-sealed static class Square extends Rectangle {
        Square(double side) { 
            super(side, side); 
        }
    }

    final static class TransparentRectangle extends Rectangle {
        TransparentRectangle(double l, double w) { 
            super(l, w); 
        }
    }


    static String processShape(Shape shape) {
        return switch (shape) {
            case null -> "Shape is null"; // 处理 null case
            case Circle c -> String.format("Circle with radius %.2f", c.radius());
            case Square s -> String.format("Square with side %.2f", s.length); // 'length' from Rectangle
            case Rectangle r when r.length == r.width -> String.format("Square-like Rectangle %.2f x %.2f", r.length, r.width); // 带守卫条件
            case Rectangle r -> String.format("Rectangle with dimensions %.2f x %.2f", r.length, r.width);
            // default -> "Unknown shape"; // 如果 Shape 不是 sealed,或者没有覆盖所有 permits 的情况,则需要 default
            // 对于 sealed interface,如果所有 permits 的类型都被 case 覆盖了,就不需要 default
        };
    }

    public static void main(String[] args) {
        System.out.println(processShape(new Circle(5.0)));
        System.out.println(processShape(new Square(4.0)));
        System.out.println(processShape(new Rectangle(3.0, 7.0)));
        System.out.println(processShape(new Rectangle(6.0, 6.0))); // "Square-like Rectangle"
        System.out.println(processShape(null)); // "Shape is null"
    }
}

12. Record 模式 (Record Patterns) - Java 21 (标准版), Java 19/20 (预览)

描述: 允许在模式匹配中解构 Record 实例,直接访问其组件。可以与 instanceofswitch 的模式匹配结合使用。

Demo:

public class RecordPatternsDemo {

    record Point(int x, int y) {}
    record ColoredPoint(Point p, String color) {}
    record Pair<T>(T first, T second) {}

    public static void printPointInfo(Object obj) {
        // instanceof 与 Record 模式
        if (obj instanceof Point(int x, int y)) {
            System.out.println("Point coordinates: x = " + x + ", y = " + y);
        } else if (obj instanceof ColoredPoint(Point(var x, var y), var color)) {
            System.out.println("ColoredPoint: x = " + x + ", y = " + y + ", color = " + color);
        } else if (obj instanceof Pair(var s1, var s2)) { // 泛型 Record 模式
            System.out.println("String Pair: (" + s1 + ", " + s2 + ")");
        } else {
            System.out.println("Not a recognized record pattern.");
        }
    }

    public static String describeObjectWithSwitch(Object obj) {
        return switch (obj) {
            case Point(int x, int y) -> String.format("A point at (%d, %d)", x, y);
            // 嵌套 Record 模式
            case ColoredPoint(Point(int x, int y), String color) ->
                String.format("A %s point at (%d, %d)", color, x, y);
            // var 关键字可以在 Record 模式中使用,以捕获整个组件
            case ColoredPoint(var p, var color) -> // p 是 Point, color 是 String
                String.format("A %s point %s", color, p.toString());
            case Pair(Integer i1, Integer i2) -> String.format("Integer Pair: (%d, %d)", i1, i2);
            case null -> "It's null!";
            default -> "Something else: " + obj.toString();
        };
    }

    public static void main(String[] args) {
        var p = new Point(10, 20);
        var cp = new ColoredPoint(p, "RED");
        var sp = new Pair<>("Hello", "World");
        var ip = new Pair<>(100, 200);

        printPointInfo(p);      // Point coordinates: x = 10, y = 20
        printPointInfo(cp);     // ColoredPoint: x = 10, y = 20, color = RED
        printPointInfo(sp);     // String Pair: (Hello, World)
        printPointInfo("Test"); // Not a recognized record pattern.

        System.out.println(describeObjectWithSwitch(p));
        System.out.println(describeObjectWithSwitch(cp));
        System.out.println(describeObjectWithSwitch(ip));
        System.out.println(describeObjectWithSwitch(null));
    }
}

13. 虚拟线程 (Virtual Threads) - Java 21 (标准版), Java 19/20 (预览) (Project Loom)

描述: 轻量级线程,由 JVM 管理,而不是操作系统。可以创建数百万个虚拟线程而不会耗尽系统资源,极大简化了高并发应用的编写,允许使用传统的阻塞式 I/O 编程模型来获得异步编程的性能优势。

Demo:

import java.util.concurrent.Executors;
import java.util.stream.IntStream;

public class VirtualThreadsDemo {
    public static void main(String[] args) throws InterruptedException {
        var startTime = System.currentTimeMillis();

        // 方法1: 直接创建并启动
        // Thread.startVirtualThread(() -> {
        //     System.out.println("Hello from virtual thread 1: " + Thread.currentThread());
        //     try {
        //         Thread.sleep(1000); // 模拟 I/O 阻塞操作
        //     } catch (InterruptedException e) {
        //         e.printStackTrace();
        //     }
        // });

        // 方法2: 使用 Thread.Builder
        // var virtualThread2 = Thread.ofVirtual().name("MyVirtualThread-2").unstarted(() -> {
        //     System.out.println("Hello from virtual thread 2: " + Thread.currentThread());
        //     try {
        //         Thread.sleep(500);
        //     } catch (InterruptedException e) {
        //         e.printStackTrace();
        //     }
        // });
        // virtualThread2.start();

        // 方法3: 使用 ExecutorService (推荐用于管理大量任务)
        // try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        //     IntStream.range(0, 10_000).forEach(i -> { // 创建 1 万个任务
        //         executor.submit(() -> {
        //             System.out.println("Task " + i + " running on: " + Thread.currentThread());
        //             try {
        //                 Thread.sleep(100); // 模拟耗时操作
        //             } catch (InterruptedException e) {
        //                 // Thread.currentThread().interrupt();
        //             }
        //         });
        //     });
        // } // executor.close() 会等待所有任务完成

        // 简单的批量创建示例
        final var NUM_THREADS = 100_000; // 尝试创建 10 万个虚拟线程
        var threads = new Thread[NUM_THREADS];

        for (var i = 0; i < NUM_THREADS; i++) {
            final var taskId = i;
            threads[i] = Thread.startVirtualThread(() -> {
                // System.out.println("Task " + taskId + " started on " + Thread.currentThread());
                try {
                    Thread.sleep(10); // 模拟 I/O 密集型任务
                } catch (InterruptedException e) {
                    // e.printStackTrace();
                }
                if (taskId % 10000 == 0) { // 每 1 万个打印一次,避免过多输出
                     System.out.println("Task " + taskId + " finished on " + Thread.currentThread());
                }
            });
        }

        // 等待所有虚拟线程完成 (仅为演示,实际中 ExecutorService 更好)
        for (var i = 0; i < NUM_THREADS; i++) {
            threads[i].join();
        }
        
        var endTime = System.currentTimeMillis();
        System.out.println(NUM_THREADS + " virtual threads processed in " + (endTime - startTime) + " ms.");
        // 如果用平台线程,创建 10 万个通常会导致 OutOfMemoryError: unable to create native thread
    }
}

14. 序列化集合 (Sequenced Collections) - Java 21

描述: 引入了一系列新的接口 (SequencedCollection, SequencedSet, SequencedMap),为那些元素具有确定出现顺序的集合提供统一的、易于访问的 API,例如获取第一个/最后一个元素,或者反向视图。

Demo:

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.SequencedCollection;
import java.util.SequencedSet;
import java.util.LinkedHashMap;
import java.util.SequencedMap;

public class SequencedCollectionsDemo {
    public static void main(String[] args) {
        // SequencedCollection (List 是一个例子)
        SequencedCollection<String> list = new ArrayList<>();
        list.add("Apple");
        list.add("Banana");
        list.add("Cherry");
        System.out.println("Original List: " + list); // [Apple, Banana, Cherry]

        System.out.println("First element: " + list.getFirst()); // Apple
        System.out.println("Last element: " + list.getLast());   // Cherry

        list.addFirst("Orange"); // 在开头添加
        list.addLast("Mango");   // 在末尾添加
        System.out.println("Modified List: " + list); // [Orange, Apple, Banana, Cherry, Mango]

        System.out.println("Removed first: " + list.removeFirst()); // Orange
        System.out.println("Removed last: " + list.removeLast());   // Mango
        System.out.println("List after removals: " + list); // [Apple, Banana, Cherry]

        SequencedCollection<String> reversedList = list.reversed();
        System.out.println("Reversed List View: " + reversedList); // [Cherry, Banana, Apple]
        // reversedList.add("Grape"); // 修改反向视图会影响原始列表
        // System.out.println("Original list after modifying reversed: " + list);

        System.out.println("\n--- SequencedSet ---");
        // SequencedSet (LinkedHashSet 是一个例子)
        SequencedSet<Integer> set = new LinkedHashSet<>();
        set.add(10);
        set.add(20);
        set.add(5);
        System.out.println("Original Set: " + set); // [10, 20, 5] (按插入顺序)

        System.out.println("First element: " + set.getFirst()); // 10
        System.out.println("Last element: " + set.getLast());   // 5
        
        SequencedSet<Integer> reversedSet = set.reversed();
        System.out.println("Reversed Set View: " + reversedSet); // [5, 20, 10]

        System.out.println("\n--- SequencedMap ---");
        // SequencedMap (LinkedHashMap 是一个例子)
        SequencedMap<String, Integer> map = new LinkedHashMap<>();
        map.put("One", 1);
        map.put("Two", 2);
        map.put("Three", 3);
        System.out.println("Original Map: " + map); // {One=1, Two=2, Three=3}

        System.out.println("First entry: " + map.firstEntry()); // One=1
        System.out.println("Last entry: " + map.lastEntry());   // Three=3

        map.putFirst("Zero", 0);
        map.putLast("Four", 4);
        System.out.println("Modified Map: " + map); // {Zero=0, One=1, Two=2, Three=3, Four=4}

        SequencedMap<String, Integer> reversedMap = map.reversed();
        System.out.println("Reversed Map View: " + reversedMap); // {Four=4, Three=3, Two=2, One=1, Zero=0}
    }
}

15. 未命名模式和变量 (Unnamed Patterns and Variables) - Java 21 (预览特性)

描述: 允许使用下划线 _ 作为未命名模式或变量的占位符。当一个变量或模式组件的值在代码中不需要使用时,这可以提高代码可读性并减少冗余。

Demo:

// 注意:这是一个预览特性,编译和运行时需要特殊标志
// 编译: javac --release 21 --enable-preview UnnamedPatternsDemo.java
// 运行: java --enable-preview UnnamedPatternsDemo
import java.util.List;

public class UnnamedPatternsDemo {

    record Point(int x, int y) {}
    enum Color { RED, GREEN, BLUE }
    record ColoredPoint(Point p, Color c) {}

    public static void main(String[] args) {
        // 1. 未命名变量 (局部变量)
        int result = calculate();
        // 以前如果 calculate() 返回多个值(例如通过数组或对象),但只关心部分
        // int[] results = complexCalculation();
        // int importantValue = results[0]; // 其他值被忽略但仍需声明
        
        // 现在,如果 calculate() 有副作用但其结果不重要(虽然这里用 int 举例不太典型)
        // _ = calculate(); // 如果 calculate() 返回值不被使用,可以这样 (更常见于try-with-resources)
        
        // 在循环中忽略索引或元素
        List<String> items = List.of("a", "b", "c");
        int count = 0;
        for (String _ : items) { // 元素本身不重要,只关心计数
            count++;
        }
        System.out.println("Counted " + count + " items.");

        // 2. 未命名模式 (在 instanceof 和 switch 中)
        Object obj = new ColoredPoint(new Point(10, 20), Color.RED);

        if (obj instanceof ColoredPoint(Point(int x, _), _)) { // 只关心 x 坐标,忽略 y 坐标和颜色
            System.out.println("Instanceof: Point x-coordinate is " + x);
        }
        
        // 3. switch 中的未命名模式
        switch (obj) {
            case ColoredPoint(Point(int x, int y), _) -> // 只关心坐标
                System.out.println("Switch: A point at (" + x + ", " + y + ")");
            case Point(_, int y) -> // 只关心 y 坐标
                System.out.println("Switch: Point y-coordinate is " +  y);
            case String _ -> // 匹配任何 String,但其值不使用
                 System.out.println("Switch: It's a String, but I don't care about its value.");
            default -> System.out.println("Switch: Other object");
        }

        // 4. try-with-resources (虽然不是模式,但 _ 可以用作未命名变量)
        try (var _ = new AutoCloseableResource("Resource1"); // _ 表示这个资源变量不直接使用
            var resource2 = new AutoCloseableResource("Resource2")) {
            System.out.println("Inside try-with-resources. " + resource2.getName() + " is used.");
            // resource1 的 close() 仍会被调用
        } catch (Exception e) {
            System.err.println("Exception: " + e.getMessage());
        }
    }

    static int calculate() { return 42; }
    
    static class AutoCloseableResource implements AutoCloseable {
        private String name;

        public AutoCloseableResource(String name) {
            this.name = name;
            System.out.println(name + " opened.");
        }

        public String getName() { return name; }
        
        @Override
        public void close() throws Exception {
            System.out.println(name + " closed.");
        }
    }
}

:exclamation: 注意:在当前的版本中,未命名模式在 switch 的模式匹配中支持有限。

JDK 24 中的 Switch Pattern Matching 预览(JEP 441)目前只支持“顶层”模式, 而 嵌套的 Record PatternsColoredPoint(Point(int x, _), Color.RED))在默认的预览集里还没有启用

  • 当你写

    case Point(int x, _) ->   
    

    这是一个顶层的 Record Pattern,JDK 24 已经在 switch 里支持,所以能正常编译运行。

  • 但像

    case ColoredPoint(Point(int x, _), Color.RED) ->   
    

    case ColoredPoint(_, Color.BLUE) ->  
    

    这里是在 switch 里做两层解构,也就是“嵌套 Record Pattern”。虽然 Record Patterns 自身(JEP 440)是可以嵌套的, 但它们在 switch 语句里的整合部分还在 incubator 模块里,并未加入到默认的预览特性里,所以编译器看不到这一语法,就会报 <identifier> expected 错误。

即将上述 15 的代码中的 switch 部分换成:

switch (obj) {
    case ColoredPoint(Point(int x, _), Color.RED) -> // 只关心 x 和颜色是 RED
        System.out.println("Switch: RED point with x=" + x);
    case ColoredPoint(_, Color.BLUE) -> // 只关心颜色是 BLUE
        System.out.println("Switch: A BLUE point");
    case Point(int x, int y) -> // 经典 Record Pattern
        System.out.println("Switch: A point at ("+ x + ", " + y + ")");
    case String _ -> // 匹配任何 String,但其值不使用
            System.out.println("Switch: It's a String, but I don't care about its value.");
    default -> System.out.println("Switch: Other object");
}

就会编译报错:

UnnamedPatternsDemo.java:29: error: <identifier> expected
            case ColoredPoint(Point(int x, int y), Color.RED) -> // 只关心 x 和颜色是 RED
                                                            ^
UnnamedPatternsDemo.java:31: error: <identifier> expected
            case ColoredPoint(_, Color.BLUE) -> // 只关心颜色是 BLUE
                                           ^
2 errors

16. 新的日期时间 API (New Date and Time API) - Java 8

描述: java.time 包提供了一套全新的、不可变的、线程安全的 API 来处理日期和时间,完全取代了老旧且问题多多的 java.util.Datejava.util.Calendar

Demo:

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class DateTimeApiDemo {
    public static void main(String[] args) {
        // 创建日期
        var today = LocalDate.now();
        var birthday = LocalDate.of(2025, Month.DECEMBER, 25);
        System.out.println("Today is: " + today);
        System.out.println("Christmas 2025 is on: " + birthday);

        // 日期计算
        var nextWeek = today.plus(1, ChronoUnit.WEEKS);
        System.out.println("Date next week: " + nextWeek);

        // 格式化
        var now = LocalDateTime.now();
        var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        var formattedNow = now.format(formatter);
        System.out.println("Formatted current time: " + formattedNow);
    }
}

17. 模块系统 (Module System - Project Jigsaw) - Java 9

描述: 引入了模块化编程的概念 (module-info.java),提供了更强的封装性和更可靠的依赖管理,替代了脆弱的 classpath 机制。

Demo: 这是一个结构性特性,无法在单个文件中演示。以下是文件结构和内容的示例:

order-system/
├── pom.xml                   ( POM 管理依赖和插件)
├── order-api/
   ├── pom.xml
   └── src/main/java/
       ├── com/mycompany/order/api/     ()
       └── module-info.java           (定义模块 com.mycompany.order.api)
├── order-core/
   ├── pom.xml
   └── src/main/java/
       ├── com/mycompany/order/core/  ()
       └── module-info.java           (定义模块 com.mycompany.order.core)
└── order-persistence/
    ├── pom.xml
    └── src/main/java/
        ├── com/mycompany/order/persistence/ ()
        └── module-info.java                 (定义模块 com.mycompany.order.persistence)

order-api/module-info.java:

// 这个模块定义了所有的数据传输对象(DTO)和服务接口
module com.mycompany.order.api {
    // 它只导出 API 包,没有内部实现
    exports com.mycompany.order.api;
}

order-core/module-info.java:

// 这是核心业务逻辑的实现
module com.mycompany.order.core {
    // 需要 API 模块来获取接口和 DTO 定义
    requires com.mycompany.order.api;
    // 可能还需要持久化模块来保存数据
    requires com.mycompany.order.persistence;

    // 它不导出任何包,因为所有实现都是内部的。
    // 或者,如果它提供了一个服务工厂,它可能会导出那个包。
}

order-persistence/module-info.java:

// 负责数据库交互
module com.mycompany.order.persistence {
    // 需要 API 模块,因为持久化层操作的是 API 定义的业务对象
    requires com.mycompany.order.api;
    // 需要 JDBC 模块
    requires java.sql;

    // 导出持久化层的接口,供核心层使用
    exports com.mycompany.order.persistence;
}

JPMS 最佳实践和官方推荐是:每个项目/构建单元创建一个模块。

具体来说,这意味着:

  • 一个 Maven Module (一个 pom.xml 文件) 对应一个 Java 模块 (一个 module-info.java 文件)。
  • 一个 Gradle Module 对应一个 Java 模块。
  • 一个独立的 JAR 包,就应该是一个 Java 模块。

绝对不是为每个 Java 包(Package)创建一个模块。这样做会造成模块数量爆炸,依赖关系变得极其复杂,完全违背了模块化旨在简化依赖、增强封装的初衷。

18. 集合工厂方法 (Collection Factory Methods) - Java 9

描述: 提供了如 List.of(), Set.of(), Map.of() 等静态工厂方法,可以方便地创建少量元素的、不可变的集合。

Demo:

import java.util.List;
import java.util.Map;
import java.util.Set;

public class CollectionFactoryDemo {
    public static void main(String[] args) {
        // 创建不可变列表
        var fruits = List.of("Apple", "Banana", "Cherry");
        System.out.println(fruits);

        // 创建不可变集合
        var numbers = Set.of(1, 2, 3);
        System.out.println(numbers);

        // 创建不可变映射
        var scores = Map.of("Alice", 95, "Bob", 88);
        System.out.println(scores);

        try {
            fruits.add("Orange"); // 会抛出 UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.err.println("Cannot modify the list: " + e.getMessage());
        }
    }
}

19. JShell:交互式 Java (REPL) - Java 9

描述: JShell 是一个交互式的“读取-求值-打印循环”(REPL) 工具,允许开发者在不创建完整程序的情况下快速试验 Java 代码片段。这是一个命令行工具,而非语言特性。

Demo:

这是一个 JShell 会话的模拟:

$ jshell                         
|  Welcome to JShell -- Version 24.0.1
|  For an introduction type: /help intro

jshell> String message = "Hello, JShell!"
message ==> "Hello, JShell!"

jshell> System.out.println(message)
Hello, JShell!

jshell> int add(int a, int b) {
   ...>     return a + b;
   ...> }
|  created method add(int,int)

jshell> add(10, 20)
$4 ==> 30

jshell> /exit
|  Goodbye

20. 标准化的 HTTP 客户端 (Standardized HTTP Client) - Java 11

描述: java.net.http 包提供了一个现代、流畅、功能丰富的 API 来处理 HTTP 请求,支持 HTTP/1.1, HTTP/2, WebSocket,并提供同步和异步两种模式。

Demo:

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.concurrent.CompletableFuture;

public class HttpClientDemo {
    public static void main(String[] args) throws Exception {
        var client = HttpClient.newHttpClient();

        // 同步请求
        var request = HttpRequest.newBuilder()
                .uri(URI.create("https://jsonplaceholder.typicode.com/todos/1"))
                .build();
        var response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println("Sync Response Body: " + response.body());

        // 异步请求
        CompletableFuture<HttpResponse<String>> asyncResponse = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
        asyncResponse.thenApply(HttpResponse::body)
                     .thenAccept(body -> System.out.println("Async Response Body: " + body))
                     .join(); // 等待异步操作完成
    }
}

21. 字符串增强方法 (String Enhancements) - Java 11

描述: 为 String 类添加了一系列实用方法,如 isBlank(), lines(), strip(), repeat() 等。

Demo:

public class StringEnhancementsDemo {
    public static void main(String[] args) {
        var multiline = "JEP 323\nJEP 321\n ";
        multiline.lines().forEach(line -> System.out.println("Line: '" + line + "'"));

        var spaced = "  Hello  ";
        System.out.println("Original: '" + spaced + "'");
        System.out.println("isBlank: " + " \t \n".isBlank()); // true
        System.out.println("strip: '" + spaced.strip() + "'"); // "Hello"
        System.out.println("repeat: " + "Abc".repeat(3)); // "AbcAbcAbc"
    }
}

22. UTF-8 默认字符集 (Default UTF-8 Charset) - Java 18

描述: Java 18 将 UTF-8 设置为所有标准 Java API 的默认字符集。这使得在不同操作系统上运行的 Java 应用程序行为更加一致,减少了因字符编码问题导致的错误。

Demo: 这是一个概念性的改变,代码演示其影响。

import java.io.FileReader;
import java.io.FileWriter;
import java.nio.charset.Charset;

public class DefaultCharsetDemo {
    public static void main(String[] args) throws java.io.IOException {
        // 在 Java 18+ 中,这默认为 UTF-8
        System.out.println("Default Charset: " + Charset.defaultCharset());

        // 以下代码在 Java 18 之前行为依赖于操作系统
        // 在 Java 18+ 中,它们将可靠地使用 UTF-8 读写文件(除非被覆盖)
        try (FileWriter writer = new FileWriter("test.txt")) {
            writer.write("你好,世界!");
        }

        try (FileReader reader = new FileReader("test.txt")) {
            int c;
            while ((c = reader.read()) != -1) {
                System.out.print((char)c);
            }
        }
    }
}

23. 简单 Web 服务器 (Simple Web Server) - Java 18

描述: 提供了一个开箱即用的、最小化的静态文件 Web 服务器。可以通过命令行工具 jwebserver 启动,或通过 SimpleFileServer API 以编程方式启动,非常适合用于测试和开发。

Demo:

  • 方法一:命令行 (在终端中运行)
# 在你的项目目录下创建一个名为 index.html 的文件
# 然后运行以下命令,即可在 http://localhost:8000 访问
$ jwebserver
  • 方法二:编程方式
import com.sun.net.httpserver.SimpleFileServer;
import com.sun.net.httpserver.HttpServer;
import java.net.InetSocketAddress;
import java.nio.file.Path;

public class SimpleWebServerDemo {
    public static void main(String[] args) {
        // 创建一个在 8080 端口,服务当前目录文件的服务器
        var server = SimpleFileServer.createFileServer(
                new InetSocketAddress(8080),
                Path.of("/Users/myqs/Downloads/project-demo"), // 服务当前目录,此处必须为绝对路径
                SimpleFileServer.OutputLevel.VERBOSE
        );
        System.out.println("Starting server on port 8080... Press Ctrl+C to stop.");
        server.start();
    }
}

24. String.formatted() - Java 15

描述: String.format() 的语法糖。

Demo:

public class StringFormattedDemo {
    public static void main(String[] args) { 
        var name = "Alice";
        var age = 30;
        var info = "User: %s, Age: %d".formatted(name, age);
        System.out.println(info);
    }
}

这里是自己平时的一些胡思乱想、瞎琢磨和对一些其它语言的学习了解而逐渐形成的一些自己的心得感悟, 一些自己觉得的最佳实践(姑妄言之),主要是对其它一些现代编程语言的借鉴和类似语法模仿(尤其是 Golang 和 Kotlin), 以形成 干净、简洁、简单、直观、快速、现代 的编程风格:

我的最佳实践(瞎建议)

  • 使用模块系统 JPMS

  • 尽量使用局部变量,慎重使用全局变量。

  • 尽可能使用局部变量类型推断 var。(参考 Golang、Kotlin 等现代编程语言的默认做法)

  • 封装数据的 Java Bean,各种 O(DO、DTO、VO) 、Entity 等应尽可能使用 record。

  • 尽量使用不可变数据结构,尽量创建不可变类。

  • 代码主要是用来给人读的。在写上层的业务代码时,尽可能的使命名和现实世界的事物一致,贴合领域。尽可能的使语句表达贴合自然语言。

  • 尽量使用数据替换而不是修改。(参考 Reactjs 的状态管理)

  • 使用虚拟线程,如果结构化并发作正式版发布了就使用结构化并发。

  • 使用谷歌开源的代码格式化工具 google-java-format 来格式化代码,这个工具是对 fmt 的借鉴。(受 Golang 工具链中的 FMT 影响。)

  • Java 的包名太冗长繁琐了(verbose),在保证唯一性的情况下尽量简洁,包前缀的 com./org. 等这些 东西应该去掉。项目名+包名 就能满足要求,具体表现为 project.layer、 project.feature、 project.layer.feature, layer 为分层架构 web、business、dao、util 等这些东西。内部项目这样做完全没问题,外部项目遇到这种命名冲突也极少, 可事先评估遇到同名项目而产生命名空间冲突的情况,再决定是否使用传统包命名规范,遇到了再改也不迟,借助工具改也不费劲。(借鉴 Golang、Kotlin)

  • Java 的检查异常争议不断,很多语言,尤其是新诞生的现代编程语言都不添加检查异常了,应该借鉴 C# 等面向对象语言, 只使用非检查异常 RuntimeException,不使用检查异常。(参考 C#)

  • 面向对象中的继承机制也广受争议,有很复杂麻烦的继承链问题,,复杂度高,心智负担重, 过度使用继承可能导致“类爆炸”和高耦合,应该优先使用组合。(借鉴 Golang)

  • 类负责封装,接口负责多态。(借鉴 Golang)

  • 架构是对问题做正交分解,分解成尽可能小的独立的块,然后任意两块就能相互组合形成功能,这样就能形成乘法效应, 用最少的代码实现最多的功能,同时又是灵活的。

  • 先设计类,如果有需要再从各类中挑选方法放到接口里,即自底向上的自然发展,自然长起来,而不是自顶向下的顶层设计,业务是按需长起来的,而不是事先冒着 极高的心智负担去猜测去做复杂的过度的设计。涌现式设计。(受 Golang 编程哲学、Robpike、许式伟影响)

  • 实现一个功能前,先去网上搜搜,看一下业界的最佳实践。使用最佳实践,使用业界(全球)主流的、流行的产品、工具、方法。(受陈皓(左耳朵耗子)影响)

  • 写代码前,先用自然语言或伪代码写一遍,再正式写代码。

  • 开发项目写代码,借鉴建筑行业盖楼的方法,先用钢筋和水泥搭一个框架,再往这个框架里续砖。 搭的框架的方法里先抛出异常(如 UnsupportedOperationException),写功能代码填充框架时再删掉异常, 以便运行测试时知道哪里还没做,避免遗忘、遗漏。

  • 在写代码前,在规划设计时,写列一个要实现的功能列表清单,每一项是要实现的一个功能一个方法,并且按重要性、核心度 从上到下排列,以便写代码时抓住重点,先写先验证核心部分。清单上下分成两大部分,用横线聪总分隔,前部分是 必须要实现的基础核心功能,后部分是不是那么重要、紧迫、非核心、可选的功能。实现一个功能就在前面打一个✅, 相当于是 task list。

  • TDD(测试驱动开发)。搭好项目结构化框架后,先在测试类里写好功能和非功能的测试代码,再跑到正式类 里实现业务逻辑,然后再单元测试这个方法,弄完后再以同样的方式写其它的方法,循环往复。并不是一开始就 先写好全部测试方法代码再去写业务代码。

  • 需求、开发时,搞清除 X-Y 问题。(陈皓观点)

  • 除非重构(重构是因之前没设计好,再重新设计一下,或者是由于技术进步,把原有的功能以更好的方式重新实现一遍), 否则不要改代码,加功能通过加模块来实现。

  • 控制 + 算法 开发模式,控制负责框架流程,逻辑是功能实现,是细小的方法,通过函数式接口以参数的方式传入流程控制的方法中。

  • 多查一下国际命名,如数据库里面的时间字段国际上多用 createdAt、updatedAt、deletedAt/removedAt, 或者人员字段 createdBy、updatedBy…… 。最好先用 AI (Gemin 2.5 Pro (ai.dev))查一下。

  • 当你讨厌 Java 语法,喜欢 C# 语法风格时,不妨看看 Kotlin。当你准备学写 C 时,不妨看看 Golang(干净、简单简洁优雅、直观、强大)。 当你准备学写 C++ 时,不妨看看 Rust(极致性能、极致控制、极致安全、极度复杂)。当你准备学写 JavaScript 时,不妨看看 TypeScript。 当你准备学写 CSS 时,不妨看看 SCSS。 (当然,TypeScript 是 JavaScript 的超集(加了类型),SCSS 是 CSS 的超集(加了几个工具方法))

架构师许式伟观点理念

  • 工程是一门有关于如何“把事做成”的学问。工程可以和所有学科交叉(工程+X)。
  • 模块:将复杂问题拆解成独立子问题。
  • 设计不是指“产品包装”,而是一门有关于“决策”的学问(要做什么事,以及把事做成什么样子)。
  • 初级产品经理和初级架构师都喜欢做加法。但是顶级产品经理做的是减法。而顶级架构师做的是乘法。
  • 产品设计:少做加法,做减法。
  • 少就是指数级的多。
  • 卖点越多的产品其实就是没有卖点。
  • 架构的开闭原则对产品同样适用(产品要满足的需求可以是无限的,但产品功能必须是封闭(有边界)的)。
  • 只要图灵完备(包、变/常量、条件/循环、方法),理论上做什么都可以。
  • 架构从手法上就是模块拆解。连接与组合。实体 (Entity) 的边界问题。
  • 把整个网站需求分解为 A、B、C、D、E 等完全正交无耦合、甚至和你的网站原始需求不直接挂钩的通用模块, 用极短的桥接代码1把这些通用模块组装起来完成页面1,桥接代码2完成页面2,这才是做乘法。
    • 而判断乘法做的好不好的标准非常简单:桥接代码的代码量越少,乘法的威力 越大,你的架构设计的能力也就越强。
  • 模块切分,通常和需求并不形成对应关系。如果对应,往往反而说明模块划分和团队分工是有问题的。
  • 需求分析要做什么?需求未来可能的发展方向预判(防止过度设计)。洞察需求的内在逻辑关联(模块切分的基础)。
  • 心性:韧性:是否能够长期坚持为一个目标去努力。要性:对成功的渴望。空杯:倾听、迭代认知的能力。
  • 很多人推崇设计模式。但是在我眼里,设计模式是没有任何价值,如果不能够明白软件架构的本质的话。
  • 软件架构就是准确把控需求的基础上对系统的解剖。 准确把控需求,不只是要准确理解当前的需求,也要准确理解需求的变化,预见什么会发生,而什么不会发生。
  • 架构只需要一个原则:开闭原则。软件实体(类,函数,模块)可以扩展,不可以修改;即扩展是开放的,修改是封闭的;面对需求,对程序的改动是通过增加代码实现的,而不是更改现有代码;
  • 架构的关键不是设计框架,而是需求的正交分解。
    • 大需求(应用程序)被切分为小需求(模块),小需求继续分解(类/组件/函数)。
  • 模块的使用界面体现了模块的需求。

Java foot image

推荐阅读