关于java:收藏这些-IDE-使用技巧你都知道吗

36次阅读

共计 8482 个字符,预计需要花费 22 分钟才能阅读完成。

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

背景

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. 梳理

1)var

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

2)notnull

疾速进行 NPE 的判空爱护:

3)nn

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

4)try catch

疾速对以后语句增加 try catch 异样捕捉,同时 IDE 还会对 catch 中的 Exception 主动做类型推断:

5)cast

疾速实现类型强转,不须要重复应用 () 包裹和光标切换;配合 instanceof 应用时还能主动实现 cast 类型的推断:

6)if

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

7)throw

疾速实现抛异样:

8)for

疾速实现汇合或数组的迭代:

9)fori

疾速实现汇合或数组的带索引值迭代;同时对整型数字也反对:

10)sout/soutv

疾速实现(不带参数 / 带参数)的打印性能:

11)return

疾速实现办法中的值返回逻辑:

12)format

疾速实现字符串格式化:

3. 高级用法

放心零碎预设的 Postfix 不足以满足咱们的编码需要,IDEA 还提供了 Postfix 的自定义性能。

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

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

  • 在弹起的页面中,依照下图进行配置,而后保留退出设置页。

此时咱们自定义的 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 的高度灵活性,独自介绍这块会占据大量的篇幅,因而这里我将从几个理论的案例场景来开辟一下思路,而具体自定义拓展过程就不具体开展介绍了。

1)Key 值映射

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

2)DB 批量查问

在数据查问时,咱们会有依据 ID 主键进行批量 DB 数据查问的诉求,如下:

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

这种写法会有一个弊病,就是当 userIds 大到肯定的量级时,该查问会变得十分耗时。

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

依照该模板,咱们的查问语句将变成这样:

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 上传的代码并输出到以后的代码编辑器中,实现成果如下图:

这里的代码共享只是一个引子,除此之外,咱们还能写很多有意思的脚本,比方在 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 模板的常识)。

#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. 梳理

1)抉择反复元素:Control + G

通常状况下,咱们能够应用 Shift + F6 对类名、办法名和变量名进行批量更改,但对于其余元素进行批量更改时,该快捷键特地适合,且不限编程语言。

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

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

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

疾速调整代码执行程序,罢黜繁琐的剪切粘贴过程:

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

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

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

前者,疾速显示 / 暗藏以后办法体;后者,疾速概览以后类的所有办法:

6)批改办法签名:Command + F6

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

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

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

8)代码抽取

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

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

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

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

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

  • 抽办法:Command + Option + M

代码调试

代码调试在开发中应用的十分多,惯例的单步、多步、进入、跳出操作这里也不非凡阐明了。

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

能够看到,Debug 模式运行时,咱们能动静扭转 age 变量的值,原本被赋值为 20 的,后果输入进去却是 10。

这个是我在开发中无意间发现的一个性能,算是一个 Trick 了。但这个性能在理论的开发过程中特地有用,尤其针对于一些代码改变后再次运行的老本比拟高的场景。比方 Android 开发过程中,可能在不从新打整包的状况下,动静批改页面中各个元素的款式、接口的申请、数据的内容等等;再比方服务端场景中,如果咱们的利用反对 Debug 模式,则能够通过该性能实现利用无需重新部署的状况下,进行动静更改上下文逻辑的操作。

写在最初

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

正文完
 0