关于Java中的方法详解

目录:

方法的基本用法

方法的重载

方法的递归


 首先,我们先来谈谈方法,方法是什么?其实方法就相当于C语言中的函数。为什么要在代码中使用方法?其实就是让代码模块化,需要该功能时可以直接调用方法,避免重复造轮子。

基本用法:

1.方法的定义:我们最先接触的就是main方法吧,main方法是这样的:public static void main(String[] args){ }。关于主方法的理解我分为以下几个方面:
a. main是由权限修饰符public修饰,说明主方法是公共的。如果权限修饰符修饰类呢?是这样的 

访问包的位置 private protected public
本类 可见 可见 可见
同包其他类或子类 不可见 可见 可见
其他包的类或子类 不可见 不可见 可见

b. 由static修饰,说明主方法是静态的,所以如果在主方法中调用其他方法,则该方法必须也是静态的。若不是静态的方法必须进行对象的实例化。
那么为什么主方法要用static修饰呢?
首先先要了解一下静态修饰符static。由static修饰的变量、常量和方法被称作静态的。他们都存放在内存的“静态区”中,这些变量和方法有独立的生存周期。内存中的静态区在整个程序运行结束之后才会释放,所以用静态修饰的代码周期是整个程序的生命周期。
       #内存中静态区的变量被本类共享,其他类调用本类静态变量和静态方法时,无需实例化就能调用。
       # final static 修饰的成员变量就是静态常量,可以理解为定义完可以共享的,假如定义一个pi,大家都能用。
       # static修饰的成员方法,无需创建类的对象。main方法中没有创建System的对象,就能实现了输出功能。
       # static修饰的代码块就是静态代码块,定义一块静态代码块,可以完成类的初始化操作,在类声明时就能运行。ps:经过                  专业测验,我发现程序中运行顺序:静态代码块>非静态代码块>构造方法>成员方法。

c. 返回值是void,返回值为空的或者没有返回值。

d.主方法的参数是一个字符串数组。可以使用args.length获取参数个数。

通过主方法我们也可以得出方法定义为:

 public static 方法返回值 方法名称 ([参数类型 形参]...){
     方法体;
     return 返回值
}


方法的调用方式是  返回值变量 = 方法名称 (实参);这里又出现了"传值","传址"两种传输数据的方式,在java中只能进行值传递。并且方法定义时的参数为形参,方法调用时的参数为实参。对于基础类型(8中)来说,形参相当于实参的拷贝,即传值调用的方式。

方法的重载

那么为什么要使用方法的重载呢?假如我们写了一个加法程序,加数数据类型有可能是int double等等。如果double数据调用int类型函数就会报错,有可能会造成数据缺失。因此就引入了方法的重载。

方法的重载必须在同一个类中或者在继承的类中。

重载的规则:方法名相同,方法的参数不同(个数或类型),方法的参数类型不影响重载。

方法的递归

递归就是方法自己调用自己,但是必须有一个趋近于终止的条件。否则会造成无休止的循环,造成栈溢出。

递归必须要有一个思想:把大事化小。

分析两个比较经典问题:

1、斐波那契数列:1 1 2 3 5 8.......

解析:容易发现,一二两项都是1,从第三项开始为前面两项之和。我们可以采用大事化小的思想,要求n=5的斐波那契数,不就是n=4的斐波那契数和n=3的斐波那契数之和,n=4的斐波那契数又是n=3的斐波那契数和n=2的斐波那契数之和....以此类推直到n=2依次返回。可以这样写:

public class Test {

		public static int fibonacci(int n) {
			if(n==1 || n==2) {
				return 1;
			}
			return fibonacci(n-1)+fibonacci(n-2);
		}
		
		public static void main(String[] args) {
			int ret = fibonacci(5);
			System.out.println(ret);
		}
}

但是发现数字太大的话,比如50。那么运行速度会非常非常慢,因为算每一个数时,前面的数都要重新运行一遍导致运行次数太多了。我优化了代码如下,通过循环赋值的方式,思路也不是很难,但是要注意哪个值先被赋! 

public class Fibonacci {

	public static int fibonacci(int n) {
		int f1 = 1;
		int f2 = 1;
		int f3 = 1;
		for(int i=3; i<=n; i++) {
			f3 = f2+f1;
			f1 = f2;
			f2 = f3;
		}
		return f3;
	}
	
	public static void main(String[] args) {
		int ret = fibonacci(6);
		System.out.println(ret);
	}
}

我发现 循环和递归都可以解决这个问题,那么循环和递归有什么优缺点

通过上面的代码我发现:

①递归代码简单,但是调用函数在栈上,所以空间消耗要比非递归代码要大很多。每一次函数调用都需要在内存栈中分配空间以保存参数,返回地址以及临时变量,而且往栈里面压入数据和弹出都需要时间。② 递归本质是把一个问题分解为多个问题,如果这多个问题存在重复计算,有时候会随着n成指数增长,造成程序执行时间过长。斐波那契的递归就是一个例子。 
③递归还有栈溢出的问题,每个进程的栈容量是有限的。

循环呢代码更加高效,占用空间小。如果两种方法都行,选择非递归。。当然也要具体问题具体分析。

2、汉诺塔问题:

相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置64个金盘(如下图)。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上上。(摘自360百科)

解析:①限制条件:n=1时不再递归

           ②每次移动时当前最大的盘子已经移到目标杆子上去了,数量就减一,越来越接近这个限制条件。

 如果是三根柱子的话,23(在A)通过C移到B,再把23(在B)通过A移到C。 如此想来如果是64根柱子,那就把上面的63根柱子通过C移到B,再把最大的柱子从A-->C,再把63根柱子通过A移到C。再以此类推直到把最后一个柱子移动完。这样就把大事化为一步一步的小事情。

public class Hanoi {
	static int i = 0;
	public static void move(char from, char to) {
		i++;
		System.out.println("第"+i+"步:"+from+"--->"+to);
	}
	
	public static void hanoi(int n, char pos1, char pos2, char pos3) {
		if(n == 1) {
			move(pos1,pos3);
		}else {
			hanoi(n-1,pos1,pos3,pos2);
			move(pos1,pos3);
			hanoi(n-1,pos2,pos1,pos3);
		}
	}
	
	public static void main(String[] args) {
		hanoi(3,'A','B','C');
		System.out.println("共移动了"+i+"步");
	}
}

3、青蛙跳台阶问题:

一个青蛙一次可以跳1级台阶、也可以跳2级台阶。跳上一个n阶的台阶有多少种跳法

解析:一个台阶有1种跳法,两个台阶有2种跳法,三个台阶有3种跳法,四个台阶有5种跳法......  不难发现好像斐波那契数呀,我这样写了,以后要是有新的想法再来补充。。。。

public class frogJump {

	public static int frogjump(int n) {
		if(n == 1 || n==2) {
			return n;
		}
//		int f1 = 1;
//		int f2 = 2;
//		int f3 = 0;
//		for(int i=3; i<=n; i++) {
//			f3 = f2+f1;
//			f1 = f2;
//			f2 = f3;
//		}
//		return f3;
		return  frogjump(n-1)+ frogjump(n-2);
	}
	
	public static void main(String[] args) {
		int ret = frogjump(5); 
		System.out.println("共有"+ret+"种跳法");
	}

}
发布了51 篇原创文章 · 获赞 14 · 访问量 2337

猜你喜欢

转载自blog.csdn.net/qq_41185460/article/details/102539116