用了这两款插件,同事再也不说我代码写的烂了
ztj100 2025-05-11 19:46 4 浏览 0 评论
同事:你的代码写的不行啊,不够规范啊。
我:我写的代码怎么可能不规范,不要胡说。
于是同事打开我的 IDEA ,安装了一个插件,然后执行了一下,规范不规范,看报告吧。
这可怎么是好,这玩意竟然给我挑出来这么多问题,到底靠谱不。
同事潇洒的走掉了,只留下我在座位上盯着屏幕惊慌失措。我仔细的查看了这个报告的每一项,越看越觉得这插件指出的问题有道理,果然是我大意了,竟然还给我挑出一个 bug 来。
这是什么插件,review 代码无敌了。
这个插件就是 SonarLint,官网的 Slogan 是 clean code begins in your IDE with {SonarLint}。
作为一个程序员,我们当然希望自己写的代码无懈可击了,但是由于种种原因,有一些问题甚至bug都无法避免,尤其是刚接触开发不久的同学,也有很多有着多年开发经验的程序员同样会有一些不好的代码习惯。
代码质量和代码规范首先肯定是靠程序员自身的水平和素养决定的,但是提高水平的是需要方法的,方法就有很多了,比如参考大厂的规范和代码、比如有大佬带着,剩下的就靠平时的一点点积累了,而一些好用的插件能够时时刻刻提醒我们什么是好的代码规范,什么是好的代码。
SonarLint 就是这样一款好用的插件,它可以实时帮我们 review代码,甚至可以发现代码中潜在的问题并提供解决方案。
SonarLint 使用静态代码分析技术来检测代码中的常见错误和漏洞。例如,它可以检测空指针引用、类型转换错误、重复代码和逻辑错误等。这些都是常见的问题,但是有时候很难发现。使用 SonarLint 插件,可以在编写代码的同时发现这些问题,并及时纠正它们,这有助于避免这些问题影响应用程序的稳定性。
比如下面这段代码没有结束循环的条件设置,SonarLint 就给出提示了,有强迫症的能受的了这红下划线在这儿?
SonarLint 插件可以帮助我提高代码的可读性。代码应该易于阅读和理解,这有助于其他开发人员更轻松地维护和修改代码。 SonarLint 插件可以检测代码中的代码坏味道,例如不必要的注释、过长的函数和变量名不具有描述性等等。通过使用 SonarLint 插件,可以更好地了解如何编写清晰、简洁和易于理解的代码。
例如下面这个名称为 hello_world的静态 final变量,SonarLint 给出了两项建议。
- 因为变量没有被使用过,建议移除;
- 静态不可变变量名称不符合规范;
SonarLint 插件可以帮助我遵循最佳实践和标准。编写符合标准和最佳实践的代码可以确保应用程序的质量和可靠性。 SonarLint 插件可以检测代码中的违反规则的地方,例如不安全的类型转换、未使用的变量和方法、不正确的异常处理等等。通过使用 SonarLint 插件,可以学习如何编写符合最佳实践和标准的代码,并使代码更加健壮和可靠。
例如下面的异常抛出方式,直接抛出了 Exception,然后 SonarLint 建议不要使用 Exception,而是自定义一个异常,自定义的异常可能让人直观的看出这个异常是干什么的,而不是 Exception基本类型导出传递。
安装 SonarLint
可以直接打开 IDEA 设置 -> Plugins,在 MarketPlace中搜索SonarLint,直接安装就可以。
还可以直接在官网下载,打开页面
https://www.sonarsource.com/products/sonarlint/,在页面中可以看到多种语言、多种开发工具的下载图标,点击下方的 EXPLORE即可到下载页面去下载了。虽然我们只是在 IDEA 中使用,但是它不管支持 Java 、不只支持 IDEA ,还支持 Python、PHP等众多语言,以及 Visual Studio 、VS Code 等众多 IDE。
在 IDEA 中使用
SonarLint 插件安装好之后,默认就开启了实时分析的功能,就跟智能提示的功能一样,随着你噼里啪啦的敲键盘,SonarLint插件就默默的进行分析,一旦发现问题就会以红框、红波浪线、黄波浪线的方式提示。
当然你也可以在某一文件中点击右键,也可在项目根目录点击右键,在弹出菜单中点击Analyze with SonarLint,对当前文件或整个项目进行分析。
分析结束后,会生成分析报告。
左侧是对各个文件的分析结果,右侧是对这个问题的建议和修改示例。
SonarLint 对问题分成了三种类型
类型 | 说明 |
Bug | 代码中的 bug,影响程序运行 |
Vulnerability | 漏洞,可能被作为攻击入口 |
Code smell | 代码意味,可能影响代码可维护性 |
问题按照严重程度分为5类
严重性 | 说明 |
BLOCKER | 已经影响程序正常运行了,不改不行 |
CRITICAL | 可能会影响程序运行,可能威胁程序安全,一般也是不改不行 |
MAJOR | 代码质量问题,但是比较严重 |
MINOR | 同样是代码质量问题,但是严重程度较低 |
INFO | 一些友好的建议 |
SonarQube
SonarLint 是在 IDE 层面进行分析的插件,另外还可以使用 SonarQube功能,它以一个 web 的形式展现,可以为整个开发团队的项目提供一个web可视化的效果。并且可以和 CI\CD 等部署工具集成,在发版前提供代码分析。
SonarQube是一个 Java 项目,你可以在官网下载项目本地启动,也可以以 docker 的方式启动。之后可以在 IDEA 中配置全局 SonarQube配置。
也可以在 SonarQube web 中单独配置一个项目,创建好项目后,直接将 mvn 命令在待分析的项目中执行,即可生成对应项目的分析报告,然后在 SonarQube web 中查看。
对于绝大多数开发者和开发团队来说,SonarQube 其实是没有必要的,只要我们每个人都解决了 IDE 中 SonarLint 给出的建议,当然最终的代码质量就是符合标准的。
阿里 Java 规约插件
每一个开发团队都有团队内部的代码规范,比如变量命名、注释格式、以及各种类库的使用方式等等。阿里一直在更新 Java 版的阿里巴巴开发者手册,有什么泰山版、终极版,想必各位都听过吧,里面的规约如果开发者都能遵守,那别人恐怕再没办法 diss 你的代码不规范了。
对应这个开发手册的语言层面的规范,阿里也出了一款 IDEA 插件,叫做 Alibaba Java Coding Guidelines,可以在插件商店直接下载。
比如前面说的那个 hello_world变量名,插件直接提示「修正为以下划线分隔的大写模式」。
再比如一些注释上的提示,不建议使用行尾注释。
还有,比如对线程池的使用,有根据规范建议的内容,建议自己定义核心线程数和最大线程数等参数,不建议使用 Excutors工具类。
有了这俩插件,看谁还能说我代码写的不规范了。
写出优雅代码的20个技巧
其实造成这些原因的很大一部分原因是由于代码规范的问题,如果写的规范,注释好,其实很多问题也就解决了。所以本文我就从代码的编写规范,格式的优化,设计原则和一些常见的代码优化的技巧等方面总结了了45个小技巧分享给大家,如果不足,欢迎指正。
1、规范命名
命名是写代码中最频繁的操作,比如类、属性、方法、参数等。好的名字应当能遵循以下几点:
见名知意
比如需要定义一个变量需要来计数
int i = 0;
复制代码
名称 i 没有任何的实际意义,没有体现出数量的意思,所以我们应当指明数量的名称
int count = 0;
复制代码
能够读的出来
如下代码:
private String sfzh;
private String dhhm;
复制代码
这些变量的名称,根本读不出来,更别说实际意义了。
所以我们可以使用正确的可以读出来的英文来命名
private String idCardNo;
private String phone;
复制代码
2、规范代码格式
好的代码格式能够让人感觉看起来代码更加舒适。
好的代码格式应当遵守以下几点:
- 合适的空格
- 代码对齐,比如大括号要对齐
- 及时换行,一行不要写太多代码
好在现在开发工具支持一键格式化,可以帮助美化代码格式。
3、写好代码注释
在《代码整洁之道》这本书中作者提到了一个观点,注释的恰当用法是用来弥补我们在用代码表达意图时的失败。换句话说,当无法通过读代码来了解代码所表达的意思的时候,就需要用注释来说明。
作者之所以这么说,是因为作者觉得随着时间的推移,代码可能会变动,如果不及时更新注释,那么注释就容易产生误导,偏离代码的实际意义。而不及时更新注释的原因是,程序员不喜欢写注释。(作者很懂啊)
但是这不意味着可以不写注释,当通过代码如果无法表达意思的时候,就需要注释,比如如下代码
for (Integer id : ids) {
if (id == 0) {
continue;
}
//做其他事
}
复制代码
为什么 id == 0 需要跳过,代码是无法看出来了,就需要注释了。
好的注释应当满足一下几点:
- 解释代码的意图,说明为什么这么写,用来做什么
- 对参数和返回值注释,入参代表什么,出参代表什么
- 有警示作用,比如说入参不能为空,或者代码是不是有坑
- 当代码还未完成时可以使用 todo 注释来注释
4、try catch 内部代码抽成一个方法
try catch代码有时会干扰我们阅读核心的代码逻辑,这时就可以把try catch内部主逻辑抽离成一个单独的方法
如下图是Eureka服务端源码中服务下线的实现中的一段代码
整个方法非常长,try中代码是真正的服务下线的代码实现,finally可以保证读锁最终一定可以释放。
所以这段代码其实就可以对核心的逻辑进行抽取。
protected boolean internalCancel(String appName, String id, boolean isReplication) {
try {
read.lock();
doInternalCancel(appName, id, isReplication);
} finally {
read.unlock();
}
// 剩余代码
}
private boolean doInternalCancel(String appName, String id, boolean isReplication) {
//真正处理下线的逻辑
}
复制代码
5、方法别太长
方法别太长就是字面的意思。一旦代码太长,给人的第一眼感觉就很复杂,让人不想读下去;同时方法太长的代码可能读起来容易让人摸不着头脑,不知道哪一些代码是同一个业务的功能。
我曾经就遇到过一个方法写了2000+行,各种if else判断,我光理清代码思路就用了很久,最终理清之后,就用策略模式给重构了。
所以一旦方法过长,可以尝试将相同业务功能的代码单独抽取一个方法,最后在主方法中调用即可。
6、抽取重复代码
当一份代码重复出现在程序的多处地方,就会造成程序又臭又长,当这份代码的结构要修改时,每一处出现这份代码的地方都得修改,导致程序的扩展性很差。
所以一般遇到这种情况,可以抽取成一个工具类,还可以抽成一个公共的父类。
7、多用return
在有时我们平时写代码的情况可能会出现if条件套if的情况,当if条件过多的时候可能会出现如下情况:
if (条件1) {
if (条件2) {
if (条件3) {
if (条件4) {
if (条件5) {
System.out.println("三友的java日记");
}
}
}
}
}
复制代码
面对这种情况,可以换种思路,使用return来优化
if (!条件1) {
return;
}
if (!条件2) {
return;
}
if (!条件3) {
return;
}
if (!条件4) {
return;
}
if (!条件5) {
return;
}
System.out.println("三友的java日记");
复制代码
这样优化就感觉看起来更加直观
8、if条件表达式不要太复杂
比如在如下代码:
if (((StringUtils.isBlank(person.getName())
|| "三友的java日记".equals(person.getName()))
&& (person.getAge() != null && person.getAge() > 10))
&& "汉".equals(person.getNational())) {
// 处理逻辑
}
复制代码
这段逻辑,这种条件表达式乍一看不知道是什么,仔细一看还是不知道是什么,这时就可以这么优化
boolean sanyouOrBlank = StringUtils.isBlank(person.getName()) || "三友的java日记".equals(person.getName());
boolean ageGreaterThanTen = person.getAge() != null && person.getAge() > 10;
boolean isHanNational = "汉".equals(person.getNational());
if (sanyouOrBlank
&& ageGreaterThanTen
&& isHanNational) {
// 处理逻辑
}
复制代码
此时就很容易看懂if的逻辑了
9、优雅地参数校验
当前端传递给后端参数的时候,通常需要对参数进场检验,一般可能会这么写
@PostMapping
public void addPerson(@RequestBody AddPersonRequest addPersonRequest) {
if (StringUtils.isBlank(addPersonRequest.getName())) {
throw new BizException("人员姓名不能为空");
}
if (StringUtils.isBlank(addPersonRequest.getIdCardNo())) {
throw new BizException("身份证号不能为空");
}
// 处理新增逻辑
}
复制代码
这种写虽然可以,但是当字段的多的时候,光校验就占据了很长的代码,不够优雅。
针对参数校验这个问题,有第三方库已经封装好了,比如hibernate-validator框架,只需要拿来用即可。
所以就在实体类上加@NotBlank、@NotNull注解来进行校验
@Data
@ToString
private class AddPersonRequest {
@NotBlank(message = "人员姓名不能为空")
private String name;
@NotBlank(message = "身份证号不能为空")
private String idCardNo;
//忽略
}
复制代码
此时Controller接口就需要方法上就需要加上@Valid注解
@PostMapping
public void addPerson(@RequestBody @Valid AddPersonRequest addPersonRequest) {
// 处理新增逻辑
}
复制代码
10、统一返回值
后端在设计接口的时候,需要统一返回值
{
"code":0,
"message":"成功",
"data":"返回数据"
}
复制代码
不仅是给前端参数,也包括提供给第三方的接口等,这样接口调用方法可以按照固定的格式解析代码,不用进行判断。如果不一样,相信我,前端半夜都一定会来找你。
Spring中很多方法可以做到统一返回值,而不用每个方法都返回,比如基于AOP,或者可以自定义
HandlerMethodReturnValueHandler来实现统一返回值。
11、统一异常处理
当你没有统一异常处理的时候,那么所有的接口避免不了try catch操作。
@GetMapping("/{id}")
public Result<T> selectPerson(@PathVariable("id") Long personId) {
try {
PersonVO vo = personService.selectById(personId);
return Result.success(vo);
} catch (Exception e) {
//打印日志
return Result.error("系统异常");
}
}
复制代码
每个接口都得这么玩,那不得满屏的try catch。
所以可以基于Spring提供的统一异常处理机制来完成。
12、尽量不传递null值
这个很好理解,不传null值可以避免方法不支持为null入参时产生的空指针问题。
当然为了更好的表明该方法是不是可以传null值,可以通过@NonNull和@Nullable注解来标记。@NonNull就表示不能传null值,@Nullable就是可以传null值。
//示例1
public void updatePerson(@Nullable Person person) {
if (person == null) {
return;
}
personService.updateById(person);
}
//示例2
public void updatePerson(@NonNull Person person) {
personService.updateById(person);
}
复制代码
13、尽量不返回null值
尽量不返回null值是为了减少调用者对返回值的为null判断,如果无法避免返回null值,可以通过返回Optional来代替null值。
public Optional<Person> getPersonById(Long personId) {
return Optional.ofNullable(personService.selectById(personId));
}
复制代码
如果不想这么写,也可以通过@NonNull和@Nullable表示方法会不会返回null值。
14、日志打印规范
好的日志打印能帮助我们快速定位问题
好的日志应该遵循以下几点:
- 可搜索性,要有明确的关键字信息
- 异常日志需要打印出堆栈信息
- 合适的日志级别,比如异常使用error,正常使用info
- 日志内容太大不打印,比如有时需要将图片转成Base64,那么这个Base64就可以不用打印
15、统一类库
在一个项目中,可能会由于引入的依赖不同导致引入了很多相似功能的类库,比如常见的json类库,又或者是一些常用的工具类,当遇到这种情况下,应当规范在项目中到底应该使用什么类库,而不是一会用Fastjson,一会使用Gson。
16、尽量使用工具类
比如在对集合判空的时候,可以这么写
public void updatePersons(List<Person> persons) {
if (persons != null && persons.size() > 0) {
}
}
复制代码
但是一般不推荐这么写,可以通过一些判断的工具类来写
public void updatePersons(List<Person> persons) {
if (!CollectionUtils.isEmpty(persons)) {
}
}
复制代码
不仅集合,比如字符串的判断等等,就使用工具类,不要手动判断。
17、尽量不要重复造轮子
就拿格式化日期来来说,我们一般封装成一个工具类来调用,比如如下代码
private static final SimpleDateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDateTime(Date date) {
return DATE_TIME_FORMAT.format(date);
}
复制代码
这段代码看似没啥问题,但是却忽略了SimpleDateFormat是个线程不安全的类,所以这就会引起坑。
一般对于这种已经有开源的项目并且已经做得很好的时候,比如Hutool,就可以把轮子直接拿过来用了。
18、类和方法单一职责
单一职责原则是设计模式的七大设计原则之一,它的核心意思就是字面的意思,一个类或者一个方法只做单一的功能。
就拿Nacos来说,在Nacos1.x的版本中,有这么一个接口HttpAgent
这个类只干了一件事,那就是封装http请求参数,向Nacos服务端发送请求,接收响应,这其实就是单一职责原则的体现。
当其它的地方需要向Nacos服务端发送请求时,只需要通过这个接口的实现,传入参数就可以发送请求了,而不需要关心如何携带服务端鉴权参数、http请求参数如何组装等问题。
19、尽量使用聚合/组合代替继承
继承的弊端:
- 灵活性低。java语言是单继承的,无法同时继承很多类,并且继承容易导致代码层次太深,不易于维护
- 耦合性高。一旦父类的代码修改,可能会影响到子类的行为
所以一般推荐使用聚合/组合代替继承。
聚合/组合的意思就是通过成员变量的方式来使用类。
比如说,OrderService需要使用UserService,可以注入一个UserService而非通过继承UserService。
聚合和组合的区别就是,组合是当对象一创建的时候,就直接给属性赋值,而聚合的方式可以通过set方式来设置。
组合:
public class OrderService {
private UserService userService = new UserService();
}
复制代码
聚合:
public class OrderService {
private UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
}
复制代码
20、使用设计模式优化代码
在平时开发中,使用设计模式可以增加代码的扩展性。
比如说,当你需要做一个可以根据不同的平台做不同消息推送的功能时,就可以使用策略模式的方式来优化。
设计一个接口:
public interface MessageNotifier {
/**
* 是否支持改类型的通知的方式
*
* @param type 0:短信 1:app
* @return
*/
boolean support(int type);
/**
* 通知
*
* @param user
* @param content
*/
void notify(User user, String content);
}
复制代码
短信通知实现:
@Component
public class SMSMessageNotifier implements MessageNotifier {
@Override
public boolean support(int type) {
return type == 0;
}
@Override
public void notify(User user, String content) {
//调用短信通知的api发送短信
}
}
复制代码
app通知实现:
public class AppMessageNotifier implements MessageNotifier {
@Override
public boolean support(int type) {
return type == 1;
}
@Override
public void notify(User user, String content) {
//调用通知app通知的api
}
}
复制代码
最后提供一个方法,当需要进行消息通知时,调用notifyMessage,传入相应的参数就行。
@Resource
private List<MessageNotifier> messageNotifiers;
public void notifyMessage(User user, String content, int notifyType) {
for (MessageNotifier messageNotifier : messageNotifiers) {
if (messageNotifier.support(notifyType)) {
messageNotifier.notify(user, content);
}
}
}
复制代码
假设此时需要支持通过邮件通知,只需要有对应实现就行。
相关推荐
-
- SpringBoot如何实现优雅的参数校验
-
平常业务中肯定少不了校验,如果我们把大量的校验代码夹杂到业务中,肯定是不优雅的,对于一些简单的校验,我们可以使用java为我们提供的api进行处理,同时对于一些...
-
2025-05-11 19:46 ztj100
- Java中的空指针怎么处理?
-
#暑期创作大赛#Java程序员工作中遇到最多的错误就是空指针异常,无论你多么细心,一不留神就从代码的某个地方冒出NullPointerException,令人头疼。...
- 一坨一坨 if/else 参数校验,被 SpringBoot 参数校验组件整干净了
-
来源:https://mp.weixin.qq.com/s/ZVOiT-_C3f-g7aj3760Q-g...
- 用了这两款插件,同事再也不说我代码写的烂了
-
同事:你的代码写的不行啊,不够规范啊。我:我写的代码怎么可能不规范,不要胡说。于是同事打开我的IDEA,安装了一个插件,然后执行了一下,规范不规范,看报告吧。这可怎么是好,这玩意竟然给我挑出来这么...
- SpringBoot中6种拦截器使用场景
-
SpringBoot中6种拦截器使用场景,下面是思维导图详细总结一、拦截器基础...
- 用注解进行参数校验,spring validation介绍、使用、实现原理分析
-
springvalidation是什么在平时的需求开发中,经常会有参数校验的需求,比如一个接收用户注册请求的接口,要校验用户传入的用户名不能为空、用户名长度不超过20个字符、传入的手机号是合法的手机...
- 快速上手:SpringBoot自定义请求参数校验
-
作者:UncleChen来源:http://unclechen.github.io/最近在工作中遇到写一些API,这些API的请求参数非常多,嵌套也非常复杂,如果参数的校验代码全部都手动去实现,写起来...
- 分布式微服务架构组件
-
1、服务发现-Nacos服务发现、配置管理、服务治理及管理,同类产品还有ZooKeeper、Eureka、Consulhttps://nacos.io/zh-cn/docs/what-is-nacos...
- 优雅的参数校验,告别冗余if-else
-
一、参数校验简介...
- Spring Boot断言深度指南:用断言机制为代码构筑健壮防线
-
在SpringBoot开发中,断言(Assert)如同代码的"体检医生",能在上线前精准捕捉业务逻辑漏洞。本文将结合企业级实践,解析如何通过断言机制实现代码自检、异常预警与性能优化三...
- 如何在项目中优雅的校验参数
-
本文看点前言验证数据是贯穿所有应用程序层(从表示层到持久层)的常见任务。通常在每一层实现相同的验证逻辑,这既费时又容易出错。为了避免重复这些验证,开发人员经常将验证逻辑直接捆绑到域模型中,将域类与验证...
- SpingBoot项目使用@Validated和@Valid参数校验
-
一、什么是参数校验?我们在后端开发中,经常遇到的一个问题就是入参校验。简单来说就是对一个方法入参的参数进行校验,看是否符合我们的要求。比如入参要求是一个金额,你前端没做限制,用户随便过来一个负数,或者...
- 28个验证注解,通过业务案例让你精通Java数据校验(收藏篇)
-
在现代软件开发中,数据验证是确保应用程序健壮性和可靠性的关键环节。JavaBeanValidation(JSR380)作为一个功能强大的规范,为我们提供了一套全面的注解工具集,这些注解能够帮...
- Springboot @NotBlank参数校验失效汇总
-
有时候明明一个微服务里的@Validated和@NotBlank用的好好的,但就是另一个里不能用,这时候问题是最不好排查的,下面列举了各种失效情况的汇总,供各位参考:1、版本问题springbo...
- 这可能是最全面的Spring面试八股文了
-
Spring是什么?Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- idea eval reset (50)
- vue dispatch (70)
- update canceled (42)
- order by asc (53)
- spring gateway (67)
- 简单代码编程 贪吃蛇 (40)
- transforms.resize (33)
- redisson trylock (35)
- 卸载node (35)
- np.reshape (33)
- torch.arange (34)
- node卸载 (33)
- npm 源 (35)
- vue3 deep (35)
- win10 ssh (35)
- exceptionininitializererror (33)
- vue foreach (34)
- idea设置编码为utf8 (35)
- vue 数组添加元素 (34)
- std find (34)
- tablefield注解用途 (35)
- python str转json (34)
- java websocket客户端 (34)
- tensor.view (34)
- java jackson (34)