方法(其他语言叫函数)是把代码变成积木的手段:写一次、调百次。这一章除语法外,有三块硬骨头值得花力气:Java 的传参模型(只有值传递,但对象能被改,这句话的完整解释在本章)、重载(同名不同参,JVM 怎么选)、递归(数据结构课第 9 章的前置)。学完这章,回头再看第 1 章那个 public static void main,每个单词就都能说清楚了。
public static int max(int a, int b) { // 修饰符 返回类型 名字(参数)
return a > b ? a : b;
}
用这个最简例子把第 1 章的 main 拆掉:public 谁都能调(访问权限,第 7 章细讲),static 属于类不属于对象(不 new 就能调,第 7 章),int 返回值类型(void 表示不返回),max 名字,(int a, int b) 参数表。方法签名 = 名字 + 参数列表(返回值不参与签名),这是理解重载的钥匙。
return 的两条规则:返回类型非 void 时所有路径必须有 return(编译器强制,漏一条编译不过);return 即刻结束方法(可以拿它提前退出错误分支,配合出参就是 C 课第 10 章讲过的模式)。
Java 的传参规则一句话:拷贝实参的值。微妙处在于「值」对不同类型含义不同:
static void changePrimitive(int x) { x = 99; } // 改复印件,无效
static void changeArray(int[] arr) { arr[0] = 99; } // 改数组内容,有效!
static void reassign(int[] arr) { arr = new int[5]; } // 改复印件(门牌),无效
int[] a = {1, 2, 3};
changePrimitive(a[0]); // a[0] 还是 1
changeArray(a); // a[0] 变 99
reassign(a); // a 还是原来那个数组
完整解释(指针课第 10 章同款逻辑):基本类型的值就是数据本身,拷贝过去怎么改都碰不到原件;引用类型的值是「对象的门牌」,拷贝门牌过去,顺着门牌能改到同一个对象(changeArray 生效),但把复印件改成新门牌(reassign)只影响方法内的变量。所以那句经典总结的正确读法是:「Java 按值传递一切;引用类型的值恰好是地址,于是能共享内容」。C++ 的 int& 真引用、Python 的可变对象共享,都是同一原理的不同包装,面试题「Java 到底是值传递还是引用传递」的标准答案就藏在 6.2 这一段。
想要「改基本类型的实参」怎么办?和 C 同款答案:包装成对象/数组,或用返回值(int[] maxAndMin 返回两个结果),Java 没有 C 的出参指针风格,但有第 7 章的字段可改。
static int abs(int x) { return x < 0 ? -x : x; }
static double abs(double x){ return x < 0 ? -x : x; }
static long abs(long x) { return x < 0 ? -x : x; }
abs(-5); // 编译器按参数类型选第一个
abs(-5.5); // 选第二个
重载(overload)= 方法名相同、参数列表不同(个数或类型),编译器按实参类型静态决定调谁。三条边界:返回值不同不算重载(签名不含返回值,int f() 和 double f() 共存编译错误);自动类型提升会参与匹配(传 int 给 abs(double) 也行,选最具体的);歧义参数(f(1, 2) 面对 f(int, double) 和 f(double, int))直接编译错误。System.out.println 能打印万物,就是因为它重载了十几个版本,你已经用了五章的「多态」其实是重载。
变长参数 int... nums 本质是数组参数的糖:
static int sum(int... nums) { // 调用 sum(1,2,3) 或 sum()
int s = 0;
for (int n : nums) s += n;
return s;
}
递归在数据结构课第 9 章已经系统讲过(栈帧成本、递推式、何时放弃递归),这里补 Java 特有的两点:JVM 默认栈512KB~1MB(比 Linux C 的 8MB 小,-Xss 可调),递归深度上限更敏感;斐波那契朴素递归的指数爆炸在 Java 同样存在,记忆化(HashMap 缓存)是第 9 章集合类的作业。作用域规则回顾:局部变量声明处生效、必须初始化;成员变量有默认值;同名时局部遮蔽成员,this.x 显式指成员(第 7 章的主角登场前,先记住这个语法)。
print(分别接收 int、double、String、int[]),再实现变长参数版 printAll(Object...)(第 8 章 Object 再回头理解,现在照抄)。