简介: 欲善其事,先利其器。对于研发同学,在日常的开发工作中,我们与之打交道最多的便是编程的IDE。能否高效和灵活的使用IDE,将对我们的工作效率起着举足轻重的作用。

一 、背景

1 、目的

欲善其事,先利其器。对于研发同学,在日常的开发工作中,我们与之打交道最多的便是编程的IDE。能否高效和灵活的使用IDE,将对我们的工作效率起着举足轻重的作用。

研发同学在开发中最主要做的两件事分别是架构设计和编码,前者主要取决于大量的项目经验积累和个人的思考深度,也是作为研发的核心竞争力,短时间内很难快速求成;后者主要取决于日常的编码练习和一定程度的IDE信息差,能够通过下文中介绍的一系列技巧进行能力的快速补齐和巩固加强。

本文的主要目的有两方面:

  • 一方面,对IDE的快捷操作和高效技巧,结合自己多年的实践和理解,进行一次系统性的总结和梳理。
  • 另一方面,希望通过本文系统性的梳理,能够帮助更多的同学提高研发效率,无论你是刚入手不久的新人,还是有着多年开发经验的专家,相信你都能够在本文中发现一片新天地,让你能够有更多的时间和精力去做更有意义的事情。

2 、定位

网上很多技术网站和个人博客,对于IDE各种技巧和便捷操作总结得非常具体且详细,对于单点的详尽程度都是极具参考和学习价值的。但其对应的问题是,这些很多很优秀的文章,出自于不同的手笔,有各自的行文风格,且分散在各个网站的散点,难以系统化。

我对本文的定位是,将各种技巧以大分类的形式进行收拢和聚合,以帮助大家构建和完善整体的知识体系,大幅度提高开发效率。对于每个分类点到即止,替代咀嚼式灌输方式的是,尽量使用渐进式引导的方式。

3 、普适性

JetBrains系列的IDE产品众多,除了下图之外,还有其他未列入的,如Google二次开发的Android Studio等。虽然归为多个产品实例,但这些IDE的内核都是一样的,只是在内核的基础上额外添加了各自的语言特性。本文将以使用量最高的一款IDE——IDEA为例进行展开,文中提到的绝大多数能力和技巧,在其他IDE均同样适用,一通则百通。

二、 Postfix Completion

1 、介绍

Postfix Completion (下称Postfix) 是一种通过 . + 模板Key 来对当前已经输出的表达式,添加和应用预设代码模板的编码增强能力。

其核心要解决的问题是,将编码过程中一些通用的代码结构范式进行抽象和沉淀,并能在同类型的场景下,通过 . + 模板Key 的方式进行唤醒和复用。

举个例子,现在需要完成下面一段代码的编写,为了对name参数进行判空保护:

if (name != null) {

}

在普通文本编辑器中,其中 if 2次,name 4次,(){}!= 共6次,再加空格Tab和光标切换,一共需要按键23次。

在IDEA编辑器中,不使用Postfix时,一共需要按键20次,不考虑代码格式化的情况可以减少到16次。

在IDEA编辑器中,使用Postfix时,只需要8次,如下图:

在这个例子中,可以对比出使用Postfix前后的效果,使用之后在编码中减少了一半的手动按键操作,且生成的代码是自带格式化的。在实际的编码过程中,各项目大小和复杂度差异性虽然很大,但细化到这种基本单位的编程范式时,它们都是融会贯通的。

与上例中nn并列的Postfix,IDEA给我们预设的还有很多,下面对一些非常高频使用的Postfix进行梳理。

2 、梳理

var

快速定义一个局部变量,自带IDE的类型推断

notnull

快速进行NPE的判空保护:

nn

同notnull,是它的简写,推荐用这个,更加便捷:

try catch

快速对当前语句添加try catch异常捕获,同时IDE还会对catch中的Exception自动做类型推断:

cast

快速实现类型强转,不需要反复使用()包裹和光标切换;配合instanceof使用时还能自动实现cast类型的推断:

if

快速实现if判断的代码范式:

throw

快速实现抛异常:

for

快速实现集合或数组的迭代:

fori

快速实现集合或数组的带索引值迭代;同时对整型数字也支持:

sout/soutv

快速实现(不带参数/带参数)的打印功能:

return

快速实现方法中的值返回逻辑:

format

快速实现字符串格式化:

3 、高级用法

担心系统预设的Postfix不足以满足我们的编码需求,IDEA还提供了Postfix的自定义功能。

这里我以自定义一个对集合判空的代码范式,来举例说明自定义Postfix的流程:

1)进入IDE设置界面,然后依次进入Editor => General => Postfix Completion => 面板左下角加号 => Java:

2)在弹起的页面中,按照下图进行配置,然后保存退出设置页。

此时我们自定义的 isempty 这个Postfix即完成了,下面来看下实际使用的效果:

在实际开发过程中,对于根据已经输入的表达式就能决定接下来代码格式的功能,我们都能使用这种自定义方式进行代码的抽象和复用。

接下来介绍IDE中一种跟Postfix功能很相像,但灵活度更高的能力——Live Template。

三、 Live Template

1、 介绍

介绍之前可以先看一段简短的编码过程:

上面这段编码中,我先后使用了Live Template的以下三个模板能力:

  • psfs:定义字符串常量
  • main:添加入口函数
  • sout:实现日志输出

这里我们将其和上面提到的Postfix对比来看,两者都是提供代码级别模板的功能。不同的是,Postfix需要一个已经输入的表达式和 . + 模板Key 来进行触发,而Live Template不需要这些,它仅仅需要 模板Key 即可触发。

Live Template提供的预设模板要比Postfix要高出一个数量级,因此这里我就不进行一一演示,我们可以进行设置面板,然后按照Editor => Live Templates的路径自行查看,如下图:

2 、高级用法

和Postfix一样,Live Template也支持自定义模板,但它的自定义模板相对来说更加灵活和开放,甚至支持我们直接植入脚本。鉴于Live Template的高度灵活性,单独介绍这块会占据大量的篇幅,因此这里我将从几个实际的案例场景来开拓一下思路,而具体自定义拓展过程就不详细展开介绍了。

Key值映射

将DB中查询到 List 结构的数据,根据Key值映射转化为 Map<K, T> 结构的数据,以便于进行后续的数据填充逻辑:

DB批量查询

在数据查询时,我们会有根据ID主键进行批量DB数据查询的诉求,如下:

List<User> users = userMapper.queryUserByIds(userIds);

这种写法会有一个弊端,就是当 userIds 大到一定的量级时,该查询会变得非常耗时。

对于该问题其中一个解法是,将这个大的 userIds 拆分成多个批次,然后让这多个批次异步并行去查询。这里便使用Live Template来抽取一个针对该场景的代码模板,如下:

按照该模板,我们的查询语句将变成这样:

List<User> users = batchQuery(userIds, 100, userMapper::queryUserByIds, null);

可以看到,和之前相比,多传一个分批的size参数,同时还支持指定的异步任务调度器的自定义配置,而返回结果和之前的查询方式保持完全一致,不需要外部有额外的适配工作。

脚本植入

这个功能是我非常看好Live Template的主要原因,它的灵活性和拓展性也主要来源于这里。它支持我们通过一个 模板Key 来唤起和执行一段脚本,这也就意味着,我们的自定义的Live Template模板是可编程的,极大程度提高了该模板的拓展性。

单描述功能会有些空洞,这里我结合一个实际案例进行介绍,我们来实现一个跨电脑的代码共享功能:

1)首先,使用python的flask框架写一个极简的服务端应用并启动,提供最简单的 push 和 pull 的能力,如下:

from flask import Flask, request

DEFAULT = 'nothing'
code = DEFAULT app = Flask(__name__) @app.route('/push')
def push():
global code
code = request.args.get('code', DEFAULT)
return 'Success' @app.route('/pull')
def pull():
return code app.run()

2)然后,我们来通过groovy脚本实现一个代码 pull 的模板,这里应用了Live Template的 groovy script 能力,对应脚本如下:

def url = new URL('http://127.0.0.1:5000/pull');
def conn = url.openConnection() as HttpURLConnection;
def result = conn.inputStream.text;
return result

3)最后,再实现代码push的模板,脚本如下(下面的代码入参,是通过剪切板赋值传递过来的):

def code = _1;
def url = new URL('http://127.0.0.1:5000/push?code=' + new URLEncoder().encode(code));
def conn = url.openConnection() as HttpURLConnection;
def result = conn.inputStream.text;
return result

此时就已经完成了跨设备的代码分享功能,为方便演示,这里就用 People1 和 People2 两个类来模拟两台独立的电脑。People1 将自己的一段代码复制到剪切板中,然后通过 push 模板调用 push 接口来将这段代码上传到Python服务应用中;People2 再通过 pull 脚本来调用服务端的 pull 接口,访问到 People1 上传的代码并输入到当前的代码编辑器中,实现效果如下图:

这里的代码共享只是一个引子,除此之外,我们还能写很多有意思的脚本,比如在IDE中查天气、通过IDE聊天等等,自行脑补拓展。

介绍完Live Template之后,接下来介绍文件级别的模板——File Template。

四 、File Template

1 、介绍

File Template,顾名思义,对应文件级别的模板。对于该模板,我们使用脚本的主要在于两个场景,分别是文件头和文件的自定义,下面结合案例依次展开。

2 、自定义文件头

按照下图的路径,来更改文件头的格式,IDE就会在我们新建一个类或接口时,根据这里的配置格式来自动生成对应的文件注释头。

3 、抽象通用Controller

看下面一段代码,这是一个针对于User这个domain的增删改查接口类:

package com.alibaba.ide.code.controller;

import com.alibaba.ide.code.entity.Result;
import com.alibaba.ide.code.entity.User;
import com.alibaba.ide.code.service.Condition;
import com.alibaba.ide.code.service.UserService;
import org.springframework.web.bind.annotation.*; import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List; /**
* @author puke
* @version 2021/2/9
*/
@RestController
@RequestMapping("api/user")
public class UserController { @Resource
private UserService userService; @PostMapping
public Result<User> create(@RequestBody User record) {
User user = userService.insert(record);
return Result.success(user);
} @PutMapping
public Result<User> update(@RequestBody User record) {
User user = userService.update(record);
return Result.success(user);
} @DeleteMapping("{id}")
public Result<Void> deleteById(@PathVariable Serializable id) {
boolean success = userService.deleteById(id);
return success ? Result.success() : Result.fail();
} @GetMapping("{id}")
public Result<User> queryById(@PathVariable Serializable id) {
User user = userService.queryById(id);
return Result.success(user);
} @GetMapping
public Result<List<User>> queryByCondition(Condition<User> condition) {
List<User> list = userService.queryByCondition(condition);
return Result.success(list);
}
}

仔细看这段代码会发现,如果基于该接口再新增另一个domain对应的Controller接口类,代码中的基本结构和逻辑都是可以复用的。此时,便是File Template排上用场的地方,我们定义一个通用的 Controller 模板,将共性的部分抽象到模板里,再将差异性的部分通过模板入参 Subject 变量传入进来(注,这里需要用到Velocity 模板[1]的知识)。

#set($SubjectOfLowerFirst = ${Subject.substring(0,1).toLowerCase()} + $Subject.substring(1))
package ${PACKAGE_NAME}; import com.alibaba.ide.code.entity.Result;
import com.alibaba.ide.code.entity.${Subject};
import com.alibaba.ide.code.service.Condition;
import com.alibaba.ide.code.service.${Subject}Service;
import org.springframework.web.bind.annotation.*; import javax.annotation.Resource;
import java.io.Serializable;
import java.util.List; #parse("File Header.java")
@RestController
@RequestMapping("api/${SubjectOfLowerFirst}")
public class ${Subject}Controller { @Resource
private ${Subject}Service ${SubjectOfLowerFirst}Service; @PostMapping
public Result<${Subject}> create(@RequestBody ${Subject} record) {
${Subject} ${SubjectOfLowerFirst} = ${SubjectOfLowerFirst}Service.insert(record);
return Result.success(${SubjectOfLowerFirst});
} @PutMapping
public Result<${Subject}> update(@RequestBody ${Subject} record) {
${Subject} ${SubjectOfLowerFirst} = ${SubjectOfLowerFirst}Service.update(record);
return Result.success(${SubjectOfLowerFirst});
} @DeleteMapping("{id}")
public Result<Void> deleteById(@PathVariable Serializable id) {
boolean success = ${SubjectOfLowerFirst}Service.deleteById(id);
return success ? Result.success() : Result.fail();
} @GetMapping("{id}")
public Result<${Subject}> queryById(@PathVariable Serializable id) {
${Subject} ${SubjectOfLowerFirst} = ${SubjectOfLowerFirst}Service.queryById(id);
return Result.success(${SubjectOfLowerFirst});
} @GetMapping
public Result<List<${Subject}>> queryByCondition(Condition<${Subject}> condition) {
List<${Subject}> list = ${SubjectOfLowerFirst}Service.queryByCondition(condition);
return Result.success(list);
}
}

模板定义完成,接下来来看一下实际的使用效果:

这里使用 Goods 作为新的domain对象,可以看到,生成的 Controller 代码已经具备 UserController 的全部能力,并且生成的代码全部都是 Goods 相关的api,这样就实现了File Template的横向迁移能力。

五、 低频高效快捷键

1、 介绍

IDEA中的快捷键多达上百个,我们很难把每个都记清楚,网上也有很多对应的总结。这里我主要梳理一些,大家使用相对比较低频,但又非常高效的快捷键。

2 、梳理

选择重复元素:Control + G

通常情况下,我们可以使用 Shift + F6 对类名、方法名和变量名进行批量更改,但对于其他元素进行批量更改时,该快捷键特别合适,且不限编程语言。

批量框选:Option + 鼠标左键拖拽

对于"对齐"的代码进行批量更改的最优解,没有之一:

整行移动:Option + Shift + ↑/↓

快速调整代码执行顺序,免除繁琐的剪切粘贴过程:

整行/块复制:Command + D

对于整行/块的复制,效率远高于纯手动的复制粘贴:

展开/收起:Command + . or Command + Shift + +/-

前者,快速显示/隐藏当前方法体;后者,快速概览当前类的所有方法:

修改方法签名:Command + F6

在方法被多文件或多处调用时,该方式替换效率极高:

查看历史剪切板:Command + Shift + V

开发中经常会出现需要复制多个文本的诉求,而PC默认的剪切板只能保存一个,该功能专门用来解决这个痛点:

代码抽取

代码抽取主要用在代码重构的时候,以最快速度达到我们抽取一个变量、方法的目的。

1)抽局部变量:Command + Option + V

2)抽成员变量:Command + Option + F

3)抽静态常量:Command + Option + C

4)抽方法入参:Command + Option + P

5)抽方法:Command + Option + M

六、 代码调试

代码调试在开发中使用的非常多,常规的单步、多步、进入、跳出操作这里也不特殊说明了。

有一点值得说的就是,利用条件断点来实现运行期的代码植入功能,先看下图:

可以看到,Debug模式运行时,我们能动态改变 age 变量的值,本来被赋值为 20 的,结果输出出来却是 10。

这个是我在开发中无意间发现的一个功能,算是一个Trick了。但这个功能在实际的开发过程中特别有用,尤其针对于一些代码改动后再次运行的成本比较高的场景。比如Android开发过程中,能够在不重新打整包的情况下,动态修改页面中各个元素的样式、接口的请求、数据的内容等等;再比如服务端场景中,如果我们的应用支持Debug模式,则可以通过该功能实现应用无需重新部署的情况下,进行动态更改上下文逻辑的操作。

七、 写在最后

跬步至千里,小流成江海,开发工作有大小,业务需求有缓急,但终究要落到眼下,从一砖一瓦的基石开始,从一行一列的编码开始,希望本文中能帮助到更多的研发同学。

作者:开发者小助手_LS

原文链接

本文为阿里云原创内容,未经允许不得转载

收藏!这些IDE使用技巧,你都知道吗的更多相关文章

  1. JAVA学习之Ecplise IDE 使用技巧(2)第二章:键盘小快手,代码辅助

    上一篇:JAVA学习之Ecplise IDE 使用技巧(1)第一章:我的地盘我做主,工作空间 第二章:键盘小快手,代码辅助 内容包括: 第一:显示行号 如何设置行号:Ecplice菜单Windows& ...

  2. JAVA学习之Ecplise IDE 使用技巧(1)第一章:我的地盘我做主,工作空间

    麦子学院/Andriod应用开发/第一阶段 Android 学前准备 第三课:Eclipse IDE 使用技巧 由马一鸣老师讲解.感谢麦子学院免费开放这部分视频资源. Eclipse由IBM开发的,2 ...

  3. Web开发:需收藏的JavaScript常用技巧

    原文来自:https://www.baidu.com/home/news/data/newspage?nid=4242707244478773456&n_type=0&p_from=1 ...

  4. 41个Web开发者必须收藏的JavaScript实用技巧

    1. 将彻底屏蔽鼠标右键 oncontextmenu=”window.event.returnValue=false” < table border oncontextmenu=return(f ...

  5. 30段极简Python代码:这些小技巧你都Get了么

    学 Python 怎样才最快,当然是实战各种小项目,只有自己去想与写,才记得住规则.本文是 30 个极简任务,初学者可以尝试着自己实现:本文同样也是 30 段代码,Python 开发者也可以看看是不是 ...

  6. 20个值得收藏的实用JavaScript技巧

    1.确定对象的数据类型 function myType(type) { return Object.prototype.toString.call(type).slice(8, -1); 使用Obje ...

  7. PostgreSQL 图形化客户端工具的使用技巧你都get了吗?

    PostgreSQL 数据库作为目前功能较强大的开源数据库,得到了广泛应用.其中,TSA就用到了这款数据库来存储处理后的一些业务数据.虽然PostgreSQL自身提供了命令行交互式客户端工具psql, ...

  8. JetBrains公司的IDE使用技巧

    1.自定义Live Templates:   点击+添加自己的.最后记住要点击,change或default来设置在哪些文件上使用代码片段.

  9. from VC的IDE使用技巧大全

    .cpp是c++源文件 .opt 工程关于开发环境的参数文件.如工具条位置等信息: .aps (AppStudio File),资源辅助文件,二进制格式,一般不用去管他. .clw ClassWiza ...

  10. Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!

    今天介跟大家分享一下我平时阅读源码的几个小技巧,对于阅读java中间件如Spring.Dubbo等框架源码的同学有一定帮助. 本文基于Eclipse IDE,我们每天都使用的IDE其实提供了很多强大的 ...

随机推荐

  1. Android 开发Day5

    展示项目 这是我的项目结构,补录的

  2. 06.Java虚拟机问题

    目录介绍 6.0.0.1 运行时数据区域有哪些?Java虚拟机栈是做什么的?本地方法栈又是做什么的? 6.0.0.2 对象的内存布局?对象的访问定位方式有哪些?使用指针访问和使用句柄访问各具有何优势? ...

  3. 安装npm install报错npm ERR! code ETIMEDOUT npm ERR! errno ETIMEDOUT npm ERR! network request to https://registry.npmjs.org/webpack-subresource-integrity failed, reason

    执行命令:npm run dev 启动前端项目报如下错误,vue-cli-service是Vue一个启动的插件,需要安装 D:\nodejs\npm.cmd run dev > yuntan1h ...

  4. Bad magic number for central directory

    Bad magic number for central directory 运行代码输出如下bug: File "/home/a/Prediction/Predict_Models.py& ...

  5. 《Effective Java》笔记 4~5

    4. 类和接口 15. 使类和成员的可访问性最小化 把API与实现清晰地隔离开,组件间通过API进行通信,不需要知道其他模块的内部工作情况,这称为:实现信息隐藏或封装 解耦系统中的各个组件 尽可能地使 ...

  6. C# OpenCvSharp+ 微信二维码引擎实现二维码识别

    微信开源了其二维码的解码功能,并贡献给 OpenCV 社区.其开源的 wechat_qrcode 项目被收录到 OpenCV contrib 项目中.从 OpenCV 4.5.2 版本开始,就可以直接 ...

  7. MySQL8.0 ERROR 1045 (28000)

    第一步:关闭服务 net stop mysql 这个需要在管理员权限才行 ,具体怎么用管理员打开cmd略过 第二步:进入到安装的bin目录 执行 :mysqld --console --skip-gr ...

  8. C++设计模式 - 代理模式(Proxy)

    接口隔离模式 在组件构建过程中,某些接口之间直接的依赖常常会带来很多问题.甚至根本无法实现.采用添加一层间接(稳定)接口,来隔离本来互相紧密关联的接口是一种常见的解决方案. 典型模式 Facade P ...

  9. Java也可以像python般range出连续集合

    Java lamada:IntStream --range(int startInclusive, int endExclusive):前包后不包 IntStream.range(0, 10).for ...

  10. 使用OHOS SDK构建lua

    参照OHOS IDE和SDK的安装方法配置好开发环境. 从github下载源码. 执行如下命令: git clone --depth=1 https://github.com/lua/lua.git ...