9、Java 外观模式从入门到实战

Java 外观模式从入门到实战(后端必看,附案例+面试考点)

前言:外观模式(Facade Pattern)是Java设计模式中最实用的"解耦工具"之一,属于结构型模式,核心是"封装复杂逻辑,提供统一入口"。很多Java开发者在项目中不知不觉就用了外观模式,却不知道它的官方名称;新手面对多个关联紧密的子系统,容易写出"牵一发而动全身"的耦合代码,维护起来苦不堪言;面试时被问到"外观模式和代理模式的区别""外观模式在框架中的应用",常常无从下手。本文从入门到实战,用极简语言拆解外观模式核心,结合可直接复制运行的代码案例、真实业务场景(订单提交、支付流程),以及高频面试考点,带你吃透外观模式,新手也能快速上手,看完就能落地到项目中。

一、为什么Java后端必须掌握外观模式?(痛点直击)

先看3个Java后端开发中最常见的场景,你一定遇到过:

  • 场景1:订单提交流程,需要调用库存扣减、订单创建、支付发起、消息通知4个子系统,业务代码中反复编写"调用子系统1→调用子系统2→调用子系统3→调用子系统4"的逻辑,代码冗余且耦合度极高;

  • 场景2:新手接手遗留系统,面对十几个关联子系统,不知道从哪里入手调用,接口繁多、逻辑复杂,容易调用出错;

  • 场景3:面试时,面试官追问"Spring中哪里用到了外观模式""外观模式的核心价值是什么",无法清晰阐述,印象分大打折扣。

而外观模式的核心价值,就是对多个复杂子系统进行封装,提供一个统一的对外接口,隐藏子系统的细节,降低客户端与子系统的耦合度。简单说,就是给复杂的"子系统集群"套一个"简化外壳",客户端只需调用这个外壳的方法,无需关心内部子系统的调用顺序和细节------这也是它被称为"外观"的原因。

核心结论:外观模式不是"花里胡哨"的设计,而是后端开发的"解耦神器"------初级开发者用它简化代码调用,中级开发者用它降低系统耦合,高级开发者用它设计优雅的系统架构,面试时更是高频考点(尤其是中高级岗位)。

二、外观模式核心概念(极简入门,无需死记硬背)

外观模式的本质很简单:封装复杂子系统,提供统一入口。就像生活中"餐厅服务员"------你(客户端)想吃一道菜,不用自己去厨房准备食材、烹饪、装盘(调用各个子系统),只需告诉服务员(外观类)"我要一份鱼香肉丝",服务员会协调厨房的各个岗位(子系统)完成整个流程,你只需要等待上菜即可。

核心角色(2个核心,1个可选,必记):

  1. 外观类(Facade):核心角色,封装多个子系统的复杂逻辑,提供统一的对外接口,负责协调子系统的调用顺序,屏蔽子系统细节;

  2. 子系统(Subsystem):被封装的具体业务模块,每个子系统都有自己的核心逻辑,可独立运行,不依赖外观类;

  3. 客户端(Client):调用者,只需调用外观类的接口,无需直接操作子系统(可选角色,对应项目中的Service、Controller等)。

核心原则:外观类不参与子系统的业务逻辑,只负责"协调调度";子系统之间可以相互依赖,但客户端只能通过外观类与子系统交互,降低耦合。

核心注意点:外观模式不改变子系统的功能,只是对其进行封装;子系统依然可以独立被调用(供内部复杂场景使用),外观类只是提供"简化版入口"。

三、外观模式入门实现(附可复制代码,新手必练)

以"订单提交流程"为案例,模拟真实业务中"库存扣减→订单创建→支付发起→消息通知"4个子系统,用外观模式封装调用逻辑,对比"无外观模式"和"有外观模式"的代码差异,一看就懂。

3.1 无外观模式:耦合度极高的代码(反例)

没有外观类时,客户端需要手动调用4个子系统,且要严格遵守调用顺序(比如必须先扣减库存,才能创建订单),代码冗余、耦合度高,一旦子系统调用顺序变化,所有客户端代码都要修改。

java 复制代码
// 1. 子系统1:库存子系统
public class StockSubsystem {
    // 扣减库存
    public boolean deductStock(Long productId, Integer quantity) {
        System.out.println("库存子系统:扣减商品[" + productId + "]库存,数量:" + quantity);
        // 模拟库存充足,扣减成功
        return true;
    }
}

// 2. 子系统2:订单子系统
public class OrderSubsystem {
    // 创建订单
    public Long createOrder(Long userId, Long productId, Integer quantity) {
        System.out.println("订单子系统:用户[" + userId + "]创建订单,商品[" + productId + "],数量:" + quantity);
        // 模拟生成订单ID
        return System.currentTimeMillis();
    }
}

// 3. 子系统3:支付子系统
public class PaymentSubsystem {
    // 发起支付
    public boolean pay(Long orderId, BigDecimal amount) {
        System.out.println("支付子系统:订单[" + orderId + "]发起支付,金额:" + amount);
        // 模拟支付成功
        return true;
    }
}

// 4. 子系统4:消息子系统
public class MessageSubsystem {
    // 发送消息通知
    public void sendMessage(Long userId, String content) {
        System.out.println("消息子系统:向用户[" + userId + "]发送消息:" + content);
    }
}

// 5. 客户端(业务调用者,如Service层)
public class Client {
    public static void main(String[] args) {
        // 1. 手动创建所有子系统实例(耦合点1:依赖所有子系统)
        StockSubsystem stockSubsystem = new StockSubsystem();
        OrderSubsystem orderSubsystem = new OrderSubsystem();
        PaymentSubsystem paymentSubsystem = new PaymentSubsystem();
        MessageSubsystem messageSubsystem = new MessageSubsystem();
        
        // 2. 手动调用子系统,严格遵守顺序(耦合点2:依赖调用顺序)
        Long productId = 1001L;
        Integer quantity = 2;
        Long userId = 10086L;
        BigDecimal amount = new BigDecimal("99.99");
        
        // 步骤1:扣减库存
        boolean stockSuccess = stockSubsystem.deductStock(productId, quantity);
        if (!stockSuccess) {
            System.out.println("库存不足,订单提交失败");
            return;
        }
        
        // 步骤2:创建订单
        Long orderId = orderSubsystem.createOrder(userId, productId, quantity);
        
        // 步骤3:发起支付
        boolean paySuccess = paymentSubsystem.pay(orderId, amount);
        if (!paySuccess) {
            System.out.println("支付失败,订单提交失败");
            return;
        }
        
        // 步骤4:发送消息
        messageSubsystem.sendMessage(userId, "订单[" + orderId + "]提交成功,支付金额:" + amount);
    }
}

【运行结果】:流程能正常执行,但客户端代码严重耦合,缺点明显:

  • 客户端依赖所有子系统,需要手动创建子系统实例;

  • 客户端必须记住子系统的调用顺序,一旦顺序出错,业务逻辑崩溃;

  • 若新增子系统(如"日志记录"),所有客户端代码都要修改,不符合"开闭原则"。

3.2 有外观模式:解耦后的优雅代码(正例)

新增外观类,封装所有子系统的调用逻辑和顺序,客户端只需调用外观类的一个方法,无需关心子系统的细节,彻底解耦。

java 复制代码
// 1. 子系统(复用上面的4个子系统,无需修改任何代码)
// StockSubsystem、OrderSubsystem、PaymentSubsystem、MessageSubsystem 代码不变

// 2. 核心:外观类(封装子系统,提供统一入口)
public class OrderFacade {
    // 持有所有子系统的引用(通过构造注入,更符合Spring依赖注入规范)
    private final StockSubsystem stockSubsystem;
    private final OrderSubsystem orderSubsystem;
    private final PaymentSubsystem paymentSubsystem;
    private final MessageSubsystem messageSubsystem;
    
    // 构造方法注入子系统(实际项目中用@Autowired自动注入)
    public OrderFacade(StockSubsystem stockSubsystem, OrderSubsystem orderSubsystem,
                       PaymentSubsystem paymentSubsystem, MessageSubsystem messageSubsystem) {
        this.stockSubsystem = stockSubsystem;
        this.orderSubsystem = orderSubsystem;
        this.paymentSubsystem = paymentSubsystem;
        this.messageSubsystem = messageSubsystem;
    }
    
    // 统一对外接口:订单提交(封装所有子系统的调用逻辑和顺序)
    public boolean submitOrder(Long userId, Long productId, Integer quantity, BigDecimal amount) {
        try {
            // 步骤1:扣减库存(外观类协调顺序,客户端无需关心)
            boolean stockSuccess = stockSubsystem.deductStock(productId, quantity);
            if (!stockSuccess) {
                System.out.println("库存不足,订单提交失败");
                return false;
            }
            
            // 步骤2:创建订单
            Long orderId = orderSubsystem.createOrder(userId, productId, quantity);
            
            // 步骤3:发起支付
            boolean paySuccess = paymentSubsystem.pay(orderId, amount);
            if (!paySuccess) {
                System.out.println("支付失败,订单提交失败");
                return false;
            }
            
            // 步骤4:发送消息
            messageSubsystem.sendMessage(userId, "订单[" + orderId + "]提交成功,支付金额:" + amount);
            return true;
        } catch (Exception e) {
            System.out.println("订单提交异常:" + e.getMessage());
            return false;
        }
    }
}

// 3. 客户端(简化版,只需调用外观类的方法)
public class Client {
    public static void main(String[] args) {
        // 1. 创建子系统实例(实际项目中由Spring管理,无需手动创建)
        StockSubsystem stockSubsystem = new StockSubsystem();
        OrderSubsystem orderSubsystem = new OrderSubsystem();
        PaymentSubsystem paymentSubsystem = new PaymentSubsystem();
        MessageSubsystem messageSubsystem = new MessageSubsystem();
        
        // 2. 创建外观类实例,注入子系统
        OrderFacade orderFacade = new OrderFacade(stockSubsystem, orderSubsystem, paymentSubsystem, messageSubsystem);
        
        // 3. 调用外观类的统一接口,无需关心子系统细节(核心优化)
        Long userId = 10086L;
        Long productId = 1001L;
        Integer quantity = 2;
        BigDecimal amount = new BigDecimal("99.99");
        boolean result = orderFacade.submitOrder(userId, productId, quantity, amount);
        
        System.out.println("订单提交最终结果:" + (result ? "成功" : "失败"));
    }
}

【运行结果】:和无外观模式的结果完全一致,但代码优势极其明显:

  • 解耦:客户端只依赖外观类,不依赖任何子系统,降低耦合度;

  • 简化调用:客户端只需调用一个方法,无需记住子系统调用顺序;

  • 易于维护:子系统的修改、新增、删除,只需修改外观类,无需修改客户端代码,符合"开闭原则";

  • 容错性强:外观类中统一处理异常,客户端无需单独处理子系统的异常。

【核心总结】:外观模式的核心不是"新增功能",而是"简化调用、降低耦合"------子系统的功能不变,只是通过外观类提供一个"简化入口",让客户端更易用。

四、外观模式实战(真实业务场景,可直接复用)

结合Java后端最常见的"用户注册+认证"场景,用外观模式封装"用户注册、角色分配、权限初始化、邮箱验证"4个子系统,实现通用、可复用的注册流程,贴合真实项目开发(Spring Boot环境),代码可直接复制到项目中使用。

4.1 实战场景说明

场景:用户中心注册功能,需要完成以下4个步骤(子系统),要求客户端(Controller)调用简单,且子系统可独立扩展:

  1. 用户注册:保存用户基本信息(用户名、密码、邮箱);

  2. 角色分配:给新用户分配默认角色(普通用户);

  3. 权限初始化:根据用户角色,初始化对应的权限(如查看个人信息、修改密码);

  4. 邮箱验证:发送验证邮件,提示用户激活账号。

要求:客户端(Controller)只需调用一个方法,即可完成整个注册流程,子系统的修改不影响客户端代码。

4.2 实战代码实现(Spring Boot环境,可直接复用)

java 复制代码
// 1. 子系统1:用户注册子系统(Service层)
@Service
public class UserRegisterSubsystem {
    // 模拟保存用户信息(实际项目中操作数据库)
    public Long register(String username, String password, String email) {
        System.out.println("用户注册子系统:保存用户信息,用户名:" + username + ",邮箱:" + email);
        // 模拟生成用户ID
        return System.currentTimeMillis();
    }
}

// 2. 子系统2:角色分配子系统(Service层)
@Service
public class RoleAssignSubsystem {
    // 给用户分配默认角色(普通用户)
    public void assignDefaultRole(Long userId) {
        System.out.println("角色分配子系统:给用户[" + userId + "]分配默认角色【普通用户】");
    }
}

// 3. 子系统3:权限初始化子系统(Service层)
@Service
public class PermissionInitSubsystem {
    // 根据用户角色初始化权限
    public void initPermission(Long userId) {
        System.out.println("权限初始化子系统:给用户[" + userId + "]初始化默认权限(查看个人信息、修改密码)");
    }
}

// 4. 子系统4:邮箱验证子系统(Service层)
@Service
public class EmailVerifySubsystem {
    // 发送邮箱验证邮件
    public void sendVerifyEmail(String email, Long userId) {
        String verifyUrl = "http://localhost:8080/verify?userId=" + userId;
        System.out.println("邮箱验证子系统:向邮箱[" + email + "]发送验证邮件,验证链接:" + verifyUrl);
    }
}

// 5. 核心:外观类(Service层,封装所有子系统,提供统一入口)
@Service
public class UserRegisterFacade {
    // Spring自动注入所有子系统(无需手动创建,符合Spring规范)
    @Autowired
    private UserRegisterSubsystem userRegisterSubsystem;
    @Autowired
    private RoleAssignSubsystem roleAssignSubsystem;
    @Autowired
    private PermissionInitSubsystem permissionInitSubsystem;
    @Autowired
    private EmailVerifySubsystem emailVerifySubsystem;
    
    // 统一对外接口:用户注册(客户端只需调用此方法)
    public boolean userRegister(String username, String password, String email) {
        try {
            // 步骤1:注册用户,获取用户ID
            Long userId = userRegisterSubsystem.register(username, password, email);
            if (userId == null) {
                System.out.println("用户注册失败:用户ID生成异常");
                return false;
            }
            
            // 步骤2:分配默认角色
            roleAssignSubsystem.assignDefaultRole(userId);
            
            // 步骤3:初始化权限
            permissionInitSubsystem.initPermission(userId);
            
            // 步骤4:发送验证邮件
            emailVerifySubsystem.sendVerifyEmail(email, userId);
            
            System.out.println("用户[" + username + "]注册成功, userId:" + userId);
            return true;
        } catch (Exception e) {
            System.out.println("用户注册异常:" + e.getMessage());
            // 实际项目中可添加事务回滚逻辑
            return false;
        }
    }
}

// 6. 客户端(Controller层,调用外观类接口)
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserRegisterFacade userRegisterFacade;
    
    @PostMapping("/register")
    public ResponseEntity<String> register(@RequestParam String username,
                                           @RequestParam String password,
                                           @RequestParam String email) {
        // 只需调用外观类的一个方法,即可完成整个注册流程
        boolean success = userRegisterFacade.userRegister(username, password, email);
        if (success) {
            return ResponseEntity.ok("注册成功,请查收邮箱验证邮件");
        } else {
            return ResponseEntity.badRequest().body("注册失败,请重试");
        }
    }
}

// 7. 测试类(模拟接口调用)
public class UserRegisterTest {
    public static void main(String[] args) {
        // 模拟Spring容器注入(实际项目中由Spring管理)
        UserRegisterSubsystem registerSubsystem = new UserRegisterSubsystem();
        RoleAssignSubsystem roleSubsystem = new RoleAssignSubsystem();
        PermissionInitSubsystem permissionSubsystem = new PermissionInitSubsystem();
        EmailVerifySubsystem emailSubsystem = new EmailVerifySubsystem();
        
        UserRegisterFacade facade = new UserRegisterFacade();
        // 手动注入子系统(实际项目用@Autowired)
        facade.setUserRegisterSubsystem(registerSubsystem);
        facade.setRoleAssignSubsystem(roleSubsystem);
        facade.setPermissionInitSubsystem(permissionSubsystem);
        facade.setEmailVerifySubsystem(emailSubsystem);
        
        // 调用外观类接口
        boolean result = facade.userRegister("test123", "123456", "test@163.com");
        System.out.println("注册结果:" + (result ? "成功" : "失败"));
    }
}

【运行结果】:

text 复制代码
用户注册子系统:保存用户信息,用户名:test123,邮箱:test@163.com
角色分配子系统:给用户[1713222000000]分配默认角色【普通用户】
权限初始化子系统:给用户[1713222000000]初始化默认权限(查看个人信息、修改密码)
邮箱验证子系统:向邮箱[test@163.com]发送验证邮件,验证链接:http://localhost:8080/verify?userId=1713222000000
用户[test123]注册成功, userId:1713222000000
注册结果:成功

【实战亮点】:

  • 贴合Spring Boot实战:使用@Service、@Autowired注解,符合真实项目开发规范,可直接复制复用;

  • 高内聚低耦合:子系统各司其职(注册、角色、权限、邮箱),外观类负责协调,修改任一子系统(如修改邮箱发送逻辑),无需修改Controller代码;

  • 可扩展性强:新增子系统(如"日志记录""短信通知"),只需修改外观类,无需改动客户端和其他子系统;

  • 容错性强:外观类统一处理异常,可添加事务回滚逻辑,避免出现"注册成功但角色分配失败"的不一致场景。

五、外观模式在框架中的应用(面试必提)

外观模式的核心价值是"简化调用、降低耦合",这也是它被广泛应用在主流Java框架中的原因,掌握这些应用场景,面试时能加分不少,还能帮助你理解框架底层设计思想。

5.1 Spring 中的外观模式(最常见)

Spring框架中,很多核心类都用到了外观模式,最典型的就是ApplicationContext

  • 子系统:Spring容器中的BeanFactory、ResourceLoader、MessageSource、Environment等子系统(负责Bean管理、资源加载、消息处理、环境配置);

  • 外观类:ApplicationContext(Spring上下文),封装了所有子系统的功能,提供统一的对外接口(如getBean()获取Bean、getResource()加载资源);

  • 客户端:开发者只需操作ApplicationContext,无需直接调用BeanFactory、ResourceLoader等子系统,简化了Spring容器的使用。

示例:我们平时用ApplicationContext.getBean()获取Bean,底层其实是调用了BeanFactory的getBean()方法,但我们无需关心BeanFactory的细节------这就是外观模式的典型应用。

5.2 MyBatis 中的外观模式

MyBatis中的SqlSession就是典型的外观类:

  • 子系统:MyBatis中的Executor(执行器)、StatementHandler(语句处理器)、ResultHandler(结果处理器)等子系统;

  • 外观类:SqlSession,封装了所有子系统的功能,提供统一的对外接口(如selectOne()、insert()、update()、commit());

  • 客户端:开发者只需操作SqlSession,无需直接调用Executor、StatementHandler等子系统,简化了MyBatis的使用。

5.3 其他框架/场景

  • Spring Boot 的自动配置:Spring Boot的AutoConfiguration类,本质是外观类,封装了Spring、MyBatis、Redis等多个框架的配置逻辑,提供"一键启动"的统一入口;

  • 第三方接口封装:项目中调用第三方支付接口(如支付宝、微信支付)时,我们通常会封装一个"支付外观类",统一处理签名、请求、响应解析,客户端只需调用外观类的方法,无需关心第三方接口的细节。

六、外观模式面试高频考点(必背,避坑)

外观模式是Java后端面试的高频考点(中高级岗位尤为突出),重点考察"核心思想""应用场景""与其他模式的区别",记住以下考点,轻松应对面试。

1. 外观模式的核心作用是什么?(高频)

核心答案(一句话记住,面试直接说):封装多个子系统的复杂逻辑,提供统一的对外接口,降低客户端与子系统的耦合度,简化客户端调用

补充:外观模式不改变子系统的功能,只是对其进行封装,子系统依然可以独立被调用。

2. 外观模式和代理模式的区别?(高频中的高频)

很多面试官会把这两个模式放在一起问,核心区别(一句话区分):外观模式关注"简化多个子系统的调用",代理模式关注"增强单个目标对象的功能"

对比维度 外观模式 代理模式
核心目的 简化调用,降低客户端与多个子系统的耦合 增强单个目标对象的功能(如日志、权限)
处理对象 多个子系统(集群) 单个目标对象
角色关系 外观类协调多个子系统,不参与业务逻辑 代理类持有目标对象引用,增强目标方法
典型应用 Spring ApplicationContext、MyBatis SqlSession Spring AOP、日志增强、权限控制

3. 外观模式的优点和缺点?(必背)

  • 优点:

    • 降低耦合:客户端与子系统解耦,减少客户端的依赖;

    • 简化调用:客户端只需调用一个接口,无需关心子系统的细节和调用顺序;

    • 易于维护:子系统的修改只需修改外观类,不影响客户端;

    • 提高容错:外观类统一处理异常,避免客户端重复处理异常。

  • 缺点:

    • 外观类可能会变得过于庞大:如果子系统过多,外观类的代码会变得复杂,难以维护;

    • 灵活性降低:客户端只能通过外观类的接口调用子系统,无法直接调用子系统的特殊方法(需权衡,可预留扩展接口)。

4. 外观模式的适用场景有哪些?(高频)

  • 客户端需要调用多个子系统,且子系统调用顺序固定、逻辑复杂;

  • 需要降低客户端与子系统的耦合度,简化客户端调用;

  • 遗留系统改造:遗留系统有多个复杂模块,需要给新系统提供统一的调用入口;

  • 框架设计:框架需要给开发者提供简单的使用接口,隐藏底层复杂逻辑(如Spring、MyBatis)。

5. 外观模式和适配器模式的区别?(易混淆)

核心区别:外观模式是"简化调用",不改变子系统的接口;适配器模式是"转换接口",改变原有接口,让不兼容的接口可以一起工作

示例:外观模式就像"服务员",简化你点菜的流程,但菜的种类(子系统接口)不变;适配器模式就像"电源适配器",将220V电压(原有接口)转换为5V(目标接口),让手机(客户端)可以使用。

七、总结(实战+面试双达标)

外观模式不难,核心是"封装复杂、提供统一入口",无需死记硬背,结合代码案例和框架应用理解,多练几次就能熟练掌握。对于Java后端开发者来说,外观模式不仅是设计模式的知识点,更是编写优雅、可维护代码的基础,也是理解框架底层设计思想的关键。

  1. 基础:掌握外观模式的2个核心角色(外观类、子系统),理解"封装、解耦、简化调用"的核心思想;

  2. 核心:吃透外观模式与代理模式、适配器模式的区别(面试高频),避免混淆;

  3. 实战:结合真实业务场景(如订单提交、用户注册),封装子系统,实现可复用的外观类,落地到项目中;

  4. 面试:记住高频考点,结合Spring ApplicationContext、MyBatis SqlSession等框架应用,清晰阐述外观模式的底层原理和价值。

记住一句话:外观模式的核心不是"隐藏子系统",而是"简化客户端调用"------它就像一个"中间协调者",让客户端无需面对复杂的子系统集群,只需专注于自己的业务逻辑,这也是它能成为后端开发"解耦神器"的关键。掌握它,不仅能让你的代码更优雅、更易维护,还能在面试中脱颖而出,成为"懂原理、能落地"的后端开发者。

补充:常见问题解决(避坑指南)

  • 问题1:外观类过于庞大,维护困难?

    解决:拆分外观类,按业务模块拆分多个外观类(如订单外观类、用户外观类),避免一个外观类管理所有子系统;

  • 问题2:客户端需要调用子系统的特殊方法,外观类接口无法满足?

    解决:预留扩展接口,在外观类中提供获取子系统实例的方法(谨慎使用,避免耦合回退),或新增专门的扩展外观类;

  • 问题3:子系统之间存在依赖,外观类调用顺序出错?

    解决:在外观类中明确子系统的调用顺序,添加注释,或通过配置文件定义调用顺序,提高可读性和可维护性;

  • 问题4:Spring环境中,外观类注入子系统失败?

    解决:确保子系统和外观类都添加了@Service注解,Spring容器能扫描到,且注入方式正确(@Autowired)。

相关推荐
清心歌2 小时前
TreeSet 深度解析
java·开发语言
迷藏4942 小时前
**RISC-V生态下的嵌入式开发新范式:从指令集到自定义外设的全流程实战**在当前国产化
java·python·risc-v
小松加哲2 小时前
Tomcat 核心原理全解析(含请求流转+组件源码+多应用配置)
java·tomcat·firefox
Lyyaoo.2 小时前
【JAVA基础面经】juc包(java.util.concurrent)
java·开发语言
‎ദ്ദിᵔ.˛.ᵔ₎2 小时前
C++ 继承
开发语言·c++
殇淋狱陌2 小时前
【初始Python】Python学习基础(数据类型、定义、变量、下标、目前的开发语言对比)
开发语言·python·学习
色空大师2 小时前
【nacos下载安装】
java·linux·nacos·ubantu
lsx2024062 小时前
Ruby 迭代器
开发语言
朱一头zcy2 小时前
Java基础复习08:IO流(File类与IO流概述、字节输入输出流、字符输入输出流、缓冲流、字符转换流、对象序列化、打印流、Commons-io包介绍)
java·笔记