4.1 认识方法
4.1.1 方法概述
方法是程序当中必不可少的组成部分。我们之前在讲hello, world的时候就已经粗略的讲过了主方法。
简单的来说,方法是一个用于封装代码的区。我们之前写程序的时候都是把所有代码放在主方法里面,这也做可行,但是有两个问题:
- 代码复用性极差
- 程序分类不明确
为什么会复用性差?如果一段代码我需要在不同的地方执行若干次,需要把整段代码都得赋值粘贴一遍。这样做十分麻烦。
分类不明确的原因:一个程序当中,不同的代码负责不同的事情。有一些代码干这件事;有一些代码干另外一件事。那么我怎么知道这段代码是干什么的?虽然可以加上注释,但是分类明确无法提现出来。
有了方法,这两个问题可以得到解决:
复用性的提高:如果一段代码我要用很多次,我就把它封装在一个方法里面。我要用的时候调用这个方法就可以了。不需要再把整段代码复制粘贴。
分类明确:一段代码如果是干这件事的,我把他封装在方法中;干另一件事的,我把他封装在另一个方法中。这样一来,程序的分类性就慢慢地体现出来了。
综上所述,可见方法的重要性。下一章我们学习如何定义方法。
学生提问:为什么有一些人把方法说成函数?两者有区别吗?
答:两者有区别。简单的来说,定义在类中的叫做方法;独立定义的叫做函数。Java是面向对象的编程语言,所有东西都定义在类当中,所以叫做方法。像C这类的面向过程语言,没有类这个概念,所以叫做函数。
本章小结:
- 把所有代码放在主方法中是有问题的
- 方法用于封装代码,主方法可以调用其他方法
4.2 方法的定义和调用方法
4.2.1 方法的定义
方法的定义语法如下:
- 若干个修饰符 返回值类型 方法名(参数类型1 参数名称1,参数类型n 参数名称n){
- //方法中的代码
- }
注2:参数数量完全可变,也可以没有
注3:方法名建议使用小驼峰命名法
就拿我们之间接触过的主方法来讲:
- public static void main(String[] args){}
- public是权限修饰符
- static是静态修饰符
- void是返回值类型,表示没有返回值
- main是方法名
- String[]是字符串数组,是主方法的参数
然而,我们这节当中先固定的把方法声明为“static void”。static具体会在面向对象(上)去讲;void讲返回值(也就是下一节)的时候会说明。static void前面还可以加上一个public,不过加不加都可以。
那么,我们先来定义一下我们自己的方法吧!
- class MethodDemo{
- public static void main(String[] args){
-
- }
-
- static void printString(){ //定义方法
- System.out.println("hello, method");
- }
- }
这个方法很简单,就是一个无返回值无参数的小方法。但是现在如果我们运行这个程序的话,什么都不会发生。因为主方法里什么都没有。我们想要让主方法调用printString方法,怎么做呢?请看下一小节:
4.2.2 方法的调用
方法的调用语法如下:
- 方法名(方法要求的参数);
注:即使方法没有参数要求,括号也得要写。
注2:如果被调用的方法和调用者在不同的类当中,需要实例化对象,这个我们先不管。
接着用我们上面的例子:
- class MethodDemo{
- public static void main(String[] args){
- printString(); //调用方法
- }
-
- static void printString(){
- System.out.println("hello, method");
- }
- }
在这个程序的第三行,主方法调用了printString()方法。现在再编译和运行一次,hello, method字符串将会被打印。
本章小结:
- 方法的定义语法是“修饰符 返回值 方法名(参数列表) { }”
- 方法先暂时声明为static void
- 方法的调用语法是“方法名(需求的参数)”
4.3 方法的参数以及返回值
4.3.1 参数以及返回值概述
如果方法中要使用到的量是未知的,是要被上一级调用者定义的,可以使用参数。例如,我写一个方法,这个方法可以计算加法。加哪两个数我这个方法知道吗?这个时候,可以在方法上定义两个参数。当上一级调用者调用我这个方法的时候,必须给我传进来两个数。这样就可以由上一级调用者指定未知的量。
返回值相当于整个方法的结果。再用一下刚才加法的例子,两个数是参数,那么两个数的和即是这个方法的返回值。有返回值的方法可以被上一级调用者用作一个量,这个量就是方法的返回值。
4.3.2 如何定义有参数的方法
参数是在定义方法时定义的。例如:
- class MethodDemo{
- public static void main(String[] args){
-
- }
-
- static void printNumber(int num){
- System.out.println("我的上级调用者输入的数字是:"+num);
- }
- }
这个方法需求的参数是一个int类型。在方法体当中会使用到这个int。
我们在调用这个方法时也得要传入一个int:
- class MethodDemo{
- public static void main(String[] args){
- printNumber(5271); //给printNumber()方法传入int类型:5271
- }
-
- static void printNumber(int num){
- System.out.println("我的上级调用者输入的数字是:"+num);
- }
- }
一个方法也可以要求多个参数。通过逗号隔开。例如:
- class MethodDemo{
- public static void main(String[] args){
- printNumber(5271,3.14); //给printNumber()方法传入int类型:5271,double类型3.14
- }
-
- static void printNumber(int num, double num2){
- System.out.println("我的上级调用者输入的第一个数字是:"+num);
- System.out.println("我的上级调用者输入的第二个数字是:"+num2);
- }
- }
在方法上定义的参数叫做“形式参数”,简称为形参;实际传入的数叫做“实际参数”,简称为“实参”。在上一个程序当中,printNumber()方法的num和num2就是形参;main()传入的5271和3.14叫做实参。
现在,我们学习了参数的使用之后,来写这样的一个方法。这个方法可以接收两个数字,并打印出这两个数字的和。
- class MethodDemo {
- public static void main(String[] agrs) {
- add(10,4); //调用add()方法。传入10和4作为实参
- }
- static void add(int num, int num2) { //add()方法,需求两个int
- System.out.println(num+num2); //将两个数字的和打印
- }
- }
4.3.3 定义有返回值的方法
返回值是一个方法的最终值。当方法返回一个值时,该方法结束。上一级调用者可以使用有返回值的方法作为一个值来使用。如果一个方法不需要返回值,将返回值定义为void。
如果方法要返回一个值,语法如下:
- return 值;
当然,如果是void的方法,可以通过return结束方法。但是没有任何值被返回。
- return;
例如,我们有一个方法,这个方法可以给你返回一个0。
- class MethodDemo {
- public static void main(String[] agrs) {
- System.out.println(myMethod()); //由于myMethod()有返回值,可以把它作为一个值来使用。
- }
-
- static int myMethod() { //返回一个int类型的方法
- return 0; //返回0
- }
- }
在这个程序当中,myMethod()方法被定义为返回int类型的方法。方法体中将0返回。因此,main()中可以将myMethod()作为一个值来使用,就可以直接将其打印输出。
定义方法,参数是两个int,返回他们的和:
- class MethodDemo{
- static int add(int num, int num2){
- return num+num2;
- }
- public static void main(String[] args){
- int sum = add(5,1);
- System.out.println(sum);
- }
- }
在这个方法当中,返回值类型为int。return是表示返回的关键字,后面紧跟的就是要返回的值。
有返回值的方法可以被上一级调用者用作一个值。在主方法当中,add(5,1)返回的值赋值给了sum这个变量,然后打印输出。
一个方法可以有多个返回值,但是一般使用判断语句区分开,例如定义减法方法:
- class MethodDemo{
- static int subtract(int num, int num2){
- if(num>num2){ //如果num>num2
- return num-num2; //返回num-num2
- }
- else{
- return num2-num; //不然,返回num2-num
- }
- }
- public static void main(String[] args){
- System.out.println(subtract(4,7));
- }
- }
4.3.4 参数可变方法
参数可变方法是Java 1.5中新加入的一个功能之一。如果一个方法的某个参数的数量不确定,可以使用参数可变方法。
语法如下:
- 若干个修饰符 返回值类型 方法名(参数类型... 参数名){
- //代码
- }
可见,如果一个参数的数量不确定,在参数列表中的参数类型后面加上“...”即可。如果一个参数类型是这样的,其会变成一个数组类型。由于数组类型我们没有学到,请参数第六章。
本章小结:
- 被上一级调用者指定的量叫做参数
- 方法的最终值叫做返回值,可以被上一级调用者用作一个量
- 方法的参数和返回值在整个方法定义时被定义
- 如果没有返回值声明为void
- return用于返回其后面的值
- 当方法返回了一个值之后,方法结束
- Java 1.5新功能之一是可变数量参数,在参数类型后面加上“...”即可
- 当一个参数类型是可变的,其变成一个数组类型
4.4 方法的重载
4.4.1 重载概述
Java允许出现多个方法方法名一样但是参数不同的情况,这种情况被称为方法重载。这样大大方便了程序的编写。比如说定义一个加法方法,两个int可以相加、两个double可以相加、两个long也可以相加.....因为重载的存在,我们不需要定义不同名称的方法了。
虚拟机负责判断使用哪个方法。
满足下列情况之一,构成重载
- 参数数量不同
- 参数顺序不同(不同类型)
- 参数类型不同
4.4.2 三种重载形式
一、参数数量不同:
- static int add(int x, int y){
- return x+y;
- }
- static int add(int x, int y, int z){
- return x+y+z;
- }
二、参数顺序不同:
- static double add(int x, double y){
- return x+y;
- }
- static double add(double y, int x){
- return x+y;
- }
注意:两者类型也得要不同。不然无法构成重载
三、参数类型不同
- static int add(int x, int y){
- return x+y;
- }
- static double add(double x, double y){
- return x+y;
- }
4.4.3 测试
我们先定义两个方法。其中一个方法需要两个int参数;另一个需要两个double参数。打印两个数的乘积。
- class MethodDemo {
- public static void main(String[] agrs) {
- getProduct(2,5); //使用int, int
- getProduct(0.5,0.5); //使用double, double
- }
- static void getProduct(int num, int num2) { //重载1:int, int
- System.out.println(num * num2);
- }
- static void getProduct(double num, double num2) { //重载2:double, double
- System.out.println(num * num2);
- }
- }
在这个程序当中,getProduct()方法有两个重载形式:int, int 和 double, double。因此,调用时可以选择使用哪个重载形式。
本章小结
- 方法重载是指多个同名称的方法拥有不同的参数的情况
- 由虚拟机判断选用哪个方法
- 方法重载在三个情况下发生:数量、顺序、类型
4.5 方法的递归
4.5.1 递归概述
递归是一种算法。指在方法中调用自己。这样的算法可以逐步逼近结果。但是要保证两点:
- 的确是可以趋近结果
- 必须是要有条件的递归,不然是死循环
递归的缺点在于如果递归次数过多,有栈内存溢出的风险。
4.5.2 阶乘例子
先简单介绍一下阶乘:
n!=n*(n-1)*(n-2)*(n-3).....*3*2*1
例如7!=7*6*5*4*3*2*1=5040
我们现在通过Java实现:
- static long getFactorial(long num){
- long sum = 0;
- if(num==0){
- return 1;
- }
- else{
- sum = num*getFactorial(num-1);
- return sum;
- }
- }
定义getFactorial方法,返回值和参数都是long类型。
在第七行中完成了递归。getFactorial(num-1)相当于重新调用了方法本身,传进去的参数是num-1。一直重复调用本身直到num==0为止。这个算法就实现了:
- class RecursionDemo{
- public static void main(String[] args){
- System.out.println(getFactorial(5));
- }
-
- static long getFactorial(long num){
- long sum = 0;
- if(num==0){
- return 1;
- }
- else{
- sum = num*getFactorial(num-1);
- return sum;
- }
- }
- }
本章小结:
- 递归是指方法调用本身
- 递归必须要有一个条件控制,不然会是死循环
- 递归的缺点是如果递归次数过多有栈内存溢出风险
4.6 栈内存
4.6.1 栈内存概述
这一章我们不讲Java,我们就了解一下在程序运行时具体发生了什么。
Java把栈内存去分成栈内存和堆内存。虽然这两个词只有一字之差,但是区别还是蛮大的。
当一个方法被调用时,其就会被分配一个栈区。方法中的变量/常量在方法中的栈区内存放。
栈内存中的栈区都是有序排放的,遵循后进先出的原则。
当声明一个变量/常量时,其在其方法的栈区中被存放:
- public static void main(String[] args){
- int num = 5;
- double d = 3.1;
- aMethod()
- }
- void aMethod(){
- int num = 3;
- double d = 4.1;
- }
我想现在大家可以明白为什么递归过多会栈内存溢出了。每一次递归都会分配一个栈区,所以会越来越卡。
这个概念就先讲到这里。
本章小结:
- Java把内存规划成栈内存和堆内存
- 当一个方法被调用时,栈内存会给方法分配一个栈区
- 变量/常量都在其方法的栈区中存放
- 栈内存是有序的
[groupid=546]Command Block Logic[/groupid]