• 进一步理解函数


    函数的定义和基本调用应该是比较容易理解的,但有很多细节可能令初学者困惑,包括参数传递、返回、函数命名、调用过程等,我们逐个介绍。

    1.参数传递
    有两类特殊类型的参数:数组和可变长度的参数。
    (1)数组
    数组作为参数与基本类型是不一样的,基本类型不会对调用者中的变量造成任何影响,但数组不是,在函数内修改数组中的元素会修改调用者中的数组内容。我们看个例子:
    public static void reset(int[] arr){
        for(int i=0;i         arr[i] = i;
        }
    }
    public static void main(String[] args) {
        int[] arr = {10,20,30,40};
        reset(arr);
        for(int i=0;i         System.out.println(arr[i]);
        }
    }
    在reset函数内给参数数组元素赋值,在main函数中数组arr的值也会变。
    这个其实也容易理解,我们在1.2节介绍过,一个数组变量有两块空间,一块用于存储数组内容本身,另一块用于存储内容的位置,给数组变量赋值不会影响原有的数组内容本身,而只会让数组变量指向一个不同的数组内容空间。
    在上例中,函数参数中的数组变量arr和main函数中的数组变量arr存储的都是相同的位置,而数组内容本身只有一份数据,所以,在reset中修改数组元素内容和在main中修改是完全一样的。
    (2)可变长度的参数
    前面介绍的函数,参数个数都是固定的,但有时候可能希望参数个数不是固定的,比如求若干个数的最大值,可能是两个,也可能是多个。Java支持可变长度的参数,如下例所示:
    public static int max(int min, int ... a){
        int max = min;
        for(int i=0;i         if(max             max = a[i];
            }
        }
        return max;
    }
    public static void main(String[] args) {
        System.out.println(max(0));
        System.out.println(max(0,2));
        System.out.println(max(0,2,4));
        System.out.println(max(0,2,4,5));
    }这个max函数接受一个最小值,以及可变长度的若干参数,返回其中的最大值。可变长度参数的语法是在数据类型后面加三个点“...”,在函数内,可变长度参数可以看作是数组。可变长度参数必须是参数列表中的最后一个,一个函数也只能有一个可变长度的参数。
    可变长度参数实际上会转换为数组参数,也就是说,函数声明max(int min,int...a)实际上会转换为max(int min,int[]a),在main函数调用max(0,2,4,5)的时候,实际上会转换为调用max(0,new int[]{2,4,5}),使用可变长度参数主要是简化了代码书写。

    2.理解返回
    对初学者,我们强调下return的含义。函数返回值类型为void时,return不是必需的,在没有return的情况下,会执行到函数结尾自动返回。return用于显式结束函数执行,返回调用方。
    return可以用于函数内的任意地方,可以在函数结尾,也可以在中间,可以在if语句内,可以在for循环内,用于提前结束函数执行,返回调用方。
    函数返回值类型为void也可以使用return,即“return;”,不用带值,含义是返回调用方,只是没有返回值而已。
    函数的返回值最多只能有一个,那如果实际情况需要多个返回值呢?比如,计算一个整数数组中的最大的前三个数,需要返回三个结果。这个可以用数组作为返回值,在函数内创建一个包含三个元素的数组,然后将前三个结果赋给对应的数组元素。
    如果实际情况需要的返回值是一种复合结果呢?比如,查找一个字符数组中所有重复出现的字符以及重复出现的次数。这个可以用对象作为返回值,我们在第3章介绍类和对象。虽然返回值最多只能有一个,但其实一个也够了。

    3.重复的命名
    每个函数都有一个名字,这个名字表示这个函数的意义,名字可以重复吗?在不同的类里,答案是肯定的,在同一个类里,要看情况。
    同一个类里,函数可以重名,但是参数不能完全一样,即要么参数个数不同,要么参数个数相同但至少有一个参数类型不一样。
    同一个类中函数名相同但参数不同的现象,一般称为函数重载
     。为什么需要函数重载呢?一般是因为函数想表达的意义是一样的,但参数个数或类型不一样。比如,求两个数的最大值,在Java的Math库中就定义了4个函数,如下所示:
    public static double max(double a, double b)
    public static float max(float a, float b)
    public static int max(int a, int b)
    public static long max(long a, long b)

    4.调用的匹配过程
    在之前介绍函数调用的时候,我们没有特别说明参数的类型。这里说明一下,参数传递实际上是给参数赋值,调用者传递的数据需要与函数声明的参数类型是匹配的,但不要求完全一样。什么意思呢?Java编译器会自动进行类型转换,并寻找最匹配的函数,比如:
    char a = 'a';
    char b = 'b';
    System.out.println(Math.max(a,b));

    参数是字符类型的,但Math并没有定义针对字符类型的max函数,这是因为char其实是一个整数(我们在2.4节会说明),Java会自动将char转换为int,然后调用Math.max(int a,int b),屏幕会输出整数结果98。
    如果Math中没有定义针对int类型的max函数呢?调用也会成功,会调用long类型的max函数。如果long也没有呢?会调用float型的max函数。如果float也没有,会调用double型的。Java编译器会自动寻找最匹配的。
    在只有一个函数的情况下,即没有重载,只要可以进行类型转换,就会调用该函数,在有函数重载的情况下,会调用最匹配的函数。

    5.递归函数
    函数大部分情况下都是被别的函数调用的,但其实函数也可以调用它自己,调用自己的函数就叫递归函数
     。为什么需要自己调用自己呢?我们来看一个例子,求一个数的阶乘,数学中一个数n的阶乘,表示为n!,它的值定义是这样的:
    0!=1
    n!=(n-1)!×n
    0的阶乘是1,n的阶乘的值是n-1的阶乘的值乘以n,这个定义是一个递归的定义,为求n的值,需先求n-1的值,直到0,然后依次往回退。用递归表达的计算用递归函数容易实现,代码如下:
    public static long factorial(int n){
        if(n==0){
            return 1;
        }else{
            return n*factorial(n-1);
        }
    }
    看上去应该是比较容易理解的,和数学定义类似。递归函数形式上往往比较简单,但递归其实是有开销的,而且使用不当,可能会出现意外的结果,比如说这个调用:
    System.out.println(factorial(100000));

    系统并不会给出任何结果,而会抛出异常。异常我们在第6章介绍,此处理解为系统错误就可以了。异常类型为java.lang.StackOverflowError,这是什么意思呢?这表示栈溢出错误,要理解这个错误,我们需要理解函数调用的实现原理,我们1.7节介绍。
    那递归不可行的情况下怎么办呢?递归函数经常可以转换为非递归的形式,通过循环实现。比如,求阶乘的例子,其非递归形式的定义是:
    n!=1×2×3×…×n
    这个可以用循环来实现,代码如下:
    public static long factorial(int n){
        long result = 1;
        for(int i=1; i<=n; i++){
            result=result*i;
        }
        return result;
    }

    想要了解更多Java基础知识,可以点击评论区链接和小编一起学习java吧,此视频教程为初学者而著,零基础入门篇!给同学们带来全新的Java300集课程啦!java零基础小白自学Java必备优质教程_手把手图解学习Java,让学习成为一种享受_哔哩哔哩_bilibili

  • 相关阅读:
    Python爬虫大作业+数据可视化分析(抓取python职位)
    无人不识又无人不迷糊的this
    2022最新阿里Java面经,转疯了
    monaco-editor 的 Language Services
    搭建虚拟通道
    Windows下的Linux子系统(WSL)
    基于docker实现mysql的主从复制 详细步骤
    GoFrame 优化接口的错误码和异常的思路
    【教程】遥感数据与作物模型同化实践
    向毕业妥协系列之机器学习笔记:决策树(一)
  • 原文地址:https://blog.csdn.net/zf888999666/article/details/128078875