以下是我在七年 Java 开发中积累的一些实用技巧和经验,涵盖编码习惯、调试优化、工具使用等方面,希望能帮你少走弯路:
一、编码习惯:让代码更优雅易维护
1. 用Optional避免空指针
-
反例:
if (user != null && user.getAddress() != null) -
正例:
String city = Optional.ofNullable(user) .map(User::getAddress) .map(Address::getCity) .orElse("未知城市"); -
好处:链式调用清晰表达 null 处理逻辑,减少嵌套
if,降低 NPE 风险。
2. 善用枚举替代魔法值
-
反例:
if (type == 1)(1 代表什么?半年后自己都难猜) -
正例:
public enum OrderType { NORMAL(1, "普通订单"), PRE(2, "预售订单"); // ... } if (OrderType.NORMAL.equals(type)) { ... } -
好处:语义化、类型安全,修改枚举值时编译器会提示所有引用处。
3. 控制方法复杂度:单一职责原则
- 建议:单个方法代码不超过 50 行,超过则拆分成小方法。
- 示例:将复杂业务拆分为
validate()、process()、notify()等原子方法,命名清晰如calculateOrderTotalPrice(),避免handleAll()这种大杂烩方法。
二、调试与优化:快速定位问题,提升性能
1. 用assert语句做防御性编程
-
场景:参数校验、状态检查(仅在开发阶段生效)
public void deleteUser(Long userId) { assert userId != null : "用户ID不能为空"; // 上线前需关闭(-ea参数启用) // ... } -
注意:生产环境默认不启用,需配合单元测试覆盖断言逻辑。
2. 日志规范:拒绝System.out.println
-
推荐做法:
private static final Logger log = LoggerFactory.getLogger(XXService.class); // 调试日志(仅开发可见) log.debug("用户{}查询订单{}", userId, orderId); // 错误日志(包含异常堆栈) log.error("处理订单失败", e); -
好处:通过
logback.xml配置日志级别,生产环境只输出info以上日志,避免性能损耗。
3. 善用explain分析 SQL 性能
-
场景:接口慢先查数据库,在 SQL 前加
EXPLAIN看执行计划EXPLAIN SELECT * FROM orders WHERE user_id=123 AND create_time > '2023-01-01'; -
关注点:
type是否为range/ref(避免ALL全表扫描),key是否命中索引。
三、工具与框架:提升开发效率
1. IDEA 快捷键提升 Coding 速度
Ctrl + Alt + V:自动提取变量(告别手动声明)Ctrl + Shift + U:大小写切换(常量命名神器)Alt + Insert:快速生成equals/hashCode/toString( lombok 虽好,有时需手动)
2. 用Arthas排查线上问题
-
场景:CPU 飙高、内存泄漏时,无需重启应用即可诊断
# 查看占用CPU最高的线程 arthas> thread -n 3 # 监控方法执行耗时 arthas> watch com.xxx.Service method params returnObj -x 2 -
官网:arthas.aliyun.com/ (阿里开源,运维必备)
3. 单元测试:先写测试再写代码(TDD)
-
示例:开发
UserService前先写测试用例@Test void getUserById() { User user = userService.getUserById(1L); assertNotNull(user); assertEquals("admin", user.getUsername()); } -
工具:
JUnit5+Mockito(模拟依赖对象,隔离测试)
四、避坑经验:那些年踩过的坑
1. 日期处理:永远优先用LocalDateTime
-
反例:
Date类线程不安全,时区处理麻烦 -
正例:
// 获取当前时间(带时区) ZonedDateTime now = ZonedDateTime.now(ZoneId.of("Asia/Shanghai")); // 格式化 String format = now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); -
工具类:
Apache Commons Lang3的DateUtils或Joda-Time(Java8 后主推java.time包)
2. 集合操作:警惕Arrays.asList的坑
List<String> list = Arrays.asList("a", "b", "c"); // 固定大小的ArrayList
// list.add("d"); // 会抛UnsupportedOperationException
- 解决方案:转为
new ArrayList<>(list)再操作,或直接用List.of()(不可变列表)。
3. 事务控制:@Transactional 的正确用法
-
坑点:
- 方法非
public时事务失效 - 同一类内方法调用(
this.xxx())不触发事务
- 方法非
-
最佳实践:
@Service public class OrderService { @Transactional(rollbackFor = Exception.class) // 标注在public方法上 public void createOrder() { orderDao.insert(...); // 同一类内调用其他方法,事务依然有效 validateOrder(); } }
五、架构设计:从小处培养设计思维
1. 用策略模式替代多层if-else
-
场景:根据不同类型执行不同逻辑(如支付方式、优惠策略)
-
实现:
// 定义策略接口 public interface PayStrategy { void pay(); } // 具体策略 public class AlipayStrategy implements PayStrategy { ... } public class WechatPayStrategy implements PayStrategy { ... } // 策略工厂 public class PayStrategyFactory { private static final Map<String, PayStrategy> strategyMap = new ConcurrentHashMap<>(); static { strategyMap.put("alipay", new AlipayStrategy()); strategyMap.put("wechat", new WechatPayStrategy()); } public static PayStrategy getStrategy(String type) { return strategyMap.get(type); } } -
调用:
PayStrategy strategy = PayStrategyFactory.getStrategy(type); strategy.pay();
2. 接口设计:遵循RESTful规范
-
示例:
GET /users/{id} # 查询单个用户 POST /users # 创建用户 PUT /users/{id} # 更新用户 DELETE /users/{id} # 删除用户 -
好处:统一接口风格,前端对接更清晰,支持
Spring Boot Starter Actuator自动生成 API 文档。
六、持续学习:保持技术敏感度
-
关注官方文档:
- Java 官方文档:docs.oracle.com/en/java/
- Spring Framework 参考指南:docs.spring.io/spring-fram…
-
技术社区:
- 掘金 / InfoQ:获取行业趋势和实战案例
- Stack Overflow:遇到问题先搜,避免重复造轮子
-
源码阅读:
- 从
Spring Core、MyBatis等框架入手,理解设计思想(如FactoryBean、Interceptor)
- 从
最后:写给新手的一句话
代码的质量,决定了你的职业高度。
少写 “能用就行” 的代码,多思考 “如何优雅实现”。每一次对细节的打磨,都是在为未来的自己积累技术资本。共勉!