方法概述
什么是方法(method、函数):
- 方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中 也称为函数或过程。
- 将功能封装为方法的目的是,可以实现代码重用,简化代码
- Java里的方法不能独立存在,所有的方法必须定义在类里。
对于上面的语法格式中具体说明如下:
- 修饰符:方法的修饰符比较多,有对访问权限进行限定的,有静态修饰符static,还有最终修饰符final等,
- 返回值类型:用于限定方法返回值的数据类型
- 参数类型:用于限定调用方法时传入参数的数据类型
- 形参名:是一个变量,用于接收调用方法时传入的数据
- return关键字:两个作用,第一停止当前方法,第二将后面的返回值还给调用处。返回值:也就是方法执行后最终产生的数据结果
- 返回值:被return语句返回的值,该值会返回给调用者
需要特别注意的是,方法中的“参数类型 形参名1,参数类型 形参名2”被称作参数列表,它用于描述方法在被调用时需要接收的参数,如果方法不需要接收任何参数,则参数列表为空,即()内不写任何内容。方法的返回值必须为方法声明的返回值类型,如果方法中没有返回值,返回值类型要声明为void,此时,方法中return语句可以省略。
- 形参:方法定义中的参数
- 实参:方法调用中的参数
方法的分类
定义方法时,要做到两个明确
- 明确返回值类型:主要是明确方法操作完毕之后是否有数据返回,如果没有,写void;如果有,写对应的数据类型
- 明确参数:主要是明确参数的类型和数量
方法调用的三种形式
- 直接调用:直接写方法名调用
- 赋值调用:调用方法,在方法前面定义变量,接收方法返回值
- 输出语句调用: 在输出语句中调用方法, System.out.println(方法名())
调用方法时的注意:
- void类型的方法,直接调用即可
- 非void类型的方法,推荐用变量接收调用
代码举例
package demo02; /* 方法其实就是若干语句的功能集合。 参数(原料):就是进入方法的数据。 返回值(产出物):就是从方法中出来的数据。 定义方法的完整格式: 修饰符 返回值类型 方法名称(参数类型 参数名称, ...) { 方法体 return 返回值; } 修饰符:现阶段的固定写法,public static 返回值类型:也就是方法最终产生的数据结果是什么类型 方法名称:方法的名字,规则和变量一样,小驼峰 参数类型:进入方法的数据是什么类型 参数名称:进入方法的数据对应的变量名称 PS:参数如果有多个,使用逗号进行分隔 方法体:方法需要做的事情,若干行代码 return:两个作用,第一停止当前方法,第二将后面的返回值还给调用处 返回值:也就是方法执行后最终产生的数据结果 注意:return后面的“返回值”,必须和方法名称前面的“返回值类型”,保持对应。 定义一个两个int数字相加的方法。三要素: 返回值类型:int 方法名称:sum 参数列表:int a, int b 方法的三种调用格式。 1. 单独调用:方法名称(参数); 2. 打印调用:System.out.println(方法名称(参数)); 3. 赋值调用:数据类型 变量名称 = 方法名称(参数); 注意:此前学习的方法,返回值类型固定写为void,这种方法只能够单独调用,不能进行打印调用或者赋值调用。 */ public class Demo02MethodDefine { public static void main(String[] args) { // 单独调用 sum(10, 20); System.out.println("==========="); // 打印调用 System.out.println(sum(10, 20)); // 30 System.out.println("==========="); // 赋值调用 int number = sum(15, 25); number += 100; System.out.println("变量的值:" + number); // 140 } public static int sum(int a, int b) { System.out.println("方法执行啦!"); int result = a + b; return result; } }
方法使用的注意事项
- 方法应该定义在类当中,但是不能在方法当中再定义方法。不能嵌套。
- 方法定义的前后顺序无所谓。
- 方法定义之后不会执行,如果希望执行,一定要调用:单独调用、打印调用、赋值调用。
- 如果方法有返回值,那么必须写上“return 返回值;”,不能没有。return语句后面不能跟数据或代码
- return后面的返回值数据,必须和方法的返回值类型,对应起来。
- 对于一个void没有返回值的方法,不能写return后面的返回值,只能写return自己。
- 对于void方法当中最后一行的return可以省略不写。
- 一个方法当中可以有多个return语句,但是必须保证同时只有一个会被执行到,两个return不能连写。
package demo03; /* 使用方法的时候,注意事项: 1. 方法应该定义在类当中,但是不能在方法当中再定义方法。不能嵌套。 2. 方法定义的前后顺序无所谓。 3. 方法定义之后不会执行,如果希望执行,一定要调用:单独调用、打印调用、赋值调用。 4. 如果方法有返回值,那么必须写上“return 返回值;”,不能没有。 5. return后面的返回值数据,必须和方法的返回值类型,对应起来。 6. 对于一个void没有返回值的方法,不能写return后面的返回值,只能写return自己。 7. 对于void方法当中最后一行的return可以省略不写。 8. 一个方法当中可以有多个return语句,但是必须保证同时只有一个会被执行到,两个return不能连写。 */ public class Demo04MethodNotice { public static int method1() { return 10; } public static void method2() { // return 10; // 错误的写法!方法没有返回值,return后面就不能写返回值。 return; // 没有返回值,只是结束方法的执行而已。 } public static void method3() { System.out.println("AAA"); System.out.println("BBB"); // return; // 最后一行的return可以省略不写。 } }
方法重载
方法重载指同一个类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载
- 多个方法在同一个类中
- 多个方法具有相同的方法名
- 多个方法的参数不相同,类型不同或者数量不同
重载的特点:
- 与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类 型)。调用时,根据方法参数列表的不同来区别。
示例:
/* * 方法的重载(overload) loading... * * 1.定义:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。 * * "两同一不同":同一个类、相同方法名 * 参数列表不同:参数个数不同,参数类型不同 * * 2. 举例: * Arrays类中重载的sort() / binarySearch() * * 3.判断是否是重载: * 跟方法的权限修饰符、返回值类型、形参变量名、方法体都没有关系! * * 4. 在通过对象调用方法时,如何确定某一个指定的方法: * 方法名 ---> 参数列表 */ public class OverLoadTest { public static void main(String[] args) { OverLoadTest test = new OverLoadTest(); test.getSum(1,2); } //如下的4个方法构成了重载 public void getSum(int i,int j){ System.out.println("1"); } public void getSum(double d1,double d2){ System.out.println("2"); } public void getSum(String s ,int i){ System.out.println("3"); } public void getSum(int i,String s){ System.out.println("4"); } //如下的3个方法不能与上述4个方法构成重载 // public int getSum(int i,int j){ // return 0; // } // public void getSum(int m,int n){ // // } // private void getSum(int i,int j){ // // } }
方法调用时参数的传递问题
方法在调用的时候参数是如何传递的呢?其实在调用的时候参数传递给方法,这个过程就是赋值的过程,参数传递和“赋值规则”完全相同,只不过参数传递在代码上看不见“=”运算符。我们先来深入的研究一下“赋值规则”吧! 我们知道“赋值”运算的时候实际上和变量的数据类型无关,无论是基本数据类型还是引用数据类型,一律都是将变量中保存的“值”复制一份,然后将复制的这个“值”赋上去。他们的区别在于,如果是基本数据类型则和堆内存当中的对象无关,如果是引用数据类型由于传递的这个值是 java 对象的内存地址,所以会导致两个引用指向同一个堆内存中的 java 对象,通过任何一个引用去访问堆内存当中的对象,此对象内存都会受到影响。
方法参数传递基本类型
- 基本数据类型的参数,形式参数的改变,不影响实际参数
依据:
- 每个方法在栈内存中,都会有独立的栈空间,方法运行结束后就会弹栈消失
代码举例
public class Demo { public static void main(String[] args) { int number = 100; System.out.println("调用change方法前:" + number);//调用change方法前:100 change(number); System.out.println("调用change方法后:" + number);//调用change方法后:100 } public static void change(int number) { number = 200; } }
方法参数传递引用类型
结论:
- 对于引用类型的参数,形式参数的改变,影响实际参数的值
依据:
- 引用数据类型的传参,传入的是地址值,内存中会造成两个引用指向同一个内存的效果,所以即使方法 弹栈,堆内存中的数据也已经是改变后的结果
示例
public class Demo { public static void main(String[] args) { int[] arr = {10, 20, 30}; System.out.println("调用change方法前:" + arr[1]);//调用change方法前:20 change(arr); System.out.println("调用change方法后:" + arr[1]);//调用change方法后:200 } public static void change(int[] arr) { arr[1] = 200; } }
变量的赋值:
- 如果变量是基本数据类型,此时赋值的是变量所保存的数据值。
- 如果变量是引用数据类型,此时赋值的是变量所保存的数据的地址值。
值传递机制:
- 如果参数是基本数据类型,此时实参赋给形参的是实参真实存储的数据值。
- 如果参数是引用数据类型,此时实参赋给形参的是实参存储数据的地址值
方法递归
递归:
- 递归其实就是方法在执行的过程中调用了另一个方法,而另一个方法则是自己本身。
递归的分类:
- 递归分为两种,直接递归和间接递归。
- 直接递归称为方法自身调用自己。
- 间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
注意事项:
- 递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
- 在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
- 构造方法,禁止递归
代码举例
/* 方法递归? 1、什么是方法递归? 方法自己调用自己,这就是方法递归。 2、当递归时程序没有结束条件,一定会发生: 栈内存溢出错误:*Error 所以:递归必须要有结束条件。(这是一个非常重要的知识点。) JVM发生错误之后只有一个结果,就是退出JVM。 3、递归假设是有结束条件的,就一定不会发生栈内存溢出错误吗? 假设这个结束条件是对的,是合法的,递归有的时候也会出现栈内存溢出错误。 因为有可能递归的太深,栈内存不够了。因为一直在压栈。 4、在实际的开发中,不建议轻易的选择递归,能用for循环while循环代替的,尽量 使用循环来做。因为循环的效率高,耗费的内存少。递归耗费的内存比较大,另外 递归的使用不当,会导致JVM死掉。 (但在极少数的情况下,不用递归,这个程序没法实现。) 所以:递归我们还是要认真学习的。 5、在实际的开发中,假设有一天你真正的遇到了:*Error 你怎么解决这个问题,可以谈一下你的思路吗? 我来谈一下我的个人思路: 首先第一步: 先检查递归的结束条件对不对。如果递归结束条件不对, 必须对条件进一步修改,直到正确为止。 第二步:假设递归条件没问题,怎么办? 这个时候需要手动的调整JVM的栈内存初始化大小。 可以将栈内存的空间调大点。(可以调整大一些。) 第三步:调整了大小,如果运行时还是出现这个错误, 没办法,只能继续扩大栈的内存大小。 (java -X)这个可以查看调整堆栈大小的参数 */ // 使用递归,请编写程序,计算1~n的和。 public class RecursionTest03{ public static void main(String[] args){ // 1~3的和 int n = 3; int r = sum(n); System.out.println(r); // 6 } public static int sum(int n){ //n最初等于3 // 3 + 2 (2是怎么的出来的:n - 1) //sum(n - 1); if(n == 1){ return 1; } // 程序能执行到此处说明n不是1 return n + sum(n-1); } }