• 收藏!这些 IDE 使用技巧,你都知道吗


    头图.png

    作者 | 璞珂
    来源 | 阿里巴巴云原生公众号

    背景

    1. 目的

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

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

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

    • 一方面,对 IDE 的快捷操作和高效技巧,结合自己多年的实践和理解,进行一次系统性的总结和梳理。

    • 另一方面,希望通过本文系统性的梳理,能够帮助更多的同学提高研发效率,无论你是刚入手不久的新人,还是有着多年开发经验的专家,相信你都能够在本文中发现一片新天地,让你能够有更多的时间和精力去做更有意义的事情。

    2. 定位

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

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

    3. 普适性

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

    1.png

    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 次,如下图:

    2.gif

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

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

    2. 梳理

    1)var

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

    3.gif

    2)notnull

    快速进行 NPE 的判空保护:

    4.gif

    3)nn

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

    5.gif

    4)try catch

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

    6.gif

    5)cast

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

    7.gif

    6)if

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

    8.gif

    7)throw

    快速实现抛异常:

    9.gif

    8)for

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

    10.gif

    9)fori

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

    11.gif

    10)sout/soutv

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

    12.gif

    11)return

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

    13.gif

    12)format

    快速实现字符串格式化:

    14.gif

    3. 高级用法

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

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

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

    15.png

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

    16.png

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

    17.gif

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

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

    Live Template

    1. 介绍

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

    18.gif

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

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

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

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

    19.png

    2. 高级用法

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

    1)Key 值映射

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

    20.gif

    2)DB 批量查询

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

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

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

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

    21.gif

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

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

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

    3)脚本植入

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

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

    • 首先,使用 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()
    
    • 然后,我们来通过 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
    
    • 最后,再实现代码 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 上传的代码并输入到当前的代码编辑器中,实现效果如下图:

    22.gif

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

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

    File Template

    1. 介绍

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

    2. 自定义文件头

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

    23.png

    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 模板的知识)。

    #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);
        }
    }
    

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

    24.gif

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

    低频高效快捷键

    1. 介绍

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

    2. 梳理

    1)选择重复元素:Control + G

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

    25.gif

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

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

    26.gif

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

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

    27.gif

    4)整行/块复制:Command + D

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

    28.gif

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

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

    29.gif

    6)修改方法签名:Command + F6

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

    30.gif

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

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

    31.gif

    8)代码抽取

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

    • 抽局部变量:Command + Option + V

    32.gif

    • 抽成员变量:Command + Option + F

    33.gif

    • 抽静态常量:Command + Option + C

    34.gif

    • 抽方法入参:Command + Option + P

    35.gif

    • 抽方法:Command + Option + M

    36.gif

    代码调试

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

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

    37.gif

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

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

    写在最后

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

  • 相关阅读:
    python主要探索函数
    数据分析分析方法
    监控hadoop任务结果shell脚本
    shell编程
    hadoop介绍
    数据探索
    Python数据分析简介
    数据挖掘基础篇之整体思路
    sqlAlchemy
    python md5 加密
  • 原文地址:https://www.cnblogs.com/alisystemsoftware/p/14500209.html
Copyright © 2020-2023  润新知