JavaSE教程_基础
以上内容中讲解了方法是什么,怎么定义,怎么调用,目前来说大家实际上掌握这些内容就行了,接下来的内容大家尽量去学,实在是掌握不了,也没有关系,后期的内容会对这一部分的知识点进行不断的讲解,慢慢的大家就会了,其实在学习编程的过程中会遇到很多这样的情况,没事,大家不要心急,学习后面内容的过程中你会对前面的内容豁然开朗。
以下要讲解的是程序的内存,例如:代码片段被存储在什么位置?方法调用的时候,在哪里开辟内存空间等等。所以这一部分内容还是很高端大气上档次的。不过话又说回来,要想真正掌握java,内存的分析是必要的。一旦掌握内存的分配,在程序没有运行之前我们就可以很精准的预测到程序的执行结果。
好了,接下来我们开始学习方法执行过程中内存是如何变化的?我们先来看一张图片:
图7-9:JVM内存结构图
上图是一张标准的java虚拟机内存结构图,目前我们只看其中的“栈”和“方法区”,其它的后期研究,方法区中存储类的信息,或者也可以理解为代码片段,方法在执行过程中需要的内存空间在栈中分配。java程序开始执行的时候先通过类加载器子系统找到硬盘上的字节码(class)文件,然后将其加载到java虚拟机的方法区当中,开始调用main方法,main方法被调用的瞬间,会给main方法在“栈”内存中分配所属的活动空间,此时发生压栈动作,main方法的活动空间处于栈底。
也就是说,方法只定义不去调用的话,只是把它的代码片段存储在方法区当中,java虚拟机是不会在栈内存当中给该方法分配活动空间的,只有在调用的瞬间,java虚拟机才会在“栈内存”当中给该方法分配活动空间,此时发生压栈动作,直到这个方法执行结束的时候,这个方法在栈内存中所对应的活动空间就会释放掉,此时发生弹栈动作。由于栈的特点是先进后出,所以最先调用的方法(最先压栈)一定是最后结束的(最后弹栈)。比如:main方法最先被调用,那么它一定是最后一个结束的。换句话说:main方法结束了,程序也就结束了(目前来说是这样)。
接下来我们来看一段代码,同时画出内存结构图,以及使用文字描述该程序的内存变化:
public class MethodTest {
public static void main(String[] args) {
System.out.println("main begin");
m1();
System.out.println("main over");
}
public static void m1() {
System.out.println("m1 begin");
m2();
System.out.println("m1 over");
}
public static void m2() {
System.out.println("m2 begin");
System.out.println("m2 over");
}
}
运行结果如下图所示:
图7-10:方法执行过程中内存变化测试程序
通过上图的执行结果我们了解到,main方法最先被调用,但是它是最后结束的,其中m2方法最后被调用,但它是最先结束的。大家别忘了调用的时候分配内存是压栈,结束的时候是释放内存弹栈哦。为什么会是上图的结果呢,我们来看看它执行的内存变化,请看下图:
图7-11:方法执行过程中内存的变化
通过上图的分析,可以很快明白,为什么输出结果是这样的顺序,接下来我们再采用文字的方式描述它的内存变化:
● 类加载器将class文件加载到方法区。
● 开始调用main方法,在栈内存中给main方法分配空间,开始执行main方法,输出”main begin”。
● 调用m1()方法,在栈内存中给m1()方法分配空间,m1()方法处于栈顶,具备活跃权,输出”m1 begin”。
● 调用m2()方法,在栈内存中给m2()方法分配空间,m2()方法处于栈顶,具备活跃权,输出”m2 begin”,继续输出”m2 over”。
● m2()方法执行结束,内存释放,弹栈。
● m1()方法这时处于栈顶,具备活跃权,输出”m1 over”。
● m1()方法执行结束,内存释放,弹栈。
● main()方法这时处于栈顶,具备活跃权,输出”main over”。
● main()方法执行结束,内存释放,弹栈。
● 栈空了,程序结束。
大家是否还记得之前的课程中曾经提到方法体当中的代码是有执行顺序的,必须遵循自上而下的顺序依次逐行执行,当前行代码必须执行结束之后,下一行代码才能执行,不能跳行执行,还记得吗?现在再和栈数据结构一起联系起来思考一下,为什么要采用栈数据结构呢?是不是只有采用这种先进后出的栈数据结构才可以保证代码的执行顺序呢!此时,你是不是感觉程序的设计者在此处设计的非常巧妙呢!