关联博客:https://www.jianshu.com/p/805e7c967f09
方法
建议一个java源文件当中只定义一个Class
1. 方法的本质:
- 方法就是一个段代码片段,并且这段代码片段可以完成某个特定功能,可以重复使用。
- 方法,对应的英语单词:Method;方法在C语言中叫作函数/Function
- 方法定义在类体当中,在一个类当中可以定义多个方法,方法编写的位置没有先后顺序
2. 方法体
- 方法体中不能定义方法!!!
- 方法体必须由大括号括起来
- 方法体由java语句构成,方法体当中的代码遵守自上而下的顺序依次执行。
3. 方法的语法结构:
[修饰符列表] 返回值类型 方法名(形式参数列表){
- 方法体;
}
3.1 关于修饰列表
- 可选项,不是必须的
- 目前统一写成:public static 【以后讲】
- 方法的修饰符列表当中有static关键字的话,怎么调用
- 类名、方法名(实际参数列表)
3.2 返回值类型
-
什么是返回值?
- 一个方法是可以完成某个特定功能的,这个功能执行之后大多数是需要返回最终的执行结果的,执行结果可能是一个具体存在的数据。而这个具体存在的数据就是返回值。
-
什么返回值类型?
- 返回值是一个具体存在的数据,数据都是有类型的,此处需要指定的是返回值的具体类型
-
返回值类型都可以指定哪些类型呢?
- java任意一种类型都可以,包括基本数据类型和所有的引用数据类型。
-
也有可能这个方法执行之后不返回任何数据,java中规定,当一个方法执行结束之后不返回任何数据是,返回值类型位置必须编写:void关键字。
-
返回值类型若不是void,表示这个方法执行结束之后必须返回一个具体的数值,当方法执行结束的时候没有返回任何数据,编译器报错。
3.3 方法名
- 只要是合法的标识符就行
- 方法名最好见名知意
- 方法名最好是动词
- 方法名
3.4 深入了解return语句
-
带有return关键字的java语句只要执行,所在的方法执行结束。
-
在“同一作用域”中,return语句下面不能编写任何代码,因为这些代码永远执行不到。
public class HelloWorld {
public static void main(String[] args) {//表示定义一个公开的静态的主方法 ps:程序执行入口
/* 方法体 */
eFor();
}
public static void eFor(){
for (int a = 1 ;a <= 10; a++){
if (a==3){
return;// 不是终止for循环,终止的是eFor()方法。
// break;//终止for执行
}
System.out.println(a);
}
System.out.println(“Hello World”);
}
}
4. 方法怎么调用?
- 方法只定义不去调用是不会执行的,只有在调用的时候才会执行。
- 方法的修饰符列表中有static关键字, 完整的调用方式是:
-
类名.方法名(实参列表);
- 有的时候"类名."可以省略,什么情况下可以省略?
- 方法在同一个类体当中时,调用时"类名."可以省略不写.
- 方法不再同一类体当中时,调用时要用"类名.方法名".
5. 形式参数列表:简称形参
-
形参是局部变量
-
形参的个数可以是:0-n个 ;多个形参之间用"逗号"隔开。
-
形参中起决定性作用的是形参的数据类型,形参的名字就是局部变量的名字。
-
方法在调用的时候,实际给这个方法传递的真实数据被称为:实际参数,简称实参
-
实参列表和形参列表: 数量相同 类型对应相同
例如:
public static int sumInt(int a,int b) { // int a,int b 是形参列表
}
方法调用时:
sumInt(“abc”,“efg”);//编译报错 不是int类型
sumInt(10,15); // (10,15) 是实参列表
demo代码示例:
public class HelloWorld {
public static void main(String[] args) {//表示定义一个公开的静态的主方法 ps:程序执行入口
/* 方法体 */
// 调用sumInt方法进行求和
HelloWorld.sumInt(12,22);
// 当前类的方法,可以省略"类名."
sumInt(10,20);
// 调用其他类的方法 必须要加"类名."
TestA.doOther();
//调用TestA.a_sumInt方法 并赋值给sunNum
int sunNum = TestA.a_sumInt(12,24);
System.out.println(sunNum);
}
// 单独定义一个方法
// 该方法完成两个int类型数据的求和 ,并输出
public static void sumInt(int a,int b){
System.out.println(a + "+" + b + "=" + (a+b) );
}
}
class TestA{
public static void doOther(){
System.out.println("testA a doOther");
}
// 单独定义一个方法
// 该方法完成两个int类型数据的求和,最终计算结果返回给调用者
public static int a_sumInt(int a,int b){
return a + b;
}
}
6. 方法执行内存分析:
方法在执行过程当中,在jvm中的内存是如何分配的呢,内存是如何变化的?
-
方法只定义不调用,是不会执行的,并在jvm中也不会给该方法分配"运行所属"的内存空间,只有在调用这个方法时,才会动态的给这个方法分配所属的内存空间。
-
在jvm内存划分上有三块主要的内存空间(除了这三个之外还有别的内存空间)
- 方法区内存
- 堆内存
- 栈内存
-
关于栈数据结构
- 栈:stack,是一种数据结构
- 先进后出,后进先出
- 数据结构反应的是数据的存储形态
- 数据结构是独立的学科,不属于任何编程语言的范畴,只不过在大多数编程语言当中要使用数据结构。
- 作为程序员需要提前精通:数据结构 + 算法【计算机专业必修一门课程】
- java程序员在不精通数据结构和算法的前提下,也可以进行java开发,因为java有一套庞大的类库支撑, 别人写好了,直接用。【javaSE当中的集合章节使用了大量的数据结构】
- 常见的数据结构:
- 数组
- 队列
- 栈
- 链表
- 二叉树
- 哈希表/散列表
- …
-
方法执行时候的代码片段存在哪里?方法执行的时候执行过程的内存在哪里分配?
- 方法代码片段属于.Class字节码文件的一部分,字节文件在类加载的时候,将其放到了方法区当中; 所以jvm中的三块主要的内容空间中方法区内存最先有数据,存放了代码片段。
- 代码片段虽然在方法区内存当中只有一份,但是可以被重复调用。 每次调用这个方法时,需要给该方 法分配独立的活动场所,在栈内存中分配。【栈内存中分配方法运行的所属内存空间】
-
方法在调用的瞬间,会给该方法分配独立的内存空间,会在发生压栈动作;方法执行结束之后,给该方法分配的内存空间全部释放,此时发生弹栈动作。
- 压栈:给方法分配内存
- 弹栈:释放该方法的内存空间
-
局部变量在"方法体"声明,局部变量在运行阶段内存在栈中分配。
7. 方法重载
-
方法重载有被称为:overload
-
什么考虑使用方法重载?
- 功能相似的时候,方法名可以相同。
- 功能不同的时候,尽可能让这两个方法名不同
-
什么条件之后构成了方法重载
- 在同一类当中
- 方法名相同
- 参数列表不同:
- 数量不同
- 类型不同
- 顺序不同
-
方法重载和什么有关系,和什么没关系?
- 方法重载和方法名+参数列表有关系
- 方法重载和返回值类型无关
- 方法重载和修饰符列表无关
-
方法重载机制的优点
- 程序调用方法的时候,比较方便,虽然调用的是不同的方法,但是感觉在使用一个方法一样
- 不需要记忆更多的方法名。
- 代码美观
demo代码示例:
public class HelloWorld {
public static void main(String[] args) {//表示定义一个公开的静态的主方法 ps:程序执行入口
// 调用方法的时候就像在使用一个方法一样
// 参数类型不同,对应调用的方法不同。
// 此时区分方法不再依靠方法名了,依靠的是参数的数据类型。
System.out.println(sum(1,3));
System.out.println(sum(1.0,3.0));
System.out.println(sum(1l,3l));
}
// 以下三个方法构成了方法重新机制
public static int sum(int a,int b){
return a + b;
}
public static long sum(long a,long b){
return a + b;
}
public static double sum(double a,double b){
return a + b;
}
}
8. 方法的递归调用
- 什么是递归 ?
- 方法自身调用自身
- 递归是很耗费栈内存的,递归算法可以不用的时候尽量不要用。
- 以下程序运行的时候发生了一个这样的的错误【不是异常,是错误Error】
- java.lang.StackOverflowError 栈内存溢出错误。
- 错误发生无法挽回,只有一个结果,就是jvm停止工作。
- 递归必须有结束条件,没有结束条件一定会发生栈内存溢出错误。
- 递归即使有结束条件,结束条件是正确的,也有可能发生栈内存溢出错误,因为递归的太深了。
demo代码示例:
public class HelloWorld {
static int count = 5;
public static void main(String[] args) {//表示定义一个公开的静态的主方法 ps:程序执行入口
// 调用doSome
System.out.println("doSome begin");
doSome();
System.out.println("doSome over");
}
// 以下的代码片段虽然只有一份
// 但可以重复调用,并且只要调用doSome方法就会在栈内存中分配一块所属内存空间。
public static void doSome(){
System.out.println("doSome begin");
if (count-- == 0) return; //递归结束条件 如果注释会报:java.lang.StackOverflowError 栈内存溢出错误。
doSome();//这行代码不结束,下一行程序是不能执行的
System.out.println("doSome over");
}
}
demo代码示例2:
/**方法的递归调用
使用递归计算1-n的求和
5--> 5 + 4 + 3 + 2 + 1 = 求和
*/
public class HelloWorld {
public static void main(String[] args) {//表示定义一个公开的静态的主方法 ps:程序执行入口
// 调用doSome
//控制台输出 调用方法
System.out.print( doSome(5));
}
public static int doSome(int x){
if (x == 1) {
System.out.print( x + " = " );
return 1 ;
}else {
System.out.print( x + " + ");
}
return x + doSome(x-1);
}
}