1. 方法(method、函数)的理解
方法
是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数
或过程
。- 将功能封装为方法的目的是,可以
实现代码重用,减少冗余,简化代码
- Java里的方法
不能独立存在
,所有的方法必须定义在类里。
2. 如何声明方法
1、声明方法的语法格式
[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]{
方法体的功能代码
}
(1)一个完整的方法 = 方法头 + 方法体。
-
方法头就是
[修饰符] 返回值类型 方法名([形参列表])[throws 异常列表]
,也称为方法签名
。通常调用方法时只需要关注方法头就可以,从方法头可以看出这个方法的功能和调用格式。 -
方法体就是方法被调用后要执行的代码。对于调用者来说,不了解方法体如何实现的,并不影响方法的使用。
(2)方法头可能包含5个部分 -
修饰符:可选的。方法的修饰符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面会一一学习。
- 其中,权限修饰符有public、protected、private。在讲封装性之前,我们先默认使用pulbic修饰方法。
- 其中,根据是否有static,可以将方法分为静态方法和非静态方法。其中静态方法又称为类方法,非静态方法又称为实例方法。咱们在讲static前先学习实例方法。
-
返回值类型: 表示方法运行的结果的数据类型,方法执行后将结果返回到调用者。
- 无返回值,则声明:void
- 有返回值,则声明出返回值类型(可以是任意类型)。与方法体中“
return 返回值
”搭配使用
-
方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”
-
形参列表:表示完成方法体功能时需要外部提供的数据列表。可以包含零个,一个或多个参数。
- 无论是否有参数,()不能省略
- 如果有参数,每一个参数都要指定数据类型和参数名,多个参数之间使用逗号分隔,例如:
- 一个参数: (数据类型 参数名)
- 二个参数: (数据类型1 参数1, 数据类型2 参数2)
- 参数的类型可以是基本数据类型、引用数据类型
-
throws 异常列表:可选,在【第09章-异常处理】章节再讲
(3)方法体:方法体必须有{}括起来,在{}中编写完成方法功能的代码
(4)关于方法体中return语句的说明: -
return语句的作用是结束方法的执行,并将方法的结果返回去
-
如果返回值类型不是void,方法体中必须保证一定有 return 返回值; 语句,并且要求该返回值结果的类型与声明的返回值类型一致或兼容。
-
如果返回值类型为void时,方法体中可以没有return语句,如果要用return语句提前结束方法的执行,那么return后面不能跟返回值,直接写return ; 就可以。
-
return语句后面就不能再写其他代码了,否则会报错:Unreachable code
补充:方法的分类:按照是否有形参及返回值
2、类比举例
3、代码示例:
package com.atguigu.test04.method;
/**
* 方法定义案例演示
*/
public class MethodDefineDemo {
/**
* 无参无返回值方法的演示
*/
public void sayHello(){
System.out.println("hello");
}
/**
* 有参无返回值方法的演示
* @param length int 第一个参数,表示矩形的长
* @param width int 第二个参数,表示矩形的宽
* @param sign char 第三个参数,表示填充矩形图形的符号
*/
public void printRectangle(int length, int width, char sign){
for (int i = 1; i <= length ; i++) {
for(int j=1; j <= width; j++){
System.out.print(sign);
}
System.out.println();
}
}
/**
* 无参有返回值方法的演示
* @return
*/
public int getIntBetweenOneToHundred(){
return (int)(Math.random()*100+1);
}
/**
* 有参有返回值方法的演示
* @param a int 第一个参数,要比较大小的整数之一
* @param b int 第二个参数,要比较大小的整数之二
* @return int 比较大小的两个整数中较大者的值
*/
public int max(int a, int b){
return a > b ? a : b;
}
}
3. 如何调用实例方法
方法通过方法名被调用,且只有被调用才会执行。
方法调用语法格式
对象.方法名([实参列表])
4. 使用的注意点
(1)必须先声明后使用,且方法必须定义在类的内部
(2)调用一次就执行一次,不调用不执行。
(3)方法中可以调用类中的方法或属性,不可以在方法内部定义方法。
错误示例:
类{
方法1(){
方法2(){ //位置错误
}
}
}
5. 关键字return的使用
- return在方法中的作用:
- 作用1:结束一个方法
- 作用2:结束一个方法的同时,可以返回数据给方法的调用者
- 注意点:在return关键字的直接后面不能声明执行语句
6. 方法调用内存分析
- 方法
没有被调用
的时候,都在方法区
中的字节码文件(.class)中存储。 - 方法
被调用
的时候,需要进入到栈内存
中运行。方法每调用一次就会在栈中有一个入栈
动作,即给当前方法开辟一块独立的内存区域,用于存储当前方法的局部变量的值。 - 当方法执行结束后,会释放该内存,称为
出栈
,如果方法有返回值,就会把结果返回调用处,如果没有返回值,就直接结束,回到调用处继续执行下一条指令。 - 栈结构:先进后出,后进先出。
举例分析:
/**
* @author 尚硅谷-宋红康
* @create 9:21
*/
public class Person {
public static void main(String[] args) {
Person p1 = new Person();
p1.eat();
}
public static void eat() {
sleep();
System.out.println("人:吃饭");
}
public static void sleep(){
System.out.println("人:睡觉");
doSport();
}
public static void doSport(){
System.out.println("人:运动");
}
}
内存分析:
7. 可变个数的形参
在JDK 5.0 中提供了Varargs(variable number of arguments)机制。即当定义一个方法时,形参的类型可以确定,但是形参的个数不确定,那么可以考虑使用可变个数的形参。
格式:
方法名(参数的类型名 ...参数名)
举例:
//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);
//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String...books);
特点:
-
可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
-
可变个数形参的方法与同名的方法之间,彼此构成重载
-
可变参数方法的使用与方法参数部分使用数组是一致的,二者不能同时声明,否则报错。
-
方法的参数部分有可变形参,需要放在形参声明的最后
-
在一个方法的形参中,最多只能声明一个可变个数的形参
8. 方法的参数传递机制
8.1 形参和实参
-
形参(formal parameter):在定义方法时,方法名后面括号()中声明的变量称为形式参数,简称形参。
-
实参(actual parameter):在调用方法时,方法名后面括号()中的使用的值/变量/表达式称为实际参数,简称实参。
8.2 参数传递机制:值传递
Java里方法的参数传递方式只有一种:值传递
。 即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。
-
形参是基本数据类型:将实参基本数据类型变量的“数据值”传递给形参
-
形参是引用数据类型:将实参引用数据类型变量的“地址值”传递给形参
9. 递归(recursion)方法
递归方法调用:方法自己调用自己的现象就称为递归。
**递归的分类:**直接递归、间接递归。
- 直接递归:方法自身调用自己。
public void methodA(){
methodA();
}
- 间接递归:可以理解为A()方法调用B()方法,B()方法调用C()方法,C()方法调用A()方法。
public static void A(){
B();
}
public static void B(){
C();
}
public static void C(){
A();
}
说明:
-
递归方法包含了一种
隐式的循环
。 -
递归方法会
重复执行
某段代码,但这种重复执行无须循环控制。 -
递归一定要向
已知方向
递归,否则这种递归就变成了无穷递归,停不下来,类似于死循环
。最终发生栈内存溢出
。
举例:
举例1:计算1 ~ n的和
public class RecursionDemo {
public static void main(String[] args) {
RecursionDemo demo = new RecursionDemo();
//计算1~num的和,使用递归完成
int num = 5;
// 调用求和的方法
int sum = demo.getSum(num);
// 输出结果
System.out.println(sum);
}
/*
通过递归算法实现.
参数列表:int
返回值类型: int
*/
public int getSum(int num) {
/*
num为1时,方法返回1,
相当于是方法的出口,num总有是1的情况
*/
if(num == 1){
return 1;
}
/*
num不为1时,方法返回 num +(num-1)的累和
递归调用getSum方法
*/
return num + getSum(num-1);
}
}
代码执行图解:
举例2:递归方法计算n!
public int multiply(int num){
if(num == 1){
return 1;
}else{
return num * multiply(num - 1);
}
}
最后说两句:
递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环
慢的多
,所以在使用递归时要慎重。在要求高性能的情况下尽量避免使用递归,递归调用既花时间又
耗内存
。考虑使用循环迭代