提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


文章目录

  • 前言
  • 1、方法的概念
  • 2、自定义方法
  • 3、递归调用
  • 4、数组



前言

多次反复编写类似功能的代码明显是不合适的,结构化编程中的模块化在Java中的体现就是自定义方法

1、方法的概念

  • 方法可以理解为一个命名的代码块,通过名称就可以重复使用这段代码,而不需要反复书写,可以达到代码重用的目的。
  • 方法可以有参数,也可以没有参数;方法可以有返回值,也可以没有返回值[必须声明返回值为void]
  • 方法定义的具体位置没有关系,可以先写再调用,也可以先调用,再编写方法。

2、自定义方法

修饰符 返回值类型 方法名称(参数列表){方法体}

  • 方法名称:标识符的命名规则定义方法,命名规则为要求见名知意,一般建议首字母小写,大写字母分词。否则必须添加注释说明
  • 任意类型都可以作为方法的返回值类型和参数
  • 参数列表,包含数据类型,参数名称;调用方法时,传入的参数必须和参数列表的数据类型相同,参数的名称可以理解为占位符,称之为形参,调用时会被传递的数据所替代
  • 自定义方法,用于封装一个具体的处理过程,当需要使用这个处理过程时只需要通过名称就可以直接调用。实际参数的传递是通过实参和形参一一对应实现的(位置对应)。

调用方式常见的有三种

  • 单独调用。这种方式无法使用方法的返回值。格式:方法名称(参数值);
  • 打印调用。这种方式可以将方法的返回值直接打印。格式:System.out.println(方法名称(参数值)); 注意使用sysout方法调用需要有返回值
  • 赋值调用。这种方式可以将方法的返回值赋值给一个变量,注意变量的数据类型必须和方法的返回值类型对应。格式:数据类型 变量名称= 方法名称(参数值)

方法的重载

两个同名的方法参数不同才可以重名定义;参数不同有3种情况:类型不同、数量不同、顺序不同[不是方法参数名称]

public static void pp() {
 System.out.println("void...pp()");
 }
 public static void pp(int k) {
 System.out.println("void...pp()");
 }
 public static void pp(int k1,String s1) {
 System.out.println("void...ppp()");
 }
 public static void pp(int s1,int k1) {
 System.out.println("void...ppp0()");
 }

一个方法中的变量和其他方法的变量没有任何关系,可以重名

参数的传递

  • 形式参数:在定义方法的时候,写在小括号之内的变量,就叫形式参数。实际上在方法定义中起到占位符的作用,会在方法调用时被传递过来的实际值所替代
  • 实际参数:在调用方法的时候,真正传入方法里的数据,叫做实际参数。

圆括号中的实参列表为调用方法时实际传入的实际参数,称为实参列表。声明方法时圆括号中的参数称
为形式参数,形式参数和实际参数在数据类型和个数上一定要匹配

对于基本类型来说,形式参数的操作【不会】影响实际参数。值是单向传递

3、递归调用

Java语言支持方法的递归调用

  • 递归调用指在方法执行过程中允许出现,直接或者间接的该方法本身的调用
  • 使用递归调用时必须可以逐渐接近结束点,不能发散
  • 递归调用比较符合正常人的思维方式,但是相当的浪费内存,所以如果能使用其他方式解决就不要使用递归

例1:计算阶乘5!

public class Test {
	 public static void main(String[] args) {
	 	int res=jie(5);
		System.out.println("5!="+res);
	 }
	 public static int jie(int k) {  //自己直接调用自己---递归调用
		 if(k==0)
	 	 return 1;
		 return k*jie(k-1);
	 }
}

例2:斐波那契数列

  • 斐波那契数列Fibonacci sequence,又称黄金分割数列,以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34… 以递推的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=3,n∈N*)
public class Test {
	 public static void main(String[] args) {
	 int months = 0;
	 Scanner sc = new Scanner(System.in);
 	 while (true) {
		 System.out.println("月份:");
 		 String ss = sc.nextLine();
	     try {
			 months = Integer.parseInt(ss);
			 if (months > 0) break;
 				System.out.println("请重新输入月份数!");
 		} catch (Exception e) {
 			System.out.println("请输入合法的月份数!");
		}
	 }
	 int num = tongji(months);
	 System.out.println(months + "月后的兔子数为:" + num);
 }
	 public static int tongji(int months) {
		 if (months == 1 || months == 2) 
		 	return 1;
		 return tongji(months - 1) + tongji(months - 2);
	 }
}

递归调用比较符合正常人的思维方式,但是相当的浪费内存,所以如果能使用其他方式解决就不要使用递归。

  • 循环和递归对比:
    递归:易于理解、速度慢、存储空间大
    循环:不易于理解、速度快、存储空间小

例3:求数列:2/1,3/2,5/3,8/5,13/8,21/13… 的前20项之和

public class Test06 {
	 public static void main(String[] args) {
	 double res = 0;
	 for (int i = 1; i <= 20; i++) {
		 res += 1. * diguiFenzi(i) / diguiFenmu(i);
	 }
	 System.out.println(res);
 }
 public static int diguiFenzi(int n) {
 	// 如果使用递归调用必须保证有退出递归的点,必须保证不断逼近退出的点
	 if (n == 1) return 2;
	 	else if (n == 2) return 3;
	 else return diguiFenzi(n - 1) + diguiFenzi(n - 2);
	 }
 public static int diguiFenmu(int n) {
 	if (n == 1 || n == 2) return n;
		else return diguiFenmu(n - 1) + diguiFenmu(n - 2);
	 }
}

例4:汉诺塔问题

  • 梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆
    盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆
    盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘
    移动次数是f(n).显然f(1)=1,f(2)=3,f(3)=7,且f(k+1)=2*f(k)+1
public class 汉诺塔问题 {
 	public static void main(String[] args) {
 	System.out.println(move(3));
	}
	public static long move(int n) {
		if (n == 1)
 			return 1;
 		return 2 * move(n - 1) + 1;
 	}
}

4、数组

数组是表示多个相同类型变量的集合(在一个数组中所存放的所有元素的类型必须一致),可以使用共同的名字引用它

  • 属于复杂数据类型
  • 由类型相同的元素组成的有顺序的数据集合
  • Java数组是固定的不能扩展[长度一旦声明,不能修改]
  • 可以存储基本数据类型或对象
  • 数组可以定义为任意数据类型,并且可分为一维数组或多维数组

一维数组:
一维数组实质上是相同类型变量的列表。要创建一个数组必须首先定义数组变量所需的类型。通用的一维数组的声明格式是

  • 数据类型 [] 数组名称
int[] arr1 = new int[10]; // int[]或arr2[]都是用于声明是数组类型,int用于声明每个元素都是int类型
int arr2[] = new int[20]; //要求使用数组之前必须先声明所需空间大小,即存储的元素个数,一旦声明则不能修改
  • 数组中的元素必须类型相同,不能存储不同类型的元素,除非使用Object[]
  • 可以通过数组的下标(索引)操作数组中所存储的元素,注意下标从0开始,例如arr[0]+=12;修改元素的值System.out.println(arr[0]);获取元素
  • 使用数组之前,必须先定义后使用,定义的方式: int[] arr或者int arr[]
  • 声明数组变量后,必须进行初始化操作,也就是定义数组的长度 arr=new int[5],这里表示开启可以存放5个int类型数据的数组,这些元素时连续存放的简单类型的数组初始化长度后,每个元素都有默认值
  • 创建数组后,每个元素都有一个默认值,如果针对的是引用类型,则默认值为null;如果是简单类型中byte short int long float double,则默认值为0,如果char类型默认值为\0,如果boolean类型,则默认false

在Java中允许直接赋值初始

  • int[] arr={1,2,3,4,5,6,7}; 注意在于int[]中不能写具体的对应长度
  • 也可以写成new int[]{1,2,3,4,5,6,7},但是int[]中不能有长度值

int arr[],brr[]; //等价于int[] arr,brr;
arr=new int[5];
brr=new int[5];
可以这么定义,不推荐

数组虽然声明了变量类型。但不存在实际的数值,它的值为null。为了使数组number 成为实际的、物理上存在的整型数组,必须用运算符new 来为其分配地址并且把它赋给number

运算符new 是专门用来分配内存的运算符,格式为:array-var = new type[size]; 开启出来的空间是连续,可以通过下标进行快速定位

数组对象中有个属性length表示的是数组的长度
.length();获取数组长度

格式化输出

int[] score = {90, 85, 55, 94, 77}; //也可以写作new int[]{90, 85, 55, 94, 77}
for(int i = 0; i < score.length; i++)
    System.out.printf("score[%d] = %d\n", i, score[i]);

可以使用动态的方式来宣告数组长度,而不用在程序中事先决定数组大小

public static void main(String[] args) {
 	Scanner sc=new Scanner(System.in);
 	int len=sc.nextInt();
 	int[] arr=initArr(len);//可以根据输入的参数创建不同长度的数组
	System.out.println(arr.length);
 }
 public static int[] initArr(int length) {
// if(length<1)
// return new int[5];
 	return new int[length];//初始化数组长度时参数不同小于0,但是允许等于0
 }

循环打印出小写字母a~z

char c='a';
for(int i=0;i<26;i++)
 System.out.println((char)(c+i));
char[] arr=new char[26];
for(int i=0;i<26;i++)
 arr[i]=(char)(c+i);
//输出数组中的数据
for(int i=0;i<arr.length;i++)
    System.out.println(arr[i]);
//foreach结构,是用于遍历集合写法的语法糖,是for循环的简化写法
for(char temp:arr)
 System.out.println(temp);