当前位置: 首页 > news >正文

【JavaSE-7】方法的使用

1、方法的概念和使用

1.1、什么是方法

方法(method)是程序中最小的执行单元,类似于 C语言中的函数,方法存在的意义:

  1. 是能够模块化的组织代码(当代码规模比较复杂的时候).
  2. 做到代码被重复使用, 一份代码可以在多个位置使用.
  3. 让代码更好理解更简单.
  4. 直接调用现有方法开发, 不必重复造轮子

注意

  • 方法必须先创建才可以使用,该过程称为方法定义
  • 方法创建后并不是直接可以运行的,需要手动使用后,才执行,该过程称为方法调用

1.2、方法的定义

方法定义语法格式:

//方法定义
修饰符 返回值类型 方法名称(参数类型1 形参1,参数类型2 形参2...){
	方法体代码;
	[return 返回值];
}

类比main函数:public static void main(String[] args),其中void是返回值类型;main是方法名称;String[] args是参数类型和形参。

例子

判断闰年

1、能被4整除且不能被100整除的是闰年

2、能被400整除的是闰年

//判断闰年方法
public static  boolean isLeapYear(int year){
	if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)){
		System.out.println(year+"是闰年");
		return true;
	}else{
		System.out.println(year+"不是闰年");
		return false;
	}
}

【注意事项】

  1. 修饰符:现阶段直接使用public static 固定搭配
  2. 返回值类型:如果方法有返回值,返回值类型必须要与返回的实体类型一致,如果没有返回值,必须写成void
  3. 方法名字:采用小驼峰命名(首字母小写,后续单词首字母大写)
  4. 参数列表:如果方法没有参数,()中什么都不写,如果有参数,需指定参数类型,多个参数之间使用逗号隔开
  5. 方法体:方法内部要执行的语句
  6. 在java当中,方法必须写在类当中
  7. 在java当中,方法不能嵌套定义
  8. 在java当中,没有方法声明一说,方法只有先定义,才能调用。
  9. 方法的返回值类型是void时,可以省略return;但是也可以写,后面不加数据即可。

1.3、方法调用的执行过程

方法的调用方式:

方法名(参数1,参数2,...);

调用过程:

  1. 调用方法方法名(参数1,参数2,...);
  2. 传递参数,注意参数的数量与类型必须与方法定义中的设置相匹配,否则程序将报错 。
  3. 找到方法地址
  4. 执行被调方法的方法体
  5. 被调方法结束返回
  6. 回到主调方法继续往下执行。

例子:

public class Test01 {
	//判断闰年方法
	public static boolean isLeapYear(int year){
		if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)){
			System.out.println(year+"是闰年");
			return true;
		}else{
			System.out.println(year+"不是闰年");
			return false;
		}

	}
	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		System.out.println("请输入年份:");
		int year = scan.nextInt();
		//方法的调用
		boolean ret = isLeapYear(year);

	}
}

在其他函数中去调用方法时的参数称为实际参数(实参)即上述main函数中的boolean ret = isLeapYear(year);这句代码中year,在方法定义中的参数称为形式参数(形参)即上述isLeapYear方法中的public static boolean isLeapYear(int year)这句代码的year
【注意事项】

  • 定义方法的时候,不会执行方法的代码,只有调用时才会执行。
  • 一个方法可以多次调用。

例子:

计算 1! + 2! + 3! + 4! + 5!

//求阶乘
public static int fac(int n){
    if(n == 1 || n == 0){
        return 1;
    }else {
        return n * fac(n - 1);
    }
}
public static void main(String[] args) {
    //计算 1! + 2! + 3! + 4! + 5!
    int sum = 0;
    for (int i = 1; i <= 5 ; i++) {
        sum += fac(i);
    }
    System.out.println(sum);
}

1.4、实参和形参的关系

  1. 形参:方法定义中的参数
    1. 等同于规定了变量定义的格式。
  2. 实参:方法调用中的参数
    1. 等同于使用变量或常量。

形参只是拿到了实参的值,形参的名字可以随意取,对方法没有影响;形参和实参的名字也可以相同。

方法放在main前或者后都可以,与 C语言不同(自顶向下编译)。在 java 中,实参的值永远都是赋值到形参中,形参和实参本质是两个实体。

例子1:

//交换两个数值
public static void swap(int x, int y){
    int tmp =x;
    x = y;
    y = tmp;
}

public static void main(String[] args) {
    int a = 10,b = 20;
    System.out.println("交换前:"+ a +" "+b);
    swap(a,b);
    System.out.println("交换后:"+ a +" "+b);
}

【运行结果】

交换前:10 20
交换后:10 20

分析
实参abmain方法中的两个变量,其分配的内存空间在main方法的栈(一块特殊的内存空间)中,而形参xyswap方法中的两个变量,xy的内存空间在swap方法运行时的栈中,因此:实参ab 与 形参xy是两个没有任何关联性的变量,在swap方法调用时,只是将实参ab中的值拷贝了一份传递给了形参xy,因此对形参xy操作不会对实参ab产生任何影响。
对于基本数据类型来说,形参相当于实参的拷贝,即传值调用

【解决办法】

引用数据类型参数(如数组)因为引用数据类型在内存中存储的是地址值,指向在堆内存中申请的一片内存。

例子2:

public class TestMethod {
    public static void main(String[] args) {
        int[] arr = {10, 20};
        swap(arr);
        System.out.println("arr[0] = " + arr[0] + " arr[1] = " + arr[1]);
    }
    public static void swap(int[] arr) {
        int tmp = arr[0];
        arr[0] = arr[1];
        arr[1] = tmp;
    }
}

// 运行结果
arr[0] = 20 arr[1] = 10

2、方法重载

2.1、为什么需要方法重载

首先,如果我们定义一个函数add()实现两个整数的相加,但后面如果想实现两个浮点数的相加时,会因为参数的数据类型的不同导致报错。

例子1:

只有我们重新写一个方法,定义形参的数据类型是double的方法才能实现,但是这种形式的缺点就是需要命名不同的方法名,并且容易记忆混乱,那么可以直接使用同一个方法名吗?由此引出方法重载

2.2、方法重载概念

在日常交流中,一个词语如果有多重含义,比如:喜欢,情侣之间说喜欢与子女对父母说喜欢,这个词语的含义是不同的,这个时候我们可以认为该词语含义被重载,具体代表什么意思就需要结合具体的场景。

java 中,如果多个方法的方法名相同,参数列表不同,与返回值无关,则称这几种方法被重载了。

其中参数列表不同包括:个数不同,类型不同,顺序不同。

例子:

之后调用该方法时通过 IDEA 会有提示,根据需要传入的实参类型,选择相应的方法。

注意事项

  • 方法名必须相同。
  • 参数列表必须不同(参数个数/参数类型/参数的次序
  • 多个方法必须定义在同一个类中。
  • 与返回值类型是否相同无关
  • 如果仅仅因为返回值类型不同,是不能构成重载的。
  • 编译器在编译代码时,会对实参类型进行推演,根据推演的结果来确定调用哪个方法。

2.3、方法签名(了解)

在同一个作用域中不能定义两个相同名称的标识符,但是在类中可以定义方法名相同的方法

是因为有方法签名的存在。

方法签名即是:经过编译器编译修改之后方法最终的名字。具体方式:方法全路径名+参数列表+返回值类型,构成方法完整的名字。每一个方法都有签名。

将编写的代码经过编译后,会生成.class文件,在该文件所在的目录下打开命令行窗口,输入javap -v 字节码文件名即可

例子:

public class TestMethod {
	public static int add(int x, int y){
		return x + y;
	}
	public static double add(double x, double y){
		return x + y;
	}
	public static void main(String[] args) {
		add(1,2);
		add(1.5, 2.5);
	}
}

生成的字节码文件:

方法签名中的一些特殊符号说明:

3、递归

3.1、递归的概念

一个方法在执行过程中调用自身,就称为“递归”。递归相当于数学的“数学归纳法”,有一个起始条件,然后有一个递推公式。

递归条件:将原问题划分成其子问题,子问题。

3.2、递归练习

按顺序打印一个数字的每一位(例如 123打印出 1 2 3 )

public class Test03 {
    //按顺序打印一个数字的每一位(例如 1234 打印出 1 2 3 4)
    public static void print(int n){
        if(n > 9){
            print(n / 10);
        }
        System.out.print(n % 10+" ");
    }
    public static void  main(String[] args) {
        print(123);
    }
}

【分析】

递归求 1 + 2 + 3 + … + 10

//递归求 1 + 2 + 3 + ... + 10
public static int fac(int n){
    if(n == 1){
        return 1;
    }else{
        return n+fac(n-1);
    }
}

写一个递归方法,输入一个非负整数,返回组成它的数字之和.

例如,输入 1729, 则应该返回1+7+2+9,它的和是19

public static int sum(int n){
	while(n > 9){
		return n % 10 + sum(n/10);
	}
	return n % 10;
}

求斐波那契数列第 n 项

第一项从 1 开始:1 1 2 3 5 8…

public static int fib(int n){
    if(n == 1 || n == 2){
        return 1;
    }else{
        return fib(n-1) + fib(n-2);
    }
}

非递归:

if(n == 1){
    return 1;
}
if(n == 2){
    return 1;
}
int n1 = 1;
int n2 = 1;
int n3 = 0;
while(n > 2){
    n3 = n1 + n2;
    n1 = n2;
    n2 = n3;
    n--;
}
return n3;

第 1 项从 0 开始 :0 1 1 2 3 5 8

public static int fib(int n){
    if(n == 1){
        return 0;
    }
    if(n == 2){
        return 1;
    }
    return fib(n-1)+fib(n-2);
}

非递归:

if(n == 1){
    return 0;
}
if(n == 2){
    return 1;
}
int n1 = 0;
int n2 = 1;
int n3 = 0;
while(n > 2){
    n3 = n1 + n2;
    n1 = n2;
    n2 = n3;
    n--;
}
return n3;

递归求解汉诺塔问题

有3根柱子,A,B,C;A柱上放N个盘子,上面小,下面大

问题:

把A柱上所有盘子挪到C柱上(可以借助B柱)

过程中:所有柱子上的盘子,也要下面大,上面小,且一次只能移动一个盘子。

// 定义一个方法用于解决汉诺塔问题
public static void hanoi(int n, char source, char auxiliary, char target){
	// 如果只有一个圆盘,直接将其从源柱移动到目标柱
	if (n == 1) {
		System.out.println("Move disk 1 from "+source+" to "+target);
		return;
	}
	// 先将 n - 1 个圆盘从源柱借助目标柱移动到辅助柱
	hanoi(n - 1, source, target, auxiliary);
	// 再将第 n 个圆盘从源柱移动到目标柱
	System.out.println("Move disk "+ n +" from "+source+" to "+target);
	// 最后将 n - 1 个圆盘从辅助柱借助源柱移动到目标柱
	hanoi(n - 1, auxiliary, source, target);
}

分析:

  1. n-1个盘子从源柱子借助目标柱子移动到辅助柱子。
  2. 把第n个盘子从源柱子移动到目标柱子。
  3. n-1个盘子从辅助柱子借助源柱子移动到目标柱子。

时间复杂度:O(2<sup>n</sup>)


如n=4时,

将上面 3 个盘子从源柱 A 借助目标柱 C 移动到辅助柱 B

将第 4 个盘子从源柱 A 移动到目标柱 C

将辅助柱 B 上的 3 个盘子借助源柱 A 移动到目标柱 C

相关文章:

  • 【C语言】函数篇
  • 安装remixd,在VScode创建hardhat
  • 软考架构师笔记-数据库系统
  • 确认机制的分类及其区别与联系探讨
  • 在springboot项目中引入log4j 2.x
  • mysql进阶(三)
  • 【CSS 】Class Variance Authority CSS 类名管理工具库
  • JVM与性能调优详解
  • 香港电讯CE2.0网络全面升级,100G服务支援企业关键应用
  • Unity InputField + ScrollRect实现微信聊天输入框功能
  • unity学习64,第3个小游戏:一个2D跑酷游戏
  • 如何用更少的内存训练你的PyTorch模型?深度学习GPU内存优化策略总结
  • Linux 上离线安装 python3
  • 哪些培训课程适合学习PostgreSQL中级认证知识?
  • 前端Vue3面试题
  • blender 坐标系 金属度
  • 基于多目标向日葵优化算法(Multi-objective Sunflower Optimization,MOSFO)的移动机器人路径规划研究,MATLAB代码
  • 小程序路径复制
  • 18年老牌软件,完美解锁pro!
  • Java 导出大数据到 Excel 表格
  • 雅安市纪委监委回应黄杨钿甜耳环事件:相关政府部门正在处理
  • 美国新泽西客运公司遭遇罢工:40年来首次,35万人受影响
  • 大陆非遗项目打铁花、英歌舞将在台演出
  • 我国城市规划“全面体检”套餐出台,城市体检将逐步与供地计划等挂钩
  • 日本一季度实际GDP环比下降0.2%
  • 中科飞测将投资超10亿元,在上海张江成立第二总部