JAVA入门—重谈方法

本文围绕Java语言中的方法展开,介绍了方法的本质、定义语法,包括修饰符列表、返回值类型、方法名、形参列表和方法体等要素。还讲解了方法的调用规则、内存分配,以及方法重载机制和递归原理,强调了方法可提高代码复用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于方法,先不说方法是什么,先来看没有使用方法写的代码所存在的问题
  • 以下代码都是完成两int类型数据的求和,相同的代码写了三遍(只不过每次参与运算的数据不同),代码没有得到重复利用
  • 应该在Java中存在一种机制:
    实现某个功能的代码只要写一遍
    要使用这个功能,只需要把具体的数据传递给它
    这个功能完成后返回一个最终结果
    这样一来,代码就能得到重复利用,提高代码复用性【这就是“方法”】
public class MethodTest01{
	public static void main(String[] aegs){
		
		//需求1:编写代码,计算10和20的和,输出结果【功能:计算两个int类型数据的和】
		int a = 10;
		int b = 20;
		int c = a+b;
		System.out.println(a+"+"+b+"="+c);
		
		//需求2:编写代码,计算666和888的和,输出结果【功能:计算两个int类型数据的和】
		int x = 666;
		int y = 888;
		int z = x+y;
		System.out.println(x+"+"+y+"="+z);
       
       //需求1:编写代码,计算10和20的和,输出结果【功能:计算两个int类型数据的和】
		int m = 111;
		int n = 222;
		int e = m+n;
		System.out.println(m+"+"+n+"="+e);
	}
}

上述三个需求其实就是一个需求
需求为:计算两个int类型数据的和,功能相同,只不过每次参与运算的数据不相同。

针对第一个例子使用方法效果

以下直接使用方法【不讲语法】,分析优点

  • 代码得到重复利用
方法的本质是什么?
  • 方法实际上就是一段代码片段,并且这段代码片段可以完成某个特定功能,并且可以被重复利用

方法:Method 调用:invoke

方法在c语言中叫做函数:Function

方法定义在类体中,在一个类当中可以定义多个方法,方法编写的位置没有先后顺序,可以随意。

方法体中不能再定义方法!!!!!!!!!!

方法体由Java语句构成,方法体中代码遵循由上而下的顺序依次执行。

public class MethodTest02{
	public ststic void main(String[] args){
		//计算两个int类型数据的和
		MethodTest02.sumInt(10,20);
		MethodTest02.sumInt(666,888);
		MethodTest03.sumInt(111,222);
	}
     //定义一个方法
     //该方法完成计算两个int类型数据的和,并且将结果输出
     public static void sumInt(int a,int b){
			int c = a+b;
			System.out.println(a+"+"+b+"="c);
		}

}
关于Java语言中的方法

1 方法怎么定义,语法结构

[修饰符列表]  返回值类型  方法名(形式参数列表){
		方法体;
}

2 对以上名词进行解释说明
2.1 修饰符列表

  • 可选项,不是必须的
  • 现阶段,统一写成:public static【以后再解释】
  • 方法的修饰符列表有“static”关键字,怎么调用这个方法
    类名.方法名(实际参数列表);

2.2返回值类型

  • 什么是返回值
    一个方法是可以完成某个特定功能的,这个功能结束后,大多数都是需要返回最终执行结果的,执行结果可能是一个具体存在的数据,而这个具体存在的数据就是返回值。

  • 返回值类型
    返回值是一个具体存在的数据,是数据就表示它一定有类型,此处需要对返回值具体类型进行指定

  • 返回值类型都可以指定哪些呢
    java任意一种类型都可以,包括基本数据类型和所有的引用数据类型

  • 需要值得注意的是,可能这个方法执行结束后不返回任何数据。java中规定,当一个方法执行结束后不返回任何数据的话,返回值类型必须编写: void关键字

  • 返回值类型可以是:byte、short、int、long、float、double、boolean、char、String、void…

  • 返回值如果不是void,即表示这个方法执行结束后必须返回一个具体数据,当方法执行结束后没有返回任何数据的话,编译会报错。怎么返回值呢?
    代码这样写:“return 值;”,并且要求这个“值”的类型必须和“方法的返回值类型”保持一致,不然编译报错

  • 返回值类型如果是void,在方法体中不能编写“return 值;”这种语句,但是要注意,可以编写“return;”
    这种语句

  • 不论是“return;”还是“return 值;”,只要有“return”关键字的语句一执行,return所在的方法结束【不是JVM结束,只是return所在的方法结束】

2.3方法名

  • 合法标识符
  • 方法名最好见名知意
  • 方法名最好是动词
  • 方法名首字母小写,后面每个单词首字母大写

2.4形式参数列表:简称形参

  • 形参是局部变量:int a; double b; float c; String s;…
  • 形参的个数:0~N个
  • 多个形参之间用“逗号”隔开
  • 形参中起决定性作用的是形参的数据类型,形参的名字就是局部变量名字
  • 方法在调用的时候,实际给这个方法传递的真实数据被称为:实际参数,简称:实参
  • 实参列表和形参列表必须满足:
    -数量相同
    -类型对应相同
    例如:
    方法定义
    public static int sum(String 变量,int 合法标识符){ //(int a,int b)是形参列表
    }
    方法调用
    sum(“abc”,“def”);编译报错!
    sum(“123”,20); //(“123”,20)是实参列表

这里总结一个小技巧有的新手总是不能理解和区分形参和实参,这里只要记住一点,凡是带有数据类型修饰的就一定是形参!!

2.5.方法体
方法体必须由大括号括起来,方法体中的代码有顺序,遵循自上而下的顺序依次执行

方法体由java语句构成,每一个java语句以“;”结尾

3.方法怎么调用?
【方法只定义而不去调用是不会执行的,只有在调用时才会执行】

语法规则:《修饰符列表有static关键字》

类名.方法名(实参列表);    //这是一条java语句,表示调用某个类的某个方法,传递这样的实参。
//public表示公开的
//class表示定义类
//MethodTest03是一个类名
public class MethodTest03{//表示定义一个公开的类,起名MethodTest03,由于是公开的类,所以名称要和源文件名称一致。
	
	//这里是类体啦
	//类体中不能直接编写java语句,除声明变量之外
	//方法出现在类体中
	
	//方法
	//public表示公开的
	//static表示静态的
	//void表示方法执行结束后不返回任何数据
	//main是方法名:主方法
	//(String[] args):形式参数列表,String[]是一种引用数据类型,args是一个局部变量的变量名
	//所以,以下只有args这个局部变量名可以自定义
	//主方法main只能这样编写,程序入口【SUN公司规定的】
	public static void main(String[] args){
		
		//这里是程序入口,一定会执行。
		//main方法是JVM调用的,一个入口位置
		//从这里作为起点,开始执行程序
		//既然如此,我们可以在此编写java语句从而来调用其他方法
		//调用MethodTest03的sum方法,传递两个实参。
		MethodTest03.sum(10,20);//实际上,执行到这里,main方法暂停,执行sum方法,sum方法执行结束后,这行代码执行结束。继续执行main方法中代码
		
		//【注意的是,方法体中代码是由上而下顺序依次执行,第118行代码不结束,
		//第124行他就不会执行】
		
		//一个方法可以被重复调用
		int a=100;
		MethodTest03.sum(a,500);//(a,500)实参列表
		
		//再调用一次
		int k=90;
		int f=10;
		MethodTest03.sum(k,f);//(k,f)实参列表
		
		
		
	}
	
	//自定义方法,不是程序入口
	//方法功能:计算两个int类型数据的和,不要求返回结果,但要求将结果直接输出到控制台
	//修饰符列表:public static
	//返回值类型:void
	//方法名:sum
	//形参列表:(int x,int y)
	//方法体:主要任务是求和后输出计算结果
	public static void sum(int i,int j){
		System.out.println(i+"+"+j+"="+(i+j));
	}
	
	
}

方法的调用不一定在main方法中,可以在其他方法中,只要是程序可以执行到的位置,都可以调用其他方法

public class MethodTest04{
	
	public static void sum(int a,int b){
		System.out.println(a+"+"+b+"="+(a+b));
		
		//调用doSome方法
		MethodTest04.doSome();
	}
	
	//主方法
	public static void main(String[] args){
		
		//调用sum方法
		MethodTest04.sum(1,2);
		
		System.out.println("Hello World!");
		
	}
	
	public static void doSome(){
		System.out.println("do some!");
	}
	
	
}

方法调用时要求实参和形参个数对应相同,数据类型对应相同
类型不同时要求进行相应的自动类型转换

public class MethodTest05{
	
	public static void main(String[] args){
		
		//编译报错,参数数量不同
		//MethodTest05.sum();		
		
		//编译报错,实参和形参类型不相同
		//MethodTest05.sum(true,false);
		
		//可以
		MethodTest05.sum(10L,20L);
		
		//存在自动类型转换,int转换long
		MethodTest05.sum(10,20);
		
		//编译报错,参数类型不是对应相同
		//MethodTest05.sum(3.0,20);
		
		//可以
		MethodTest05.sum((long)3.0,20);
	}
	
	public static void sum(long a,long b){
		System.out.println(a+"+"+b+"="+(a+b));
	}
	
}

方法的调用:

1 方法的修饰符列表有static关键字的,完整的调用方式:类名.方法名(实参列表);
2 有的时候,可以省略不写“类名.”,在什么情况下可以省略?

m1(){
		m2();
	}

m1方法和m2方法在同一个类体当中,“类名.”可以省略不写
调用的方法和被调用的方法在同一个类体当中

3 建议在一个java源文件中只定义一个class,比较清晰,这里是为了说明的目的
在一个java源文件中定义多个class,不要模仿

public class MethodTest06{
	
	public static void main(String[] args){
		
		//完整调用
		MethodTest06.m();
		
		m();//编译通过
		
		//调用其他类的方法【不是本类】
		A.doOther();
		
		//编译报错,省略“类名.”后,默认从MethodTest06,即当前类中找“doOther”方法,但是该方法不存在
		//doOther();

	}
	
	public static void m(){
		System.out.println("m方法被调用!");
		
		//完整调用
		MethodTest06.m2();
		
		//省略的方式
		m2();
		
		A.m2();
	}
	
	public static void m2(){
		System.out.println("m2方法被调用!");
	}
	

}

class A{
	
	public static void doOther(){
		System.out.println("A的doOther方法被调用!");
	}
	
	public static void m2(){
		System.out.println("A的m2方法被调用!");
	}
	
}

对于当前程序来说:

  • main方法最先被调用,main方法也是最后一个结束
  • m3方法最后调用,m3方法也是最先结束

main方法结束后,整个程序结束【在现阶段来】

无需刻意记忆,
方法体中代码由上而下按顺序依次执行,上一行代码未执行结束,下一行代码永远不会执行

public class MethodTest07{
	
	public static void main(String[] args){
		
		System.out.println("main begin");
		m1();
		System.out.println("main over");
		
	}
	
	public static void m1(){
		System.out.println("m1 begin");
		m2();
		System.out.println("m1 over");
	}
	
	public static void m2(){
		System.out.println("m2 begin");
		m3();
		System.out.println("m2 over");
	}
	
	public static void m3(){
		System.out.println("m3 begin");
		System.out.println("m3 over");
	}
	
	
}

输出结果:
main begin
m1 begin
m2 begin
m3 begin
m3 over
m2 over
m1 over
main over

方法返回值类型不是void的时候:

1.方法必须百分百保证执行“return 值;”语句来值的返回。没有即报错。

2.一个方法有返回值时,我们调用这个方法,方法返回了一个值,对于调用者来说,可以选择接收,也可以选择不接收但是大部分情况下时要接收的

public class MethodTest08{
	
	public static void main(String[] args){
		
		//调用方法
		divide(10,3);
		//【什么都没发生,因为这里并没有选择接收返回值,但是方法运行了】
		
		//接收返回值
		//利用变量接收
		//变量数据类型必须和返回值类型保持一致,或者可以自动类型转换
		//boolean b=divide(10,3);//编译报错,类型不兼容
		
		//赋值运算符右边先执行,将执行结果赋值给左边的变量
		int i=divide(10,3);
		System.out.println(i);
		
		long x=divide(10,3);
		System.out.println(x);
		
		System.out.println(divide(10,3));//也可以直接将返回值打印输出出来
		
	}
	
	//编译报错:缺少返回语句
	/*
	public static int divide(int a,int b){
		
	}
	*/
	
	//编译报错:缺少返回值
	/*
	public static int divide(int a,int b){
		return;
	}
	*/
	
	//编译报错:方法定义的时候要求返回int类型,此时返回布尔类型,类型不兼容
	/*
	public static int divide(int a,int b){
		return true;
	}
	*/
	
	//可以,但是没有满足需求
	/*
	public static int divide(int a,int b){
		return 1;
	}
	*/
	
	/*
	public static int divide(int a,int b){
		int c=a/b;
		return c;
	}
	*/
	
	public static int divide(int a,int b){
		return a/b;
	}
	
}

深入探究return语句

  • return语句一旦执行,所在方法执行结束

  • 在“同一个作用域”中,return语句下面不能编写任何代码,因为这些代码会永远执行不到,编译报错

public class MethodTest09{
	
	public static void main(String[] args){
		
		/*
		int retValue=m();
		System.out.println(retValue);
		*/
		
		//编译错误,调用m方法并没有传递实参,即没有括号,编译器会认为这个m是某个变量。
		//System.out.println(m);
		
		//编译通过,正确地调用m方法。即使形参没有,但是(形参)这个格式一定要有。
		System.out.println(m());
		
		
		
	}
	
	//编译报错,缺少返回语句,编译器认为 return 1;这条语句并不能完全保证执行到。
	/*
	public static int m(){
		int a=10;
		if(a>3){
			return 1;
		}
		
	}
	*/
	
	//编译通过,带有else的控制语句一定会保证有一个分支执行,return 1; 或者 return 0;这两个肯定会执行一个
	/*
	public static int m(){
		int a=10;
		if(a>3){
			return 1;
		}else{
			return 0;
		}
		
	}
	*/
	
	//在这里,需要说明的是下面的代码和上面的完全一样。
	//首先if语句里a>3可能为真也可能为假
	//真-有return 1执行,整个方法执行结束
	//假-有return 0执行,整个方法执行结束
	//不管怎么样,都会有 return 值 语句执行
    /*
	public static int m(){
		int a=10;
		if(a>3){
			return 1;
		}
		return 0;
		
		
	}
	*/
	
	/*
	public static int m(){
		int a=10;
		if(a>3){
			return 1;
			//System.out.println("Hello World!");//此行代码永远执行不到,无法访问的语句,编译报错
		}
		System.out.println("Hello World!");//现在你应该明白在同一个作用域中return语句下面不能加java语句了吧
		return 0;
		
		
	}
	*/
	
	//另外,还可以使用三元运算符达到最简代码的目的
	public static int m(){
		return 10>3?1:0;//后面的10>3?1:0是三元运算符,真输出1,假输出0
	}

}

在返回值类型为void的情况下使用 return;语句

return;语句出现在返回值类型为void的方法中是为了结束当前方法

public class MethodTest10{
	
	public static void main(String[] args){
		
		m();
		
		//return 10;不能这样写,这里是main方法,main方法返回值类型是void类型,只能写return;
		//当然写了return;语句后,下面也不能写代码了,因为访问不到。
		
		for(int i=10;i>0;i--){
			
			if(i==2){
				return;
			}
			System.out.println("i的值-->"+i);
			
		}
		
		System.out.println("Hello Word!");
		
		//retrun;直接终止了整个main方法的运行,所以Hello World!并没有输出
		
		//小结一下,main方法它还是方法,不要将他特殊化,return语句肯定能在main方法里使用
		
	}
	
	//编译报错,对返回值类型为void的方法不能使用 return 值; 这样的语句
	/*
	public static void m(){
		return 10;
	}
	*/
	
	public static void m(){
		
		for(int i=0;i<10;i++){
			if(i==5){
				return;//直接终止整个m方法,Hello World执行不到
				//break;//终止当前for循环
			}
			System.out.println("i -->"+i);
		}
		
		System.out.println("Hello World!");

	}
	
}

方法在执行过程中,在JVM中的内存是如何分配的,内存是如何变化的

1.方法只定义,不调用不会执行,并且在JVM中也不会给方法分配“运行所属”的内存空间。
只有在调用这个方法的时候,才会动态的给这个方法分配所属的内存空间

2.在JVM内存划分上有这样三块主要的内存空间(当然除了这三块主要的还有其他内存空间)

  • 方法区内存
  • 堆内存
  • 栈内存

3.关于“栈”数据结构
在这里插入图片描述

  • 栈:stack,一种数据结构
  • 数据结构反应的是数据的存储形态
  • 数据结构是独立的学科,不属于任何编程语言范畴,只不过在大多数语言当中要使用数据结构
  • 作为程序员要提前精通:数据结构和算法【计算机专业必修】
  • java程序员不精通数据结构和算法也能进行java开发,因为java语言拥有一套庞大的类库支撑
    别人写好,直接使用即可【javase当中的集合章节使用了大量数据结构】
  • 常见的数据结构:
    -数组
    -队列
    -栈
    -链表
    -二叉树
    -哈希表/散列表

4.方法代码片段存在哪里?方法执行的时候执行过程的内存在哪里分配?

  • 方法代码片段属于.class字节码文件的一部分,字节码文件在类加载的时候,
    将其放到了方法区当中。所以JVM中的三块主要内存空间中方法去内存最先有
    数据。存放了代码片段

  • 代码片段虽然在方法区内存中只有一份,但是可以被重复调用。
    每一次调用这个方法的时候,需要给该方法分配独立活动场所,在
    栈内存中分配。【栈内存中分配方法运行的所属内存空间】

5.方法在调用的瞬间,会给该方法分配内存空间,会在栈中发生压栈动作,方法执行结束后,给该方法分配的内存全部释放,此时发生弹栈动作。

  • 压栈:给方法分配内存
  • 弹栈:释放该方法的内存空间

6.局部变量在“方法体”内声明,局部变量运行阶段内存在栈中分配。
在这里插入图片描述
内存分析练习
注意:在 Editplus中,字体为红色的表示一个类的名字,并且这个类是JavaSE类库中自带的
我们自定义的MethodTest01类名是黑色显示的
JavaSE类库中自带的类,例如:String.class、System.class,这些类的名字也是标识符
只要是类名,就一定是标识符

重点:方法调用的时候,在参数传递的时候,实际上传递的是变量保存的那个“”传过去了

public class MethodTest01{
	
	public static void main(String[] args){
		
		int a=10;
		int b=20;
		int retValue=sumInt(a,b);
		System.out.println("retValue = "+retValue);
		
		
	}
	
	public static int sumInt(int i,int j){
		int result=i+j;
		int num=3;
		int retValue=divide(result,num);
		return retValue;
	}
	
	public static int divide(int x,int y){
		int z=x/y;
		return z;
	}
	
	
}

画出这个程序执行过程的内存分析图

画图依据:
1.只要涉及到参数传递的问题,传递的是变量保存的值
2.画图的时候,必须遵循“方法自上而下的顺序依次执行”这个原则

在这里插入图片描述

public class MethodTest02{
	
	public static void main(String[] args){
		
		int i=10;
		method(i);
		System.out.println("main -->"+i);
		
	}
	
	public static void method(int i){
		i++;
		System.out.println("method -->"+i);
	}

}

画图分析
程序每走一步,画一步即可,注意变量是先右后左
在这里插入图片描述

方法重载机制
  • 以下程序先不使用方法重载机制,分析程序的缺点???
  • 以下程序没有语法错误,运行也是正常的,你就分析以下代码风格存在什么缺点 !
public class OverloadTest01{
	//主方法
	public static void main(String[] args){
		int x = sumInt(10, 20);
		System.out.println(x);

		long y = sumLong(10L, 20L);
		System.out.println(y);

		double z = sumDouble(10.0, 20.0);
		System.out.println(z);
	}

	// 定义一个计算int类型数据的求和方法
	public static int sumInt(int a, int b){
		return a + b;
	}

	// 定义一个计算long类型数据的求和方法
	public static long sumLong(long a, long b){
		return a + b;
	}

	// 定义一个计算double类型数据的求和方法
	public static double sumDouble(double a, double b){
		return a + b;
	}
}

sumInt、sumLong、sumDouble不是功能“相同”,是功能“相似”。
三个方法功能不同,但是相似,分别起了三个不同的名字,有什么缺点??

缺点包括两个:

  • 第一个:代码不美观(不好看、不整齐)。【这是次要的】
  • 第二个:程序员需要记忆更多的方法名称,程序员比较累。

使用方法重载机制,解决之前的两个缺点。

  • 优点1:代码整齐美观。
  • 优点2:“功能相似”的,可以让“方法名相同”,更易于以后的代码编写。

在Java语言中,是怎么进行方法区分的呢?

  • 首先java编译器会通过方法名进行区分。
  • 但是在java语言中允许方法名相同的情况出现。
  • 如果方法名相同的情况下,编译器会通过方法的参数类型进行方法的区分。
public class OverloadTest02{
	public static void main(String[] args){
		// 对于程序员来说,只需要记忆一个方法名即可。
		System.out.println(sum(10, 20));
		System.out.println(sum(10L, 20L));
		System.out.println(sum(10.0, 20.0));
	}
	// 定义一个计算int类型数据的求和方法
	public static int sum(int a, int b){
		System.out.println("int求和");
		return a + b;
	}

	// 定义一个计算long类型数据的求和方法
	public static long sum(long a, long b){
		System.out.println("long求和");
		return a + b;
	}

	// 定义一个计算double类型数据的求和方法
	public static double sum(double a, double b){
		System.out.println("double求和");
		return a + b;
	}
}
方法重载(overload)

什么时候需要考虑使用方法重载??
在同一个类当中,如果“功能1”和“功能2”它们的功能是相似的,那么可以考虑将它们的方法名一致,这样代码既美观,有便于后期的代码编写(容易记忆,方便使用)。

【注意】:方法重载overload不能随便使用,如果两个功能压根不相干不相似,根本没关系,此时两个方法重载机制的话,会导致编码更麻烦,无法进行方法功能的区分。

什么时候代码会发生方法重载??

时候代码会发生方法重载?	 
		条件1:在同一个类当中
		条件2:方法名相同
		条件3:参数列表不同
					参数的个数不同算不同
					参数的类型不同算不同
					参数的顺序不同算不同

		只要同时满足以上3个条件,那么我们可以认定方法和方法之间发生了
		重载机制。
		
注意:
	不管代码怎么写,最终一定能让java编译器很好的区分开这两个方法。

	方法重载和方法的“返回值类型”无关。
	方法重载和方法的“修饰符列表”无关。
public class OverloadTest03{
	public static void main(String[] args){
		m1();
		m1(100);

		m2(10, 3.14);
		m2(3.14, 10);

		m3(100);
		m3(3.14);

	}

	public static void m1(){
		System.out.println("m1无参数的执行!");
	}
	
	// 这个方法的参数个数和上面的方法的参数个数不同。
	public static void m1(int a){
		System.out.println("m1有一个int参数执行!");
	}

	public static void m2(int x, double y){
		System.out.println("m2(int x, double y)");
	}

	// 参数的顺序不同,也算不同。
	public static void m2(double y, int x){
		System.out.println("m2(double y, int x)");	
	}

	public static void m3(int x){
		System.out.println("m3(int x)");
	}

	// 参数的类型不同。
	public static void m3(double d){
		System.out.println("m3(double d)");
	}

	//分析:以下两个方法有没有发生重载?
	// 编译器报错了,不是重载,这是重复了:呵呵。
	/*
	public static void m4(int a, int b){
	
	}
	public static void m4(int x, int y){
	
	}
	*/

	// 这两个方法有没有发生重载呢?
	// 这不是重载,这是方法重复了。
	/*
	public static int m5(){
		return 1;
	}
	public static double m5(){
		return 1.0;
	}
	*/

	//这两个方法重载了吗?
	// 这个方法没有修饰符列表
	// 这不是重载,是重复了。
	/*
	void m6(){
	
	}
	// 这个有修饰符列表
	public static void m6(){
	
	}
	*/

}

class MyClass{
	// 不在同一个类当中,不能叫做方法重载。
	public static void m1(int x, int y){
	
	}
}

通过源代码查看发现println()方法重载了

public class OverloadTest04{
	public static void main(String[] args){
		// 大家是否承认:println是一个方法名。
		// println我承认是方法名了,但是这个方法谁写的?SUN公司的java团队写的。
		// 你直接用就行。
		// println()方法肯定是重载了。(不信,你可以翻阅一下SUN公司写的源代码看看。)
		// 对于println()方法来说,我们只需要记忆这一个方法名就行。
		// 参数类型可以随便传。这说明println()方法重载了。
		System.out.println(10);
		System.out.println(3.14);
		System.out.println(true);
		System.out.println('a');
		System.out.println("abc");

		System.out.println(100L);
		System.out.println(3.0F);

		// 调用m方法
		m(100);
	}

	public static void m(int i){
		
	}

}
方法的递归
	1、什么是方法递归?
		方法自己调用自己,这就是方法递归。

	2、当递归时程序没有结束条件,一定会发生:
		栈内存溢出错误:StackOverflowError
		所以:递归必须要有结束条件。(这是一个非常重要的知识点。)

		JVM发生错误之后只有一个结果,就是退出JVM。

	3、递归假设是有结束条件的,就一定不会发生栈内存溢出错误吗?
		假设这个结束条件是对的,是合法的,递归有的时候也会出现栈内存溢出错误。
		因为有可能递归的太深,栈内存不够了。因为一直在压栈。
	
	4、在实际的开发中,不建议轻易的选择递归,能用for循环while循环代替的,尽量
	使用循环来做。因为循环的效率高,耗费的内存少。递归耗费的内存比较大,另外
	递归的使用不当,会导致JVM死掉。
	(但在极少数的情况下,不用递归,这个程序没法实现。)
	所以:递归我们还是要认真学习的。

	5、在实际的开发中,假设有一天你真正的遇到了:StackOverflowError
	你怎么解决这个问题,可以谈一下你的思路吗?
		我来谈一下我的个人思路:
			首先第一步:
				先检查递归的结束条件对不对。如果递归结束条件不对,
				必须对条件进一步修改,直到正确为止。

			第二步:假设递归条件没问题,怎么办?
				这个时候需要手动的调整JVM的栈内存初始化大小。
				可以将栈内存的空间调大点。(可以调整大一些。)
			
			第三步:调整了大小,如果运行时还是出现这个错误,
			没办法,只能继续扩大栈的内存大小。
			
			(java -X)这个可以查看调整堆栈大小的参数

递归原理
在这里插入图片描述

public class RecursionTest01{
	// 入口
	public static void main(String[] args){
		doSome();
	}

	public static void doSome(){
		System.out.println("doSome begin");
		// 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
		// 目前这个递归是没有结束条件的,会出现什么问题?
		doSome();
		// 这行代码永远执行不到。
		System.out.println("doSome over");
	}

	/*
	public static void doSome(){
		System.out.println("doSome begin");
		// 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
		doSome();
		// 这行代码永远执行不到。
		System.out.println("doSome over");
	}

	public static void doSome(){
		System.out.println("doSome begin");
		// 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
		doSome();
		// 这行代码永远执行不到。
		System.out.println("doSome over");
	}

	public static void doSome(){
		System.out.println("doSome begin");
		// 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
		doSome();
		// 这行代码永远执行不到。
		System.out.println("doSome over");
	}

	public static void doSome(){
		System.out.println("doSome begin");
		// 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
		doSome();
		// 这行代码永远执行不到。
		System.out.println("doSome over");
	}

	public static void doSome(){
		System.out.println("doSome begin");
		// 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
		doSome();
		// 这行代码永远执行不到。
		System.out.println("doSome over");
	}

	public static void doSome(){
		System.out.println("doSome begin");
		// 调用方法:doSome()既然是一个方法,那么doSome方法可以调用吗?当然可以。
		doSome();
		// 这行代码永远执行不到。
		System.out.println("doSome over");
	}

	public static void doSome(){
		// 假设突然有一天,一个条件成立了,这个doSome结束了
		if(某个条件成立了){
			return;
		}
	}
	*/
}

递归没有结束条件的时候会发生栈内存溢出错误
在这里插入图片描述

// 先不使用递归,请编写程序,计算1~n的和。
public class RecursionTest02{
	public static void main(String[] args){
		// 1~10的和
		int retValue1 = sum(10);
		System.out.println(retValue1);

		// 1~3的和
		int retValue2 = sum(3);
		System.out.println(retValue2); // 6 (1 + 2 + 3)
	}

	// 单独编写一个计算1~n和的方法
	public static int sum(int n){
		int result = 0;
		for(int i = 1; i <= n; i++){
			result += i;
		}
		return result;
	}
}

// 使用递归,请编写程序,计算1~n的和。
public class RecursionTest03{
	public static void main(String[] args){
		// 1~3的和
		int n = 3;
		int r = sum(n);
		System.out.println(r); // 6
	}

	// 大家努力的去看,去听,自己写不出来没关系,关键是能不能看懂。
	// 单独编写一个计算1~n和的方法
	// 这个代码修改为递归的方式。
	// 3 + 2 + 1
	public static int sum(int n){
		//n最初等于3
		// 3 + 2 (2是怎么的出来的:n - 1)
		//sum(n - 1); 
		if(n == 1){
			return 1;
		}
		// 程序能执行到此处说明n不是1
		return n + sum(n-1);
	}
}
// 使用递归的方式计算N的阶乘
// 5的阶乘:5 * 4 * 3 * 2 * 1

// 用递归的方式实现一个。
// 使用for循环的方式实现一个。
public class RecursionTest04{

	public static void main(String[] args){
		int n = 5;
		int jieGuo = jieCheng(n);
		System.out.println(jieGuo); // 120

		System.out.println(jieCheng2(5));
	}

	public static int jieCheng2(int n){
		int result = 1;
		for(int i = 2; i <= n; i++){
			result *= i;
		}
		return result;
	}

	public static int jieCheng(int n){
		// 5 * 4 * 3 * 2 * 1
		if(n == 1){
			return 1;
		}
		/*
		int result = n * jieCheng(n - 1);
		return result;
		*/

		return n * jieCheng(n - 1);
	}
}

下一篇:面向对象详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值