Mockito教程

https://www.cnblogs.com/Ming8006/p/6297333.html

目录

1 Mockito 介绍
  1.1 Mockito是什么?
  1.2 为什么需要Mock
  1.3 Stub和Mock异同
  1.4 Mockito资源
  1.5 使用场景
2 使用Mockito 
  2.1 验证行为
  2.2 模拟我们所期望的结果
  2.3 RETURNS_SMART_NULLS和RETURNS_DEEP_STUBS
  2.4 模拟方法体抛出异常
  2.5 使用注解来快速模拟 
  2.6 参数匹配
  2.7 自定义参数匹配
  2.8 捕获参数来进一步断言
  2.9 使用方法预期回调接口生成期望值(Answer结构)
  2.10 修改对未预设的调用返回默认期望
  2.11 用spy监控真实对象 
  2.12 真实的部分mock
  2.13 重置mock
  2.14 验证确切的调用次数
  2.15 连续调用
  2.16 验证执行顺序
  2.17 确保模拟对象上无互动发生
  2.18 找出冗余的互动(即未被验证到的)
3 Mockito如何实现Mock
参考 

1 Mockito 介绍 [3]


 返回

1.1 Mockito是什么?

Mockito是mocking框架,它让你用简洁的API做测试。而且Mockito简单易学,它可读性强和验证语法简洁。

1.2 为什么需要Mock

测试驱动的开发( TDD)要求我们先写单元测试,再写实现代码。在写单元测试的过程中,我们往往会遇到要测试的类有很多依赖,这些依赖的类/对象/资源又有别的依赖,从而形成一个大的依赖树,要在单元测试的环境中完整地构建这样的依赖,是一件很困难的事情。如下图所示: 

为了测试类A,我们需要Mock B类和C类(用虚拟对象来代替)如下图所示:

1.3 Stub和Mock异同[1]

  • 相同:Stub和Mock都是模拟外部依赖
  • 不同:Stub是完全模拟一个外部依赖, 而Mock还可以用来判断测试通过还是失败 

1.4 Mockito资源

  • 官网: http://mockito.org
  • API文档:http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html
  • 项目源码:https://github.com/mockito/mockito 

1.5 使用场景

  • 提前创建测试; TDD(测试驱动开发)
  • 团队可以并行工作
  • 你可以创建一个验证或者演示程序
  • 为无法访问的资源编写测试
  • Mock 可以交给用户
  • 隔离系统  

2 使用Mockito [2][4]


 返回

添加maven依赖

      <dependency><groupId>org.mockito</groupId><artifactId>mockito-all</artifactId><version>1.9.5</version><scope>test</scope></dependency>

添加junit依赖

      <dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency>

添加引用

import static org.mockito.Mockito.*;
import static org.junit.Assert.*;

2.1 验证行为

 

    @Testpublic void verify_behaviour(){//模拟创建一个List对象List mock = mock(List.class);//使用mock的对象mock.add(1);mock.clear();//验证add(1)和clear()行为是否发生verify(mock).add(1);verify(mock).clear();}

 

2.2 模拟我们所期望的结果

 

    @Testpublic void when_thenReturn(){//mock一个Iterator类Iterator iterator = mock(Iterator.class);//预设当iterator调用next()时第一次返回hello,第n次都返回worldwhen(iterator.next()).thenReturn("hello").thenReturn("world");//使用mock的对象String result = iterator.next() + " " + iterator.next() + " " + iterator.next();//验证结果assertEquals("hello world world",result);}

 

 

    @Test(expected = IOException.class)public void when_thenThrow() throws IOException {OutputStream outputStream = mock(OutputStream.class);OutputStreamWriter writer = new OutputStreamWriter(outputStream);//预设当流关闭时抛出异常doThrow(new IOException()).when(outputStream).close();outputStream.close();}

 

2.3 RETURNS_SMART_NULLS和RETURNS_DEEP_STUBS

RETURNS_SMART_NULLS实现了Answer接口的对象,它是创建mock对象时的一个可选参数,mock(Class,Answer)。

在创建mock对象时,有的方法我们没有进行stubbing,所以调用时会放回Null这样在进行操作是很可能抛出NullPointerException。如果通过RETURNS_SMART_NULLS参数创建的mock对象在没有调用stubbed方法时会返回SmartNull。例如:返回类型是String,会返回"";是int,会返回0;是List,会返回空的List。另外,在控制台窗口中可以看到SmartNull的友好提示。

 

    @Testpublic void returnsSmartNullsTest() {List mock = mock(List.class, RETURNS_SMART_NULLS);System.out.println(mock.get(0));//使用RETURNS_SMART_NULLS参数创建的mock对象,不会抛出NullPointerException异常。另外控制台窗口会提示信息“SmartNull returned by unstubbed get() method on mock”System.out.println(mock.toArray().length);}

 

RETURNS_DEEP_STUBS也是创建mock对象时的备选参数

RETURNS_DEEP_STUBS参数程序会自动进行mock所需的对象,方法deepstubsTest和deepstubsTest2是等价的

 

    @Testpublic void deepstubsTest(){Account account=mock(Account.class,RETURNS_DEEP_STUBS);when(account.getRailwayTicket().getDestination()).thenReturn("Beijing");account.getRailwayTicket().getDestination();verify(account.getRailwayTicket()).getDestination();assertEquals("Beijing",account.getRailwayTicket().getDestination());}@Testpublic void deepstubsTest2(){Account account=mock(Account.class); RailwayTicket railwayTicket=mock(RailwayTicket.class);        when(account.getRailwayTicket()).thenReturn(railwayTicket); when(railwayTicket.getDestination()).thenReturn("Beijing");account.getRailwayTicket().getDestination();verify(account.getRailwayTicket()).getDestination();    assertEquals("Beijing",account.getRailwayTicket().getDestination());}    public class RailwayTicket{private String destination;public String getDestination() {return destination;}public void setDestination(String destination) {this.destination = destination;}        }public class Account{private RailwayTicket railwayTicket;public RailwayTicket getRailwayTicket() {return railwayTicket;}public void setRailwayTicket(RailwayTicket railwayTicket) {this.railwayTicket = railwayTicket;}}

 

2.4 模拟方法体抛出异常

    @Test(expected = RuntimeException.class)public void doThrow_when(){List list = mock(List.class);doThrow(new RuntimeException()).when(list).add(1);list.add(1);}

2.5 使用注解来快速模拟 

在上面的测试中我们在每个测试方法里都mock了一个List对象,为了避免重复的mock,是测试类更具有可读性,我们可以使用下面的注解方式来快速模拟对象:

    @Mockprivate List mockList;

OK,我们再用注解的mock对象试试 

    @Testpublic void shorthand(){mockList.add(1);verify(mockList).add(1);}

运行这个测试类你会发现报错了,mock的对象为NULL,为此我们必须在基类中添加初始化mock的代码

 

public class MockitoExample2 {@Mockprivate List mockList;public MockitoExample2(){MockitoAnnotations.initMocks(this);}@Testpublic void shorthand(){mockList.add(1);verify(mockList).add(1);}
}

 

或者使用built-in runner:MockitoJUnitRunner

 

@RunWith(MockitoJUnitRunner.class)
public class MockitoExample2 {@Mockprivate List mockList;@Testpublic void shorthand(){mockList.add(1);verify(mockList).add(1);}
}

 

2.6 参数匹配

 

    @Testpublic void with_arguments(){Comparable comparable = mock(Comparable.class);//预设根据不同的参数返回不同的结果when(comparable.compareTo("Test")).thenReturn(1);when(comparable.compareTo("Omg")).thenReturn(2);assertEquals(1, comparable.compareTo("Test"));assertEquals(2, comparable.compareTo("Omg"));//对于没有预设的情况会返回默认值assertEquals(0, comparable.compareTo("Not stub"));}

 

除了匹配制定参数外,还可以匹配自己想要的任意参数

 

    @Testpublic void with_unspecified_arguments(){List list = mock(List.class);//匹配任意参数when(list.get(anyInt())).thenReturn(1);when(list.contains(argThat(new IsValid()))).thenReturn(true);assertEquals(1, list.get(1));assertEquals(1, list.get(999));assertTrue(list.contains(1));assertTrue(!list.contains(3));}private class IsValid extends ArgumentMatcher<List>{@Overridepublic boolean matches(Object o) {return o == 1 || o == 2;}}

 

注意:如果你使用了参数匹配,那么所有的参数都必须通过matchers来匹配,如下代码:

 

    @Testpublic void all_arguments_provided_by_matchers(){Comparator comparator = mock(Comparator.class);comparator.compare("nihao","hello");//如果你使用了参数匹配,那么所有的参数都必须通过matchers来匹配verify(comparator).compare(anyString(),eq("hello"));//下面的为无效的参数匹配使用//verify(comparator).compare(anyString(),"hello");}

 

2.7 自定义参数匹配

 

    @Testpublic void argumentMatchersTest(){//创建mock对象List<String> mock = mock(List.class);//argThat(Matches<T> matcher)方法用来应用自定义的规则,可以传入任何实现Matcher接口的实现类。when(mock.addAll(argThat(new IsListofTwoElements()))).thenReturn(true);mock.addAll(Arrays.asList("one","two","three"));//IsListofTwoElements用来匹配size为2的List,因为例子传入List为三个元素,所以此时将失败。verify(mock).addAll(argThat(new IsListofTwoElements()));}class IsListofTwoElements extends ArgumentMatcher<List>{public boolean matches(Object list){return((List)list).size()==2;}}

 

2.8 捕获参数来进一步断言

较复杂的参数匹配器会降低代码的可读性,有些地方使用参数捕获器更加合适。

 

       @Testpublic void capturing_args(){PersonDao personDao = mock(PersonDao.class);PersonService personService = new PersonService(personDao);ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);personService.update(1,"jack");verify(personDao).update(argument.capture());assertEquals(1,argument.getValue().getId());assertEquals("jack",argument.getValue().getName());}class Person{private int id;private String name;Person(int id, String name) {this.id = id;this.name = name;}public int getId() {return id;}public String getName() {return name;}}interface PersonDao{public void update(Person person);}class PersonService{private PersonDao personDao;PersonService(PersonDao personDao) {this.personDao = personDao;}public void update(int id,String name){personDao.update(new Person(id,name));}}

 

2.9 使用方法预期回调接口生成期望值(Answer结构)

 

@Testpublic void answerTest(){when(mockList.get(anyInt())).thenAnswer(new CustomAnswer());assertEquals("hello world:0",mockList.get(0));assertEquals("hello world:999",mockList.get(999));}private class CustomAnswer implements Answer<String>{@Overridepublic String answer(InvocationOnMock invocation) throws Throwable {Object[] args = invocation.getArguments();return "hello world:"+args[0];}}

 

也可使用匿名内部类实现

 

    @Testpublic void answer_with_callback(){//使用Answer来生成我们我们期望的返回when(mockList.get(anyInt())).thenAnswer(new Answer<Object>() {@Overridepublic Object answer(InvocationOnMock invocation) throws Throwable {Object[] args = invocation.getArguments();return "hello world:"+args[0];}});assertEquals("hello world:0",mockList.get(0));assertEquals("hello world:999",mockList.get(999));}

 

2.10 修改对未预设的调用返回默认期望

 

    @Testpublic void unstubbed_invocations(){//mock对象使用Answer来对未预设的调用返回默认期望值List mock = mock(List.class,new Answer() {@Overridepublic Object answer(InvocationOnMock invocation) throws Throwable {return 999;}});//下面的get(1)没有预设,通常情况下会返回NULL,但是使用了Answer改变了默认期望值assertEquals(999, mock.get(1));//下面的size()没有预设,通常情况下会返回0,但是使用了Answer改变了默认期望值assertEquals(999,mock.size());}

 

2.11 用spy监控真实对象  

  • Mock不是真实的对象,它只是用类型的class创建了一个虚拟对象,并可以设置对象行为
  • Spy是一个真实的对象,但它可以设置对象行为
  • InjectMocks创建这个类的对象并自动将标记@Mock、@Spy等注解的属性值注入到这个中

 

    @Test(expected = IndexOutOfBoundsException.class)public void spy_on_real_objects(){List list = new LinkedList();List spy = spy(list);//下面预设的spy.get(0)会报错,因为会调用真实对象的get(0),所以会抛出越界异常//when(spy.get(0)).thenReturn(3);//使用doReturn-when可以避免when-thenReturn调用真实对象apidoReturn(999).when(spy).get(999);//预设size()期望值when(spy.size()).thenReturn(100);//调用真实对象的apispy.add(1);spy.add(2);assertEquals(100,spy.size());assertEquals(1,spy.get(0));assertEquals(2,spy.get(1));verify(spy).add(1);verify(spy).add(2);assertEquals(999,spy.get(999));spy.get(2);}

 

2.12 真实的部分mock

 

    @Testpublic void real_partial_mock(){//通过spy来调用真实的apiList list = spy(new ArrayList());assertEquals(0,list.size());A a  = mock(A.class);//通过thenCallRealMethod来调用真实的apiwhen(a.doSomething(anyInt())).thenCallRealMethod();assertEquals(999,a.doSomething(999));}class A{public int doSomething(int i){return i;}}

 

2.13 重置mock

 

    @Testpublic void reset_mock(){List list = mock(List.class);when(list.size()).thenReturn(10);list.add(1);assertEquals(10,list.size());//重置mock,清除所有的互动和预设reset(list);assertEquals(0,list.size());}

 

2.14 验证确切的调用次数

 

    @Testpublic void verifying_number_of_invocations(){List list = mock(List.class);list.add(1);list.add(2);list.add(2);list.add(3);list.add(3);list.add(3);//验证是否被调用一次,等效于下面的times(1)verify(list).add(1);verify(list,times(1)).add(1);//验证是否被调用2次verify(list,times(2)).add(2);//验证是否被调用3次verify(list,times(3)).add(3);//验证是否从未被调用过verify(list,never()).add(4);//验证至少调用一次verify(list,atLeastOnce()).add(1);//验证至少调用2次verify(list,atLeast(2)).add(2);//验证至多调用3次verify(list,atMost(3)).add(3);}

 

2.15 连续调用

 

    @Test(expected = RuntimeException.class)public void consecutive_calls(){//模拟连续调用返回期望值,如果分开,则只有最后一个有效when(mockList.get(0)).thenReturn(0);when(mockList.get(0)).thenReturn(1);when(mockList.get(0)).thenReturn(2);when(mockList.get(1)).thenReturn(0).thenReturn(1).thenThrow(new RuntimeException());assertEquals(2,mockList.get(0));assertEquals(2,mockList.get(0));assertEquals(0,mockList.get(1));assertEquals(1,mockList.get(1));//第三次或更多调用都会抛出异常mockList.get(1);}

 

2.16 验证执行顺序

 

    @Testpublic void verification_in_order(){List list = mock(List.class);List list2 = mock(List.class);list.add(1);list2.add("hello");list.add(2);list2.add("world");//将需要排序的mock对象放入InOrderInOrder inOrder = inOrder(list,list2);//下面的代码不能颠倒顺序,验证执行顺序inOrder.verify(list).add(1);inOrder.verify(list2).add("hello");inOrder.verify(list).add(2);inOrder.verify(list2).add("world");}

 

2.17 确保模拟对象上无互动发生

 

    @Testpublic void verify_interaction(){List list = mock(List.class);List list2 = mock(List.class);List list3 = mock(List.class);list.add(1);verify(list).add(1);verify(list,never()).add(2);//验证零互动行为verifyZeroInteractions(list2,list3);}

 

2.18 找出冗余的互动(即未被验证到的)

 

    @Test(expected = NoInteractionsWanted.class)public void find_redundant_interaction(){List list = mock(List.class);list.add(1);list.add(2);verify(list,times(2)).add(anyInt());//检查是否有未被验证的互动行为,因为add(1)和add(2)都会被上面的anyInt()验证到,所以下面的代码会通过verifyNoMoreInteractions(list);List list2 = mock(List.class);list2.add(1);list2.add(2);verify(list2).add(1);//检查是否有未被验证的互动行为,因为add(2)没有被验证,所以下面的代码会失败抛出异常verifyNoMoreInteractions(list2);}

 

3 Mockito如何实现Mock[3]


 返回

Mockito并不是创建一个真实的对象,而是模拟这个对象,他用简单的when(mock.method(params)).thenRetrun(result)语句设置mock对象的行为,如下语句:

// 设置mock对象的行为 - 当调用其get方法获取第0个元素时,返回"first"
Mockito.when(mockedList.get(0)).thenReturn("first");

在Mock对象的时候,创建一个proxy对象,保存被调用的方法名(get),以及调用时候传递的参数(0),然后在调用thenReturn方法时再把“first”保存起来,这样,就有了构建一个stub方法所需的所有信息,构建一个stub。当get方法被调用的时候,实际上调用的是之前保存的proxy对象的get方法,返回之前保存的数据。

参考

[1] 单元测试之Stub和Mock

[2] mockito简单教程

[3] Mockito入门

[4] 学习Mockito

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

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

相关文章

日本科学家利用AI扫描大脑活动,「看到」了你正在「想什么」

原文来源&#xff1a;biorxiv作者&#xff1a;Guohua Shen、Tomoyasu Horikawa1、Kei Majima、Yukiyasu Kamitani「雷克世界」编译&#xff1a;嗯~阿童木呀、KABUDA你有没有想象过&#xff0c;将来的某一天&#xff0c;计算机可以“看到”你在想什么。图表由Kamitani实验室提供…

python正方形阴影面积计算_利用Python求阴影部分的面积实例代码

一、前言说明今天看到微信群里一道六年级数学题&#xff0c;如下图&#xff0c;求阴影部分面积看起来似乎并不是很难&#xff0c;可是博主添加各种辅助线&#xff0c;写各种方法都没出来&#xff0c;不得已而改用写Python代码来求面积了二、思路介绍1.用Python将上图画在坐标轴…

Yann LeCun力挺观点:算法对AI提升不大,奇点仍然很遥远

来源&#xff1a;AI前线策划&#xff5c;Tina&#xff0c;译者&#xff5c;薛命灯、核子可乐、Debra什么是奇点&#xff08;Singularity&#xff09;&#xff1f;奇点是指在未来某个假想的时间点&#xff0c;因为技术发展太过迅速&#xff0c;以致于达到我们无法理解的地步。奇…

PNAS:神经元网络对于长期记忆的重要性

来源&#xff1a;中国生物技术网概要&#xff1a;大脑如何长期存储记忆一直是神经科学家的一个永恒的谜团。大脑如何长期存储记忆一直是神经科学家的一个永恒的谜团。在一项新研究中&#xff0c;来自挪威奥斯陆大学整合神经可塑性研究中心(CINPLA)的研究人员发现&#xff0c;神…

语音入口大战升级!亚马逊Alexa放招:即插即用,霸占你所有空间

来源&#xff1a;AI科技大本营&#xff08;微信ID&#xff1a;rgznai100)翻译 | 刘畅编辑 | Donna&#xff0c;波波大多数人都认为&#xff0c;智能音箱就是语音入口之争的主战场……比如双十一期间&#xff0c;阿里巴巴不惜血本也要99元强推天猫精灵&#xff0c;亏损数亿终于砸…

解密人类变革时代:2018年13大科技趋势

来源&#xff1a;中国智慧城市导刊近日&#xff0c;美国著名创投研究机构CB Insights发布了《13 Trends Shaping Tech In 2018》报告&#xff0c;在通过对过去一年的科技研究归总分析后&#xff0c;CB Insights公布了2018年将会兴起并重塑科技行业的13大趋势。汽车可“订阅”使…

NovuMind 首秀 CES,展示业界最高性能的 AI 芯片

来源&#xff1a;极客公园概要&#xff1a;神秘低调的硅谷创业公司 NovuMind 亮相本届 CES&#xff0c;向业界首次展示其自主研发的高性能、低功耗的 AI 芯片&#xff0c;为业界带来前所未有的超强算力。2017 年被称作是「人工智能芯片元年」&#xff0c;各路巨头和创业公司奋力…

MIT重磅报告:一文看清AI商业化现状与未来

来源&#xff5c;公众号“AI 前线”&#xff0c;&#xff08;ID&#xff1a;ai-front&#xff09;作者&#xff5c;MIT斯隆管理评论编辑&#xff5c;Emily人工智能显然已经成为 2017 年最为火热的话题之一&#xff0c;越来越多的企业开始关注并探索人工智能的相关实践。为了了解…

Arthas 思维导图笔记

https://www.processon.com/view/link/60b9cd711efad420f6d868a8

揭秘高盛区块链报告:区块链技术在五大领域从理论走向实践 | 附报告下载

来源&#xff1a;亿欧概要&#xff1a;目前对区块链潜在作用讨论的焦点在于使用分布式账本建立去中心化市场&#xff0c;并削弱现有中间商的控制权&#xff0c;但区块链的潜力比这种单薄的说法来得更加细致也更加深远。最近&#xff0c;区块链很火&#xff0c;资本市场沾上“区…

星级评分--演进式部署

1、组件初始配置 2、改进版加入半星评分 3、所有服务都改 1、组件初始配置 2、改进版加入半星评分 3、所有服务都改

CES现场低调的主线,近在咫尺的5G商业化 | CES2018技术趋势

来源&#xff1a;36氪概要&#xff1a;5G的商业化眼看就要来了。同之前几年一样&#xff0c;在今年的CES上&#xff0c;5G也注定是关键词之一。5G技术的商用在日渐迫近&#xff0c;高通、英特尔等公司都在为此努力。5G作为一种基础性的技术&#xff0c;相比于时下风头正盛的AI、…

《Science》杂志:机器学习究竟将如何影响人类未来的工作?

作者&#xff1a;Smiletalker概要&#xff1a;《Science》杂志也发表了一篇长文&#xff0c;从几个不同角度详细阐述了机器学习对于未来人类工作的影响。人工智能、机器学习相关技术已经多次刷新了人们对于「计算机能做什么」的认知&#xff0c;那么紧接着的一个问题就是「计算…

部署流水线

1、多阶段部署流水线 2、包含适应度函数阶段的部署流水线 3、部署流水线扇出测试多个场景 1、多阶段部署流水线 2、包含适应度函数阶段的部署流水线 3、部署流水线扇出测试多个场景 扇入&#xff1a;是指直接调用该模块的上级模块的个数。扇入大表示模块的复用程度高。 扇出…

java函数和构造函数_JAVA的构造器和C++的构造函数有什么区别?

慕瓜9086354Java和C在构造函数中调用虚函数的区别首先可以认为&#xff0c;Java所有instance函数都是虚函数。C类的构造函数中调用虚函数&#xff0c;并不能有多态效果。这样设计的理由是&#xff1a;在基类的构造函数中&#xff0c;派生类的成员尚未初始化&#xff0c;而派生类…

Jeff Dean晒Google Brain团队2017成绩单!

来源&#xff1a;全球人工智能谷歌大脑负责人 Jeff Dean 今天发文晒AutoML、语言理解、机器学习算法、机器学习系统等核心研究工作成绩单&#xff0c;全面总结谷歌大脑在2017年取得的成绩。一、自动化机器学习&#xff08;AutoML&#xff09;我们开发了使用强化学习和进化算法设…

mysql恢复主服务器_MySQL 5.6主从复制第二部分[恢复某一台从服务器]

MySQL 5.6主从复制第二部分[恢复某一台从服务器]&#xff0c;本来第二部分是想测试主服务器挂掉&#xff0c;提升从服务器的情况&#xff0c;可是出了点点意外&#xff0c;改本来第二部分是想测试主服务器挂掉&#xff0c;提升从服务器的情况&#xff0c;可是出了点点意外&…

亮相2018CES:解读阿里云IoT筹谋全球物联网市场的一盘大棋!

来源&#xff1a;阿里巴巴概要&#xff1a;近年来国内物联网市场高歌猛进&#xff0c;2017年也被称为物联网真正意义上的元年。成立还不到一年的阿里云IoT&#xff0c;以平台、市场、标准三位一体的策略&#xff0c;迅速在物联网领域崭露头角、稳固地位。一年一度的CES在拉斯维…

CompletableFuture 异步编程

本文从实例出发&#xff0c;介绍 CompletableFuture 基本用法。不过讲的再多&#xff0c;不如亲自上手练习一下。所以建议各位小伙伴看完&#xff0c;上机练习一把&#xff0c;快速掌握 CompletableFuture。 全文摘要&#xff1a; Future VS CompletableFuture CompletableFu…

麦肯锡:物联网九大应用潜力无限 2025年经济价值高达11.1万亿美元

来源&#xff1a;WPR概要&#xff1a;物联网有可能从根本上改变人类与周围世界的互动方式。如果一切顺利的话&#xff0c;那么到2025年&#xff0c;物联网——将实体和数字世界连接起来的这项技术&#xff0c;其经济价值可达每年11&#xff0e;1万亿美元。物联网有可能从根本上…