第七章 方法

1 方法

1.1 方法的引入


/*
对于一个java程序员来说,如果没有“方法”,会存在什么问题?
代码无法得到复用。(怎么提高复用性,可以定义方法,然后需要使用该功能的时候,直接调用一下方法即可,这样代码就可以得到复用了)
*/

public class MethodTest01{
public static void main(String[] args){
// 需求1:请编写程序,计算100和200的求和。
int x = 100;
int y = 200;
int z = x + y;
System.out.println(x + "+" + y + "=" + z);

// 需求2:请编写程序,计算666和888的求和。
// 这个要求2实际上和需要1是完全相同的,只不守具体求和时的数据不同。
int a = 666;
int b = 888;
int c = a + b;
System.out.println(a + "+" + b + "=" + c);

// 需求3:请编写程序,计算111和222的和
int m = 111;
int n = 222;
int k = m + n;
System.out.println(m + "+" + n + "=" + k);

/*
需求1和需求2本质上相同,只不过参与运算的数值不同,
代码编写了三份,显然代码没有得到重复复用,专业术语叫做“复用性”差。
功能/业务逻辑相同,为什么要重复编写代码,代码能不能写一次,以后要是需要再次使用该“业务/需求”的时候,直接调用就可以了。。

如果想达到代码复用,那么需要学习java语言中的方法机制。
*/
}
}

 

1.2 体验方法的作用

什么是方法,有什么用?
方法(method)
方法是可以完成某个特定功能的并且可以被重复复用的代码片段。
在c语言中,方法被称为“函数”,在java中不叫函数,叫做方法。
你定义了一个/抽取了一个方法出来,而这个方法确无法完成某个功能,那么你抽取的这个方法毫无意义。一般一个方法就是一个"功能单元"。假设在以后的开发中,某个功能是可以独立抽取出来的,建议定义为方法,这样以后只要需要这个功能,那么直接调用这个方法即可,而不需要重复编写业务逻辑代码。



/*
这个程序是体验程序,体验一下方法的好处。

注意:
程序开始执行的时候是先执行main方法
因为main方法是一个入口。

在java语言中所有的方法体中的代码都必须遵循自上而下的顺序依次逐行执行。

main方法不需要程序员手动调用,是由JVM调用的。
但是除了main方法之外其他的方法,都需要程序员手动调用,方法只有调用的时候才会执行,方法不调用是不会执行方法里面的代码的。
*/

public class MethodTest02{

// 方法定义在类体当中。
// 方法定义的先后顺序没有关系,都可以。
/*
public static void sumInt(int x, int y){
int z = x + y;
System.out.println(x + "+" + y + "=" + z);
}
*/

// 主方法,入口
public static void main(String[] args){
// 需求1:请编写程序,计算100和200的和。
sumInt(100, 200);
// 需求2:请编写程序,计算666和888的和。
sumInt(666, 888);
// 需求3:请编写程序,计算111和222的和。
sumInt(111, 222);
}

// 专门在这个类体中定义一个方法,这个方法专门来完成求和。
// x y z在以下的sumInt方法中属于局部变量
// 局部变量有且个特点:方法结束之后,局部变量占用的内存会自动释放。

public static void sumInt(int x, int y){
int z = x + y;
System.out.println(x + "+" + y + "=" + z);
}
}

 

1.3 方法的语法机制

1、方法怎么定义,语法机制是什么?
[修饰符列表] 返回值类型 方法名(形式参数列表){
方法体;
}

注意:
[] 这个符号叫做中括号,以上中括号[]里面的内容表示不是必须的,是可选的。
方法体由Java语句构成。
方法定义之后需要去调用,不调用是不会执行的。

1.1、关于修饰符列表:
修饰符列表不是必选项,是可选的,目前为止,大写统一写成:public static

1.2、关于返回值类型:
第一:返回值类型可以是任何类型,只要是java中合法的数据类型即可,数据类型包括基本数据类型和引用数据类型。
第二:什么是返回值?
返回值一般指的是一个方法执行结束之后的结果,结果通常是一个数据,所以被称为“值”,而且还叫“返回值”
方法就是为了完成某个特定的功能,方法结束之后大部分情况下都是有一个结果的,而体现结果的一般都是数据,数据得有类型,这就是返回值类型。
main{
// 调用a方法
a();  如果a方法执行结束之后有返回值,这个返回值返回给main了
}
a(){}
方法执行结束之后的返回值实际上是给调用者了,谁调用就返回给谁。
第三:当一个方法执行结束不返回任何值的时候,返回值类型也不能空白,必须写上void关键字。所以void表示该方法执行结束后不返回任何结果。
第四:
如果返回值类型那里写的不是void,那么你在方法体执行结束的时候必须使用"return 值;"这样的语句来完成“值”的返回,如果没有没有返回“值”编译器会报错
return 值; 这样的语句的作用是什么?作用是“返回值”

第五:只要有“return”关键字的语句执行,当前方法必然结束。

第六:
如果返回值类型那里写的是void,那么在方法体当中不能有“return 值;”这样的java语句。
但是可以有“return;”语句。这个语句“return;”的作用是终止当前方法的。

第七:除了void之外,剩下的都必须有“return 值;”这样的语句。

1.3、方法名
方法名要见名知意。
方法名在标识符命名规范当中,要求首字母小写,后面每个单词首字母大写。
只要是合法的标识符即可。

1.4、形式参数列表
简称:形参
注意:形式参数列表中的每一个参数都是“局部变量”,方法结束之后内存释放。
形参的个数是:0~N个。
public static void sumInt(){}
public static void sumInt(int x){}
public static void sumInt(int x, int y){}
public static void sumInt(int x, int y, double z){}
形参有多个的话使用“逗号,”隔开。
形参的数据类型起决定性作用,形参对应的变量名是随意的。

1.5、方法体:
由Java语句构成。java语句以";"结尾。
方法体当中编写的是业务逻辑代码,完成某个特定功能。
在方法体中的代码遵循自上而下的顺序依次逐行执行。
在方法体中处理业务逻辑代码的时候可能需要数据,数据来源就是通过这些形参来获取。


2、方法定义之后怎么调用呢?
方法必须调用才能执行。
怎么调用,语法是什么?
类名.方法名(实际参数列表);
实参和形参的类型必须一一对应,另外个数也要一一对应。


public class MethodTest03{
// main方法执行结束之后不需要给JVM返回任何执行结果。
public static void main(String[] args){

// 调用方法
// 错误:不兼容的类型:String无法转换为int
// MethodTest03.divide("abc", 200);

// 错误原因:实际参数列表和形式参数列表长度不同
// MethodTest03.divide();

// (111, 222)叫做实际参数列表,简称形参
MethodTest03.divide(111, 222);

// 调用sum方法
// 怎么去接收这个方法的返回结果?
// 使用变量来接收这个方法的返回值。
// 注意:变量的定义需要指定变量的数据类型。
// 变量的数据类型是什么呢?
int result = MethodTest03.sum(100, 200);
System.out.println(result);

// result变量可以是double类型吗?
// double是大容量。int是小容量,自动类型转换
double result = MethodTest03.sum(100, 300);
System.out.println(result);

// 对于没有返回值的方法,变量能接收吗?
// divide方法结束没有返回值,不能接收。
// 错误:不兼容的类型:void无法转换为int
// int i = divide(100, 50);

// 当一个方法有返回值的时候,我可以选择不接收吗?
// 可以,但是这样没有意义,一般程序返回了执行结果,都是需要接收这个结果的。
// 我们可以不接收,但是这个返回值该返回还是会返回的,只不过不用变量接收。
// 以下代码虽然没有使用变量一接收这个返回值,但是这个值还是返回了。
// 返回之后内存马上释放,因为没有使用变量接收。
MethodTest03.sum(122, 22);

byte b1 = 10;
byte b2 = 20;
int result = sum(b1, b2);  // (b1, b2)是实参自动类型转换
System.out.println(result);

}

// 计算两个int类型数据的和
public static int sum(int a, int b){
return a + b;
}

// 方法定义到这里也可以。没有顺序要求。
// 业务是什么?计算两个int类型数据的商
// 方法执行结束之后返回执行结果。
// 错误:缺少返回语句
/*
public static int divide(int x, int y){
int z = x / y;
}
*/

// 错误:不兼容的类型:String无法转换为int
/*
public static int divide(int x, int y){
int z = x / y;
return "abc"
}
*/

// 可以
/*
public static int divide(int x, int y){
int z = x / y;
return z;
}
*/

// 可以
/*
public static int divide(int x, int y){
return x / y;
}
*/

// 可以
/*
public static int divide(int a, int b){
return a / b;
}
*/

// 如果我不需要执行之后的返回值
// 这个结果我希望直接输出
// 错误:不兼容的类型:意外的返回值
/*
public static void divide(int a, int b){
return a / b;
}
*/

// 可以
/*
public static void divide(int a, int b){
return; // 用来终止这个方法的
}
*/

// 可以
/*
public static void divide(int a, int b){

}
*/

// 可以
public static void divide(int a, int b){
System.out.println(a / b);
}

}

 

1.4 调用方法时,什么时候类名可以省略


/*
在方法调用的时候,什么时候“类名.”是可以省略的。什么时候不能省略?
a()方法调用b()方法的时候,a和b方法都在同一个类中,“类名.”可以省略。
如果不在同一个类中,“类名.”不能省略

*/

public class MethodTest04{

public static void println3(){
System.out.println("hello World3!");
}

public static void main(String[] args){
// 调用
MethodTest04.println1();
MethodTest04.println2();
MethodTest04.println3();

// “类名.”可以省略吗?
println1();
println2();
println3();

// 跨类调用
// 像这种情况下:“类名.”就不能省略了。
Myclass.println1();
Myclass.println2();
Myclass.println3();
}

public static void println1(){
System.out.println("hello World1!");
}

public static void println2(){
System.out.println("hello World2!");
}
}

// 类2
class Myclass{
public static void println1(){
System.out.println("MyClass's hello World!");
}

public static void println2(){
System.out.println("Myclass's hello World2!");
}

public static void println3(){
System.out.println("Myclass's hello World3!");
}
}

 

1.5 代码执行顺序


// 任何一个方法体当中的代码都是遵循自上而下的顺序依次执行的。

/*
执行结果推测
main begin
m1 begin
m2 begin
m3 begin
T's m3 method execute!
m3 over
m2 over
m1 over
main over

main方法最先执行,最后执行。
main方法结束之后,整个程序就结束了。
*/

public class MethodTest05{
public static void main(String[] args){
System.out.println("main begin");
// 调用m1方法
m1();
System.out.println("main over");
}

public static void m1(){
// 调用程序不一定写到main方法中,不要把main方法特殊化。
// mian方法也是一个普通方法
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}

// m2方法可以调用T类的m3()方法吗?
public static void m2(){
System.out.println("m2 begin");
T.m3()
System.out.println("m2 over");
}
}

class T{
public static void m3(){
System.out.println("m3 begin");
System.out.println("T's m3 method execute!");
System.out.println("m3 over");
}
}

 

1.6 break和return的区别


/*
break; 语句和 return; 语句有什么区别?
break; 用来终止switch和离它最近的循环。
return; 用来终止离它最近的一个方法。
*/

public class MethodTest06{
public static void main(String[] args){
for(int i = 0; i < 10; i++){
if(i == 5){
// break; // 终止for循环
return;  // 终止当前的方法,和break;不是一个级别的。
// 错误:不兼容的类型:意外的返回值
// return 10;
}
System.out.println("i = " + i);
}
System.out.println("Hello world");
}

}

 

1.7 返回语句


// 分析以下代码,编译器会报错吗?
public class MethodTest07{
public static void main(String[] args){

// 调用m方法
int result1 = m();
System.out.println(result1);

// 调用x方法
int result2 = x(true);
System.out.println(result2);

// 调用x方法
int result3 = x(false);
System.out.println(result3);

}

// 错误:缺少返回语句
/*
public static int m(){
boolean flag = true; // 编译器不负责运行程序,编译器只讲道理
// 对于编译器来说,编译器只知道flag变量是boolean类型
// 编译大会认为flag可能是false,有可能是true
if(flag){
// 编译器觉得:以下这行代码可能会执行,当然也可能会不执行
// 编译器为了确保程序不出现任何异常,所以编译器说:缺少返回语句
return 1;
}
}
*/

// 报错:缺少返回语句
/*
public static int m(){
int flag = 1;
if(flag == 1){
return 10;
}else if(flag == 2){
System.out.println("hello");
}else{
return 20;
}
}
*/

// 怎么修改这个程序呢?
// 第一种方案:保证每个分支都有"return 值;"语句即可
/*
public static int m(){
boolean flag = true;
if(flag){
return 1;
}else{
return 0;
}
}
*/

// 第二种方案:该方案实际上是方案1的变形
// return语句一旦执行,所在的方法就会结束。
/*
public static int m(){
boolean flag = true;
if(flag){
return 1;
}
return 0;
}
*/

// 在同一个域当中,“return 语句”下面不能再编写其它代码,编写之后编译报错。
/*
public static int m(){
boolean flag = true;
if(flag){
return 1;
// 错误:无法访问的语句
// System.out.println("hello");
}
// 这行代码和上面打印代码的区别是:不在同一个域当中。
System.out.println("hello2");
return 0;
// 错误:无法访问的语句
System.out.println("hello3");
}
*/

// 第三种方案
// 三目运算符
public static int m(){
boolean flag = true;
return flag ? 1 : 0;
}

public static int x(boolean flag){
return flag ? 1 : 0;
}

}

 

1.8 方法执行时内存变化


// 局部变量:只在方法体中有效,方法结束之后,局部变量的内存就释放了。
// JVM三块主要的内存:栈内存、堆内存、方法区内存。
// 方法区最先有数据:方法区中放代码片段。存放class字节码。
// 堆内存:后面讲。
// 栈内存:方法调用的时候,该方法需要的内存空间在栈中分配。
// 方法不调用是不会在栈中分配空间的。

// 方法只有在调用的时候才会在栈中分配空间,并且调用时就是压栈。
// 方法执行结束之后,该方法所需要的空间就会释放,此时发生弹栈动作。

// 栈中存储什么?方法运行过程中需要的内存,以及栈中会存储方法的局部变量。

public class MethodTest08{
// 主方法,入口
public static void main(String[] args){
System.out.println("main begin");
int x = 100;
m1(x);
System.out.println("main over");
}

public static void m1(int i){
System.out.println("m1 begin");
m2(i);
System.out.println("m1 over");
}

public static void m2(int i){
System.out.println("m2 begin");
m3(i);
System.out.println("m2 over");
}

public static void m3(int i){
System.out.println("m3 begin");
System.out.println(i);
System.out.println("m3 over");
}
}

 

 

 

1.9 作业

计算阶乘


public class Homework1{
public static void main(String[] args){
/*
// 计算5的阶乘
int n = 5;
int result = 1;
for(int i = n; i > 0; n--){
// System.out.println(i);
result *= i;
}
System.out.println(result);
*/

// 调用方法计算阶乘
int value1 = factorial(5);
int value2 = factorial(6);
System.out.println(value1);
System.out.println(value2);

}

// 提取一个方法出来,这个方法专门用来计算某个数的阶乘
// 这个数不一定是5,可能是其他的值,可能是6,也可能是7,不确定。
// 像这种不确定的数据,对于方法来说我们就可以定义为:形以。
// 该方法是为了完成阶乘的,最终是需要珍上计算结果的,所以该方法应该有返回值。
// 将最后的结果返回给调用方,谁调用我,我就返回给谁,返回值类型定义为int
public static int factorial(int x){
int result = 1;
for(int i = x; i > 1; i--){
result *= i;
}
return result;
}

}

 

输出大于某个正整数n的最小的质数


/*
编写一个方法,输出大于某个正整数n的最小的质数
比如:这个正整数n是2
也就是要输出:大于2的最小的质数,结果就是3

比如:这个正整数n是9
也就是要输出:大于9的最小的质数,结果就是11

思路:
首先,系统一定会先给你一个“正整数n”,然后你基于这个n往后++,每加1得到的新数判断一下是否为质数。
*/

public class Homework2{
public static void main(String[] arg){
/*
// 假设目前系统给了一个正整数n,n为5
int n = 5;
// 请找出大于5的最小的质数
while(true){
n++; // n自加1
// 判断此时的n是否为质数
boolean flag = isPrime(n);
if(flag){
System.out.println(n);
break;
}
}
*/

// 对一个单独的方法进制测试
/*
boolean flag = isPrime(7);
System.out.println(flag);
*/

printMiniPrime(5);
printMiniPrime(10);
printMiniPrime(12);
printMiniPrime(100);

}

// 这方法就是用来打印最小质数的。
public static void printMiniPrime(int n){
while(true){
n++;  // n自加1
// 判断此时的n是否为质数
boolean flag = isPrime(n);
if(flag){
System.out.println(n);
break;
}
}
}

// 定义一个专门的方法,来判断某个数字是否为质数
// 这个方法的形参是:被判断的数字num
// 这个方法的返回值类型是true表示质数,是false表示非质数。
public static boolean isPrime(int num){
// 质数只能被1和自身整除
for(int i = 2; i <= num / 2; i++){
if(num % i == 0){
return false;
}
}
// 程序能够执行到此处说明num已经是质数了
return true;
}
}

 


public class MethodTest09{
public static void main(String[] args){

// 调用sum方法
int result = sum(10, 20);
System.out.println(result);

// 上面的两行代码能否合并为一行
// 可以
System.out.println(sum(10, 20));

/*
boolean flag = m();
if(flag){
System.out.println("真的...");
}
*/

if(m()){
System.out.println("真的...");
}


}

public static boolean m(){
return true;
}

// 求和的方法
public static int sum(int a, int b){
return a + b;
}

}

 


/*
编写一个方法,输出大于某个正整数n的最小的质数
比如:这个正整数n是2
也就是要输出:大于2的最小的质数,结果就是3

比如:这个正整数n是9
也就是要输出:大于9的最小的质数,结果就是11

思路:
首先,系统一定会先给你一个“正整数n”,然后你基于这个n往后++,每加1得到的新数判断一下是否为质数。
*/

public class Homework2升级版本{
public static void main(String[] arg){
printMiniPrime(7);

}

public static void printMiniPrime(int n){
while(!isPrime(++n)){}
System.out.println(n);

}

public static boolean isPrime(int num){
for(int i = 2; i <= num / 2; i++){
if(num % i == 0){
return false;
}
}
return true;
}
}

 

 

2 方法的重载

2.1 方法重载机制的引入


/*
方法重载机制?
1、以下程序先不使用方法重载的机制,分析程序的缺点??
以下程序没有语法错误,运行也是正常的,你就分析一下代码风格存在什么缺点?
sumInt、sumLong、sumDouble不是功能“相同”,是功能“相似”。
三个方法功能不同,但是相似,分别起了三个不同的名字,有什么缺点?

缺点包括两个:
第一个:代码不美观(不好看)。
第二个:程序员需要记忆更多的方法名称,程序员比较累

*/

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;
}

}

 

2.2 使用方法重载的优点


/*
使用方法重载机制,解决之前的两个缺点。
优点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;
}
}

 

2.3 方法重载的发生的条件


/*
方法重载(overload):
什么时候需要考虑使用方法重载?
在同一个类当中,如果“功能1”和“功能2”它们的功能是相似的,那么可以考虑将它们的方法名一致,这样代码既美观,又便于后期的代码编写(容易记忆,方便使用)。
注意:方法重载不能随意使用,如果两个功能压根不相干,不相似,根本没关系,此时两个方法使用重载机制的话,会导致编码更麻烦。无法进行方法功能的区分。

什么时候代码会发生方法重载?
条件1:在同一个类当中
条件2:方法名相同
条件3:参数列表不同
参数的个数不同算不同
参数的类型不同算不同
参数的顺序不同算不同
只要同时满足以上3个条件,那么我们可以认定方法和方法之间发生了重载机制。


注意:
不管代码怎么写,最终一定能让java编译器很好的区分开这两个方法。
方法重载和方法的形式参数名称无关
方法重载和方法的返回值类型无关
方法重载和修饰符列表无关
*/

public class OverloadTest03{
public static void main(String[] args){
m1();
m1(100);

m2(111, 111.111);
m2(111.111, 111);
}

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 y){
System.out.println("m3(double y)");
}

// 分析:以下两个方法有没有发生重载
// 编译器报错了,不是重载,这是重复了
/*
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){

}
}

 

2.4 println方法采用了方法重载机制


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

 

2.5 方法重载机制 应用-代码封装


// 方法重载机制:overload

public class S{
// 以下所有的p()方法构成了方法的重载。
// 换行的方法
public static void p(){
System.out.println();
}

// 输出byte
public static void p(byte b){
System.out.println(b);
}
// 输出short
public static void p(short b){
System.out.println(b);
}
// 输出int
public static void p(int b){
System.out.println(b);
}
// 输出long
public static void p(long b){
System.out.println(b);
}
// 输出float
public static void p(float b){
System.out.println(b);
}
// 输出double
public static void p(double b){
System.out.println(b);
}
// 输出boolean
public static void p(boolean b){
System.out.println(b);
}
// 输出char
public static void p(char b){
System.out.println(b);
}
// 输出String
public static void p(String b){
System.out.println(b);
}

}

 

 

3 方法递归

3.1 递归没有结束条件时会发生栈溢出错误

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

2、当递归时程序没有结束条件,一定会发生:
栈内存溢出错误:StackOverflowError
所以:递归必须要有结束条件。

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");

}
}

 

 

3.2 不使用递归计算1~n的和


// 先不使用递归,计算1~n的和。

public class RecursionTest02{
public static void main(String[] args){
// 1~10的和
int value1 = sum(10);
System.out.println(value1);
}

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

 

3.3 使用递归计算1~n的和及内存图分析


// 使用递归,计算1~n的和。

public class RecursionTest03{
public static void main(String[] args){
// 1~10的和
int value1 = sum(3);
System.out.println(value1);
}

// 单独编写一个计算1~n和的方法
// 使用递归的方法
// 1 + 2 + 3
// 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);
// return 3 + sum(2);  
// return 3 + 2 + sum(1);
// return 3 + 2 + 1;

}
}

 

 

 

3.3 习题


// 使用递归的方法计算N的阶乘
// 5的阶乘:5 * 4 * 3 * 2 * 1

public class RecursionTest04{
public static void main(String[] args){
// 调用factorial方法
int value1 = factorial(5);
System.out.println(value1);
}

// 定义一个方法计算N的阶乘
public static int factorial(int n){
// 当n==1的时候返回1
if(n == 1){
return 1;
}
return n * factorial(n - 1);
}
}
 
posted @   路走  阅读(139)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示