方法
方法的引入
所谓方法就是指为了完成某件事件而采用的解决方案(步骤)。
例如:洗衣服
1、将衣服放入盆中
2、添加洗衣液
3、搓洗衣物
4、清洗衣物
5、晾衣服
洗衣服一共有以上五个步骤,我们将这些步骤取名为:洗衣服的方法。生活中经常会需要洗衣服,这个时候只需要按照洗衣服的方法进行洗衣服即可。放到程序中来说,方法就是一段可以重复调用的代码。
假设有一个游戏程序,程序在运行过程中,要不断地发射炮弹。发射炮弹的动作需要编写100行的代码,在每次实现发射炮弹的地方都需要重复地编写这100行代码,这样程序就会变得很臃肿,可读性也非常差。为了解决这样的问题,通常会将发射炮弹的代码提取出来,放到一个 { } 中并给它取个名字(方法名)。这样在每次需要做发射炮弹这个动作的地方,只需要通过这些名字(方法名)以此来调用这些方法,从而就可以完成发射炮弹的动作。
方法的定义
格式:
修饰符 返回值类型 方法名 (参数类型1 参数名1,参数类型2 参数名2,…){
方法步骤1
…
return 返回值;
}
修饰符:
方法的修饰符比较多,有对访问权限进行限定的,有静态修饰符static,还有最终修饰符final,这里不做深入描述,暂时用public static
这种.
返回值类型:
用于限定方法中返回值返回的数据的数据类型.
方法名:
方法名的命名遵循Java中标识符的命名规则,方法名的命名形式遵循驼峰命名法,即:xxxXxxXxx
参数类型:
用于限定调用方法时传入参数的数据类型(基本数据类型或者引用类型).
参数名:
是一个变量,用于接收调用方法时传入的数据.
参数列表:
方法中的 "参数类型 参数名 , 参数类型 参数名2…"被称为参数列表,它用于表述方法在被调用时需要接受的参数.如果方法不需要接收任何参数,则参数列表为空
return关键字:
用于结束方法以及返回方法指定类型的值.
返回值:
被return语句返回的值,该值会返回给调用者.
\
当方法中没有返回值的时候,返回值类型要声明为void
,此时,方法中的return语句可以省略
如下:
**修饰符 void 方法名(参数类型 参数名1,参数类型 参数名2…){
方法步骤1
…
}**
总结:
方法三要素:方法名、参数列表、返回值类型
实例
main方法剖析public static void main(String []args){ }
修饰符:public static
返回值类型:void(说明main()没有返回值)
方法名:main
参数类型:String [ ] (String类型的数组)
参数名:argss
方法的调用方式
格式:
方法名(值1,值2,...)
案例:
求长方形的面积(用户传入长和宽,程序返回长方形的面积值)public class Demo{ public static void main(String []args){ //调用方法 //方法名(值1,值2..) int area = getRectangleArea(3,4);//调用求长方形面积的方法,传入长和宽的值,最终将方法求得的长方形面积赋值给变量area(为了能在控制台展示) System.out.println(area);//打印出求得的长方形的面积 } //定义求长方形面积的方法 public static int getRectangleArea(int length,int width){ int area=0;//定义长方形的面积变量 area = length*width;//面积等于长乘宽 return area;//向调用方法的人返回求得到的面积值 } }
运行结果
12
注:要计算不同的长宽值的长方形的面积,直接在调用getRectangleArea()方法时改变方法中的值即可.
代码剖析//定义求长方形面积的方法 public static int getRectangleArea(int length,int width){ int area=0;//定义长方形的面积变量 area = length*width;//面积等于长乘宽 return area;//向调用方法的人返回求得到的面积值 }
修饰符:public static
返回值类型:int
方法名:getRectangleArea
参数类型1:int
参数名1:length
参数类型2:int
参数名2:width
返回值:area
综合案例
要求:打印出三种长宽不同的矩形,每种矩形打印三次.
不使用方法这一理念来实现时:public class PrintRectangle{ public static void main(String []args){ //打印出一个3*4的矩形 for(int i=1;i<=3;i++){//外层循环每循环1次,内层就打印3次 for(int j=1;j<=4;j++){ System.out.print("*"); } System.out.println(); } System.out.println("------------我是分隔符---------"); //打印出一个4*5的矩形 for(int i=1;i<=4;i++){//外层循环每循环1次,内层就打印5次 for(int j=1;j<=5;j++){ System.out.print("*"); } System.out.println(); } System.out.println("------------我是分隔符---------"); //打印出一个7*7的矩形 for(int i=1;i<=7;i++){//外层每循环1次,内层就打印7次 for(int j=1;j<=7;j++){ System.out.print("*"); } System.out.println(); } } }
运行结果
利用方法这一理念来解决这道题
public class PrintRectangle{ public static void main(String []args){ //打印出一个3*4的矩形 print(3,4); //打印出一个4*5的矩形 print(4,5); //打印出一个7*7的矩形 print(7,7); } public static void print(int length,int width){ for(int i=1;i<=length;i++){ for(int j=1;j<=width;j++){ System.out.print("*"); } System.out.println(); } System.out.println("------------我是分隔符---------"); } }
运行结果
通过上面的综合案例就能很直观地感受到方法的好处了!
方法调用执行过程中的内存机制(非常重要)
方法的注意事项
1、方法定义在类中与其它方法并列,不能方法套用方法。
2、方法形参上定义的变量的作用域仅在方法中
3、不能定义两个完全相同的方法
4、定义好的方法可以反复使用,提高代码的复用性
5、调用方法传参的时候支持类型的自动提升
6、如果返回值类型为void不能return值,也不能直接打印
7、如果返回值类型不是void可以直接打印
8、如果方法返回值类型不是void,针对返回值我们可以获取也可以不获取。
方法定义的练习
a. 定义无返回值无参数方法,如打印3行,每行3个星号的矩形
b. 定义无返回值有参数方法,如打印指定M行,每行N个星号的矩形
c. 定义有返回值无参数方法,如键盘录入得到一个整数
d. 定义有返回值有参数方法,如求三个数的平均值package com.learning; import java.util.Scanner; /* a. 定义无返回值无参数方法,如打印3行,每行3个*号的矩形 b. 定义无返回值有参数方法,如打印指定M行,每行N个*号的矩形 c. 定义有返回值无参数方法,如键盘录入得到一个整数 d. 定义有返回值有参数方法,如求三个数的平均值 */ public class Practise { public static void main(String[] args) { printRectangle0(); System.out.println("-----------------------"); printRectangle1(3,4); System.out.println("-----------------------"); System.out.println(recive()); System.out.println("-----------------------"); System.out.println(average(3,4,5)); } //定义无返回值无参数方法,如打印3行,每行3个*号的矩形 public static void printRectangle0() { for(int x=1;x<=3;x++) { for(int y=1;y<=3;y++) { System.out.print("*"); } System.out.println(); } } //定义无返回值有参数方法,如打印指定M行,每行N个*号的矩形 public static void printRectangle1(int M,int N) { for(int x = 0;x<M;x++) {//外层控制行号 for(int y = 0;y<N;y++) {//内层控制*号个数 System.out.print("*"); } System.out.println(); } } //定义有返回值无参数方法,如键盘录入得到一个整数 public static int recive() { Scanner sc = new Scanner(System.in); int a = sc.nextInt(); return a; } //定义有返回值有参数方法,如求三个数的平均值 public static double average(int x,int y,int z){ int average01 = (x+y+z)/3; return average01; } }
方法的重载
方法重载的由来
有了需求才会衍生供给,方法的重载由来可以在这个例子中得到很好的体现。假设要在程序中实现一个对数字求和的方法,由于参与求和的数字的个数和类型都不确定,因此要针对不同的情况去设计不同的方法。接下来通过一个案例实现对两个整数相加,对三个整数相加以及对两个小数相加的功能,具体实现如下所示。public class MethodOverloading{ public static void main(String []args){ //调用方法并将其结果打印出来 System.out.println(addTwoInt(1,2)); System.out.println(addThreeInt(1,2,3)); System.out.println(addTwoDouble(1.0,2.0)); } //下面的方法实现了两个整数的相加 public static int addTwoInt(int x,int y){ return x + y; } //下面的方法实现了三个整数的相加 public static int addThreeInt(int x,int y,int z){ return x+y+z; } //下面的方法实现了两个小数的相加 public static double addTwoDouble(double x,double y){ return x+y; } }
在上述案例中,虽然这三个方法的本质需求都是对数据进行求和但是因为参数类型和参数个数的不同程序需要针对每一种求和的情况都定义一个方法,每个方法的名称也都不相同,当有成千上万个这样的方法时,成千上万个不同名字的方法,对于程序员来说并不是能够完全记忆得住的,什么情况下该调用哪种方法就很难分得请。为了解决这个问题,Java允许在一个程序中定义多个名称相同的方法,但是每个方法的参数类型或者参数个数必须不同,这就是方法的重载。
接下来通过方法重载的方式对上述的案例进行修改。public class MethodOverloading{ public static void main(String []args){ //调用方法并将其结果打印出来 System.out.println(add(1,2)); System.out.println(add(1,2,3)); System.out.println(add(1.0,2.0)); } //下面的方法实现了两个整数的相加 public static int add(int x,int y){ return x + y; } //下面的方法实现了三个整数的相加 public static int add(int x,int y,int z){ return x+y+z; } //下面的方法实现了两个小数的相加 public static double add(double x,double y){ return x+y; } }
上述两个案例的运行结果都是一样的,在第二个案例中定义了三个同名的add()方法,但他们的参数类型或参数个数不一样,从而这三个方法形成了方法的重载,在main()方法中,调用add()方法时,通过传入的参数的类型及其个数就可以确定调用的是哪一个重载的方法,如add(1,2)调用的是两个整数求和的方法。
值得一提的是,方法的重载与返回值类型和变量名无关,它需要满足两个条件,一是方法名相同,二是方法的参数列表不同(参数个数不同或者参数类型不同再或者参数类型顺序不同)。/*方法的重载条件 一、方法名相同 二、参数列表必须不同:一下条件至少满足一个才构成重载关系 a.参数个数不同 ①和②参数个数不同 构成重载关系 b.参数类型不同 ①和③参数类型不同 构成重载关系 c.参数类型顺序不同 ④和⑤参数类型顺序不同 构成重载关系 */ public class MethodOverloading{ public static void main(String []args){ } //① public static void method(int x,int y){ } //② public static void method(int x,int y,int z){ } //③ public static void method(double x,double y){ } //④ public static void method(char x,double y) { } //⑤ public static void method(double x,char y) { } }
基本类型的值传递(形参的改变不会影响实参)
基本类型的值传递,形参的改变不影响实参
问:第4行代码中打印出a的值是多少?public class MethodOverloading01{ public static void main(String[]args){ int a = 10; method(a); System.out.println(a);//最终打印出来a的值是多少? } public static void method(int a){ a=a+2; } }
答案是 10
程序从上到下执行,Java虚拟机找到程序的入口main()方法,调用main()方法进栈,main()方法中的局部变量随着方法的入栈而入栈.继续执行到method()方法,这个时候method()方法也随之入栈。如下图所示。
引用类型的值传递(形参的改变会影响实参)
引用类型的值传递,传递的是地址值。形参的改变会影响实参
问:第6行代码中打印出来的数组元素是怎么样的public class MethodOverloading01{ public static void main(String[]args){ int [] arr = {3,6,11}; method(arr); for(int i=0;i<arr.length;i++){ System.out.println(arr[i]);//最终打印出来的数组元素是什么 } } public static int [] method(int[] arr){ arr[1]=13; return arr; } }
答案:3,13,11
程序自上而下执行,Java虚拟机找到程序的入口main()方法,main()方法被调进栈内。main()方法中的东西也随之调入栈内,其中当程序执行到 int [] arr = {3,6,11};时,int [] arr 则会留在栈中,并给它分配一个地址值04xF,让这个地址值指向堆中的{3,6,11},当程序执行到method(arr);时,method()方法也随之入栈。method()方法上定义了一个数组,而method(arr);这一句则是将main()方法中int [] arr它存放的地址值04xF交给了method()方法,所以此时method()方法上的int [] arr与main()方法中的int[]arr是同一个数组,程序调用method()方法,执行完method()中的语句arr[1]=13;后堆中的数据元素也会随着被改变,当method()方法执行完毕并被弹出栈内后,堆中被method()方法改变的数据元素依然是出于被改变的状态,所以最终遍历数组时,遍历出的数组元素为 3,13,11