factorial(0)必须返回1,因为数学定义规定0!=1;若以n==1为终止条件,factorial(0)将递归调用factorial(-1)、factorial(-2)…导致栈溢出。

Java递归求阶乘:为什么factorial(0)必须返回1
因为阶乘的数学定义中,0! 就是 1,不是“没有意义”或“要特殊跳过”。如果写成 if (n == 1) return 1;,那 factorial(0) 就会掉进递归无限调用——factorial(0) → factorial(-1) → factorial(-2)…直到栈溢出报错 StackOverflowError。
- 基础终止条件只能是
n ,返回 1(涵盖 0 和 1) - 输入负数时,应提前抛异常,而不是靠递归自己“停下来”
-
int类型在factorial(13)就开始溢出,别等factorial(20)还用int
示例:
public static long factorial(int n) {<br> if (n < 0) throw new IllegalArgumentException("n must be >= 0");<br> if (n <= 1) return 1;<br> return n * factorial(n - 1);<br>}
Java递归算斐波那契:fib(50)卡住不是代码错,是算法问题
直接写 return fib(n-1) + fib(n-2) 看似简洁,但时间复杂度是 O(2ⁿ),fib(50) 要算上万亿次调用。这不是 JVM 慢,是重复子问题没缓存——fib(48) 被算几十次,fib(47) 更多。
- 小数值(
n )可以纯递归练手,但生产环境别这么用 - 加个
Map<integer long></integer>缓存已算结果,性能立刻从指数级降到 O(n) - 注意键类型用
Integer,别用int——自动装箱可能引发空指针(尤其 map.get 返回 null 时)
缓存版示意:
private static Map<Integer, Long> cache = new HashMap<>();<br>public static long fib(int n) {<br> if (n < 0) throw new IllegalArgumentException("n must be >= 0");<br> if (n <= 1) return n;<br> if (cache.containsKey(n)) return cache.get(n);<br> long result = fib(n-1) + fib(n-2);<br> cache.put(n, result);<br> return result;<br>}
立即学习“Java免费学习笔记(深入)”;
递归深度超限:StackOverflowError 不是配 JVM 参数就能解决
Java 默认栈大小约 1MB,对应大概 8000–10000 层递归调用(取决于方法参数和局部变量数量)。哪怕你把 -Xss2m 调到 2MB,也只多撑一倍——而 fib(10000) 需要上万层,factorial(100000) 更是远超安全范围。
- 真要处理大 n,必须改迭代:用
for循环+变量累积,栈空间恒定 O(1) - 递归转迭代不难,但别硬套“尾递归优化”——Java 不支持尾递归自动优化(Scala、Kotlin 才有)
- JVM 参数能延缓问题,但掩盖不了设计缺陷;该换思路时别死磕递归
递归方法签名里要不要加 static?看调用场景
如果只是工具类里的纯计算(比如 MathUtils.factorial()),加 static 合理:不用实例化、无状态、线程安全(前提是你没用共享可变变量)。但如果递归逻辑依赖对象字段(比如树节点遍历中要更新 this.depth),那就不能静态——否则编译报错或逻辑错乱。
- 静态递归方法不能访问
this,也不能调用非静态成员 - 非静态递归方法要注意:多个线程同时调用同一个实例的递归方法,若方法内修改了实例变量,就会互相干扰
- 不确定时,优先写
static;需要维护状态,就拆成构造器传参 + 私有递归辅助方法
递归看着简单,但阶乘的边界、斐波那契的重复计算、栈深限制、方法静态性——每个点都容易在跑通第一个例子后被忽略。真正卡住你的,往往不是语法,而是这些隐含约束。










