收集!这些IDE技巧你知道吗?对于研发同学来说,在日常的开发工作中,编程IDE是我们接触最多的。能否高效、灵活地使用IDE,对我们的工作效率起着决定性的作用。一、背景1、目的工欲善其事,必先利其器。对于研发同学来说,在日常的开发工作中,编程IDE是我们接触最多的。能否高效、灵活地使用IDE,对我们的工作效率起着决定性的作用。R&D学生在开发中主要做的两件事是架构设计和编码。前者主要靠大量项目经验的积累和个人思考的深度。这也是研发的核心竞争力。短时间内很难见效。;后者主要靠日常的编码实践和一定程度的IDE资料差距,可以通过下面介绍的一系列技巧快速补充和巩固。这篇文章的主要目的有两个:一方面是结合自己多年的实践和理解,对IDE的快捷操作和高效技巧进行系统的总结和梳理。另一方面,希望通过本文的系统回顾,能够帮助更多的同学提高研发效率。无论你是新手还是有多年开发经验的高手,相信都能在本文中找到一片新天地。让你有更多的时间和精力去做更有意义的事情。2、在网上定位了很多技术网站和个人博客,非常具体详细的总结了IDE的各种技巧和便捷操作,单点的详细程度,很有参考和学习价值。但相应的问题是,这些优秀的文章很多来自不同的文体,各有文风,散落在各个网站上,难以系统化。我对这篇文章的定位是以大类的形式收集和聚合各种技能,帮助大家构建和完善整体的知识体系,大大提高开发效率。就各分类点而言,尽量采用循序渐进的指导,而不是咀嚼滴注。3.通用性JetBrains系列中有很多IDE产品。除了下图,还有其他没有列出来的,比如AndroidStudio,是Google二次开发的。虽然归类为多个产品实例,但这些IDE的内核都是相同的,只是在内核的基础上增加了额外的语言特性。本文将以使用最广泛的IDEIDEA为例。文中提到的大部分能力和技巧也适用于其他IDE。2.PostfixCompletion1.简介PostfixCompletion(以下简称Postfix)是一种编码增强能力,通过对当前输出的表达式添加并应用预设的代码模板。+模板键。核心要解决的问题是在编码过程中抽象沉淀出一些通用的代码结构范式,并在同类型场景中通过.+模板键。例如,现在需要写下面这段代码,为了保护name参数不为null:if(name!=null){}在普通的文本编辑器中,if2次,name4次,(){}!=一共6次,加上空格Tab和光标切换,一共需要23次按键。在IDEA编辑器中,在不使用Postfix的情况下,总共需要敲击20次,无论代码格式如何,都可以减少到16次。在IDEA编辑器中,使用Postfix时,只需要8次,如下图所示:本例可以对比使用Postfix前后的效果。使用后,手动按键操作编码减少一半,生成的代码自带格式化。在实际的编码过程中,虽然每个项目的规模和复杂度差别很大,但是当它们细化到这个编程范式的基本单元时,它们都是集成在一起的。对于上面例子中与nn并列的Postfix,IDEA已经为我们预设了很多。下面整理一下一些非常常用的Postfix。2.整理var快速定义局部变量,IDE内置类型推断notnull快速进行NPE空保护:nn与notnull相同,是它的简写。推荐使用这个,比较方便:trycatch快速将trycatch添加到当前语句异常捕获中,同时IDE会自动对catch中的Exception进行类型推断:cast快速实现类型无需重复使用()换行和光标切换的转换;与instanceof一起使用,还可以自动实现cast类型推断:if是fast实现if判断的代码范式:throw快速实现抛出异常:for快速实现集合或数组的迭代:fori快速实现集合或数组的迭代具有索引值;同时还支持整数:sout/soutv快速实现(无参数/有参数)打印功能:return快速实现方法中的值返回逻辑:format快速实现字符串格式化:3.高级用法担心系统预置的Postfix不足以满足我们的编码需求,IDEA还提供了Postfix自定义功能。这里我以自定义代码范式判断空集合为例来说明自定义Postfix的过程:1)进入IDE设置界面,然后在左下方进入Editor=>General=>PostfixCompletion=>加号面板一角=>Java:2)在弹出的页面中,按照下图进行配置,然后保存退出设置页面。至此,我们自定义的isemptyPostfix就完成了。下面看一下实际使用效果:在实际开发过程中,我们可以使用该函数根据输入的表达式来判断下一个代码格式。一种抽象和重用代码的自定义方法。接下来介绍IDE中一个类似Postfix但更加灵活的能力——LiveTemplate。三、LiveTemplate1、在介绍之前,可以看一个简短的编码过程:在上面的编码中,我使用了LiveTemplate的以下三种模板能力:psfs:定义字符串常量main:添加入口函数sout:实现Log输出这里我们将其与上面提到的Postfix进行对比,两者都提供了代码级的模板。区别在于Postfix需要输入表达式和.+templateKey来触发,但是LiveTemplate不需要这些,它只需要模板Key来触发。LiveTemplate提供的预设模板比Postfix高一个数量级,这里就不一一演示了。我们可以设置面板,然后按照Editor=>LiveTemplates的路径自行查看,如下图:2.Advanced和Postfix一样,LiveTemplate也支持自定义模板,但是它的自定义模板相对更加灵活并且开放,甚至支持我们直接植入脚本。鉴于LiveTemplate的灵活性高,单独介绍会占用很多篇幅,所以这里我会从几个实际案例场景出发展开思路,具体的自定义扩展过程就不详细介绍了。Key-valuemapping是将在DB中查询到的List结构中的数据,根据Key-value映射转换成Map结构中的数据,方便后续的数据填充逻辑:在DB中批量查询数据时,我们将根据批量查询DB数据的ID主键如下:Listusers=userMapper.queryUserByIds(userIds);这种写法有一个缺点,就是当userIds足够大时,查询会变得非常耗时。解决这个问题的一种方法是将大的userId拆分成多个batch,然后让这多个batch异步并行查询。这里我们使用LiveTemplate为这个场景提取一个代码模板,如下:根据这个模板,我们的查询语句会变成这样:Listusers=batchQuery(userIds,100,userMapper::queryUserByIds,null);可以看出,与之前相比,多了一个batchsize参数的传递,同时还支持自定义配置指定的异步任务调度器,返回结果与之前的查询方式完全一致,并且无需额外的外部Adapt工作。脚本植入的功能是我非常看好LiveTemplate的主要原因,它的灵活性和扩展性主要来源于这里。它支持我们通过模板Key来调用和执行脚本,这意味着我们自定义的LiveTemplate模板是可编程的,大大提高了模板的可扩展性。单一的描述功能会有些空洞。下面我就以一个实际案例来介绍一下。下面来实现一个跨机代码共享功能:1)首先,使用python的flask框架编写一个极简服务端应用并启动,提供最简单的推拉能力如下:fromflaskimportFlask,requestDEFAULT='nothing'code=DEFAULTapp=Flask(__name__)@app.route('/push')defpush():全局代码code=request.args。get('code',DEFAULT)return'Success'@app.route('/pull')defpull():returncodeapp.run()2)然后,我们通过groovy脚本实现一个代码拉取模板,这里是groovy应用了LiveTemplate的脚本能力,对应的脚本如下:defurl=newURL('http://127.0.0.1:5000/pull');defconn=url.openConnection()asHttpURLConnection;defresult=连接。输入流.文本;返回结果3)最后实现代码推送模板,脚本如下(以下代码入参通过剪贴板赋值):defcode=_1;defurl=newURL('http://127.0.0.1:5000/push?code='+newURLEncoder().encode(code));defconn=url.openConnection()asHttpURLConnection;defresult=conn.inputStream.text;returnresultnow跨设备代码共享功能已经完成。为了演示方便,这里使用People1和People2类来模拟两台独立的计算机。People1复制一段代码到剪贴板,然后通过推送模板调用推送接口将代码上传到Python服务应用;People2通过pull脚本调用服务端的pull接口访问People1上传的代码,并输入到当前代码编辑器中,效果如下:这里的代码分享只是一个介绍,除此之外,我们还可以这样写许多有趣的脚本,例如在IDE中查看天气,通过IDE聊天等。扩展您自己的思维。介绍完LiveTemplate,接下来就是介绍文件级模板——FileTemplate。四、FileTemplate1、介绍FileTemplate,顾名思义,对应文件级模板。对于这个模板,我们主要在两种场景下使用脚本,分别是文件头和文件的自定义,下面会依次展开。2.自定义文件头按照下图中的路径更改文件头的格式,当我们新建类或接口时,IDE会根据这里配置的格式自动生成对应的文件注释头。3.AbstractUniversalController看下面一段代码,是一个对域User进行增删改查的接口类:packagecom.alibaba.ide.code.controller;importcom.alibaba.ide.code。entity.Result;导入com.alibaba.ide.code.entity.User;导入com.alibaba.ide.code.service.Condition;导入com.alibaba.ide.code.service.UserService;导入org.springframework.web。bind.annotation.*;导入javax.annotation.Resource;导入java.io.Serializable;导入java.util.List;/***@authorpuke*@version2021/2/9*/@RestController@RequestMapping("api/user")publicclassUserController{@ResourceprivateUserServiceuserService;@PostMappingpublicResultcreate(@RequestBodyUserrecord){Useruser=userService.insert(record);返回结果.成功(用户);}@PutMappingpublicResultupdate(@RequestBodyUserrecord){Useruser=userService.更新(记录);返回结果。成功(用户);}@DeleteMapping("{id}")publicResultdeleteById(@PathVariableSerializableid){booleansuccess=userService.deleteById(id);返回成功?结果.成功():结果.失败();}@GetMapping("{id}")publicResultqueryById(@PathVariableSerializableid){Useruser=userService.queryById(id);返回结果.成功(用户);}@GetMappingpublicResult>queryByCondition(Conditioncondition){Listlist=userService.queryByCondition(condition);返回结果.成功(列表);}}仔细看这段代码,你会发现,如果在这个接口的基础上再增加一个域对应的Controller接口类,代码中的基本结构和逻辑在这个时候是可以复用的,即文件模板派上用场的地方。我们定义一个通用的Controller模板,将通用的部分抽象到模板中,然后通过模板入参Subject变量传入不同的部分(注意,这里需要Velocity模板知识[1])。#set($SubjectOfLowerFirst=${Subject.substring(0,1).toLowerCase()}+$Subject.substring(1))package${PACKAGE_NAME};导入com.alibaba.ide.code.entity.Result;导入com.alibaba.ide.code.entity.${Subject};importcom.alibaba.ide.code.service.Condition;importcom.alibaba.ide.code.service.${Subject}Service;importorg.springframework.web.bind.annotation.*;导入javax.annotation.Resource;导入java.io.Serializable;导入java.util.List;#parse("FileHeader.java")@RestController@RequestMapping("api/${SubjectOfLowerFirst}")publicclass${Subject}Controller{@Resourceprivate${Subject}Service${SubjectOfLowerFirst}Service;@PostMappingpublicResult<${Subject}>create(@RequestBody${Subject}record){${Subject}${SubjectOfLowerFirst}=${SubjectOfLowerFirst}Service.insert(record);返回Result.success(${SubjectOfLowerFirst});}@PutMappingpublicResult<${Subject}>update(@RequestBody${Subject}记录){${Subject}${SubjectOfLowerFirst}=${SubjectOfLowerFirst}Service.update(record);返回Result.success(${SubjectOfLowerFirst});}@DeleteMapping("{id}")publicResultdeleteById(@PathVariableSerializableid){booleansuccess=${SubjectOfLowerFirst}Service.deleteById(id);返回成功?结果.成功():结果.失败();}@GetMapping("{id}")publicResult<${Subject}>queryById(@PathVariableSerializableid){${Subject}${SubjectOfLowerFirst}=${SubjectOfLowerFirst}Service.queryById(id);返回Result.success(${SubjectOfLowerFirst});}@GetMappingpublicResult>queryByCondition(Condition<${Subject}>条件){List<${Subject}>list=${SubjectOfLowerFirst}Service.queryByCondition(条件);返回结果.成功(列表);}}模板定义完成,来看看实际使用效果:这里使用了Goods作为新的领域对象,可以看到,生成的Controller代码已经有了UserController的所有能力,生成的代码都是Goods相关的API,从而实现了FileTemplate的水平迁移能力。五、低频高效的快捷键1、介绍IDEA中有数百个快捷键。每一个都记得清楚,对应的总结网上也有很多。这里我主要梳理了一些,大家使用频率比较低,但是效率很高的快捷键。2、整理和选择重复的元素:Control+G通常我们可以使用Shift+F6来批量更改类名、方法名和变量名,但是这个快捷键特别适合批量更改其他元素,没有限制编程语言。批框选择:Option+鼠标左键拖放批量更改“对齐”代码的最优解,没有之一:整行移动:Option+Shift+↑/↓快速调整代码执行顺序,避免繁琐的裁剪剪切粘贴过程:复制整行/块:Command+D对于整行/块的复制,效率比纯手工复制粘贴要高很多:展开/折叠:Command+。或Command+Shift++/-前者快速显示/隐藏当前方法体;后者,快速概览当前类的所有方法:修改方法签名:Command+F6当方法被多个文件或多个地方调用时,此方法极其高效:查看历史剪贴板:Command+Shift+V在开发中,经常需要复制多篇文字,而PC默认的剪贴板只能保存一篇。这个功能就是专门用来解决这个痛点的:代码提取代码提取主要用于代码重构,达到我们尽快提取一个变量和方法的目的。1)提取局部变量:Command+Option+V2)提取成员变量:Command+Option+F3)提取静态常量:Command+Option+C4)提取方法输入参数:Command+Option+P5)提取方法:Command+Option+M6。代码调试代码调试在开发中用的比较多,常规的单步、多步、进入、退出等操作这里不做特别说明。值得一提的是使用条件断点来实现运行时的代码植入功能,首先看下图:可以看到在运行Debug模式的时候,我们可以动态改变age变量的值,这个值本来是赋值为20,输出为10。这是我在开发中无意中发现的一个功能,也算是一个Trick。但是这个功能在实际开发过程中特别有用,特别是一些代码改动后重新运行成本比较高的场景。例如,在Android开发过程中,可以动态修改页面中各个元素的样式、界面的请求、数据的内容等,而无需重新打包整个包;再比如在服务端场景,如果我们的应用支持Debug模式,就可以使用这个函数动态改变context逻辑,而不需要重新部署应用。7.写在最后。步履万里,细流成江海,开发工作有大小,业务需求有轻重缓急,但终究会落到现在,从每一块砖的基石开始,从一行一行的编码开始,一个专栏,希望在这篇文章中能够帮助到更多的研发同学。作者:开发助手_LS原文链接本文为阿里云原创内容,未经允许不得转载