CompletableFuture详解

目录

介绍

Future介绍

CompletableFuture介绍

CompletableFuture常用的API介绍

常用的静态方法源码解析

runAsync

源码

案例

结果 

 supplyAsync

源码

案例

结果

规律

CompletableFuture获取返回值方法介绍

返回值区别

代码演示

返回结果 

CompletableFuture其他常用的API

thenApply

源码 

案例

结果

thenCompose

源码

案例

结果

thenAccept和thenRun 

源码

案例

结果

complete

源码

案例

结果

whenComplete

源码

案例

结果 

exceptionally 

源码

案例

结果

 whenComplete+exceptionally案例

结果

 handle

源码

案例

结果

 allof

源码

案例

结果

anyOf

源码

案例

结果

提示


介绍

Future介绍

了解CompletableFuture可以先了解Future,可以查看下面这篇文章

Callable、Future和FutrueTask详解-CSDN博客

CompletableFuture介绍

CompletableFuture是一个类,主要实现了Future和ComletionStage两个接口。因此,CompletableFuture包含了Futrure和CompletionStage的功能。

CompletableFuture常用的API介绍

官方推荐CompletableFutrue静态方法,所以我们先介绍它的静态方法

常用的静态方法源码解析

线程池工具类

线程池工具类_java线程池工具类-CSDN博客

runAsync

runAsync:无返回值,可以自定义线程池,如果没有自定义线程池,默认使用ForkJoinPool线程池

源码
// 默认线程池 
public static CompletableFuture<Void> runAsync(Runnable runnable) {return asyncRunStage(asyncPool, runnable);}
// 自定义线程池public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor) {return asyncRunStage(screenExecutor(executor), runnable);}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture.runAsync(() -> {System.out.println(Thread.currentThread().getName() + "  -------执行异步任务,无返回值结果---------");});CompletableFuture.runAsync(() -> {System.out.println(Thread.currentThread().getName() + "  -----自定义线程执行异步任务,无返回结果------");}, ThreadPoolUtils.getThreadPool());}
结果 

ForkJoinPool.commonPool-worker-9  -------执行异步任务,无返回值结果---------
myPool-0  -----自定义线程执行异步任务,无返回结果------

 supplyAsync

supplyAsync:有返回值,可以自定义线程池,如果没有自定义线程池,默认使用ForkJoinPool线程池

源码
//默认线程池public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) {return asyncSupplyStage(asyncPool, supplier);}
// 自定义线程池
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor) {return asyncSupplyStage(screenExecutor(executor), supplier);}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> completableFuture1 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "  ------执行异步任务,有返回值------");return "返回值1";});System.out.println(completableFuture1.get());CompletableFuture<String>  completableFuture2 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "  ------自定义线程池执行异步任务,有返回值------");return "返回值2";}, ThreadPoolUtils.getThreadPool());System.out.println(completableFuture2.get());}
结果

ForkJoinPool.commonPool-worker-9  ------执行异步任务,有返回值------
返回值1
myPool-0  ------自定义线程池执行异步任务,有返回值------
返回值2

规律

  • supply开头:可以返回异步执行的结果
  • run开头:不会返回结果,只是执行线程任务

CompletableFuture获取返回值方法介绍

  • join:返回结果或者抛出一个unchecked异常(CompletionException),不需要显式捕获异常
  • get:此方法继承Future的get阻塞方法,返回结果或者一个具体的异常(ExecutionException,InterruptedException)
  • getNow:如果当前任务执行完成,返回执行结果,否则返回默认值

返回值区别

  • join与get区别在于join()返回计算的结果或者抛出一个异常,而get会返回一个具体的异常
  • getNow与join和get的区别,getNow返回当前执行好的结果,如果当前未执行完,则返回设定好的默认值。

代码演示

public static void main(String[] args) {CompletableFuture completableFuture1 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "  ------执行异步任务,有返回值------");return "返回值1";});try {System.out.println("get():" + completableFuture1.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "  ------自定义线程池执行异步任务,有返回值------");return "返回值2";}, ThreadPoolUtils.getThreadPool());System.out.println("join():" + completableFuture2.join());CompletableFuture completableFuture3 = CompletableFuture.supplyAsync(() -> {System.out.println(Thread.currentThread().getName() + "  ------自定义线程池执行异步任务,有返回值------");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}return "返回值3";}, ThreadPoolUtils.getThreadPool());System.out.println("------这一步,直接返回默认值------");System.out.println("getNow():" + completableFuture3.getNow("默认值"));try {System.out.println("-----进行线程阻塞,等待completableFuture3内容执行完成-----");System.out.println("get():" + completableFuture3.get());} catch (ExecutionException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("-----当前异步任务已经执行完成,返回结果-----");System.out.println("getNow():" + completableFuture3.getNow("默认值"));}

返回结果 

ForkJoinPool.commonPool-worker-9  ------执行异步任务,有返回值------
get():返回值1
myPool-0  ------自定义线程池执行异步任务,有返回值------
join():返回值2
------这一步,直接返回默认值------
getNow():默认值
-----进行线程阻塞,等待completableFuture3内容执行完成-----
myPool-1  ------自定义线程池执行异步任务,有返回值------
get():返回值3
-----当前异步任务已经执行完成,返回结果-----
getNow():返回值3

CompletableFuture其他常用的API

thenApply

thenApply拿到上一步异步线程返回的结果进行后续处理。可以拿到上一次线程执行的返回结果,并且可以一直传递下去。

源码 
public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {return uniApplyStage(null, fn);}
案例
public static void main(String[] args) {String str = "上海";String str2 = "深圳";CompletableFuture<StringBuffer> future = CompletableFuture.supplyAsync(() -> {return new StringBuffer("北京");// a是上一步异步线程返回的结果}, ThreadPoolUtils.getThreadPool()).thenApply(a -> a.append(str)).thenApply(b -> b.append(str2));try {System.out.println(future.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}
结果

北京上海深圳

thenCompose

thenCompose方法功能跟thenApply相似,都是会在上一个任务执行完成以后,拿到上一步的执行结果,进行后续处理,不同的是,两个方法的参数不一致,并且,thenCompose在执行的时候,需要创建一个新的CompletableFuture。

源码
  public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) {return uniComposeStage(null, fn);}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {/*** supply直接返回,会得到一个CompletableFuture<String>* 经过thenApply处理,CompletableFuture<String>会转成一个CompletableFuture<Integer>* 但CompletableFuture是同一个*/CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {// 返回一个字符串return "北京";// a是上一步异步线程返回的结果}, ThreadPoolUtils.getThreadPool()).thenApply(a -> {if ("北京".equals(a)) {return 1;} else {return 0;}});System.out.println(future.get());CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync(() -> {// 返回一个字符串return "北京";// 重新再创建一个CompleteFuture}, ThreadPoolUtils.getThreadPool()).thenCompose(a -> CompletableFuture.supplyAsync(() -> {if ("北京".equals(a)) {return 1;} else {return 0;}}));System.out.println(completableFuture.get());}
结果

1

1
1

thenAccept和thenRun 

 thenAccept和thenRun:进行返回值回调。这样CompletableFuture就会没有返回值

源码
public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {return uniAcceptStage(null, action);}public CompletableFuture<Void> thenRun(Runnable action) {return uniRunStage(null, action);}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {//模拟计算 1+1+1CompletableFuture future = CompletableFuture.supplyAsync(() -> {return 1;}, ThreadPoolUtils.getThreadPool()).thenApply(a -> a + 1).thenAccept(b -> System.out.println(b + 1));System.out.println("thenAccept返回了什么?" + future.get());CompletableFuture future2 = CompletableFuture.supplyAsync(() -> {return 1;}, ThreadPoolUtils.getThreadPool()).thenApply(a -> a + 1).thenRun(()->{System.out.println("----------线程运行----------");});System.out.println("thenRun返回了什么?" + future2.get());}
结果

3
thenAccept返回了什么?null
----------线程运行----------
thenRun返回了什么?null

两者区别:两者入参不同, thenRun参数是Runnable ,thenAccept参数是Consumer<? super T> action,thenAccept可以拿到上一步获取到的值。

complete

complete()方法用于手动完成一个异步任务,并设置其结果。一旦调用complete()方法,CompleteFuture对象的状态会立即变成已完成。如果多个线程尝试调用complete()方法,只有第一个成功的线程能够设置结果,其他线程调用将被忽略

源码
public boolean complete(T value) {boolean triggered = completeValue(value);postComplete();return triggered;
}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {return "10";}, ThreadPoolUtils.getThreadPool());future.complete("1000");future.complete("10000");//输出1000System.out.println(future.get());}
结果

调用complete以后:1000

whenComplete

whenComplete是一个回调方法,会将CompletableFuture执行的结果和异常传递给它,如果是正常执行,则异常为null,如果异常,则get()方法会抛出异常。

源码
public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) {return uniWhenCompleteStage(null, action);}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {return "10";}, ThreadPoolUtils.getThreadPool()).whenComplete((v,e)->{System.out.println("获取到的异常:"+e);System.out.println("获取到的值:"+v);});// 这个地方会抛出异常System.out.println("whenComplete无异常回调以后以后:" + completableFuture.get());CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {int i = 1 / 0;return "10";}, ThreadPoolUtils.getThreadPool()).whenComplete((v,e)->{System.out.println("获取到的异常:"+e);System.out.println("获取到的值:"+v);});// 这个地方会抛出异常System.out.println("whenComplete有异常回调以后:" + future.get());}
结果 
获取到的异常:null
获取到的值:10
whenComplete无异常回调以后以后:10
获取到的异常:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
获取到的值:null
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.ArithmeticException: / by zeroat java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)at com.common.base.util.CompletableFutureTest2.main(CompletableFutureTest2.java:30)
Caused by: java.lang.ArithmeticException: / by zeroat com.common.base.util.CompletableFutureTest2.lambda$main$2(CompletableFutureTest2.java:23)at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)at java.lang.Thread.run(Thread.java:748)

exceptionally 

exceptionally()对CompletableFuture异常进行捕获处理,并且设定一个返回值

源码
public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) {return uniExceptionallyStage(fn);}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {int i = 1 / 0;return "10";}, ThreadPoolUtils.getThreadPool()).exceptionally(e -> {//e.printStackTrace();System.out.println("执行的异常:" + e.getMessage());return "捕获异常以后自定义返回值";});System.out.println("捕获异常以后:" + future.get());}
结果
执行的异常:java.lang.ArithmeticException: / by zero
捕获异常以后:捕获异常以后自定义返回值

注意:大多数的时候我们通常都会把whenComplete+exceptionally结合起来一起使用

 whenComplete+exceptionally案例

Callable、Future和FutrueTask详解-CSDN博客

有这么一个问题,你的女朋友正在炒菜,发现厨房没盐了,需要你把新买的盐拿过来,如果使用FutureTask,那么会造成阻塞,现在使用CompletFuture来解决这个问题

public static void main(String[] args) throws InterruptedException {System.out.println("------------女朋友开始炒菜----------------");try {CompletableFuture.supplyAsync(() -> {return "让男朋友去买盐";}, ThreadPoolUtils.getThreadPool()).whenComplete((v, e) -> {if (e == null) {System.out.println("获取到任务:" + v);}}).exceptionally(e -> {e.printStackTrace();System.out.println("执行异常:" + e.getCause());return null;});System.out.println("----------女朋友继续炒菜-----------------");Thread.sleep(2000);System.out.println("-----------------女朋友炒菜结束--------------");} catch (Exception e) {e.printStackTrace();}}
结果

------------女朋友开始炒菜----------------
获取到任务:让男朋友去买盐
----------女朋友继续炒菜-----------------
-----------------女朋友炒菜结束--------------

通过上面代码我们可以知道,主线程女朋友炒菜可以一步都到底,不会出现线程阻塞的情况

 handle

handle()方法是一个回调方法,跟whenComplete没有多大的区别,唯一的区别是handle有返回值

源码
public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) {return uniHandleStage(null, fn);}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {return "10";}, ThreadPoolUtils.getThreadPool()).handle((v, e) -> {System.out.println("不存在异常的值" + e);System.out.println("上一步返回值:" + v);return "100";});//这个地方会抛出异常System.out.println("无异常回调handle以后:" + future.get());CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {int i = 1 / 0;return "10";}, ThreadPoolUtils.getThreadPool()).handle((v, e) -> {System.out.println("不存在异常的值" + e);System.out.println("上一步返回值:" + v);return "返回异常" + e.getMessage();});//这个地方会抛出异常System.out.println("有异常回调handle以后:" + completableFuture.get());}
结果

不存在异常的值:null
上一步返回值:10
无异常回调handle以后:100
不存在异常的值:java.util.concurrent.CompletionException: java.lang.ArithmeticException: / by zero
上一步返回值:null
有异常回调handle以后:返回异常java.lang.ArithmeticException: / by zero

 allof

allof是将多个CompletableFuture合并在一起

源码
    public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {return andTree(cfs, 0, cfs.length - 1);}
案例
 public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {return "用户信息";}, ThreadPoolUtils.getThreadPool());//角色idCompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}return "角色信息";}, ThreadPoolUtils.getThreadPool());// 组合用户和角色idCompletableFuture future2 = CompletableFuture.allOf(future, future1);CompletableFuture<String> completableFuture = future2.thenApply(a -> {return future.join()+" " + future1.join();});//通过回调函数,获取对应的结果集System.out.println("等待两个线程执行完毕,进行合并" + completableFuture.join());}
结果

等待两个线程执行完毕,进行合并用户信息 角色信息

anyOf

多个任务中,那个任务先返回,就先返回哪个结果 

源码
   public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {return orTree(cfs, 0, cfs.length - 1);}
案例
public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {return "用户信息";}, ThreadPoolUtils.getThreadPool());//角色idCompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}return "角色信息";}, ThreadPoolUtils.getThreadPool());// 组合用户和角色idCompletableFuture future2 = CompletableFuture.anyOf(future, future1);System.out.println("anyOf谁先执行完,谁就先返回:" + future2.join());}
结果

anyOf谁先执行完,谁就先返回:用户信息

提示

CompletableFuture提供了很多方法,上面的方法都是CompletableFuture提供的同步方法,如果在上面的方法后缀加上Async,那么成了调用它的异步方法,比如thenApplyAsync()。

参考文章:CompletableFuture使用详解 - 简书

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

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

相关文章

【Docker】Swarm内部的负载均衡与VIP

在Docker Swarm中&#xff0c;有两种方式可以实现内部的负载均衡&#xff1a;Service VIP和Routing Mesh。 Service VIP&#xff08;Virtual IP&#xff09;&#xff1a;Service VIP是一种基于VIP的负载均衡方式&#xff0c;它为每个服务分配一个虚拟IP地址。当请求到达Servic…

Word异常退出文档找回怎么操作?4个正确恢复方法!

“刚刚我在用word编辑文档&#xff0c;但是突然word就显示异常了&#xff0c;然后莫名其妙就自动退出了&#xff0c;这可怎么办&#xff1f;我还有机会找回这些文档吗&#xff1f;” 当我们在使用Microsoft Word时&#xff0c;突然遭遇到程序异常退出的情况&#xff0c;可能会让…

TCP 连接建立

1&#xff1a;TCP 三次握手过程是怎样的&#xff1f; 客户端和服务端都处于 CLOSE 状态&#xff0c;服务端主动监听某个端口&#xff0c;处于 LISTEN 状态 第一次握手&#xff1a;客户端带着序号和SYN为1&#xff0c;把第一个 SYN 报文发送给服务端&#xff0c;客户端处于 SYN-…

Elasticsearch:对时间序列数据流进行降采样(downsampling)

降采样提供了一种通过以降低的粒度存储时间序列数据来减少时间序列数据占用的方法。 指标&#xff08;metrics&#xff09;解决方案收集大量随时间增长的时间序列数据。 随着数据老化&#xff0c;它与系统当前状态的相关性越来越小。 降采样过程将固定时间间隔内的文档汇总为单…

【论文】Bao:一种用于现代多核嵌入式系统的轻型静态分区管理程序

Bao&#xff1a;一种用于现代多核嵌入式系统的轻型静态分区管理程序 个人学习过程中 Bao Hypervisor 论文翻译&#xff08;借助翻译工具个人校对&#xff09;&#xff0c;仅供学习使用&#xff0c;由于个人对一些技术专有名词不够熟悉&#xff0c;翻译不当的地方欢迎指出 论文地…

[Docker]十二.Docker consul集群搭建、微服务部署,Consul集群+Swarm集群部署微服务实战

一.Docker consul集群搭建 Consul 是 Go 语言写的开源的服务发现软件&#xff0c; Consul 具有 服务发现、健康检查、 服务治理、微服务熔断处理 等功能,在微服务中讲过如何搭建consul集群&#xff0c;接下来看看在 Dokcer 中如何去创建搭建consul 集群 1.linux上面部署consul集…

Qt 天气预报项目

参考引用 QT开发专题-天气预报 1. JSON 数据格式 1.1 什么是 JSON JSON (JavaScript Object Notation)&#xff0c;中文名 JS 对象表示法&#xff0c;因为它和 JS 中对象的写法很类似 通常说的 JSON&#xff0c;其实就是 JSON 字符串&#xff0c;本质上是一种特殊格式的字符串…

【UE】剔除环境颜色

效果 步骤 1. 新建一个空白项目&#xff0c;勾选光线追踪选项 2. 新建一个Basic关卡 3. 添加初学者内容包到内容浏览器 4. 新建一个材质“M_Red” 打开“M_Red”&#xff0c;设置基础颜色为红色 在场景中随便布置一些物品&#xff0c;然后给其中的一个球体设置材质为“M_Red”…

Oracle--索引

文章目录 一、索引是什么?二、索引的原理三、索引的特征四、创建索引的方式五、怎么确认索引六、案列七、复合索引 一、索引是什么? 索引&#xff08;INDEX&#xff09;是数据库中用于提高查询效率的一种数据结构。它可以加速数据库表的数据查找、过滤和排序等操作。索引是一…

python 协程

1. 协程 协程&#xff0c;又称微线程&#xff0c;纤程。英文名Coroutine。 https://www.cnblogs.com/coder-qi/p/10163416.html 协程不是计算机提供的&#xff0c;是人为创造的上下文切换技术&#xff0c;也可以被称为微线程。简而言之 其实就是在一个线程中实现代码块相互切…

Lesson 08 string类 (中)

C&#xff1a;渴望力量吗&#xff0c;少年&#xff1f; 文章目录 二、string类的介绍与使用2. 使用&#xff08;5&#xff09;string类对象的修改操作 三、拷贝1. 引入2. 浅拷贝3. 深拷贝 总结 二、string类的介绍与使用 2. 使用 &#xff08;5&#xff09;string类对象的修改…

Scrum敏捷开发流程及支撑工具

Scrum是一种敏捷开发框架&#xff0c;用于管理复杂的项目。以下这些步骤构成了Scrum敏捷开发流程的核心。通过不断迭代、灵活应对变化和持续反馈&#xff0c;Scrum框架帮助团队快速交付高质量的产品。 以下是Scrum敏捷开发流程的基本步骤&#xff1a; 产品Backlog创建&#xf…

微服务--06--Sentinel 限流、熔断

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.微服务保护雪崩问题服务保护方案1.1.请求限流1.2.线程隔离1.3.服务熔断 2.Sentinel2.1.介绍和安装官方网站&#xff1a;[https://sentinelguard.io/zh-cn/](https…

打造高效项目团队,离不开有效的反馈机制

为了确保项目高效交付&#xff0c;项目经理需要在管理过程中及时发现问题并解决&#xff0c;所以80%的时间都在进行沟通以及各种项目汇报。但项目经理往往会陷入低频沟通、无意义汇报的困局&#xff0c;进而导致四处救火、项目各种延误、团队的工作效率低下。例如&#xff1a; …

Leetcode2336 无限集中的最小数字

题目&#xff1a; 现有一个包含所有正整数的集合 [1, 2, 3, 4, 5, ...] 。 实现 SmallestInfiniteSet 类&#xff1a; SmallestInfiniteSet() 初始化 SmallestInfiniteSet 对象以包含 所有 正整数。int popSmallest() 移除 并返回该无限集中的最小整数。void addBack(int nu…

VERAS:AI驱动的Revit可视化渲染插件

Veras 是一款基于生成式AI 的可视化工具&#xff0c;可以使用自然语言生成3D渲染效果&#xff0c;兼容Revit、Rhino 和 SketchUp。Veras for Revit工具使用 Revit 模型内部的 3D 视图。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编…

如何把ipa文件(iOS安装包)安装到iPhone手机上? 附方法汇总

文章目录 概要整体架构流程技术名词解释技术细节小结 概要 技术细节 目录 Appuploader 常见错误及解决方法 苹果APP安装包ipa如何安装在手机上&#xff1f;很多人不知道怎么把ipa文件安装到手机上&#xff0c;这里就整理了苹果APP安装到iOS设备上的方式&#xff0c;仅供参考 苹…

说说你对slot的理解?slot使用场景有哪些?

面试官&#xff1a;说说你对slot的理解&#xff1f;slot使用场景有哪些&#xff1f; 一、slot是什么 在HTML中 slot 元素 &#xff0c;作为 Web Components 技术套件的一部分&#xff0c;是Web组件内的一个占位符 该占位符可以在后期使用自己的标记语言填充 举个栗子 <t…

安科瑞智能照明系统在福建二建大厦项目上的应用

【摘要】&#xff1a;智能化已经成为当今建筑发展的主流技术、涵盖从空调系统、消防系统到安全防范系统以及完善的计算机网络和通信系统。但是长期以来、智能照明在国内一直被忽视、大多数建筑物仍然沿用传统的照明控制方式、部分智能大厦采用楼宇自控&#xff08;BA&#xff0…

vue2全局混入正确使用场景和错误场景示例

全局混入在 Vue.js 中的使用场景需要谨慎考虑&#xff0c;因为它会影响所有组件。以下是一些正确和错误的使用场景的例子&#xff1a; 正确的使用场景&#xff1a; 全局工具方法&#xff1a; // 正确的使用场景 Vue.mixin({methods: {$formatDate: function (date) {// 格式化…