写文章的原因
为什么会想到这个主题呢?
直接原因:是最近到新公司上班,里面需要要求代码质量,组长会审核,然后合并代码上线。 然后就想到了这个主题,想和大家讨论下什么是优质代码
从大学实习到毕业上班,一共去过5家公司,公司的项目各种各样,有接项目的软件公司(政府,制造业),也有做自研项目的公司(生物,广告)。
对于优质代码的评价我通常会从"人能看懂"、"机器能高效跑"、"团队能维护"三个角度来分析
工作经验分析
1.软件公司项目开发
能明显感觉到,不同的公司对于代码的评价是不同的,及时是同一家公司,根据项目的不同规模,对代码质量的把控是完全不同的。
之前在一家接政府相关项目的软件公司,有的小项目,前后端都是自己一个人做的,用现成的框架半个月就写完代码,1个月就上线交付了,对于这种项目,公司的要求就是提到的功能点尽快做出来,整体测试没问题,然后配置下线上环境就交付了,
对于这种项目,优质代码的定义我觉得就是不看代码本身,功能完成,测试完成,上线完成,交付完成。就算是好的代码了,毕竟你用SpringBoot的生态,半个月的时间写的东西,别人也能很快上手和很快的改,而且通常没有复杂的业务需求,代码的质量差是可以容忍的
这家公司有些时候会接到一些大单,开发周期在1年左右,而且需要维护2-3年左右的时间,这个时候就需要对代码有一定的要求了。
但更多的是在功能层面的,对于维护相关也比较少,在功能层面可能数据量很大,需要对查询做一些优化功能,mysql也会涉及到多表联查,些索引,聚合搜索相关的优化等等。
对于一些逻辑比较复杂的业务,在函数上写个简单的注释,或者画个流程图放在公司的文档中,问题其实也不大,后续的人也能维护。
2.自研公司项目开发
做自研的公司我觉得和软件公司开发最大的区别是自研的公司已经有稳定的收入来源,有自己的核心业务,通常是在维护的基础上慢慢的进行迭代更新,简单来说,代码开发的功能可以慢,但是上线不能出问题是底线。
比如我现在在一家ADX的程序化广告公司上班,需要对接上百个渠道和上百个媒体,每个渠道和广告都有自己的逻辑,如果是只注重功能的实现而不是维护的话,代码就完全没法看了,特别涉及到一些公司收益的核心业务,比如放量和控量管理,如果出错比较严重而且没有备用方案的话,基本可以准备跑路了。
所以技术负责人会对代码进行质量的审核(可读性,功能实现,后续维护...这个具体后面会详细讲)。
3.初步总结
我举例了我在自研和软件公司开发的例子,主要想说明,对于的不同的公司,不同的项目对代码质量的要求是完全不同的,优质代码的评价并不唯一,适合的才是最好的,维护优质代码也需要付出很多成本。
当然,我前面提到的都是公司对于代码质量的看法,当然写代码最后还是落在某个人的身上,代码的质量最后还是靠自己来维护的。公司层面最多就是个监督,保下限。
代码质量提升
我会从 "人能看懂"、"机器能高效跑"、"团队能维护"三个角度来进行代码质量的优化
1.可读性:让人 “一眼看懂” 的代码才是基础
Java 是强类型语言,语法相对严谨,可读性的重要性尤为突出 —— 毕竟团队中 80% 的时间都在 “读代码” 而非 “写代码”。
命名符合规范:
遵循 Java 社区约定(如阿里巴巴 Java 开发手册):类名用PascalCase(如UserService),方法 / 变量用camelCase(如getUserId()),常量用UPPER_SNAKE_CASE(如MAX_RETRY_COUNT)。
避免模糊命名(如a、temp、doSomething()),要让名字 “自注释”:比如calculateOrderTotalPrice()比calc()清晰 10 倍。
结构清晰,逻辑扁平:
类的职责单一(符合 “单一职责原则”),一个类只做一件事(如UserValidator只负责用户参数校验,不掺杂业务计算)。
方法体不宜过长(建议控制在 30 行内),复杂逻辑拆分成小方法(如将 “解析 Excel→校验数据→入库” 拆分成 3 个方法)。
避免嵌套过深(如if-else超过 3 层),可用 “提前 return”“switch-case”“策略模式” 简化(例如用enum的抽象方法替代多层if)。
注释 “精准而不冗余”:
类和公共方法必须写文档注释(/** */),说明用途、参数含义、返回值、异常场景(如@param @return @throws)。
复杂逻辑(如算法实现、特殊业务规则)需要写行注释,但 “显而易见的逻辑” 无需注释(如i++不用加 “// 自增 1”)。
2.健壮性:“跑起来稳如老狗” 的保障
Java 的异常机制、强类型检查为健壮性提供了基础,但需要开发者正确使用。
异常处理 “精准且不吞”:
区分 “受检异常”(Checked Exception,如IOException)和 “非受检异常”(Unchecked Exception,如NullPointerException):
受检异常:必须显式处理(try-catch或throws),且要说明 “为什么会抛”(如FileNotFoundException需注释 “文件可能不存在”);
非受检异常:通过预判避免(如用Optional处理null,if (obj != null)),而非无脑try-catch。
严禁 “吞异常”(catch (Exception e) {}),至少要打日志(log.error("失败原因", e)),否则出问题无从排查。
边界校验 “滴水不漏”:
对入参(尤其是外部接口、用户输入)做校验:比如userId不能为负数,String长度不超过限制(用Validation框架如Hibernate Validator更高效)。
集合操作避免越界(list.get(i)前先判断i < list.size()),用for-each替代for循环减少索引错误。
减少 “不必要的消耗”:
避免频繁创建临时对象(如循环中new String(),改用StringBuilder),减少 GC 压力;
集合初始化指定容量(如new ArrayList<>(100)而非默认10,避免多次扩容);
慎用static大对象(可能导致内存泄漏,尤其在 Web 容器中),及时释放资源(try-with-resources处理InputStream/Connection)。
并发安全 “恰到好处”:
多线程场景下,用ConcurrentHashMap替代HashMap,AtomicInteger替代synchronized (this);
避免 “过度同步”(如给整个方法加synchronized,可缩小锁范围到临界区),必要时用Lock接口更灵活。
3.可维护性:“别人接手时不骂娘” 的关键
对于生命周期长、团队协作频繁的项目,可维护性直接决定代码的 “存活周期”。
依赖清晰,低耦合:
业务服务层:用接口隔离具体实现(依赖倒置原则)
场景:电商系统中,订单支付需要支持支付宝、微信支付、银联等多种方式。
反例:订单服务直接依赖AlipayService、WechatPayService等具体实现类,新增支付方式时需修改订单服务代码。
工具类设计:避免 “万能工具类”,按职责拆分
之前呆过一个公司,工具类乱飞,一个类中引入了不知道多少个StringUtils类,这种一般都是用公司内部统一维护一个工具类,然后在内部的工具类里面继承common.lang3这种现成的工具类。
职责明确,比如说对日期的一些格式化操作,就放在DateUtils里面就行了,有的人放在StringUtils,然后下一次用就找不到了,然后自己再重新写一个,这样的话,如果这个方法有问题,两个地方只改了一个,最后又出问题
数据层与业务层:通过 DTO 隔离领域模型
对于业务参数接收Param,实体类映射Dto,返回类响应Vo,接收和返回的对象不要用Map,创建一个对象花不了多少时间的,属性的复制就beanUtils一下就可以了,其实也快的。接收用map,返回用map,有些时候看代码也是挺无语的。
消除 “技术债务”:
不写 “死代码”(注释掉的代码直接删除,靠版本控制回溯),不堆 “魔法值”(用enum或常量类替代,如Status.SUCCESS代替1)。
及时重构 “坏味道” 代码:比如重复代码抽成公共方法,过长参数列表用 “建造者模式” 或DTO封装。
拥抱 “设计模式” 但不滥用:
Java 是设计模式的 “主战场”,合理使用能简化维护:
用 “工厂模式” 统一对象创建(如UserFactory创建不同类型的User),避免到处new对象导致修改困难;
用 “装饰器模式” 扩展功能(如LoggingInputStream在不修改InputStream的前提下增加日志);
但警惕 “过度设计”(如一个简单工具类硬套 “建造者模式”,反而增加复杂度)。
遵循《Java Language Specification》和行业规范(如阿里巴巴 Java 开发手册),减少团队沟通成本;
用好 Java 的新特性简化代码:比如 Java 8 的Stream(替代冗长的for循环)、Lambda(简化匿名内部类),Java 11 的var(局部变量类型推断,不滥用);
总结:好 Java 代码的 “终极检验”
如果你的代码满足:
新人接手时,不用反复问你 “这个变量啥意思”“这段逻辑为啥这么写”;
改需求时,只需新增代码(而非大面积修改原有逻辑);
线上跑半年,没出现过 “莫名其妙的 NullPointerException”“内存溢出”;
用 Java 的特性(接口、枚举、Stream 等)让代码更简洁,而非硬套其他语言的写法(如用 Java 写 “函数式代码” 却忽略面向对象优势)。
那它大概率就是 “好代码” 了。本质上,好代码是 “对人友好、对机器高效、对团队负责” 的平衡 —— 毕竟代码的最终目的是 “解决问题”,而不是 “炫技”。