方法
1 方法是什么
最简单的来说,本质上就是一段可以被重复使用的代码片段。和C语言中的函数差不多。
每个方法一般都是只完成一个工作。
1.1举例
1.1.1不使用方法完成
阶乘求和:
int result = 0;
for (int num = 1; num <= 5; num++) {
int factorResult = 1;
for (int i = 1; i <= num; i++) {
factorResult *=i;
}
result +=factorResult;
}
System.out.println(result);
使用了四个变量,同时记得四个变量会加大我们的负担
1.1.2 使用方法
public static void main(String[] args) {
int result = 0;
for (int num = 1; num <=5; num++) { //num实参
result += factor(num); //方法调用
}
System.out.println(result);
}
//方法定义
public static int factor(int num) { //num形参
int factorResult = 1;
for (int i = 1; i <=num; i++) {
factorResult *= i;
}
return factorResult;
}
把代码分成多个方法后,就可以有效的降低大脑思考的负担,也就减少了出现 bug 的可能。
注意:
调用方法时,将实参作为形参的初始值,之后就没有关系了。形参的 num 和外面的实参的num没有关联关系,形参的 num 的作用域只是在当前的factor方法之内,并且形参的名字可以随便起。
1.2方法调用的过程
遇到方法调用的代码,就会跳转到方法内部执行(同时,将实参的值赋给形参作初始值),顺序执行方法内部的代码,然后,方法内部遇到return或者 方法执行完毕时,代码就会回到调用位置继续执行。
方法内部的代码,只有在被调用后才会执行,反之不执行。
2 方法的定义和使用
2.1方法的定义
方法定义的几个重要元素:
1、方法的名称;
2、方法的指令们;
3、方法的形参(没有时写”( )“空括号);
4、方法可能出现的返回值类型(没有返回值时用void );
5、其他修饰信息。
标准格式如下:
public static int factor(int num) {
//修饰 返回值类型 方法名称(形参列表)
//指令:
int factorResult = 1;
for (int i = 1; i <=num; i++) {
factorResult *= i;
}
return factorResult; //返回值
}
return 有两个功能:
(1) 当返回值类型为void ,没有返回值时,也可以使用return 让方法结束;
(2) 返回一个值。
2.2 方法的调用
方法调用的几个重要元素:
1、调用那个方法,即方法名称;
2、使用哪些具体的值进行本次调用,即调用时的实参;
(实参的数目和类型,需要和形参一致,如果不匹配,会编译出错)
3、调用方法可能得到的返回值的后续处理,保存或者直接再次使用。
在C语言中:如果函数的定义写到函数调用的上方,此时不必写函数声明,但是如果函数的定义在调用的下方,或者在其他文件中,就需要在调用之前,加上声明。
在Java中:没有 ”声明“ 这样的概念。
实际开发中,一般先写调用,复杂的项目开发时,代码中到底有哪些方法,它们都是干什么的,在一开始并不一定都能想清楚(包括该方法的参数是什么类型,有几个,返回值是什么类型)。
如果先明确了方法是如何调用的,对方法的实现来说是有很大帮助的。
3 方法执行过程分析
3.1 内存、栈、栈帧的关系
3.2 内存、栈、栈帧的定义
内存 :
程序和数据平常存储在硬盘(硬盘是一种可记忆盘)等存储器上,不管你开机或关机了,它们都是存在的,不会丢失。硬盘可以存储的东西很多,但其传输数据的速度较慢。所以需要运行程序或打开数据时,这些数据必须从硬盘等存储器上先传到另一种容量小但速度快得多的存储器(无记忆盘),之后才送入CPU进行执行处理。这中间的存储器就是内存。
栈:
是一个广义概念,它具体指什么需要在具体情况下分析,例如在 数据结构、操作系统、Java/JVM 中都有涉及到栈,但它们的概念并不完全相同。
栈,是JVM中一块特殊的内存区域。
在Java中,JVM也是操作系统创建出来的一个Java进程,本来一个程序对于内存的使用,都是通过操作系统来进行管理的。但在Java中,为了让内存管理更加方便,会在JVM启动时从操作系统中申请一大块内存,自己再进行管理。 JVM 会对申请到的这一大块内存进一步的进行区域划分。其中的一块区域就称为“栈”。栈里面存的是一些特定的内容,核心就是方法和方法之间的调用关系。
栈帧:就是图中的小长方形,包含被调用的方法的一些信息
1、该方法的地址是啥
2、该方法的参数有哪些
3、该方法内部的局部变量
4、该方法的返回地址(回到哪里)
5、该方法的返回值
3.3 方法执行过程
以猜数字游戏为例子:
// main方法
public static void main(String[] args) {
guessNumberGame();
}
//方法一:guessNumberGame()
public static void guessNumberGame() {
while(true){
int choice = menu();
if(choice == 1){
game();
}else if(choice == 0){
System.out.println("白白~");
break;
}else{
System.out.println("输入错误,请重试...");
}
}
}
//方法二:game()
public static void game() {
Random random = new Random();
int toGuess = random.nextInt(100)+1;
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("请输入你猜测的数:");
int num = scanner.nextInt();
if(num>toGuess){
System.out.println("猜大了");
}else if(num<toGuess){
System.out.println("猜小了");
}else {
System.out.println("恭喜你,猜对了!");
break;
}
}
}
//方法三:menu()
public static int menu() {
System.out.println("***********************");
System.out.println(" 1、play 0、exit ");
System.out.println("***********************");
System.out.println("请输入您的选择:");
Scanner scanner = new Scanner(System.in);
int choice = scanner.nextInt();
return choice;
}
(1) main方法是程序入口,先将main方法放入栈中,最先放入,所以在栈底
(2) 在main方法中遇到guessNumberGame()语句,就会进入该方法,该方法进栈,继续执行该方法中的语句
(3) 在guessNumberGame()方法中又遇到 menu() 方法,则进入该方法,menu()方法进栈
(4) 在menu方法中有多个println方法,我们叫它们println1、println2、println3……它们都是println只是参数不同。执行println1时,它会进栈,执行完了以后,它就会被从栈中删除,这也就是所谓的入栈和出栈。
入栈:调用某个方法,就会把该方法对应的一些信息,放到栈里面;
出栈:当某个方法执行完毕,就会把该方法对应的信息从栈中删除掉。
menu方法中的其他入栈出栈就不再过多赘述。
(5) menu方法执行完了之后,它也会被从栈中删除
(6) 回到guessNumberGame方法中,接着执行碰到game方法,game方法再入栈,执行结束后出栈,以此类推到程序整个执行结束。
3.4 通过调试观察栈帧变化
4 方法的重载
重载的规则
重载(overload):允许多个同名的方法存在。
构成重载的规则主要有两个:
1、方法名相同,但是方法的参数类型不同;
编译器会根据实参的类型决定匹配哪个版本的方法来进行执行。
public static void main(String[] args) {
System.out.println(add(10,20));
System.out.println(add(10.5,20.5));
System.out.println(add(10,10.5));
System.out.println(add(10.5,10));
//两个参数类型不同,交换位置也算重载
}
public static int add(int a,int b){
return a+b;
}
public static double add(double a,double b){
return a+b;
}
public static double add(int a,double b){
return a+b;
}
public static double add(double a,int b){
return a+b;
}
2、方法名相同,但是方法的参数个数不同
编译器会根据实参的个数决定匹配哪个版本。
public static void main(String[] args) {
System.out.println(add(10,20));
System.out.println(add(10,20,30));
}
public static int add(int a,int b){
return a+b;
}
public static int add(int a,int b,int c){
return a+b+c;
}
注意:
(1) 重载和返回值类型无关。如果两个方法,参数一样、参数类型一样、方法名一样,这种不构成重载,并且会编译出错。
(2) 构成重载的两个方法,得是在同一个作用域中。