网上天天有人喊“AI写代码太牛逼了”,我一开始也是半信半疑。直到上个迭代,我们用一个模拟订单系统做了一次真实的AI辅助编码实验,把AI塞进Java后端开发流程里跑了整整两周,我才敢说句实话:AI写Java,确实行,但“行”的方式跟网上演示的那些花活完全不是一回事。它不是替你写代码,而是替你把那些写吐了的模板代码、胶水代码、重复逻辑快速铺出来,然后留给你一屁股审查和返工的活。
这篇文章不吹不黑,把我这两周的真实操作、翻车记录、提示词打法、代码审查清单全部摊开讲。适合正在用AI写Java的人,也适合团队里准备把AI纳入开发流程、但还没想清楚边界在哪的技术负责人。
1. 先说结论:AI写Java到底行在哪
如果你问“AI能不能直接写一个生产级的Java微服务”,我的答案是:能,但你敢不敢直接上线是另一回事。如果问“AI能不能把Java开发里那些没营养的体力活干完”,我的答案是:相当能,而且效率提升非常明显。
1.1 AI在Java领域真正擅长的三件事
第一件事是样板代码生成。Java是出了名的“啰嗦”,一个DTO要写getter/setter,一个Service要写接口和实现,一个Mapper要写一堆注解。这些代码有固定套路、没有业务逻辑、写起来手指酸痛。AI在这类任务上几乎不会出错,你只要把类名、字段名、类型说清楚,它甚至能帮你把POJO转VO的转换器都写好。
第二件事是单元测试补全。让AI写核心逻辑,它可能会飘,但让它给一个已有的Service方法写单元测试,它反而很稳。因为测试代码的模式极其固定:mock依赖、构造入参、调用方法、断言结果。只要你在提示词里给出方法签名和业务规则,它写出来的测试覆盖率相当可观。
第三件事是边缘小工具和脚本。比如写一个枚举字典、一个Json解析工具、一个定时任务壳子、一个Excel导入导出的工具类。这些代码“做完就忘”,不值得花太多精力,AI生成的完成度非常高,基本改改就能用。
1.2 适合AI发挥的典型场景清单
用了一轮之后,我整理出几个“AI参与度最高且返工率最低”的场景,给团队当参考:
| 场景 | AI参与度 | 我的实测体验 |
|---|---|---|
| 新项目脚手架搭设 | 高 | 目录结构、基础配置、启动类一次搞定 |
| Controller + Service + DAO 三层模板 | 高 | 只要给清字段和接口语义,生成质量稳定 |
| 标准CRUD接口 | 高 | 分页、条件查询、新增、修改基本零改动 |
| 单元测试 | 高 | 规则写清楚,测试比人写得还全 |
| 枚举、常量、工具类 | 高 | 生成即用,基本不用改 |
| 复杂业务规则引擎 | 低 | 生成了也基本是废稿,不如自己写 |
| 并发控制与分布式事务 | 低 | 表面正确,实际坑多到怀疑人生 |
| 遗留系统改造 | 极低 | 没有上下文,AI纯靠猜,改出过一堆兼容性问题 |
这个表基本反映了我对AI写Java的定位:它是个熟练的“初级开发”,不是架构师。在结构化、模式化的工作上它能顶上两个人力,但在需要全局视野、边界意识、性能敏感的地方,它跟一个刚毕业的实习生犯的错一模一样,甚至犯得更自信。
2. 翻车实录:AI写Java最爱在哪里“装逼”
网上那些AI写代码的演示视频,都是挑成功案例放。我这两周把AI写的代码拿去做Code Review,挑出来的毛病比我自己写的还多,而且集中在几个固定的类型上。
2.1 性能与并发:看起来对,跑起来乱
AI写业务CRUD很稳,但一涉及性能它就露馅。最典型的一个案例:我让它写一个订单汇总接口,要求按用户分组统计订单金额。它直接写了一个for循环,在循环里逐条查数据库,然后内存里做累加。单看代码,逻辑完全正确,但数据量一上来,接口直接超时。
更吓人的是并发场景。它写过一个库存扣减的Service,直接在方法上加了synchronized关键字。单机部署也许勉强能跑,一旦服务多实例部署,这个锁就完全失效了。同样的问题还出在它喜欢把线程池参数写死、直接用Executors.newFixedThreadPool而不考虑队列策略上。
// AI生成的原始版本:循环内查询,N+1问题拉满 for (Long userId : userIdList) { User user = userMapper.selectById(userId); // 每次都查一次库 List<Order> orders = orderMapper.selectByUserId(userId); total += orders.stream().mapToLong(Order::getAmount).sum(); }这种代码不是不能用,而是得有人替它擦屁股。我现在的做法是:所有AI生成的代码,第一轮Review先不看业务逻辑,专门盯循环内是否有IO、锁的粒度是否合理、线程池参数是否可配置。
2.2 事务与边界:AI最容易踩的隐性地雷
AI对Spring事务的理解,就像一个背了答案但没理解原理的学生。它知道在Service方法上加@Transactional,但不知道事务失效的经典三坑:同类内部调用、异常被try-catch吞掉、非public方法。
有次它生成一个批量导入的方法,逻辑是把一批数据先删后插。它在方法里加了事务注解,但又在catch块里把异常打日志后吞掉了。结果就是事务注解形同虚设,删了一半数据的场景一旦发生,连回滚的机会都没有。这种代码放在生产环境,是要出大事的。
边界条件更是重灾区。AI写的空值判断经常只处理它“猜”到的那个方向。比如一个金额计算,它只判断了入参是不是null,没判断字符串是不是空串、是不是负数、有没有超过数据库字段精度。我见过它生成的一个金额格式化工具,遇到负数直接输出错误格式,就是因为没让AI在提示词里看到“负数”这种边界条件。
2.3 依赖与版本:报错信息撒谎的典型情况
最折磨人的是依赖问题。AI的知识库有一定滞后性,它经常生成某个库的旧API或者根本不存在的方法。有一次它写了一段用Apache Commons Lang的代码,引用了一个StringUtils.isNotEmpty的“升级版”方法,我查了半天才发现这个方法是它自己发明的。
更离谱的一次,它主动“修复”了一段代码,把Spring Boot 2.x的写法改成了Spring Boot 3.x的写法,并且贴心地加了一个新依赖。结果这个依赖跟项目里已有的旧版本冲突,启动直接报NoSuchMethodError。类冲突排查比写代码痛苦一百倍,而AI对这类问题的解释完全是“自信地胡说八道”。
所以我现在给团队立了个规矩:AI生成代码时,必须先声明项目使用的框架版本和关键依赖版本,并且生成的代码必须本地编译通过才能进入评审,不给AI自由发挥技术栈的空间。
3. 让AI写出可交付代码的提示词打法
很多人说AI写Java不行,其实是用错了提示词。你让它“写一个订单模块”,它当然给你画一个空中楼阁。但如果你把需求拆到“写一个基于Spring Boot 3.x、MyBatis-Plus的订单分页查询接口,入参是订单编号模糊搜索和状态精确匹配,出参按创建时间倒序”,它写出来的东西基本就是能用的。
3.1 好的任务描述长什么样
我踩过无数坑之后,总结出一个提示词模板,每次让AI写Java代码都用这个结构:
你是一名Java后端开发工程师,项目技术栈是Spring Boot 3.1 + MyBatis-Plus + MySQL 8.0。请实现以下功能: 功能描述:…… 入参说明:字段名、类型、是否必填、校验规则 出参说明:字段名、类型、格式要求 业务规则:包括边界条件、异常处理方式、是否要求事务 额外要求:输出完整类代码,包含必要的import,不省略任何方法体
这个模板看起来简单,但每一行都有它存在的道理。“技术栈声明”是为了防止AI用错版本,“业务规则”是逼它把边界条件考虑进去,“不省略方法体”是防止它写一堆注释占位符糊弄你。
我试过拿一个真实的报表需求来测试:用这个完整模板时,AI生成的代码评审时只需要改两个小问题;如果只给一句话需求“写个报表接口”,它生成的东西我恨不得从头重写,因为返回结构、分页逻辑、权限过滤全都要返工。
3.2 分步拆解与上下文注入的实战模板
另一个关键技巧是分步拆解。不要指望AI一次生成一个完整复杂模块,正确做法是把一个大需求拆成多个小任务,每个任务单独生成,人工检查通过后再进下一步。
举个例子,我之前让AI帮忙开发一个“用户积分变动记录”的功能。完整的做法是分四步:
第一步,让AI根据表结构生成积分记录实体和Mapper层:
以下是我的数据库表结构:user_points_log(id, user_id, change_type, change_value, balance_after, created_at)。请生成对应的Java实体类和MyBatis-Plus Mapper接口,字段类型要匹配MySQL类型,实体类加Swagger注解。
第二步,生成Service接口和实现类,把核心的“积分变更”方法单独描述清楚:
请在Service中实现一个changeUserPoints(Long userId, Integer changeValue, String changeType)方法。要求:changeValue正数为增加、负数为扣减;扣减后余额不能小于0;余额校验失败抛出BizException;需要用@Transactional确保变更记录和余额更新的原子性。
第三步,让AI补全Controller层和DTO的转换:
请根据上面的Service生成Controller接口,入参是userId、changeValue、changeType,出参是变更后的余额。注意DTO校验,changeValue不能为null,changeType长度不能超过20。
第四步,把AI生成的代码汇总起来,人工做一次完整的业务链路评审,补齐它没想到的细节,比如幂等性、操作人记录、并发扣减时的乐观锁。
这套流程跑下来,AI生成的代码质量明显上了一个台阶,因为每一段代码的任务边界都非常清晰,AI不需要自己脑补上下文,我们也更容易发现它在哪一步产生了偏差。
4. 人审防线:AI代码进入仓库前的四道关
AI写代码不可怕,可怕的是没人审查就让它进入仓库。我把这两周实验中所有的Review经验整理出来,形成了四个固定检查关卡,现在团队里所有AI辅助生成的代码都必须走这个流程。
4.1 编译验证与单测自动核对
第一关永远是机器检查。AI生成的代码,不管看起来多完美,第一件事是本地编译。编译通过之后,跑一遍现有的单元测试和集成测试,确认它没有破坏已有功能。
我强烈建议在做这一步时,要求AI同时生成对应的单元测试。如果AI连测试都不愿意写,那说明它自己也没把握这段代码是对的。让AI先写测试再写实现,这个顺序反着来,能卡掉一大半隐藏问题——因为AI为了通过自己写的测试,会更认真地对待边界条件。
这一关的成本最低,收益最高。任何一段AI代码如果过不了编译和测试,直接打回重新生成,不要花时间去人工修。
4.2 Code Review必须盯住的检查点
第二关是人工Code Review。AI代码的审查重点和人工代码不太一样,不需要把时间花在读业务逻辑上,因为逻辑大概率是对的。真正要盯的是那些AI“表面正确、实则隐患”的点。
我整理了一个AI代码专项审查清单,每次评审都对着看:
| 检查点 | 具体关注内容 |
|---|---|
| 循环与查询 | 循环体内有没有SQL查询、远程调用、文件IO |
| 锁的粒度 | synchronized是否锁了过大范围、是否用了分布式锁 |
| 事务边界 | @Transactional是否在类内部被绕过、异常是否被吞 |
| 连接与流 | 数据库连接、IO流、HTTP客户端是否正常关闭 |
| 空值策略 | 每个外部入参是否都有防御性判断 |
| 数值精度 | BigDecimal使用是否规范、有没有double直接算钱 |
| 日志规范 | 有没有打印敏感信息、有没有用print代替logger |
| 魔法值 | 状态码、类型值有没有抽成常量或枚举 |
| 依赖引用 | 有没有引入新依赖、版本与现有框架是否冲突 |
| 异常处理 | 捕获到异常后是记录还是吞掉、有没有合适的兜底 |
这个清单看着很长,其实每条都对应一次真实翻车。圈出来的每一个问题,我们都在这两周的实验里遇到过至少一次。
4.3 把“AI生成标记”变成流程规范
第三关是流程标记。我们在MR描述里加了一个固定前缀,凡是AI生成的代码,必须标注“AI辅助生成”和具体的生成日期、使用的提示词版本。这个做法的好处是:评审的人会带着更审慎的眼光去看这段代码,而不是因为它“看起来正常”就放过。
同时它也为后续追踪提供了线索。如果某段代码上线后出了性能问题,我们能快速从MR里筛出AI辅助生成的那部分,回顾当时的提示词哪里描述不清,从而优化提示词模板。这个闭环跑起来之后,团队里的提示词资产会越来越准,AI生成的代码质量也会逐步提升。
第四关是上线前的回归验证。AI生成代码涉及的接口,必须在测试环境走一遍完整的业务链路,尤其是有状态变更、金额计算、数据导入导出的场景。这一步没有捷径,AI生成的代码天然缺少“对这个项目的血泪理解”,只有靠完整的业务验证来兜底。
5. 实测一个小项目:AI参与度与修正记录
光说不练没有说服力。我拿一个模拟项目X做了整整两周实验,项目规模不大,但覆盖了典型的Java后端开发场景,包括用户模块、订单模块、报表模块。整个过程我记录了AI每个模块的表现和返工率,列出来给各位参考。
5.1 项目概述与分工方式
项目模拟的是一个轻量级的订单管理系统,技术栈是Spring Boot 3.1 + MyBatis-Plus + MySQL,团队成员一共三个人,我的分工是:需求拆解和提示词编写由我来做,AI负责生成代码初稿,另一位同事负责人工审查和修正。
我们把需求拆成了12个独立任务,每个任务对应一个或两个类。整个过程中,AI大约生成了5千多行Java代码。说实话,这个量放在过去,我一个人三四天才能写完,AI一个晚上就全部铺出来了,效率提升是实打实的。
5.2 各模块的AI生成质量对照
下面是每个模块的实际表现:
| 模块 | AI生成行数 | 直接可用 | 小改可用 | 较大返工 | 典型问题 |
|---|---|---|---|---|---|
| 用户实体与Mapper | 520 | 90% | 10% | 0 | 字段类型映射有误 |
| 用户注册登录Service | 760 | 40% | 40% | 20% | 密码校验逻辑不完整 |
| 订单CRUD接口 | 1,100 | 60% | 30% | 10% | 分页参数校验缺失 |
| 订单汇总报表 | 450 | 0 | 20% | 80% | 循环查库、金额精度错 |
| 积分变更功能 | 690 | 30% | 40% | 30% | 事务失效、并发未处理 |
| 单元测试 | 1,200 | 70% | 25% | 5% | 断言覆盖不全 |
这个数据最扎眼的是报表模块,AI生成的代码返工率高达80%。原因不复杂:报表查询涉及多表关联、条件动态拼接、金额精度控制,这些恰恰是AI最不擅长的“非模式化逻辑”。反而是用户实体和Mapper这种纯模板代码,AI表现堪称完美。
5.3 典型问题复盘:一个订单汇总接口的今生来世
拿订单汇总报表模块举个例子。AI最初生成的版本是标准的“能跑但错得离谱”:加了根据订单状态过滤,但没加时间范围;用了double计算金额,精度一塌糊涂;分页查询时先count再list的做法倒是没问题,但count的条件和list的条件没保持一致。
我们花了大概40分钟修正这些问题,等于自己重新捋了一遍业务逻辑。但反过来想,如果完全从零手写,光是搭那个多表关联的查询骨架就至少要一个小时。AI至少帮我们把“骨架”搭好了,我们只负责把“魂”注入进去。
第二次提示词调整后,我们把需求描述细化到“按创建时间倒序、支持订单状态和支付状态双条件筛选、金额汇总用BigDecimal保留两位小数”,AI重新生成的版本就直接达到了可交付水平。这个对比说明:AI代码的最终质量,极大程度上取决于提需求的人有没有把字段和规则说到位。
6. 给团队和个人接入AI的几条可落地建议
这两周实验下来,我对AI写Java的态度从一个极端摆到另一个极端,最后落在一个务实的中间位置。下面这几条建议,是我觉得任何团队和个人想认真用AI做Java开发,都应该先想清楚的事。
6.1 什么时候坚决不用AI
AI不是万能的,有些场景我建议直接禁用。第一是涉及资金计算和账务逻辑的代码,不是说AI一定写错,而是这类代码一旦出错,代价远超它带来的效率收益,风险不值得冒。第二是历史遗留系统的改造,AI没有你项目里那些隐形的约定和潜规则的上下文,它生成的东西往往带着“新项目思维”,很容易踩兼容性的坑。
第三是复杂的并发和分布式事务场景。AI对这类问题的理解是纸面上的,它不知道你的实际流量模型、数据库隔离级别、消息队列的投递语义。这些代码必须由对系统全貌有把握的资深工程师来写,AI顶多能帮你生成一些辅助性的工具类。
还有一个场景很容易被忽略:你正在学习某个新框架或新特性时。这时候如果用AI直接生成代码,你会错过大量亲手尝试和踩坑的成长机会。AI可以当参考答案,但不要当老师,它讲解错误概念时跟讲正确概念时一样自信。
6.2 提示词资产管理:把经验沉淀成模板
AI写Java代码的水平,很大程度上取决于你的提示词水平。我们把这次实验中所有好用的提示词全部沉淀成了团队内部的模板库,按场景分类存放,包括“标准CRUD提示词”“分页查询提示词”“单元测试提示词”“DTO转换提示词”。
这个模板库的维护方式是“使用-反馈-修订”闭环。每次用模板生成的代码在审查时发现了问题,就追溯是提示词描述得不清楚,还是AI本身能力受限。如果是前者,立刻修订模板。几轮迭代下来,模板的命中率越来越高,团队里新来的同事只要照着模板提需求,AI生成的东西已经跟老手手写的差距不大了。
一个很具体的经验:模板里必须写“禁止使用deprecated API”和“不能省略import”,这两条能省掉一半的编译报错时间。
6.3 一个可复用的落地规范草案
最后分享一份我们内部正在执行的“AI辅助编码使用规范”草案,各位可以直接抄走,按团队情况调整:
第一,AI只能作为编码辅助工具,不能作为代码质量责任人。任何一段AI生成的代码,最终署名和负责的都是提交它的人。
第二,AI生成的代码必须经过编译、单测、Review、业务验证四道关卡,不能跳过任何一道。
第三,涉及资金、权限、核心事务链路的代码,禁用AI直接生成,只能允许AI生成辅助工具类和测试代码。
第四,所有AI辅助生成的代码在提交时打上标记,便于追溯质量问题和优化提示词。
第五,不得直接将AI生成的第三方依赖代码复制进项目,必须人工核对依赖版本、许可证和已知漏洞。
这套规范不复杂,但它在团队里立了一个很重要的心理预期:AI是个能干的帮手,但它干的活必须有人验收。有了这个预期,AI带来的效率提升才能稳固地转化为交付速度,而不是变成线上事故的隐患。
这两周跑下来,我个人的体会是:AI写Java这件事,真正的门槛不在AI,而在使用者的工程素养。你越是能把需求拆得清清楚楚、把边界条件列得明明白白、把验收动作做得严严实实,AI给你的回报就越高。反过来,如果你指望丢一句“写个订单系统”它就产出生产级代码,那不是AI牛逼,是你想得太美。
最后再分享一个小技巧:让AI写比较复杂的方法时,先让它输出“实现思路”,确认思路没问题后再让它写代码。这个两段式输出能拦住一大半“思路错了还硬卷编码”的情况,尤其是那些涉及多表关联和状态流转的业务逻辑,先对齐思路,再谈实现。