JavaSE基础(四)——Java方法
前言
本篇笔记记录Java方法的定义以及相关的知识点。
Java方法
方法
Java中的方法是解决问题的一些手段,完成某些功能的操作,方法的定义和C语言中的函数概念相同。
在Java中,方法是一些语句的集合,组成方法的语句共同在一起执行一个功能,其概念与特性可以分为以下几点:
- 方法是解决某个问题的步骤的有序组合;
- 方法包含于类或者对象中;
- 方法在程序中被创建,在其他地方被引用。
方法在定义和设计方面需要遵循一些规则,具体规则如下:
- 类中只能有一个主方法,主方法是类中的唯一的入口,程序的开始;
public static void main(String[] args){
// 这个就是主方法;
}
- 方法名的命名规则要遵循首字母小写的驼峰规则;
- 方法要有返回值类型(void表示无返回值类型);
- 方法名后面的参数表可以为空,如果需要接受参数的话需要给定参数的数据类型和变量名;
public String show(String str){
return str;
}
- public:是修饰符,后期面向对象阶段会有详细讲解;
- String:方法返回值类型,即该方法执行结束之后返回到调用处的结果的数据类型;
- show:方法名;
- String str:参数。
返回值需要注意的是,一个方法最终只能有一个返回值,这个返回值可以是基本类型也可以是复杂类型,后期再进行详细说明。
如果方法被static修饰符修饰,那么这个方法属于静态方法,那么在该静态方法中不能直接调用非静态方法,可以通过对象调用的方式来解决这个问题(这个问题也会在后期详细讲解)。
方法详解
在此之前,我们先说明一下方法的调用。方法调用就是同过定义的方法名来使用这个方法来实现一些功能,调用的方式如下:
public static void main(String[] args){
// 调用show方法
show();
}
// 被static修饰的方法不能直接调用非static方法,具体原因后续讲解
public static void show(){
System.out.println("我已经被调用了");
}
上述代码在执行过程中,调用了show方法,来执行一些功能。
除了定义方法需要注意到的地方(上面有说明),我们更要注意的是参数和返回值类型。参数共分为两类:实际参数、形式参数。
- 实际参数:在调用方法时,要传递的参数;
- 形式参数:被调用的方法接收调用方传递参数的变量。
具体如下:
public static void main(String[] args){
String name = "老程";
// 调用show方法,写入实际参数
show(name);
}
// 该参数为形式参数
public static void show(String name){
System.out.println(name + "调用了该方法");
}
【注】:形式参数的作用域在被调用的方法内部。
接下来说一下返回值类型。返回值类型可以理解为被调用的方法在执行结束后想将一些数据(可能是计算过后的结果,也可能是方法执行的状态),返回的数据类型必须严格定义,即方法定义的返回值类型为什么,return后面的数据就应该是什么。
理论上只要能定义给方法的返回值类型都可以作为返回值返回给方法调用处,具体写法如下:
public static void main(String[] args){
String name = "老程";
// 调用show方法,写入实际参数
String str = show(name);
}
// 该参数为形式参数
public static String show(String name){
name = name + "调用了该方法";
return name;
}
重复一下返回值需要注意的地方:
-
方法的返回值必须与定义好的返回值类型相对应,否则会报错;
-
如果方法定义了返回值类型,那么方法必须存在返回值;
-
返回值类型必须与调用处接收返回值的数据类型匹配;
-
原则上,只要是能定义变量的类都可以设定为返回值类型。
-
如果方法的返回值类型定义为void, 则表示该方法为无返回值类型,无返回值类型表示该对象没有返回值,调用方也不需要接收返回数据;
-
方法的返回值为void的时候也可以在方法体中数学return语句,只不过这时候的语句代表结束该方法执行;
-
返回值类型包括引用类型和基本类型(最后这一条会在面向对象中详细讲解)。
值传递与引用传递
学过C语言的同学都知道,调用函数的时候需要考虑传递参数使用的是值传递还是引用传递,这个概念曾经折磨过好多C语言初学者。来到Java的世界你会发现,这个折磨人的概念被解决掉了(因为Java根本不屑与在这个概念上折磨开发者,以后有的是折磨程序员的方法,不差这一个,哈哈哈)。
在Java中只存在值传递,没有引用传递。Java方法调用传递参数的时候是将存放变量的地址传递到方法中,我们在被调用的方法中定义一个形式参数,我们在进行参数传递的时候会把值拷贝一份传递给被调用方法中的形式参数,所以我们传递的是这个值,修改的也仅仅是值,不会对原来数据进行修改。
方法重载
方法重载的作用是不同数据类型的参数可能要通过同种功能来进行操作,所以我们可以定义同名的方法,将返回值类型、参数个人以及参数类型进行修改(一会说具体规则),这样就可以通过传递对应类型的参数或者不同个数的参数调用对应的方法。
方法重载的规则:
- 方法名称必须相同;
- 参数列表必须不同(个数不同或类型不同或参数排列顺序不同等);
- 方法的返回值类型可以相同也可以不相同;
- 只有返回值类型不同不能作为重载方法。
public static void main(String[] args){
int a = 1;
int b = 2;
float i = 1.5f;
float j = 2.5f;
int maxInt = max(a, b);
float maxFloat = max(i,j);
System.out.println(maxInt);
System.out.println(maxFloat);
}
public int max(int a, int b){
return a>b?a:b;
}
public float max(float a,float b){
return a>b?a:b;
}
上述代码在调用的时候,编译器会根据请求参数的类型等各种条件自动调用相关的方法执行操作。如果编译器找不到匹配的方法则会报错。
可变参数
可变参数是Java5之后出现的一个特性,可以传递很多个同类型的参数,所以也被称为不定向参数。可变参数如下:
public static void main(String[] args){
int result = max(1,2,3,4,5,6,3,1,4,7);
System.out.println("最大的数为:" + result);
}
// 可变参数由三个点组成
public static int max(int... i){
if(i.length == 0){
System.out.println("参数为空");
}
int t = 0;
// 循环遍历传递过来的可变参数,判断哪个数字最大然后返回最大的数字给调用方。
for(int a = 0 ; a < i.length-1 ; a ++){
if(i[a] < t){
t = i[a];
}
}
return t;
}
- 在方法的声明中,在指定参数类型后加一个省略号;
- 一个方法只允许有一个可变参数;
- 可变参数必须是方法的最后一个参数。
递归
写在递归前面
在方法这一篇笔记中,额外介绍一个知识点。这个知识点不同于方法,也不同于技术,更为准确的说应该是一种思想。大家都知道,Java语言的创立有一个目的是:让程序员可以将人类思维用于编程。这虽然是个好事,但是不得不说,人类思维和Java编程思维依旧有不小的差距,就像曾经我给我的学生们说过一个思维问题:
请你帮忙去超市买两个橙子回来,如果遇到西瓜帮我带四个。问:你去超市遇到了西瓜,最后买了什么回来?
这个问题其实很简单,有很多同学知道答案,也有很多同学听我这么问就知道问题不简单。其实,按照中文的理解,最后买了两个橙子、四个西瓜(不用管能否拎动,求别杠),中华文化博大精深,有些时候少几个字也不影响理解,然而在编程思维看来,却不是这样。
买两个橙子是已经被定义的变量,所以是客观存在的,毕竟买回来后需要变量使得计算机可以找到橙子;西瓜只是一个判定条件,从始至终都没有说过买西瓜的是,也就可以理解为西瓜在if语句中的一个布尔表达式,如果西瓜成立,则将变量橙子的数量变为4,而西瓜则没有分配变量的引用,所以无论有没有遇到西瓜,你买到的东西和西瓜没有关系,只是数量上会有所区别。
综上,这就是一种编程思想,这种思想可以帮助我们更快的培养出适合编程工作的思维,从而在编程方面的学习、工作效率更快、避免出错。
详解
递归通俗来讲就是一种方法调用,但是与我们日常使用的不太一样,递归的思想是自己调用自己(对,你没有听错,典型的把自己卖了还替别人数钱)。
一般来说,自己调用自己很容易出现死循环,就是不停地自己调用自己,直到虚拟机承受不了负载(就是通俗说一下,实际上是内存空间不够了),然后出现异常。
public void show(){
show();
}
一般来说,递归的目的是为了解决某些需要做重复操作的功能,如果从头写到尾会造成代码冗余(就是没必要、多余的意思),比如:从1加到100,其实就是将前面的数求和并记录下来,然后与后面的数进行求和,一直执行到100。像这样的问题我们使用递归就可以用很少的代码解决了(一般不这么做,就是举个例子,求别杠)。递归的结构如下:
- 递归头:定义不调用自身的一出口,否则会陷入死循环;
- 递归体:什么时候调用自己本身的入口。
单纯的文字说明肯定不是那么容易理解,下面我们来做一个例题,求出n!(n的阶乘)。
public static int f(int n){
if(n == 1){
// 这里表示当数字算到1的时候,后面已经没有需要累乘的数字了,我们直接把1返回到上一次调用处,并且结束递归
return 1;
}else{
// 只要乘数不为1,则继续减一相乘,调用当前计算方法
/*
根据方法的调用过程我们可以看到,其实在乘数不为1的时候,一直都没有进行累乘操作,一直在等待乘数为1的时候,这个时候再从最后一次调用开始,沿着之前调用的顺序原路返回,没返回一次就进行一次相乘,直到回到第一次递归,我们便计算完成。
*/
return n * f(n-1);
}
}
public static void main(String[] args){
System.out.println(f(5));
}
同学可以把上面的代码执行一下看一看计算是否正确,同时也可以修改一下传入的参数,看看算的对不对。
总结
本篇笔记记录的知识点不是特别多,但是对于初学者来说可能比较复杂,嘛,反正我记录的是笔记,只是在笔记中加了一些对于初学者的解释,我也有私心的,家里妹妹也可能学这行,可能到时候会给她一个启蒙作用吧。
大家要记住方法的相关要点,这方面在未来的编程中会一直伴随我们且逐步加深;关于递归这一块需要多看多理解,我比较懒,解释的可能并不是很透彻,毕竟现在灌输太多对初学者不友好,大家一起加油。