Java中的函数(方法)

每个语言里面基本上都有函数,不过现在好多语言里面已经慢慢的修改了它的名称,不叫函数了,而是叫做方法。为了方便起见,下面我还是以“函数”这个名称来说。我们一直在用函数,但是如果突然问你,为什么会有函数的存在?或者说函数的作用是什么?想必这个问题不好回答吧,下面我们就具体来看看吧!

函数

  • 函数的定义

1.函数的存在意义

函数是为了完成某些特定的功能而实现的一种结构,它主要解决的是那些重复且具有独立功能的代码段。

正如前面第三章练习题当中,我们讲了一道石头剪刀布的题。在那道题中,我们用了两个switch语句来对com和usr这两个对象具体的功能进行了说明,其实我们可以发现这两个switch语句除了对象不一样其他部分基本都是一样的,简言之就是它的功能是一样的。但是写两个switch语句又使得代码有点长,造成了代码冗余,因此我们引出了“函数”这个概念。

这也就意味着我们可以将这些具有独立功能的代码段进行封装,得到一个函数,这样不仅可以降低代码冗余,也降低了主函数的代码量,而且也使得程序更加清晰明了。我们在需要用到这个函数的时候,只需要在主函数中调用它,然后传入适当的参数即可。这是不是很方便呢?

2.函数的格式

函数访问权限   函数类型   返回值类型   函数名(参数列表){

                  函数体

                  return返回值;

}

访问权限:指的就是函数的使用范围(内部和外部),它包括四个,分别是public、protected、默认不写、private。

函数类型:指的是函数的分类,说的就是函数的具体使用场景和场合。包括static静态函数、默认不写成员函数、                                    abstract抽象函数、native本地函数、synchronized同步函数等。

函数名:程序员给这一段代码自定义的名称。

参数列表:由若干个( 参数类型,参数名...)组成,主要用于接受外界传递给函数的的一些数据。

函数体:就是具有独立功能的代码块。

return:return仅仅表示结束当前函数,如果函数有返回值,则需要在函数结束前将返回值返回给调用者。

返回值:指的是这一具有独立功能的代码块的运算结果,这个运算结果需要传递给外界(配合return使用)。

返回值类型:就是返回值的数据类型(可兼容即可)。

 

一个简单的函数举例:

public static void main(String[] args){
    System.out.println("Welcome to Java!");
}

     public 公共的,代表访问权限。

     static静态的,代表函数类型。

     void无返回值,代表返回值类型,但是不代表没有返回。

     main主函数的名称。

     String[ ]表示一个字符串数组。

     args参数名,这个可以修改,之所以要有参数就是为了后面运行程序时可以传参数。

PS:如果一个函数中没有return,是因为该函数没有返回值,所以return可以忽略不写,但是不代表不存在

但是如果函数有返回值,那么return必须写,return+返回值。

  • 函数的分类

有返回值有参数的函数;

有返回值无参数的函数;

无返回值有参数的函数;

无返回值无参数的函数;

PS:有返回值的函数,参与运算、输出、赋值;无返回值的函数,只能调用,不能对返回结果进行额外的操作。

  • 函数的传参

实际参数(实参)——就是在调用函数的时候,给函数传递的数据(常量、变量)。

形式参数(形参)——就是定义函数的时候,参数列表当中的数据。

局部变量——在函数当中创建的变量都称为是局部变量

一个问题:实参到底是将什么传递给了形参?

答案:常量在常量池当中的地址,对象在堆内存当中的地址。

举个例子:

首先,有两个函数,一个是main函数,一个是pow函数。main函数开始运行,创建两个整型变量a和b,然后在常量池当中找到4的地址,把它们给a和b。紧接着开始运行pow函数,pow函数中,创建三个变量a、b、sum,与此同时main函数当中的实际参数a、b把他们当中存储的数据的地址传递给pow函数当中的形式参数a和b,然后pow函数计算出a^b的结果256,然后在常量池当中找到256的地址,把它给sum,然后执行return sum,这也就说明pow函数结束了。在结束之前sum把256的地址又传递给main函数,与此同时,main函数当中在创建一个double型的变量c来存储256的地址。这就是函数传参的过程,其实挺简单的。

  • 函数栈

函数的运行是基于栈(一个先进后出的容器结构)内存运行的。我们可以将每一个函数理解为是函数帧,位于栈顶的函数帧优先运行,主函数main肯定是第一个进栈的。return表示结束当前函数,也就是当前函数弹栈。

举一个函数在栈中运行的具体实例:

public static void main(String[] args){
    int a=4;
    int b=4;
    double c=pow(a,b);
    System.out.print(c);
}

public static double pow(double a,int b){
    if(b==0){
        return 1;
    }
    double sum=1;
    for(int i=1;i<=Math.abs(b);i++){
        sum*=a;
    }
    return b>0?sum:1/sum;
}

我们可以看到,代码中有两个函数,下面就通过一幅动图来具体说明函数在栈中运行的具体过程。

  • 函数的重载

函数的重载指的就是同一个类中出现的同名函数,它与函数权限、返回值类型、参数名没有关系,只和参数类型的排列组合有关系(此外要注意一下参数类型的向下兼容问题)。另外,函数重载的好处就在于我们可以扩展函数的功能(函数重名,但是参数类型不一样,执行内容也可以不一样)。

举个例子:看下面这个代码。

class Chongzai{
    public static void main(String[] args){
        int a=3;
        int b=4;
        System.out.println(add(a,b));
        double d=3.14;
        double e=5.44;
        System.out.println(add(d,e));
        System.out.println(add(9,3.14));
        System.out.println(add(3.14,9));
    }
    //1.对两个小数进行加法运算
	public static double add(double a,double b){
        System.out.println("double+double");
        return a+b;
    }
    //2.对两个字符串进行拼接
    public static String add(String a,String b){
        System.out.println("String+String");
        return a+b;
    }
    //3.
    public static double add(int a,double b){
        System.out.println("int+double");
        return a+b;
    }
    //4.
    public static double add(double a,int b){
        System.out.println("double+int");
        return a+b;
    }
    //5.对两个整数进行加法运算
    public static int add(int a,int b){
        System.out.println("int+int");
        return a+b;
    }
}

运行结果:

 

int+int
7
double+double
8.58
int+double
12.14
double+int
12.14

main函数在执行到第一个输出语句时,开始在底下其他的函数当中找合适自己的函数,因为这个输出语句里面add函数中的参数a,b都是int类型的,所以找到了第5个函数,传入的两个参数也都是int类型的;紧接着执行到第二个输出语句时,又开始找适合自己的函数,因为此时add函数中的参数的d,e都是double类型的,所以找到了第1个函数,传入的两个参数也都是double类型的;同理,第三个输出语句add函数中的参数一个是int类型,一个是double类型,于是就找到了第3个函数;第四个输出语句add函数中的参数一个是double类型的,一个是int类型的,于是找到了第4个函数。

因此,在寻找适合自己函数的时候,要遵循以下流程:
1.看是否有确切的参数定义 int+int,查看是否有(int,int)
2.看是否有可兼容的参数定义 int+int,查看是否有(double,double)
3.如果可兼容的参数定义有多个int+int,(double,int)或(int,double) 此时报错“引用不明确”。

  • 函数的递归调用

函数递归的体现就是函数自身调用函数自身。一般而言,但凡能够被迭代(循环)解决的问题,递归都可以解决,但是递归能解决的问题,迭代就不一定了。在某些问题上,递归所写的代码要比迭代写的代码少;在某些问题上,迭代是写不出来的,所以只能用递归。这就是它两之间的区别和联系。

递归其实是分治法的一种实现方式(实现思路),分治法是一种算法思想,它主要解决的问题是:将大问题进行拆分,拆分成若干个小的问题进行求解,最终将每个小问题的解进行合并。其实,分治法就是一种暴力破解法(穷举),也是一种搜索最优答案的算法。

递归递归,先递后归。我们可以把递归分成三个部分,即前进段(就是讲问题从大化小)、结束段(就是问题无法再继续化小,则处理当前的问题)、返回段(就是将小问题都处理完毕之后,向上进行返回(有些问题是不需要返回的)。

举个例子:我们分别用迭代和递归的思想来求斐波那契数列的前10项和。 

问题:斐波那契数列,求前10项。
        前10项:1 1 2 3 5 8 13 21 34 55 ......
        f(n)指的是斐波那契的第n项
        f(n)=f(n-1)+f(n-2) f(1)=1 f(2)=1
                    f(5)
                f(4)        f(3)
            f(3)    f(2) f(2)   f(1)
        f(2)    f(1)

class Test01{
    public static void main(String[] args){
        for(int i=1;i<=10;i++){
            System.out.println(fibo(i));//用递归的思想求前10项
        }
        System.out.println("===================");
        fiboIterator(10);//用迭代的思想求前10项
    }
    //迭代函数
    public static void fiboIterator(int n){
        int a=1;
        int b=1;
        System.out.println(a);
        System.out.println(b);
        int count=2;
        int c;
        while(true){
            c=a+b;
            System.out.println(c);
            count++;
            if(count==n){
                return;
            }
            a=b;
            b=c;
        }
    }
    //递归函数
    public static int fibo(int n){
        if(n==1||n==2){
            return 1;
        }
        return fibo(n-1)+fibo(n-2);
    }
}

运行结果:

1
1
2
3
5
8
13
21
34
55
===================
1
1
2
3
5
8
13
21
34
55

通过上面这个例子我们可以看出,迭代代码比较简洁,代码量很少,但是如果计算量比较大的话,它越往后计算越慢。但是递归不会出现这个问题,两者各有利弊。

 

  • 常用函数

我们常用的函数大致可以分为两类,Math类和String类,我大概举几个例子。

1.Math类函数:

class Test01{
    public static void main(String[] args){
	//返回比任何其他值都更接近e(即自然对数的底数)的值。
        System.out.println(Math.E);//2.718281828459045

	//返回比任何其他值都更接近pi(即圆的周长与直径之比)的值。
        System.out.println(Math.PI);//3.141592653589793

	//返回指定数字(-2)的绝对值。
	System.out.println(Math.abs(-2));//2

        //返回最小的(最接近正无穷大)double值,该值小于等于参数,并等于某个整数。
        System.out.println(Math.ceil(-2.1));//-2.0

        //返回最大的(最接近正无穷大)double值,该值小于等于参数,并等于某个整数。
        System.out.println(Math.floor(2.3));//2.0

        //返回sqrt(x?+y?),即sqrt(3?+4?)。
        System.out.println(Math.hypot(3,4));//5.0

	//返回两个值中较大的一个。
	System.out.println(Math.max(2,10));//10

	//返回两个值中较小的一个。
	System.out.println(Math.min(1,9));//1

	//返回第一个参数的第二个参数次幂的double值,即2^4。
	System.out.println(Math.pow(2,4));//16.0

	//返回正确舍入的值的double类型正平方根,即根号下9。
	System.out.println(Math.sqrt(9));//3.0

	//返回一个带正号的随机值,该值大于等于0,且小于1。
	System.out.println(Math.random());//0.06910097634766355

        //返回最接近参数并等于某一整数的double值,即四舍五入。
        System.out.println(Math.rint(3.5));//4.0
    }
}

2.String类函数

class Test02{
    public static void main(String[] args){
        String s = "ababABcdcd";
		
	//1、2、3、4为查询相关操作
	//5、6、7、8、9、10、11为判断相关操作
	//12、13、14、15为修改相关操作
		
        //1.获取此字符串指定角标处的字符
        System.out.println(s.charAt(0));//a
		
	//2.在字符串中从左到右查找指定元素第一次出现的位置   
        System.out.println("abcccccd".indexOf('c'));//2
        System.out.println("abcccccd".indexOf("cd"));//6
		
	//3.返回此字符串的长度。
	System.out.println(s.length());//10
		
	//4.返回一个新字符串,它是此字符串的一个子字符串。
        System.out.println(s.substring(0,3));//[0,3)//aba
		
        //5.判断指定子串是否包含在此字符串中,如果包含返回true。
        System.out.println(s.contains("ab"));//true
		
	//6.测试此字符串是否以指定的前缀开始。
	System.out.println(s.startsWith("ab"));//true
		
        //7.测试此字符串是否以指定的后缀结束。
        System.out.println(s.endsWith("cd"));//true

        //8.按照字典顺序比较两个字符串的大小,返回值有负数 0 正数
        //负数 前者在后者ASCII之前
        //0    前者和后者相等
        //正数 前者在后者ASCII之后
	/*
        abc
        abd c-d=-1
        */
        System.out.println("abc".compareTo("abd"));//-1  
        System.out.println("abc".compareTo("abc"));//0
		
        //9.比较两个字符串是否相等(比的是内容)
        System.out.println("abc".equals("abc"));//true
		
        //10.将此String与另一个String比较,不考虑大小写。
        System.out.println("ABC".equalsIgnoreCase("abc"));//true
		
	//11.当且仅当 length()为 0 时返回 true。
        System.out.println("".isEmpty());//true
		
	//12.返回一个新的字符串,它是通过用newChar替换此字符串中出现的所有oldChar得到的。
        System.out.println(s.replace('a','c'));//cbcbABcdcd
		
	//13.使用默认语言环境的规则将此String中的所有字符都转换为大写。
        System.out.println(s.toUpperCase());//ABABABCDCD
		
	//14.使用默认语言环境的规则将此String中的所有字符都转换为小写。
	System.out.println(s.toLowerCase());//abababcdcd
		
	//15.返回字符串的副本,忽略前导空白和尾部空白。
        System.out.println("  abc abc abc   ".trim());//abc abc abc
    }
}

 

好了,到这里为止关于Java中函数(方法)的内容我就已经说完了,后面还会持续更新其他内容,欢迎大家观看!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章