Java基础:函数声明与调用

方法函数

在接触到Java的第一个程序时就存在函数。主函数是Java中规定写法的一种函数:主函数通常写在公开类中,在执行Java程序的时候自动主函数中的代码。

函数的定义:

函数是写在类中具有一定特殊功能的代码块,函数是可以自己定义的,只是主函数会被解释器自动扫描和运行,自定义的函数需要进行手动的调用。

函数的意义:

函数存在的意义实际上是为了让代码能够重复使用。例如将做加法运算的程序写到一个单独的函数中,在使用到加法运算的时候只需要调用这个函数,而无需重新书写新的加法算法。这被称为代码的重用性,也就是对一套代码的重复使用。

当所有的加法运算使用同一套加法代码的时候,如果加法算法存在特殊的改动,则只需要更改加法函数的内容,其他调用的位置将因为加法函数的更新而自动更新算法,这样就可以实现标准的统一,方便维护和更改。

函数设计原则:

因为函数存在的意义是让代码能够最大程度的重用,并且能够方便对算法进行维护和改良。那在设计方法的时候就要考虑到“高内聚、低耦合”的概念。

内聚性可以简单理解为做一件事情的专注程度,耦合度表示做一件事会对其他事情造成的级联影响。高内聚也就可以理解为对当前做的事情极致专一,低耦合也就表示自身的改变不会对太多模块造成过大的影响。

例如要做一套登录的功能,那如果将所有的代码都集中到一个方法中,那任何一步出现维护或者改动都要对整个方法进行测试或者导致整套流程的崩溃。那如果将输入、验证、展示等相关的功能都抽离出来成为单独的方法,在登录流程运行过程中调用这些方法。在进行开发和测试的时候就可以实现对某个功能的验证,并且在某个功能出现故障后不会影响到其他的模块,排查维护也更简单。

那将流程代码拆分到什么程度才能最完美的高内聚低耦合,这涉及到了业务的颗粒度问题。如果拆分的过细会导致维护困难代码复杂,但是拆分的过粗就违背了高内聚低耦合原则, 实际上每一个项目和流程都应有自己的业务颗粒度,也就有不同的拆分细度,这需要参考整个系统的强度和更多专业知识来研究。

声明函数

声明也就是创建,自定义函数时可以参考着主函数的每个部分来设计自己的函数结构:

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

参照着主函数的结构,可以得出创建函数的语法:

修饰符 返回值类型 自拟函数名称(参数表){
    方法体中的代码
}

自己设计函数必要存在的有三个部分:方法名、返回值类型、参数表。那如下就是一个最精简的方法声明实例,但其并不能执行。在未来的知识中将看到类似函数:

void fun();

如果要设计一个能够运行的函数,可以在主函数的基础上稍加改装:

public static void hello(){
	System.out.print("in hello!");
}

其中具体的内容与主函数的各部分解释都是相同的,可参见第一个程序一章。方法都是写到类中的,如果类里还有主函数,那整个java文件的内容应该如下:

public class Index{
    public static void main(String[] args){
        System.out.println("helloworld!");
    }
    public static void hello(){
        System.out.print("in hello!");
    }
}

第一个方法的创建存在很多不明白意义的单词和很多没有解释的理论,可以在未来学习更深的知识后了解其中的意义。

当下可暂写成实例的代码,以运行成功为主要目的。

调用函数

除了主函数之外,任何自定义的函数都必须手动调用才能执行。调用也就是执行函数内容,使用一段指令来完成对函数的调用,这段指令通常写在主函数以及其他函数中:

对象或类名.函数名(参数表);

函数名也就是自定义函数的名称,参数表将在下文中讲到。关于函数名前面的对象或类名将在学习面向对象之后知晓其原理,在初学函数阶段,可以忽略函数名前面的所有内容,那对一个自定义函数的调用如下:

public static void main(String[] args){
    System.out.print("test hello!");
    hello2();//调用
}
public static void hello1(){//声明
    System.out.print("in hello!");
}

初学过程中,通常使用主函数触发对方法的调用,所以调用语句通常写在主函数里。但实际上未来开发过程中,更多的时候是方法与方法之间的调用:

public static void main(String[] args){
    System.out.println("test hello!");		//1
    hello1();//调用							//2
    System.out.println("test hello end!");	//7
}

public static void hello1(){//声明
    System.out.println("in hello1!");		//3
    hello2();//调用							//4
    System.out.println("hello1 end!");		//6
}

public static void hello2(){//声明
    System.out.println("in hello2!");		//5
}

参数与返回

除了大括号所包裹的方法体之外,方法名、参数表、返回值、修饰符被称为方法的签名。修饰符通常涉及到方法的访问范围和方法的类型,将在未来学习中学到。

在使用一个方法的时候要明确以下三点:

  1. 这个方法具体的作用是什么。
  2. 这个方法的启动需要什么参数。
  3. 这个方法执行完成后能获得什么数据。

实际上为了让程序更易读易用,会使用文档注释的方式在方法上标注以上三条观点。

参数表

参数表表示方法的启动需要的值以及值的类型。参数可以为多个,并且可以为任意类型。调用方法的时候就要传入方法所需的参数,这些参数作为局部变量只能在方法体内使用。

在声明方法的参数表里需要声明参数类型以及在方法内使用的参数名称,因为参数声明并不具有实际值,所以只是一个形式参数,需要在调用时赋值,被称为形参。

在调用方法时只需传入指定数量和指定类型的值在参数表的指定位置,这些值将在方法启动前赋值给形参,所以被称为实参。

形参和实参也可以使用自动类型提升的方式来进行赋值。

public static void main(String[] args){
    // 调用同一个方法,通过传入的参数不同,从而得到不同的结果
    add(1,1.0);
    add(2,2.0);
    add(3,3.0);
}

public static void add(int a,double b){
    System.out.println(a+b);//直接输出参数相加结果
}

其中主函数的String[] args实际上也是一个形参,因为调用者是java解释器,传入的参数通常是在控制台中传入的。也就是说String[]args的类型,那args实际上就是一个变量名。虽然形参变量的声明是可以自拟的,但仍然推荐使用Java官方推荐的写法args

返回值

调用方法的位置通过此方法的调用可以获取值,返回值类型表示调用此方法可以获取一个什么类型的值。这个值可以是一个运算结果或者一个任意意义的值,具体要看方法的实际意义,但只要规定了方法的返回值类型,方法就必须返回指定类型的值才可以。

void表示此方法并没有任何返回值,也就不能在方法体内返回任何值。

return关键字用于终止方法,并携带值回到方法的调用位置,所携带的值将通过方法的调用语句赋值给变量:

// 调用方法传入两个值,获得两个值相加之后的结果
public static void main(String[] args){
    int i = 10;
    double j = 11.11;

    double h = addNumber(i,j);//此处传递i和j的值
    System.out.print(h);
}


public static double addNumber(int a,double b){
    double c = a+b;
    return c;//此处返回c的值
}

在方法中,return语句通常写到方法的末尾,当然也可以在方法执行的中途进行返回。如果方法进行了返回则会终止方法的执行,表示return语句后的所有语句都永远无法执行,将会在编译时报错。

但是通过流程控制语句书写多个return就可以选择性的终止方法,并且因为Java认为任何一个if语句都可能不执行,所以也不会在编译时出现有永远无法执行的代码的错误:

public static double addNumber(int a,double b){
    double c = a+b;

    /*
	java认为,所有的if代码块都可能不会执行。
	所以“如果方法声明了返回值类型,但是返回语句在if之中,就要有个保底的返回在if之下”!
	如果if代码块执行,则保底返回不执行,反之则执行保底返回。
    */
    if(c >= 100){
        return c;//此处返回c的值
    }
    return 0;
    
    //如下代码:不执行if就执行else,反之都要执行return,java认为,此程序必定会返回内容,故合法。
    if(c >= 100){
        return c;//此处返回c的值
    }else{
        return 0;
    }
    
    //推荐使用第一种书写方式
}

合理的采用分支语句控制方法的返回可以减少代码量并使流程清晰易读。例如方法中存在多个分支条件都可返回结果,通常会采用连续的if else,但实际上只要进入了某一个if代码块就不会再去执行其他的代码块,那使用单独的if语句并在其中添加返回语句同样在进入了某一个代码块后停止方法,也不会进入其他的代码块了:

public static double addNumber(int a,double b){
    double c = a+b;
	
    //以下两种返回的流程是相同的!推荐使用第二种!
    
    //使用if else连贯性的返回数据
    if(c >= 100){
        return c;
    }else if(c>=200){
        return 0;
    }else if(c>=300){
        return 1;
    }else{
        return 2;
    }
    
}

将上面代码进行改装之后,可以如下编写:

public static double addNumber(int a,double b){
    double c = a+b;
    //使用if进行返回(推荐使用方式)
    if(c >= 100){
        return c;
    }
    if(c>=200){
        return 0;
    }
    if(c>=300){
        return 1;
    }
    return 2;
}

关于返回语句和if语句的配合优化可在熟练掌握基础知识后,在项目重构阶段进行研究和探索。

递归调用

如果在一个方法中调用自己,这样的语法是允许的,但如果程序运行起来之后,方法的调用会不停的嵌套,递进式的进行调用。那必要存在条件让方法能够结束递进,通常会携带参数层层退回,那这样的写法就叫递归。

递归可以实现类似循环的功能,但递归会占用栈空间,递归层数过多会导致栈空间溢出问题。而循环过多会产生过多无用的变量或者对象,其占用的大多是空间较大的堆空间。相比采用递归的方式,使用循环更保险一些。

利用递归将内层方法执行的结果向上层返回,直到返回到第一层方法调用的位置展示结果,利用此特性可以求得指定长度的斐波那契数列:

//使用递归的方式获得斐波那契数值,传入的值表示求第几位+1斐波那契值,返回的表示指定位上的值。
public static int Fibonacci(int n) {
    if(n == 0)
        return 0;
    if(n == 1)
        return 1;
    return Fibonacci(n-2) + Fibonacci(n-1);
}

方法的重载Overload

在同一个类中,允许存在多个同名的方法,但是方法的参数表要不同。主要区别在于:个数,类型,顺序。这被称为方法的重载,方法的重载只与参数表有关,与返回值无关。

参数表的数量不同:

public static void eat(){}
public static void eat(int a){}

参数的类型不同:

public static void eat(String a){}
public static void eat(int a){}

参数类型的顺序不同:

public static void eat(String a,int b){}
public static void eat(int b,String a){}

调用重载方法

在调用方法时,通过传入不同个数、类型、顺序的参数来区分调用。

public static void main(String[] args){
    eat();
}

public static void eat(int a){
    System.out.print("in int eat");
}

public static void eat(){
    System.out.print("in eat");
}

参数的类型自动转换

当实参类型与任何一个方法的参数类型都不同时,Java会尝试从实参类型向上慢慢寻找能够自动类型转换的类型,当找到最近的能够转换的参数时将调用其方法。

例如下文中存在两个参数分别定义了short和int类型的形参,但实参类型为byte类型。那Java就会从byte类型想上寻找最近的能够自动类型提升的形参,也就是short类型,并调用方法:

public class Index{
	public static void main(String[] args){
		byte b = 10;
		run(b);
	}
    public void run(int i){
		System.out.print("in int");
	}
	public void run(short f){//byte向上转型为short执行此处代码。
		System.out.print("in short");
	}
}

  • 17
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值