深入解析原型模式:从理论到实践的全方位指南

在这里插入图片描述

引言:为什么需要原型模式?

在软件开发过程中,对象创建是一个频繁且关键的操作。传统方式(如直接使用new关键字)在某些场景下会显得效率低下且不够灵活。想象这样一个场景:我们需要创建10只属性完全相同的羊,每只羊都有姓名(如"tom")、年龄(如1岁)和颜色(如白色)等属性。按照常规做法,我们需要反复调用构造函数并设置相同的属性值,这不仅代码冗余,而且当对象结构复杂时,会显著影响性能。

这正是原型模式(Prototype Pattern)大显身手的地方。原型模式是一种创建型设计模式,它通过复制现有对象(称为原型)来创建新对象,而不是通过新建类实例的方式。这种方式特别适合以下场景:

  • 当创建对象的过程复杂或代价高昂时
  • 当系统需要独立于其产品的创建、组合和表示时
  • 当需要避免使用与产品层次结构平行的工厂类层次结构时
  • 当一个类的实例只能有几个不同状态组合中的一种时

本文将全面剖析原型模式的各个方面,包括其定义、原理、实现方式、在Spring框架中的应用、深浅拷贝的区别以及实际开发中的注意事项。

原型模式的定义与核心思想

基本概念

原型模式是指用原型实例指定创建对象的种类,并且通过拷贝这些原型来创建新的对象。这种模式属于创建型设计模式,它允许一个对象再创建另外一个可定制的对象,而无需知道创建的细节。

用更形象的方式理解:就像《西游记》中孙大圣拔出猴毛,变出其他孙大圣一样,原型模式通过"克隆"现有对象来生成新对象。这种"克隆"能力使得系统可以:

  1. 动态加载类
  2. 动态创建对象
  3. 避免重复初始化过程
  4. 保持对象状态一致性

工作原理

原型模式的工作原理可以概括为:将一个原型对象传给需要创建新对象的组件,这个组件通过请求原型对象拷贝自身来实施创建过程。在Java中,这通常通过调用对象的clone()方法实现。

具体来说,原型模式包含三个主要角色:

  1. Prototype(抽象原型类):声明克隆自身的接口,通常是一个抽象类或接口
  2. ConcretePrototype(具体原型类):实现克隆操作的具体类
  3. Client(客户端):通过调用原型对象的克隆方法来创建新对象

传统创建方式的局限性

让我们回到开头的"克隆羊"问题。传统方式创建10只相同属性的羊存在以下缺点:

  1. 效率问题:每次创建新对象都需要重新获取原始对象的属性,当对象结构复杂时,效率较低
  2. 灵活性不足:总是需要重新初始化对象,而不是动态地获得对象运行时的状态
  3. 代码冗余:需要重复编写相同的属性设置代码
  4. 维护困难:当需要修改属性时,必须在所有创建点进行修改
// 传统方式创建10只相同的羊
List<Sheep> sheepList = new ArrayList<>();
for(int i=0; i<10; i++){Sheep sheep = new Sheep();sheep.setName("tom");sheep.setAge(1);sheep.setColor("白色");sheepList.add(sheep);
}

相比之下,原型模式通过克隆已有对象的方式,可以优雅地解决这些问题。

原型模式的UML结构与实现

UML类图解析

原型模式的UML类图清晰地展现了其核心结构:

https://i.imgur.com/xyz1234.png

  1. Prototype接口/抽象类
    • 声明clone()方法
    • 作为所有具体原型类的父类
    • 定义克隆契约
  2. ConcretePrototype具体实现类
    • 实现clone()方法
    • 提供自我复制的具体逻辑
    • 可以有多个不同的具体实现
  3. Client客户端
    • 维护一个原型实例
    • 通过调用原型实例的clone()方法创建新对象
    • 无需知道具体原型类的细节

在这里插入图片描述

Java中的实现方式

在Java中,原型模式通常通过实现Cloneable接口并重写Object类的clone()方法来实现。Cloneable是一个标记接口,表示该类支持克隆操作。

基本实现步骤如下:

  1. 实现Cloneable接口
  2. 重写Object类的clone()方法
  3. clone()方法中调用super.clone()
  4. 根据需要处理深拷贝问题
// 羊类实现克隆功能
public class Sheep implements Cloneable {private String name;private int age;private String color;// 构造方法、getter和setter省略@Overrideprotected Object clone() {Sheep sheep = null;try {sheep = (Sheep)super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return sheep;}
}// 客户端使用
public class Client {public static void main(String[] args) {Sheep originalSheep = new Sheep("tom", 1, "白色");// 克隆10只羊List<Sheep> sheepList = new ArrayList<>();for(int i=0; i<10; i++){Sheep clonedSheep = (Sheep)originalSheep.clone();sheepList.add(clonedSheep);}}
}

原型模式在Spring框架中的应用

Spring框架广泛使用了原型模式来管理bean的生命周期。在Spring中,bean的作用域(scope)可以是单例(singleton)或原型(prototype)。当bean的作用域设置为prototype时,每次请求该bean都会创建一个新的实例。

配置方式:

<bean id="monster" class="com.example.Monster" scope="prototype"/>

或者使用注解方式:

@Scope("prototype")
@Component
public class Monster {// ...
}

Spring内部实现原型bean的关键代码逻辑:

// 简化版的Spring原型bean创建逻辑
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) {// ...其他代码if (mbd.isPrototype()) {// 处理原型作用域的beanObject prototypeInstance = null;try {beforePrototypeCreation(beanName);prototypeInstance = createBeanInstance(beanName, mbd, args);} finally {afterPrototypeCreation(beanName);}return prototypeInstance;}// ...其他代码
}

Spring通过isPrototype()方法判断当前bean是否为原型作用域,如果是,则每次都会创建一个新的实例。这种机制正是原型模式的典型应用。

深拷贝与浅拷贝:原型模式的核心问题

浅拷贝(Shallow Copy)详解

浅拷贝是原型模式默认的拷贝方式,它具有以下特点:

  1. 对于基本数据类型的成员变量,直接进行值传递,复制属性值给新对象
  2. 对于引用类型的成员变量,只复制引用值(内存地址),新旧对象共享同一实例
  3. 使用默认的clone()方法实现
  4. 实现简单,效率高

以羊类为例,浅拷贝的实现:

public class Sheep implements Cloneable {private String name;    // String是不可变对象,可视为基本类型private int age;private String color;private Sheep friend;   // 引用类型成员@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();  // 默认实现是浅拷贝}
}

浅拷贝的问题在于,当对象包含引用类型成员时,克隆对象和原对象会共享这些成员。修改其中一个对象的引用成员,会影响另一个对象。

深拷贝(Deep Copy)详解

深拷贝解决了浅拷贝的共享引用问题,它具有以下特点:

  1. 复制对象的所有基本数据类型的成员变量值
  2. 为所有引用数据类型的成员变量申请新的存储空间
  3. 递归复制引用对象,直到整个对象图都被复制
  4. 克隆对象与原对象完全独立,互不影响

深拷贝可以通过两种方式实现:

方式一:重写clone方法实现深拷贝
public class Sheep implements Cloneable {private String name;private int age;private String color;private Sheep friend;@Overrideprotected Object clone() throws CloneNotSupportedException {Sheep clonedSheep = (Sheep)super.clone();if(this.friend != null) {clonedSheep.friend = (Sheep)this.friend.clone();}return clonedSheep;}
}
方式二:通过对象序列化实现深拷贝
public class DeepCopyUtil {@SuppressWarnings("unchecked")public static <T extends Serializable> T deepCopy(T object) {try {ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(object);ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return (T)ois.readObject();} catch (Exception e) {e.printStackTrace();return null;}}
}// 使用方式
Sheep original = new Sheep("tom", 1, "白色");
Sheep copy = DeepCopyUtil.deepCopy(original);

序列化方式实现深拷贝的优点是不需要手动处理每个引用字段,可以自动完成整个对象图的深拷贝。但要求所有相关类都必须实现Serializable接口。

深浅拷贝的选择策略

在实际开发中,选择深拷贝还是浅拷贝应考虑以下因素:

  1. 对象复杂度:简单对象可用浅拷贝,复杂对象图建议深拷贝
  2. 性能要求:深拷贝代价更高,性能敏感场景需权衡
  3. 安全性需求:需要完全隔离对象状态时,必须使用深拷贝
  4. 开发成本:深拷贝实现更复杂,维护成本更高

原型模式的最佳实践与注意事项

适用场景

原型模式特别适用于以下场景:

  1. 创建成本高的对象:当创建新对象的成本较高(如需要进行大量计算或资源获取)时,通过复制现有对象可以提高性能
  2. 避免使用子类工厂:当系统应该独立于其产品的创建、组合和表示时,原型模式可以避免创建与产品层次结构平行的工厂类层次结构
  3. 运行时动态配置:当需要动态加载类或运行时确定实例化哪些类时
  4. 状态保存与恢复:当需要保存和恢复对象状态,同时又希望对外隐藏实现细节时
  5. 大量相似对象创建:如游戏开发中大量相同或相似敌人的创建,图形编辑器中相同图形的复制等

优点与价值

原型模式具有以下显著优点:

  1. 性能提升:避免重复执行初始化代码,特别是当初始化需要消耗大量资源时
  2. 动态性:可以在运行时动态添加或删除产品,比静态工厂方法更灵活
  3. 简化创建结构:不需要专门的工厂类来创建产品,减少了类的数量
  4. 状态保存:可以保存对象的状态,方便后续恢复到某个历史状态
  5. 减少约束:某些语言中,构造函数有较多约束,而clone方法可以绕过这些限制

潜在问题与解决方案

尽管原型模式强大,但在使用时也需要注意以下问题:

  1. 深拷贝实现复杂
    • 问题:当对象引用关系复杂时,实现深拷贝可能很困难
    • 解决方案:考虑使用序列化方式实现深拷贝,或使用第三方库如Apache Commons Lang中的SerializationUtils
  2. 违背开闭原则(OCP)
    • 问题:需要为每个类配备克隆方法,对已有类进行改造时需要修改源代码
    • 解决方案:在设计初期就考虑克隆需求,或使用组合模式替代继承
  3. 循环引用问题
    • 问题:对象图中存在循环引用时,可能导致无限递归或栈溢出
    • 解决方案:使用特殊标记处理已拷贝对象,避免重复拷贝
  4. final字段问题
    • 问题:Java中final字段在clone后无法修改
    • 解决方案:在clone方法中重新初始化final字段,或避免在可克隆类中使用final字段
  5. 线程安全问题
    • 问题:克隆过程如果不是原子的,可能导致不一致状态
    • 解决方案:同步clone方法,或保证克隆操作的原子性

性能优化建议

为了提高原型模式的效率,可以考虑以下优化策略:

  1. 原型管理器:维护一个注册表存储常用原型,避免重复创建原型实例
  2. 延迟拷贝:结合享元模式,对于不变的部分共享,可变的部分延迟拷贝
  3. 并行克隆:对于大批量克隆操作,可以采用并行处理提高效率
  4. 缓存策略:缓存频繁使用的克隆对象,减少实际克隆次数
// 原型管理器示例
public class PrototypeManager {private static Map<String, Prototype> prototypes = new HashMap<>();static {prototypes.put("sheep", new Sheep("tom", 1, "白色"));prototypes.put("monster", new Monster("dragon", 100));}public static Prototype getPrototype(String type) {return prototypes.get(type).clone();}
}

原型模式与其他设计模式的关系

与工厂方法模式比较

  1. 相似点
    • 都是创建型设计模式
    • 都可以隐藏对象创建的细节
    • 都可以提高系统灵活性
  2. 不同点
    • 工厂方法通过子类决定实例化哪个类,而原型模式通过克隆自身创建新对象
    • 工厂方法需要与产品类平行的工厂类层次,原型模式不需要
    • 原型模式可以动态获取对象运行时状态,工厂方法创建的是新初始化的对象

与抽象工厂模式比较

  1. 相似点
    • 都可以创建一系列相关或依赖对象
    • 都强调创建过程的封装
  2. 不同点
    • 抽象工厂关注产品族,原型模式关注单个产品的复制
    • 抽象工厂通过不同工厂创建不同产品,原型模式通过克隆创建产品
    • 原型模式更适合创建复杂或代价高的对象

与单例模式的关系

原型模式和单例模式看似矛盾,但实际上可以结合使用:

  1. 原型单例:将单例对象作为原型,需要时可以克隆出非单例对象
  2. 单例原型管理器:使用单例模式管理原型注册表
  3. 注意事项:实现单例的clone方法时,通常应该直接返回单例实例,而不是创建新对象
public class Singleton implements Cloneable {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}@Overrideprotected Object clone() throws CloneNotSupportedException {return instance;  // 直接返回单例实例}
}

与备忘录模式的关系

原型模式可以与备忘录模式结合,实现对象状态的保存和恢复:

  1. 原型作为备忘录:使用克隆对象作为备忘录存储状态
  2. 恢复状态:通过将当前对象替换为备忘录中的克隆对象来恢复状态
  3. 优势:不需要额外定义备忘录类,实现更简洁

实际应用案例

案例一:图形编辑器中的图形复制

在图形编辑器中,用户经常需要复制图形元素。使用原型模式可以高效实现这一功能:

// 图形接口
public interface Graphic extends Cloneable {void draw();Graphic clone();
}// 具体图形实现
public class Rectangle implements Graphic {private int width;private int height;public Rectangle(int width, int height) {this.width = width;this.height = height;}@Overridepublic void draw() {System.out.println("Drawing rectangle: " + width + "x" + height);}@Overridepublic Graphic clone() {return new Rectangle(this.width, this.height);}
}// 使用示例
Graphic original = new Rectangle(100, 50);
Graphic copy = original.clone();
copy.draw();  // 输出: Drawing rectangle: 100x50

案例二:游戏中的敌人生成

在游戏开发中,同类型敌人往往有相同的属性和行为,但各自独立。原型模式非常适合这种场景:

public class Enemy implements Cloneable {private String type;private int health;private int attackPower;private Position position;public Enemy(String type, int health, int attackPower) {this.type = type;this.health = health;this.attackPower = attackPower;// 初始化代价高的操作loadTextures();loadAIBehavior();}@Overridepublic Enemy clone() {try {Enemy clone = (Enemy)super.clone();// 深拷贝positionclone.position = new Position(this.position.getX(), this.position.getY());return clone;} catch (CloneNotSupportedException e) {throw new AssertionError(); // 不会发生}}public void setPosition(int x, int y) {this.position = new Position(x, y);}// 其他方法...
}// 使用示例
Enemy prototypeEnemy = new Enemy("Orc", 100, 20);
prototypeEnemy.setPosition(0, 0);// 生成多个敌人
List<Enemy> enemies = new ArrayList<>();
for(int i=0; i<10; i++) {Enemy enemy = prototypeEnemy.clone();enemy.setPosition(i*10, 0);enemies.add(enemy);
}

案例三:配置对象的复制

在系统配置管理中,我们经常需要基于某个模板配置创建多个相似配置:

public class SystemConfig implements Cloneable {private String host;private int port;private Map<String, String> settings;public SystemConfig(String host, int port) {this.host = host;this.port = port;this.settings = loadDefaultSettings(); // 耗时操作}@Overridepublic SystemConfig clone() {try {SystemConfig clone = (SystemConfig)super.clone();// 深拷贝settingsclone.settings = new HashMap<>(this.settings);return clone;} catch (CloneNotSupportedException e) {throw new AssertionError();}}// 其他方法...
}// 使用示例
SystemConfig baseConfig = new SystemConfig("localhost", 8080);// 为不同服务创建配置
SystemConfig dbConfig = baseConfig.clone();
dbConfig.setHost("db-server");
dbConfig.setPort(3306);SystemConfig cacheConfig = baseConfig.clone();
cacheConfig.setHost("cache-server");
cacheConfig.setPort(6379);

总结

原型模式是一种强大的创建型设计模式,它通过复制现有对象来创建新对象,而不是通过实例化类。这种模式特别适用于以下场景:

  1. 当创建新对象的成本较高,而复制现有对象更高效时
  2. 当系统需要独立于其产品的创建、组合和表示时
  3. 当需要保存和恢复对象状态时
  4. 当需要避免使用与产品层次结构平行的工厂类层次结构时

原型模式的核心在于理解深浅拷贝的区别,并根据实际需求选择合适的拷贝策略。浅拷贝简单高效但共享引用,深拷贝完全独立但实现复杂。在实际开发中,通常需要权衡性能、安全性和实现复杂度来做出选择。

Java语言内置了对原型模式的支持,通过Cloneable接口和clone()方法实现。Spring框架也广泛应用了原型模式来管理bean的生命周期。理解原型模式的工作原理和最佳实践,可以帮助我们设计出更灵活、更高效的面向对象系统。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85425.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85425.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/85425.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HuggingFace镜像配置失效问题深度解析:Python模块导入机制的陷阱

前言 在使用HuggingFace的transformers和datasets库时&#xff0c;国内用户经常会遇到网络连接问题。虽然设置了镜像源环境变量&#xff0c;但仍然报错无法连接到huggingface.co。本文将深入分析这个问题的根因&#xff0c;并从Python模块导入机制的角度解释为什么环境变量设置…

leetcode146-LRU缓存

leetcode 146 思路 什么是LRU缓存&#xff1f; LRU&#xff08;Least Recently Used&#xff09;缓存是一种常见的缓存淘汰策略&#xff0c;核心思想是&#xff1a;当缓存容量满时&#xff0c;优先淘汰最久未使用的数据。LeetCode 146 题要求实现一个支持get和put操作的 LR…

MQTT:构建高效物联网通信的轻量级协议

MQTT – 轻量级物联网消息推送协议 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是机器对机器(M2M)/物联网(IoT)连接协议。它被设计为一个极其轻量级的发布/订阅消息传输协议。对于需要较小代码占用空间和/或网络带宽非常宝贵的远程连接非常有用&#xf…

AI自动生成复杂架构图,流程图,思维导图

AI自动生成复杂架构图&#xff0c;流程图&#xff0c;思维导图方案 1. 背景 在我们自己去绘制架构图&#xff0c;流程图&#xff0c;思维导图的时候&#xff0c;我们通常需要花费大量的时间去绘制。 目前的一些直接生图的模型也只能生成简单的流程图&#xff0c;不能生成复杂…

129. 求根节点到叶节点数字之和 --- DFS +回溯(js)

129. 求根节点到叶节点数字之和 --- DFS 回溯&#xff08;js&#xff09; 题目描述解题思路完整代码 题目描述 129. 求根节点到叶节点数字之和 解题思路 和 257. 二叉树的所有路径&#xff08;js&#xff09; 是一样的思路。 不一样的地方就是遇到叶子节点的时候把路径拼接…

SpringBoot电脑商城项目--修改默认收货地址

1. 修改默认收货地址-持久层 1.1 规划sql语句 检测当前用户向设置为默认收货地址的这条数据是否存在 SELECT * FROM t_address WHERE aid#{aid} 在修改用户的收获默认地址之前&#xff0c;先将所有的收货地址设置为非默认 UPDATE t_address SET is_default0 WHERE uid#{uid} …

LabVIEW FPGA 资源扩展

针对NI CompactRIO 9045 控制器 Kintex-7 70T FPGA 资源不足问题&#xff0c;通过 NI 9151 R 系列可重配置 I/O 模块扩展外部 FPGA 处理能力&#xff0c;在保留原有机箱架构下实现实时任务分流&#xff0c;解决Slice、LUT 等资源紧张问题&#xff0c;提升系统并行处理能力。 ​…

【漏洞复现】Apache Kafka Connect 任意文件读取漏洞(CVE-2025-27817)

文章目录 前言一、Apache Kafka 简介二、漏洞描述三、影响版本四、FOFA查询语句五、漏洞原理分析六、漏洞复现七、修复建议前言 由于Apache Kafka客户端未对用户输入进行严格验证和限制,未经身份验证的攻击者可通过构造恶意配置读取环境变量或磁盘任意内容,或向非预期位置发…

day13-软件包管理

1.每日复盘与今日内容 1.1复盘 yum源/apt源配置文件,核心下载地址.二进制部署服务.编译安装软件. 2.软件包管理-实战部分 2.1 yum源/apt源配置 源下载软件的地址配置多种源 1️⃣系统也有默认的源&#xff0c;里面也包含很多常用的软件. 2️⃣安装nginx、yum源 3️⃣安…

榕壹云快递寄件系统:聚合快递、智能追踪、二次开发,一站式物流解决方案

在电商物流高速发展的今天&#xff0c;快递寄件需求呈现爆炸式增长。传统分散的寄件方式效率低下&#xff0c;用户迫切需要一个整合多家快递公司的便捷平台。榕壹云公司开发的快递寄件系统应运而生&#xff0c;通过聚合多家快递资源、优化操作流程、提供丰富的功能模块&#xf…

一款功能强大的专业CSV编辑工具

Rons Data Edit是一款为Windows操作系统设计的现代CSV文件编辑器&#xff0c;它结合了优雅、强大和易用性&#xff0c;它可以打开任何格式的分隔文本文件(如CSV、TSV等)&#xff0c;并允许用户完全控制文件的内容和结构。 功能特点 支持明暗主题&#xff0c;可以在预定义的20多…

什么是软件架构?和系统设计有何区别?

一、软件架构的定义与核心要素 1.1 基本概念 软件架构(Software Architecture)是指系统的高层结构,包含: 组件(Components)及其相互关系指导设计的架构原则和决策满足质量属性(Quality Attributes)的技术方案引用权威定义:IEEE 1471标准将架构描述为"系统的基本组织,…

九尾狐编程语言新算法“超维时空演算体”

一、核心架构设计 1&#xff0e;量子&#xfe63;生物混合计算基座 ◇底层采用量子纠缠拓扑网络&#xff0c;处理超越经 典计算复杂度的问题&#xff08;如 NP - Hard 优化&#xff09;&#xff0e;中层嵌入类脑脉冲神经网络&#xff0c;模拟人脑跨领域联想能力&#xff0c;…

RoboVerse--为机器人学习打造的大一统世界--UC Berkeley...--2025.4.26

ROBOVERSE 包含一个可扩展的仿真平台、大规模的合成数据集&#xff0c;以及统一的基准测试。 该仿真平台通过统一协议&#xff0c;支持新任务和演示的无缝接入&#xff0c;保证了灵活性和可扩展性。该数据集包含 1,000 多个多样化任务及超过 1,000 万个状态转换&#xff0c;构…

Fiddler抓包工具实战指南:结合Charles、Postman优化Web与移动调试流程

在Web开发与移动端调试的工作流程中&#xff0c;网络请求的可视化、分析和控制能力对开发效率有着决定性影响。特别是在处理复杂接口联调、性能瓶颈排查&#xff0c;甚至安全漏洞分析时&#xff0c;一款可靠的抓包工具几乎成为了每一位开发者的“标配”。 Fiddler作为长期深受…

6/19作业

思维导图 单选题 树 1. 向一棵平衡二叉树中插入一个结点后&#xff0c;一定会改变其平衡性。 &#xff08; &#xff09; A 正确 B 错误 正确答案&#xff1a;B 你的答案&#xff1a;A 官方解析&#xff1a; 向平衡二叉树中插入节点并不一定会改变其平衡性。平衡二叉树(如AVL树…

angular 图斑点击,列表选中并滚动到中间位置

如图所示&#xff1a; html代码&#xff1a; 1. #listContainer 2. [attr.data-id]"center.id" <div class"resTableCss" #listContainer><div *ngFor"let center of tbList" [attr.data-id]"center.id" class"res-it…

Java线程同步的简单理解

为什么需要线程同步 对于以下代码&#xff1a;两个线程对同一个变量分别进行100000次加一和减一操作&#xff0c;但是每次运行的输出基本都是不同的&#xff08;注意线程的join操作保证了两个线程都运行完之后才执行System.out.println&#xff09; import org.junit.Test;pu…

Makefile的通用模板 + 倒计时小程序(13)

文章目录 Makefile 的通用模板1. Makefile 的推导原则2. 设计 Makefile 的通用模板3. 通用模板代码&#xff08;可以直接拿来用&#xff09; Linux 第一个系统程序-进度条&#xff08;7-3.00.00&#xff09;1. 补充回车与换行2. 行缓冲区3. 倒计时小程序 Makefile 的通用模板 …

【ArcGIS】水文分析与流域划分

【ArcGIS】水文分析与流域划分 一、基础数据处理1、下载数据2、拼接DEM数据3、填充洼地4、流向分析5、流量分析6、河网生成&#xff08;栅格计算器&#xff09;7、河网分级8、河流链接&#xff08;提取子流域的关键&#xff09; 二、多个小流域提取1、捕捉倾泻点2、集水区&…