Java零基础快速入门|对象的创建和使用(上)

本篇文章主要内容:
对象的创建和使用
Java虚拟机内存管理
学习目标
理解构造方法以及重载机制,通过构造方法可以完成对象的创建,并且能够通过引用访问对象的内存,了解 Java 虚拟机内存管理,能够画出程序执行过程的内存图,并了解空指针异常是如何发生的,以及方法调用时参数是如何传递的。
知识框架

对象的创建和使用
对象的创建
类定义之后,就可以使用类这个“模板”来创造“对象”了,一个类是可以创建多个对象的哦!怎么创建呢,语法是什么?其实语法格式很简单:new 类名(),这样就可以完成对象的创建了。俗话说,你想要什么java 都可以给你,想要啥你就new 啥。请看下面代码:

为了使用对象更加方便,建议使用变量接收一下?例如以下代码:

以上代码最初接触的时候,大家肯定会感觉非常陌生,这也是正常的,Student s1 = new Student()实际上和int i = 10 是类似的,对于 int i = 10 来说,int 是一种基本数据类型,i 是变量名,10 是int 类型的字面量。那对于 Student s1 = new Student()来说,其中Student 是一种引用数据类型,s1 是变量名,new Student()执行之后是一个Student 类型的对象。
大家要注意了,java 语言当中凡是使用 class 关键字定义的类都属于引用数据类型,类名本身就是这种引用数据类型的类型名。
对象的使用
创建了对象之后怎么去访问这个对象的属性呢,或者说学生对象现在有了,怎么去访问他的学号、姓名、性别、年龄等信息呢。请看以下代码:


运行结果如下图所示:

接下来解释一下以上的输出结果,通过以上的 Student 类可以创建很多学生对象,假设通过 Student 类实例化了两个学生对象,那必然会有两个不同的学号,以上程序中并没有给学号赋值,但是获取了到的学号都是0,这是怎么回事呢?
这是因为在java 语言当中,当实例变量没有手动赋值,在创建对象的时候,也就是说在 new 的时候,系统会对实例变量默认赋值, 它们的默认值请参考下表:

对象创建和使用的深层次解密
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 虚拟机内存分配之后,来看看以下代码在执行过程中,内存是如何变化的:

以上代码在执行过程中内存的变化如下图所示:





注意:上图所描述内存图有些地方为了帮助大家更好的理解,有些位置画的不是很精确, 随着后面内容的学习我们再进一步修改,目前上图已经够大家用了。
上图中i 变量和 s1 变量都是局部变量,都在栈内存当中,只不过i 变量是基本数据类型int, 而s1 变量是引用数据类型Student。
上图中堆区当中的称为“对象”,该“对象”内部 no、name、age、sex 都是实例变量/属性,这些变量在new 对象的时候初始化,如果没有手动赋值,系统会赋默认值。
上图堆区中“对象”创建完成之后,该对象在堆区当中的内存地址是:0x1111,程序中的“=”将0x1111 这个堆内存地址赋值给s1 变量,也就是说 s1 变量保存了堆内存对象的内存地址,我们对于这种变量有一种特殊的称呼,叫做“引用”。也就是说对于 Student s1 = new Student() 代码来说,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零基础视频教程给大家,配合学习效果更佳!!
