Java零基础入门:方法(函数)深度掌握
——让代码像搭积木一样复用
- 为什么需要方法呢?
万能的童鞋们都会做饭吧?我们想象一下,当我们在做菜的时候,都需要进行备菜这个环节,那么我们就需要重复的进行切菜这个动作了,但是在代码中我们重复的进行切菜这个动作显得十分麻烦,因此在代码中我们就会把这个动作打包成一个动作单——:“切菜”。
那么方法(method)就是程序中的动作单元。
对于没有方法的问题呢
public class NoMethod {
public static void main(String[] args) {
// 计算 1+2
int a = 1, b = 2;
int sum1 = a + b;
System.out.println("结果:" + sum1);
// 计算 3+4(同样的代码又写一遍)
int c = 3, d = 4;
int sum2 = c + d;
System.out.println("结果:" + sum2);
// 计算 5+6(再写一遍……)
int e = 5, f = 6;
int sum3 = e + f;
System.out.println("结果:" + sum3);}}
同样的逻辑同样的代码我们重复的写了3遍,一处错我们就需要改三处,非常的麻烦,也容易出现漏改。
那我们把它改成方法呢?
public class WithMethod {
public static void main(String[] args) {
int result1 = add(1, 2);
System.out.println("结果:" + result1);
int result2 = add(3, 4);
System.out.println("结果:" + result2);
int result3 = add(5, 6);
System.out.println("结果:" + result3);}
// 把"加法"打包成一个方法
public static int add(int x, int y) {
return x + y;}}
这样就大大的节省了我们的时间,看起来也就非常的简洁了。
- 方法的定义(怎样塑造一个方法)
2.1 语法结构
修饰符 返回值类型 方法名(参数列表) {
// 方法体(具体做什么)
return 返回值; // 如果有返回值
}
例如:public static int add(int a, int b) {
return a + b;}
ok,那我现在就分解的讲一下里面代码所包含的意思
部分 | 说明 | 本例 |
public | 修饰符:谁可以调用 | 所有人都能调用 |
static | 修饰符:静态方法(属于类,不属于对象) | 可以直接用 类名.方法名() 调用 |
int | 返回值类型:方法执行完"吐"出什么类型 | 吐出一个整数 |
add | 方法名:见名知意 | 表示"加法" |
(int a, int b) | 参数列表:调用时传入的数据 | 两个整数 |
{ ... } | 方法体:具体执行逻辑 | 把两数相加 |
return a + b | 返回语句:把结果交回去 | 返回相加的结果 |
那如果我们遇到只需要做事而不需要结果的类型呢?也有方法——无返回值的方法
public static void sayHello() {
System.out.println("你好,欢迎学习Java!");
// 没有 return(或者写 return; 提前结束)}
sayHello();// 直接调用,不需要接收返回值
- 方法的调用(怎么使用方法)
3.1 调用语法
// 1. 同一个类中,直接写方法名
返回值类型 变量 = 方法名(实际参数);
// 2. 不同类中,用 类名.方法名()
返回值类型 变量 = 类名.方法名(实际参数);
例如:
public class MethodDemo {
public static void main(String[] args) {
// 调用 add 方法
int result = add(10, 20);
System.out.println("10 + 20 = " + result); // 30
// 调用无返回值方法
printStar();
// 调用带参数的打印方法
printMessage("Hello Java", 3);}
// 加法方法
public static int add(int a, int b) {
return a + b;}
// 无返回值:打印星星
public static void printStar() {
System.out.println("★★★★★");
// 带参数:打印指定消息指定次数 }
public static void printMessage(String msg, int times) {
for (int i = 0; i < times; i++) {
System.out.println(msg);}}}
main 方法开始
├─ 调用 add(10, 20)
│ │
│ ├─ a = 10, b = 20
│ ├─ 计算 a + b = 30
│ └─ return 30 ← 回到 main
├─ result = 30
├─ 调用 printStar()
│ └─ 打印 ★★★★★ ← 回到 main
└─ main 结束
- 参数的传递
切记:java只有值传递!不管是基本类型还是引用类型,传递的都是“值的拷贝”
4.1基本类型参数传递
基本类型参数传递就是你有一本笔记本,上面写着 "10",你让同学帮你改一下,你把笔记本上的 "10" 抄了一份,交给同学,同学在自己的纸条上把 "10" 改成了 "100"。结果:你的笔记本上还是 "10",同学的纸条上是 "100",你给的是"值的复印件",同学改的是复印件,你的原件不受影响。
public class ValuePassDemo {
public static void main(String[] args) {
int num = 10;
change(num);
System.out.println("main 中 num = " + num); // 10(没变!)}
public static void change(int x) {
x = 100;
System.out.println("change 中 x = " + x); // 100}}
在这个例题中呢,main函数首先在本体函数中找到num的值,再调取change函数,把num的值拷贝给x,在内存中,num → 10(在main的栈帧里)x → 10(在change的栈帧里,是拷贝)change 里修改 x = 100:x → 100(只改了自己的拷贝)change 结束,x 的栈帧销毁num → 还是 10(毫发无损),所以基本类型传参,方法内部修改不影响外面的变量。
4.2 引用类型参数传递
引用类型参数传递就是你有一把钥匙(地址),能打开公司的一间储物间(堆内存中的对象),储物间里放着 {1, 2, 3},你配了一把钥匙的复制品(拷贝地址),交给同事,同事拿着复制品去储物间,把里面的 1 换成了 100。结果:储物间里的东西确实被改了,你再拿自己的钥匙去开储物间 → 看到的是 {100, 2, 3},你给的不是储物间本身,而是一把钥匙的复制品。同事通过这把钥匙进了同一个储物间,改了里面的东西。
public class ReferencePassDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
changeArray(arr);
System.out.println("main 中 arr[0] = " + arr[0]); // 100(变了!)}
public static void changeArray(int[] a) {
a[0] = 100; // 修改数组第一个元素
System.out.println("change 中 a[0] = " + a[0]); // 100}}
在这个例题中呢,main函数首先得到本体中arr的地址和其中的值,再调用changearr函数,将地址值拷贝一份给a,在内存中,
arr → 0x100 ─┐
a → 0x100 ─┴→ {1, 2, 3}(堆内存中的同一个数组)change 里 a[0] = 100:通过地址 0x100 找到数组,修改了堆里的数据,change 结束,a 的栈帧销毁,但堆里的数组已经被改了!arr 还是指向 0x100 → {100, 2, 3}(看到了变化),所以引用类型传参,方法内部通过引用修改对象内容会影响外面。但如果方法内部让引用指向新对象,外面不受影响。
4.3 引用类型"重新指向"
引用类型"重新指向"就是同事拿着你的钥匙复制品,突然说:"我不要这间储物间了,我要去另一间!"他配了一把新储物间的钥匙,走进去放了 {100, 200, 300},结果:同事在新储物间里玩得很开心但你的钥匙还是指向原来的储物间 {1, 2, 3},你毫不知情
public class ReferenceReassignDemo {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
reassign(arr);
System.out.println("main 中 arr[0] = " + arr[0]); // 1(没变!)}
public static void reassign(int[] a) {
a = new int[]{100, 200, 300}; // a 指向了新数组
System.out.println("reassign 中 a[0] = " + a[0]); // 100}}
在这个例题中呢,main函数首先得到本体中arr的地址和其中的值,再调用reassign函数,里:a = new int[]{...},a → 0x200 → {100, 200, 300}(a 指向了新地址),但 arr 还是指向 0x100 → {1, 2, 3}(没变),所以传递的是地址的拷贝,不是地址本身。方法里重新赋值只是改了拷贝,不影响原来的引用。
- 方法重载(OverLoad)
5.1为什么需要重载?
想象一下,你去餐厅点餐,"来一份炒饭","来一份炒饭,加辣","来一份炒饭,加辣,不要葱"。如果餐厅规定"每种菜只能有一个名字",那菜单上就得写:炒饭1() → 普通炒饭,炒饭2(辣) → 加辣炒饭,炒饭3(辣, 不要葱) → 加辣不要葱炒饭。多丑啊!现实中我们都叫它"炒饭",只是加的料不同。方法重载就是让同一个方法名能适应不同参数,调用时写同一个名字,编译器自动帮你选对版本。
编译器怎么区分重载方法呢? 其实编译器不看方法名,也不看返回值,它看的是方法签名。方法签名 = 方法名 + 参数类型列表。
add(int, int) → 签名1
add(int, int, int) → 签名2
add(double, double) → 签名3
add(int, double) → 签名4
add(double, int) → 签名5
调用 add(1, 2) 时,编译器看到你传了两个 int,就去找签名1 → 找到了 → 调用成功。
5.2 重载规则深度解析
规则1:方法名必须相同
这是重载的前提。名字不同就是两个完全无关的方法,谈不上重载。
规则2:参数列表必须不同
"不同"有三种方式,满足任意一种即可:
方式 | 示例 | 说明 |
参数个数不同 | add(int, int) vs add(int, int, int) | 最直观 |
参数类型不同 | add(int, int) vs add(double, double) | 最常用 |
参数顺序不同 | add(int, double) vs add(double, int) | 较少用,可读性差 |
规则3:返回值类型不能单独作为重载依据
public static int add(int a, int b) { return a + b; }
public static double add(int a, int b) { return a + b; } // ❌ 编译报错
因为方法调用时,返回值可以不被接收:add(1, 2); // 谁知道该调哪个?编译器懵了
如果允许仅靠返回值重载,上面这行代码编译器就不知道选 int 版本还是 double 版本。所以 Java 规定:返回值不参与重载区分。
规则4:参数名不同不算重载
public static int add(int x, int y) { return x + y; }
public static int add(int a, int b) { return a + b; } // ❌ 编译报错
对编译器来说,(int, int) 就是 (int, int),不管你参数叫 x,y 还是 a,b。
5.3 正确重载示例逐行拆解
版本1:两个数相加
public static int add(int a, int b) {
System.out.println("调用:两个整数相加");
return a + b;}
行 | 解释 |
public static int | 公开、静态、返回整数 |
add(int a, int b) | 接收两个 int 参数 |
System.out.println(...) | 打印提示(方便观察调用了哪个版本) |
return a + b | 返回两数之和 |
调用:add(1, 2) → 编译器匹配 (int, int) → 调用此版本
版本2:三个整数相加
public static int add(int a, int b, int c) {
System.out.println("调用:三个整数相加");
return a + b + c;}
调用:add(1, 2, 3) → 编译器匹配 (int, int, int) → 调用此版本,参数个数不同,签名就不同,完美重载。
版本3:两个小数相加
public static double add(double a, double b) {
System.out.println("调用:两个小数相加");
return a + b;}
调用:add(1.5, 2.5) → 编译器匹配 (double, double) → 调用此版本
版本4 & 5:参数顺序不同
public static double add(int a, double b) { ... } // 版本4
public static double add(double a, int b) { ... } // 版本5
add(1, 2.5); // int + double → 版本4
add(1.5, 2); // double + int → 版本5
这种重载方式可读性差,实际开发中尽量少用。读者看到 add(1, 2.5) 和 add(1.5, 2) 调用了不同方法,容易困惑。
5.4 练习
1.打印星星三角形
Public class try{
Public static void printStar(int rows){
For(int i=1;i<rows;i++){
For(int j=1;j<=I;j++){
System.out.println(“★”);}
System.out.println();}}}
2. 打印矩形
public static void main(String[] args) {
prinStar(5, 4);}
public static void prinStar(int rows, int cols) {
if (rows <= 0 || cols <= 0) {
System.out.println("行数和列数必须大于0");
return;}
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= cols; j++) {
System.out.print("★");}
System.out.println();}}
3. 指定字符打印三角形
public static void printStar(char ch, int rows) {
for (int i = 1; i <= rows; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(ch);}
System.out.println();}}
- 实战场景深度解析
6.1 计算机
public class Try11 {
public static void main(String[] args) {
boolean continueCulation = true;
Scanner sc = new Scanner(System.in);
do {
System.out.println("欢迎进入简易计算器");
System.out.println("本计算器支持加减乘除");
System.out.println("请输入第一个数字:");
double num1 = sc.nextDouble();
System.out.println("请输入运算符:");
char operator = sc.next().charAt(0);
System.out.print("请输入第二个数字: ");
double num2 = sc.nextDouble();
double result =calculate(num1, num2, operator);
}while(continueCulation);
System.out.println("感谢使用计算器,再见!");
sc.close();}
private static double calculate(double num1, double num2, char operator) {
switch (operator) {
case '+':
return num1 + num2;
case '-':
return num1 - num2;
case '*':
return num1 * num2;
case '/':
if (num2 != 0) {
return num1 / num2;
} else {
return Double.NaN; // 除数为零时返回NaN }
default:
return Double.NaN; // 无效运算符返回NaN }}}
6.2 repeat 重载
// 版本1:重复字符串
public static String repeat(String str, int times) {
if (isEmpty(str) || times <= 0) return "";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < times; i++) {
sb.append(str);}
return sb.toString();}
// 版本2:重复字符
public static String repeat(char ch, int times) {
if (times <= 0) return "";
char[] arr = new char[times];
for (int i = 0; i < times; i++) {
arr[i] = ch;}
return new String(arr);}
版本1用 StringBuilder 循环拼接字符串
版本2直接创建 char[] 数组填充后转 String,性能更好(少了 StringBuilder 的中间操作)
七、课后练习
练习1(基础):写一个方法 isPrime(int n),判断一个整数是否为素数,返回 boolean。
练习2(基础):写一个方法 reverse(int num),将整数反转(如 123 → 321)。
练习3(进阶):重载 compare 方法,支持比较两个整数的大小、两个字符串的长度、两个数组的和。
练习4(挑战):写一个递归方法 factorial(int n) 计算 n 的阶乘,并加上终止条件校验。