本篇文章主要内容:对象的创建和使用
Java虚拟机内存管理
学习目标
理解构造方法以及重载机制,通过构造方法可以完成对象的创建,并且能够通过引用访问对象的内存,了解Java虚拟机内存管理,能够画出程序执行过程的内存图,并了解空指针异常是如何发生的,以及方法调用时参数是如何传递的。
知识框架
对象的创建和使用
对象的创建
类定义之后,就可以使用类这个“模板”来创造“对象”了,一个类是可以创建多个对象的哦!怎么创建呢,语法是什么?其实语法格式很简单:new类名(),这样就可以完成对象的创建了。俗话说,你想要什么java都可以给你,想要啥你就new啥。请看下面代码:
为了使用对象更加方便,建议使用变量接收一下?例如以下代码:
以上代码最初接触的时候,大家肯定会感觉非常陌生,这也是正常的,Students1=new Student()实际上和int i=10是类似的,对于int i=10来说,int是一种基本数据类型,i是变量名,10是int类型的字面量。那对于Students1=new Student()来说,其中Student是一种引用数据类型,s1是变量名,newStudent()执行之后是一个Student类型的对象。
大家要注意了,java语言当中凡是使用class关键字定义的类都属于引用数据类型,类名本身就是这种引用数据类型的类型名。
对象的使用
创建了对象之后怎么去访问这个对象的属性呢,或者说学生对象现在有了,怎么去访问他的学号、姓名、性别、年龄等信息呢。请看以下代码:
运行结果如下图所示:对象的创建和使用
接下来解释一下以上的输出结果,通过以上的Student类可以创建很多学生对象,假设通过Student类实例化了两个学生对象,那必然会有两个不同的学号,以上程序中并没有给学号赋值,但是获取了到的学号都是0,这是怎么回事呢?
这是因为在java语言当中,当实例变量没有手动赋值,在创建对象的时候,也就是说在new的时候,系统会对实例变量默认赋值,它们的默认值请参考下表:
对象创建和使用的深层次解密
java虚拟机内存管理
为了更好的理解上面的程序,先来看看java 虚拟机是如何管理它的内存的,请看下图:java 虚拟机内存管理
① 程序计数器:
1)概念:可以看做当前线程所执行的字节码的行号指示器。
2)特点:线程私有的内存
②java虚拟机栈(重点):
1)概念:描述的是java方法执行的内存模型。(每个方法在执行的时候会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每个方法从调用直至完成的过程,就对应一个栈帧从入栈到出栈的过程。)
2)特点: 线程私有, 生命周期和线程相同。 这个区域会出现两种异常:StackOverflowError异常: 若线程请求的深度大于虚拟机所允许的深度。OutOfMemoryError异常:若虚拟机可以动态扩展,如果扩展是无法申请到足够的内存。
③ 本地方法栈:
1)概念:它与虚拟机栈所发挥的作用是相似的,区别是java虚拟机栈为执行java方法服务,而本地方法栈是为本地方法服务。
2)特点:线程私有,也会抛出两类异常:StackOverflowError和OutOfMemoryError。
④ java堆(重点):
1)概念:是被所有线程共享的一块区域,在虚拟机启动时创建。
2)特点:线程共享,存放的是对象实例(所有的对象实例和数组),GC管理的主要区域。可以处于物理上不连续的内存空间。
⑤ 方法区(重点):
1)概念:存储已被虚拟机加载的类信息、常量、静态变量,即时编译器编译后的代码等数据。
2)特点:线程共享的区域,抛出异常OutOfMemory异常:当方法区无法满足内存分配需求的时候。
以上所描述内容,有看得懂的,也有看不懂的,例如:线程、本地方法等,这个需要大家在学习后面内容之后,返回来再看一看,那个时候你就全部明白了。针对于目前来说,大家必须要知道java虚拟机有三块主要的内存空间,分别是“虚拟机栈(后面简称栈)”、“方法区”、“堆区”,方法区存储类的信息,栈中存储方法执行时的栈帧以及局部变量,堆区中主要存储new出来的对象,以及对象内部的实例变量。其中垃圾回收器主要针对的是堆内存,方法区中最先有数据,因为程序执行之前会先进行类加载。栈内存活动最频繁,因为方法不断的执行并结束,不断的进行压栈弹栈操作。
将目前阶段需要掌握的内存空间使用一张简单的图表示出来,这个图是大家需要掌握的:java 虚拟机内存管理简图
大概了解了java虚拟机内存分配之后,来看看以下代码在执行过程中,内存是如何变化的:
以上代码在执行过程中内存的变化如下图所示:第一步进行类加载
第二步main 方法调用,给main 方法分配栈帧(压栈)
第三步执行int i = 10,局部变量
第四步执行new Student(),在堆中创建对象,同时初始化实例变量
第五步将堆区中学生对象的内存地址赋值给局部变量s1
注意:上图所描述内存图有些地方为了帮助大家更好的理解,有些位置画的不是很精确, 随着后面内容的学习我们再进一步修改,目前上图已经够大家用了。
上图中i变量和s1变量都是局部变量,都在栈内存当中,只不过i变量是基本数据类型int,而s1变量是引用数据类型Student。
上图中堆区当中的称为“对象”,该“对象”内部no、name、age、sex都是实例变量/属性,这些变量在new对象的时候初始化,如果没有手动赋值,系统会赋默认值。
上图堆区中“对象”创建完成之后,该对象在堆区当中的内存地址是:0x1111,程序中的“=”将0x1111这个堆内存地址赋值给s1变量,也就是说s1变量保存了堆内存对象的内存地址,我们对于这种变量有一种特殊的称呼,叫做“引用”。也就是说对于Students1=newStudent()代码来说,s1不是对象,是一个引用,对象实际上是在堆区当中,s1变量持有这个对象的内存地址。
java中没有指针的概念(指针是C语言当中的机制),所以java程序员没有权利直接操作堆内存,只能通过“引用”去访问堆内存中的对象,例如:s1.no、s1.name、s1.sex、s1.age。访问一个对象的内存,其实就是访问该对象的实例变量,而访问实例变量通常包括两种形式,要么就是读取数据,要么就是修改数据,例如:System.out.println(s1.no)这就是读取数据,s1.no= 100这就是修改数据。请看以下代码:
运行结果如下所示:修改实例变量之后的执行结果
执行了以上程序之后,堆内存对象的实例变量发生了变化,如下图所示:实例变量执行赋值运算之后的内存图
如果基于以上的代码再创建一个对象,内存图会是怎么的呢?先看代码:
JVM 内存结构图如下所示:创建多个对象的内存结构图
通过上图的学习,可以看出假设new出100个学生对象,会有100个no,100个age...是这样吧。
通过以上内容的学习,需要每位同学掌握:局部变量存储在哪里?实例变量存储在哪里?实例变量在什么时候初始化?对象和引用有什么区别?在java中怎么访问堆内存当中的对象?这些你都掌握了吗?
最后附Java零基础视频教程给大家,配合学习效果更佳!!