方法
- 简述
- 方法的本质
方法就是一段代码片段,并且这段代码片段可以完成某个特定的功能,并且可以被重复的使用。 - 方法,对应的英语单词:Method,方法在c语言中叫做函数/Function
- 方法定义在类体当中,在一个类当中可以定义多个方法,方法编写的位置没有先后顺序,可以随意。
- 方法体当中不能再定义方法
- 方法体当中由java语句构成,方法体中的代码遵守由上而下的顺序依次执行。
- 方法的本质
- 1.方法定义,语法结构
【修饰符列表】返回值类型 方法名【形式参数列表】{
方法体;
} - 2.对以上语法进行解释说明
- 2.1关于修饰符列表
- 是可选项,不是必须的
- 目前统一写成:public static【以后讲】
- 方法的修饰符列表当中“有static关键字”的话,怎么调用这个方法?
类名.方法名(实际参数列表)
- 2.2返回值类型
- 什么是返回值?
一个方法是可以完成某个特定功能的,这个功能结束之后大多数都是需要返回最终执行结果的,执行结果可能是一个具体存在的数据。而这个具体存在的数据就是返回值。 - 返回值类型
返回值是一个具体存在的数据,数据都是有类型的,此处需要指定的是返回值的具体类型。 - 返回值都可以指定哪些类型呢?
java任意一种类型都可以,包括基本数据类型和所有的引用数据类型。 - 也可能这个方法执行结束之后不返回任何数据,java中规定,当一个方法执行结束之后不返回任何数据的话,返回值类型的位置需要写:void关键字
- 返回值类型若不是void,表示这个方法执行结束返回一个具体的数值,当方法执行结束的时候没有返回任何数据的话编译器报错,怎么返回值呢,代码怎么写呢?“return 值;”,并且要求“值”的数据类型必须和"方法的返回值类型"一致。不然编译器报错。
- 返回值类型是void的时候,在方法体当中不能编写"return 值;"这样的语句。但是注意可以编写"return;"这样的语句。
- 只要带有return关键字的语句执行,return语句所在的方法结束。【不是JVM结束,是所在的方法结束】
- 什么是返回值?
- 2.3方法名
- 只要是合法的标识符就行
- 方法名最好见名知意
- 方法名最好是动词
- 方法名首字母要求小写,后面每个单词首字母大写
- 2.4形式参数列表:简称形参
- 形参是局部变量
- 形参的个数可以是:0-N个
- 多个形参之间用“逗号”隔开
- 形参中起决定性作用的是形参的数据类型,形参的名字就是局部变量的名字。
- 方法在调用的时候,实际给这个方法传递的真实数据被称为:实际参数,简称实参。
- 2.5 方法体必须由大括号括起来,方法体当中的代码有顺序,遵循自上而下的顺序依次执行。并且方法体由java语句构成,每一个java语句以";"结尾。
- 2.1关于修饰符列表
- 3.方法怎么调用呢?
方法只定义不调用时不会执行的,只有在调用的时候才会执行。
语法规则:《方法的修饰符列表当中有static》
类名.方法名(实参列表);<这是一条java语句,表示调用的某个类型的某个方法,传递这样的实参。>
/*
方法的调用不一定在main方法当中,可以在其他方法当中
只要是程序可以执行到的位置,都可以去调用其他方法
*/
pubilc class MethodTest04
{
public static void sum(int a,int b){
System.out.println(a + "+" + b + "=" + (a+b));
//调用doSome方法
MethodTest04.doSome();//允许
}
//主方法,程序入口
public static void main(String[] args){
MethodTest04.sum(1,2);
}
public static void doSome(){
System.out.println("do some!");
}
}
/*
实参和形参要求个数相同,数据类型对应相同
*/
public class MethodTest04
{
public static void main(String[] args){
//编译通过,存在自动类型转换
MethodTest04.sum(10,20);
//编译错误,double为大容量,需要强制类型转换符
MethodTest04.sum(3.0,20);
MethodTest04.sum((long)3.0,20);//编译通过
}
public static void sum(long a,long b){
System.out.println(a + "+" + b + "=" + (a+b));
}
}
- 方法调用省略"类名."
方法的修饰符列表当中有static关键字,完整的调用方式是:类名.方法名(实参列表);
但是,有的时候"类名."可以省略,什么情况下可以省略呢?
m1(){
m2();
}
m1方法和m2方法在同一个类当中的时候,"类名."可以省略不写
**建议一个java文件中定义一个class,比较清晰**
public class MethodTest04
{
public static void main(String[] args){
MethodTest04.m();//可以
m();//可以
//调用其他类中的方法【不是本类中的】
A.doOther();//可以
//“类名.”省略后,默认从当前类中找到"doOther"方法,在当前类中该方法不存在
doOther();//不可以
}
public static void m{
System.out.println("m method execute!");
MehtodTest04.m2();//完整的方式
m2();//省略的方式,调用本类的m2()方法
A.m2();//调用A类中的m2()方法
}
public static void m2{
System.out.println("m2 method execute!");
}
}
class A
{
public static void doOther{
System.out.println("A's doOther method invoke!");
}
public static void m2{
System.out.println("A'm2 method execute!");
}
}
``
```java
/*
返回值:
采用变量接收,变量的数据类型和返回值的数据类型相同,或者可以自动类型转换
*/
public class MethodTest05
{
public static void main(String[] args){
//boolean b = divide(10,3);//编译报错,类型不兼容
long m = divide(10,3);//编译通过,自动类型转换
int i = divide(10,3);//编译通过
System.out.println(divide(10,3));
}
public static int divide(int a,int b){
return a / b;
}
}
```java
/*
深入return语句
*带有return关键字的java语句只要执行,所在的方法执行结束
*在“同一个作用域”当中,return语句下面不能编写任何代码,因为这些代码永远都执行不到,所以编译报错。
*/
public class MethodTest09
{
public static void main(String[] args){
]
//编译报错,缺少返回语句,以下程序编译器认为无法百分百保证"return 1"会执行
/*
public static int m(){
int a = 10;
if(a > 3){//编译器只会识别到"a > 3"是boolean类型,但不会得到true或者false
return 1;
}
*/
//以下程序可以保证"return 1;"或者"return 0;"执行,编译通过。
/*public static int m(){
int a = 10;
if(a > 3){
return 1;
}else{//也可以将else{}去掉
return 0;
}*/
/*public static int m(){
int a = 10;
if(a > 3){
return 1;
//这里不能编写代码,编译错误,因为无法访问的语句
//System.out.println("Hello!");
}
//这里的代码可以执行到
System.out.println("Hello!");
return 0;
//编译错误,无法访问的语句
//System.out.println("Hello!");
}*/
public static int m(){
return 10 > 3 ? 1 : 0;
}
}
/*
返回值类型是void的方法当中使用"return;"语句.
"return;"语句出现在返回值为void方法当中主要是为了结束当前方法
*/
public class MethodTest09
{
public static void main(String[] args){
m();
for(int i = 10;i > 0;i --){
if(i == 2){
return;//结束的是main方法
}
System.out.println("data:-->" + i);
}
System.out.println("Hello World!");
}
public static void m(){
for(int i = 0;i < 10;i ++){
if(i == 5){
return;//不是终止for循环,而是终止main方法
//break;//终止的for循环
}
System.out.pritnln("i -->" + i);
}
System.out.println("Hello World!");
}
}
方法内存分配
- 方法在执行过程当中,在JVM中的内存是如何分配的呢,内存是如何变化的?
- 方法如果只定义,不去调用,是不会执行的,并且在JVM 当中也不会给该方法分配"运行所属"的内存空间。只有调用该方法时,才会动态的给这个方法分配所属的内存空间。
- 在JVM内存分配划分上有这样三块主要的内存空间:
- 方法区内存
- 堆内存
- 栈内存
- 关于栈数据结构
- 栈:stack,是一种数据结构
- 数据结构反映的是数据存储形态
- 数据结构是独立的学科,不属于任何编程语言的范畴,只不过在大多数编程语言当中要使用数据结构。
- 作为程序员需要提前精通,数据结构 + 算法
- java程序员在不精通数据结构和算法的前提下,也能进行java开发,因为java有一套庞大的类库支持,别人写好了,直接用。【JavaSE当中的集合章节使用了大量的数据结构】
- 常见的数据结构:
- 数组
- 队列
- 栈
- 链表
- 二叉树
- 哈希表/散列表
- …
- 栈的示意图
- 方法代码片段存在哪里?方法执行的时候执行过程的内存在哪里分配?
- 方法代码片段属于.class文件的一部分,字节码文件在类加载的时候,将其放到了方法区当中,所以JVM中的三块主要的内存空间中方法区内存最先有数据,存放了代码片段。
- 代码片段虽然在方法区内存当中只有一份,但是可以被重复调用。每一次调用这个方法的时候,需要给该方法分配独立的活动场所,在栈内存中分配。【栈内存中分配方法运行的所属内存空间】
- 方法在调用的瞬间,会给该方法分配内存空间,会在栈中发生压栈动作,方法执行结束之后,给该方法分配的内存空间会全部释放,此时发生弹栈动作。
- 压栈:给方法分配内存
- 弹栈:释放该方法的内存空间
- 局部变量在“方法体”中声明。局部变量在运行阶段内存在栈中分配。
//**重点:方法调用的时候,在参数传递的时候,实际上传递的是变量中保存的那个“值”。**
// 注意:在EditPlus当中,字体颜色为红色的表示一个类的名字,并且这个类是JavaSE类库中自带的。
public clsss MethodTest01
{
public static void main(String[] args){
int a = 10;
int b = 20;
int retValue = sumInt(a,b);
System.out.println("retValue = " + retValue);
}
public static int sumInt(int i,int j){
int result = i + j;
int num = 3;
int retValue = divide(result,sum);
return retValue;
}
public static int divide(int x,int y){
int z = x / y;
return z;
}
}
上述代码的运行内存分配图如下:
- 画图原则:
1.只要涉及到参数传递的问题,传递的是变量中保存的值
2.画图的时候,必须遵循的方法自上而下的顺序依次执行
方法重载
- 引用,以下代码如果不引入“方法重载机制”,不适用overload,分析程序存在哪些缺陷?
public class MethodTest03
{
public static void main(String[] args){
//调用方法
int result1 = sumInt(1,2);
System.out.println("result1 -->" + result1);
double result2 = sumDouble(1.0,2.0);
System.out.println("result2 -->" + result2);
long result3 = sumLong(1L,2L);
System.out.println("result3 -->" + result3);
}
//定义一个方法,可以计算两个int类型数据的和
public static int sumInt(int a,int b){
return a + b;
}
//定义一个方法,可以计算两个double类型数据的和
public static double sumDouble(double a,double b){
return a + b;
}
//定义一个方法,可以计算两个long类型数据的和
public static long sumLong(long a,long b){
return a + b;
}
}
- 分析:
- 分析1:sumInt、sumLong、sumDouble方法虽然功能不同,但是功能是有限的,都是求和。在以下程序当中功能相似的方法,分别起了三个不同的名字,这对于程序员来说,调用方法的时候不方便,程序员需要记忆更多的方法,才能完成调用。【不方便】
- 分析2:代码不美观
- 有没有这样的一种机制:
- 功能虽然不同,但是功能相似的时候,有没有这样的一种机制,可以让程序员使用这些方法的时候就像在使用同一个方法一样,这样程序员以后编写代码比较方便,也不需要记忆更多的方法名,代码也会很美观。
- 有这种机制,方法重载机制/Overload
- 最终希望达到的效果是,程序员在使用上面的三个相似的方法的时候,就像在用一个方法一样。
- Java支持这种机制【有些语言不支持,例如以后要学习的:javascript】
/*
该方法还是一个体验程序,体验一下方法重载的优点:
*程序员调用方法的时候,比较方便,虽然调用的是不同的方法,但是就感觉在使用同一个方法一样,不需要
记忆更多的方法名。
*代码美观
前提:
功能相似的时候,方法名可以相同。
但是,功能不同的时候,尽可能让这两个方法的名字不同。
*/
public class OverlaodTest01
{
public static void main(Stirng[] args){
//调用方法的时候就像在使用一个方法一样;
//参数的类型不同,对应调用的方法不同;
//此时区分方法不再依赖方法名了,依靠的是参数的数据类型
System.out.println(sum(1,2));
System.out.println(sum(1.0,2.0));
System.out.println(sum(1L,2L));
}
//以下三个方法构成了方法重载机制
public static int sum(int a,int b){
System.out.println("sum(int,int)");
return a + b;
}
public static long sum(long a,long b){
System.out.println("sum(long,long)");
return a + b;
}
public static double sum(double a,double b){
System.out.println("sum(double,double)");
return a + b;
}
}
- 正式讲述方法重载
/*
方法重载:
1.方法重载又被称为:overload
2.什么时候考虑使用方法重载?
*功能相似的时候,尽可能让方法名相同
【但是:功能不同/不相似的时候,尽可能让方法名不同】
3.什么条件满足之后构成了方法重载?
*在同一个类当中
*方法名相同
*参数列表不同:
-数量不同
-顺序不同
-类型不同
4.方法重载和什么有关系,和什么没关系?
*方法重载和方法名+参数列表有关系
*方法重载和返回值类型无关
*方法重载和修饰符列表无关
*/
public class OverloadTest03
{
public static void main(String[] args){
m1();
m1(10);
m2(1,2.0);
m2(2.0,1);
m3(10);
m3(3.0);
}
//以下方法构成方法重载
public static void m1(){}
public static void m1(int a){}
//以下方法构成方法重载
public static void m2(int a,double b){}
public static void m2(double a,int b){}
//以下方法构成方法重载
public static void m3(int x){}
public static void m3(double x){}
//以下方法不能构成方法重载,为方法重复
/*
public static void m4(int a,int b){}
public static void m4(int b,int a){}
*/
/*返回值类型
public static void x(){
}
public static int x(){
return 1;
}//注意可以不接收返回值
*/
/*修饰符列表
void y(){}
public static void y(){}
*/
}
- 方法重载的应用
public class OverloadTest04
{
public static void main(String[] args){
/*
System.out.println("Hello World!");
System.out.println(10);
System.out.println(true);
*/
U.p(10);
U.p(fasle);
U.p("Hello World!");
U.p(3.0);
}
}
class U
{
public static void p(byte b){
System.out.println(b);
}
public static void p(short b){
System.out.println(b);
}
public static void p(int b){
System.out.println(b);
}
public static void p(long b){
System.out.println(b);
}
public static void p(float b){
System.out.println(b);
}
public static void p(double b){
System.out.println(b);
}
public static void p(boolean b){
System.out.println(b);
}
public static void p(char b){
System.out.println(b);
}
public static void p(String b){
System.out.println(b);
}
}
方法递归
初认识
- 1.什么是递归?
方法自身调用自身
a(){
a();
} - 2.递归是很耗费栈内存的,递归算法可以不用的时候尽量不用
- 3.如果递归没有设置结束条件,程序就会报如下错误:【不是异常,是错误error】
java.lang.StackOverflowError
栈内存溢出错误
错误无法发生挽回,只有一个结果,就是JVM停止工作 - 4.递归必须有结束条件,没有结束条件一定会发生栈内存出错误。
- 5.递归即使有了结束条件,即使结束条件是正确的,也可能会发生栈内存出错误,因为递归的太深了。
/*
使用递归计算1-N的求和
--> 1 + 2 + 3 + 4
--> 4 + 3 + 2 + 1 : n的最初值是4,建议采用这种方式
*/
public class RecursionTest03
{
public static void main(String[] args){
//1-4的和
int n = 4;
int RetValue = sum(n);
System.out.println(RetValue);
}
public static int sum(int n){
//4 + 3 + 2 + 1
if(n == 1){
return 1;
}
return n + sum(n-1);
}
}
- 上述递归代码运行内存示意图
- 递归原理图