JAVA方法详解



方法概述

JAVA的方法是类或对象的行为特征的抽象,它和C语言的函数其实非常像,只是它的地位发生了变化。
C语言的函数是JAVA的基本模块,函数与函数之间互相交互,每个函数好像一个步骤,它们组合起来一步步完成任务。所以,C语言是面向过程的语言。
而JAVA里类是一个基本模块,方法只是类或对象的组成部分方法无法独立存在
方法不能像C语言的函数那样独立运行,必须依靠类或者对象作为调用者,才能执行



方法的参数传递机制

其实很简单,就是复制值(或者说拷贝),也就是所谓的:值传递
调用方法传递的实参本身不会受到影响,系统只是把实参的值复制了一份,把拷贝给了形参形参和实参的值虽然相等,但是它们的存储区域是完全不同的,互相不会受到干扰

引用类型的参数传递

其实不管实参是基本类型还是引用类型,本质都是值传递,只是引用类型的特殊功能让结果看起来不一样。
引用类型本质完全可以按照C语言的指针理解,道理是一致的。

可以把引用类型当做一把钥匙,形参只是复制了一把相同的钥匙,所以这也是值传递。但是,两把相同的钥匙都指向同一个房间,形参这把钥匙虽然没办法影响实参那把钥匙,但是形参的钥匙可以进入两把钥匙共同指向的房间,改变房间里面的东西,房间就是对象



方法递归

递归就是某个方法在方法体内调用自身
这样做,方法会不断重新进入自身内部,但是,每一次进入方法体内部,实参都会发生变化,最终朝着可以执行到结尾的方向进行
随着最新的方法执行到结尾,得到的返回值会让倒数第二个方法也能顺利执行到结尾,以此类推,所有的方法都可以执行到结尾,得到返回值。而我们需要的,是第一个方法的返回值
以最常见的例子举例
例 如 有 如 下 数 学 题 。 已 知 有 一 个 数 列 : f(0)=1 , f(1)=4 ,
f(n+2)=2*f(n+1)+f(n),其中n是大于0的整数,求f(10)的值。

public class Recursion {
	public static int f(int n) {
		if (n == 0) {
			return 1;
		} else if (n == 1) {
			return 4;
		} else {
			return 2 * f(n - 1) + f(n - 2);
		}
	}

	public static void main(String[] args) {
        System.out.println(f(10));
    }
}

这是最简单的递归应用,n = 0和n = 1的值是确定的,依靠这2个值,就可以逐渐计算出所有的值。
一开始的实参是10,但是返回值的计算需要依赖f(9)和f(8)的值,而f(9)的返回值计算依赖f(8)和f(7),以此类推,方法会不断传入更小的实参,直到运行f(1)和f(0),这2个值可以得到确定的结果。之后,f(2),f(3)的值会不断被计算出来,直到计算到最终需要的结果f(10)。

递归的注意事项

递归一定要朝着能确定结果的方向进行
比如数列的例子,实参n = 1和n = 0时,返回值是已知的。所以,方法在不断调用自身时,一定要朝着这个方向进行。



形参个数可变的方法

java支持可变参数,通用语法为
(0~n个确定的形参, 类型名… 不可变形参名) // 括号里面的是参数列表

可变参数本质其实是数组,所以,在参数传递时,传入零到多个同类型参数或者直接传入一个相同类型的数组的引用作为实参都是可以的

之所以设计可变参数,是因为在调用方法时,实参传值的代码更加简洁,如果形参定义为数组,实参在传递时,就只能传入一个数组。
举例如下

// 这个方法可以接收0到n个整数,并且返回它们的和
public static int add(int... num){
    int sum = 0;
    for (int i : num) {
        sum += i;
    }
    return sum;
}

public static void main(String[] args) {
    // 如果形参是可变参数,调用方法传入实参会非常简洁
    System.out.println("和为" + add(1, 2, 3));
}

对比代码:

// 如果把形参改变为数组,调用方法时,实参也必须是数组
public static int add(int[] num){
    int sum = 0;
    for (int i : num) {
        sum += i;
    }
    return sum;
}

public static void main(String[] args) {
    // 下面的调用方式会报错
    //System.out.println("和为" + add(1, 2, 3));  
    // 必须实参也传递数组,代码不简洁
    System.out.println("和为" + add(new int[] {1, 2, 3});
}

当然,如果形参是可变参数,实参传递值时,也可以传递一个数组,但这肯定是不好的用法
反过来,如果形参是数组,实参不能用可变参数的方式传值,只能传递一个数组

可变参数使用的要求

1.可变参数只能定义在参数列表的最后一行,同时一个方法只能有一个可变参数
理由很简单,为了让程序不要产生歧义,细想就能明白。

2.可变参数在重载方法的判定优先级很低
如果有2个重载的方法,一个是确定的参数,一个是可变参数。但是调用者在传递实参时,两个方法都符合要求,系统会默认调用确定参数的方法
举例:

public static int add(int... num){
    int sum = 0;
    for (int i : num) {
        sum += i;
    }
    return sum;
}

public static int add(int a, int b){
    return a+b;
}

public static void main(String[] args) {
    System.out.println(add(1,2));
}

如果调用方法,传递1和2两个实参,虽然2个add方法都符合要求,但系统一定会选择确定参数的方法

3.可变参数方法和格式相同的数组方法不能重载
这其实很好理解,因为可变参数的本质就是数组
举例:
public static int add(int[] num)
public static int add(int… num)

上面2个方法签名本质是一样的,不能重载。

但如果第一个改成:
public static int add(int a, int[] num)
那样方法签名就不一样了,就可以重载了。



方法的重载

如果一个类里有多个同名的方法,每个方法形参列表不同,这些方法就称为方法重载。

为什么需要方法重载

方法的重载非常实用,用String类的valueOf方法举例。
valueOf会把传入的参数转换为字符串。比如:String.valueOf(int),会把传入的整数转换为对应的字符串。
由于传入的参数可以是多种类型,比如布尔类型、double类型、float类型等,但是实现的功能都是大同小异的,都是转换为字符串。
方法重载让这些方法的名字一致,但是参数不一样。调用者只需要了解valueOf这个方法的功能,不需要考虑传入的参数类型,因为每一种类型都有一个重载的版本

如果某个功能可以接受多种不同的参数,就可以使用方法重载,为每种参数都设计一个重载的版本。

方法重载的要求

方法重载要求两同一不同:同一个类的方法,方法名相同,形参列表不同
方法重载与修饰符和返回值类型无关
之前已经说过了,方法签名只包括方法名和参数列表,如果方法的调用只是执行功能,不需要返回值,那么调用者就没办法通过返回值区分不同的方法。
系统根据调用者传入的实参类型,来确定到底进入哪一个方法的重载版本

方法重载不推荐包含可变参数

由于可变参数可以传入的参数数量有多个可能,会出现可变参数和重载的确定参数方法都符合调用的条件。
但前面也说过了,系统会优先考虑符合条件的确定参数方法
比如:
public static int add(int num1, int num2){}
public static int add(int… num){}

这两个方法也算方法的重载,因为方法名相同,但是形参列表不同。
不过,当实参时2个整数时,两个重载的方法按理都符合条件。这时候,系统会选择确定参数的重载方法
虽然这也算方法的重载,但太容易引起歧义,所以最好不要使用这种包含可变参数的重载方式

而且,这种场景并不需要使用方法重载,只需要保留可变参数的方法。

实参和形参不匹配的重载

这种方式其实我认为没必要深究。
比如有多个重载的方法,形参都只有一个,有:short、int、long。但是实参是char类型,这时候,所有重载方法的形参都不和实参匹配。那系统会调用哪个重载方法呢?答案是:调用int的那个重载方法。

原因是:当实参类型小于形参类型时,实参类型会提升为更大的同时最接近的形参类型
char类型更大的类型中,最接近的就是int类型,所以会选择int类型的重载方法。

虽然java允许你实参和形参不匹配,帮助你做自动类型转换,但实际编写代码,最好传入匹配的参数,免得影响可读性。



类方法

如果方法前面加上static修饰符,就说明这个方法属于类
比如Math类里面全是这种工具类方法:abs方法,sqrt方法,sin方法等。
如果某个方法是类方法,那它的调用者是类名而不是对象名。语法为:类名.类方法名(参数列表);
但是java有非常不好的设计,允许使用引用变量名调用类方法。甚至连值为null的引用变量都可以调用类方法。比如:

Integer i = null;
// 值为null的引用都能调用Integer的类方法,本质还是integer.toString(100)
System.out.println(i.toString(100));
// java底层会把上一行代码替换为
System.out.println(Integer.toString(100));

记住:类成员的调用者就写成类名,不好的设计不要使用(即使它不会出现编译错误)。

和其他类成员一样,类方法里面不能出现实例成员。这是因为类方法是属于类的,所有对象共同拥有,不能和单独某个对象的成员产生任何联系。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值