【NB】Java面向对象前瞻之内存分配

明明哦   ·   发表于 6个月前   ·   编程代码
踩着时间的脚跟走,终于码完字了,步入面向对象,Java有趣的部分也就到来了,面向对象描述预计5帖以上,本帖着重来介绍一下Java的内存机制,这是对面向对象一个很好的铺垫,相关联的还有一种新的数据类型:数组。
往期阅读:
#【NB】Java的数据类型#
#【NB】Java优雅的流程控制#
另外,高额悬赏任务也在进行中,欢迎解答:
#【悬赏1200葫芦】Java高级问题#
文档,源代码链接:https://shimo.im/docs/hYjTWWxqkgXQQkvW/
接下来,正文开始!
我们衡量一个语言是否为编程语言的标准其实很大一部分就是内存分配,我们在介绍变量的时候就提到过内存,知晓变量是存储在内存而非硬盘中的,这就表明了Java的确是带有内存分配的,因此可以确定其为编程语言,同类的语言还有:C++、C、Go、Python、JavaScript等等,它们都涉及内存分配,相反的还有Html、CSS这类语言,它们仅能勉强称作语言,并不能称其为编程语言,CSS3中虽然已经牵扯到内存分配,但是我们仍然不会将其看做编程语言。Java属于编程语言,那么Java是如何规划内存的,这是我们非常有必要知道的。

20 Reply   |  Until 6个月前 | 4441 View

tlyl6818
发表于 6个月前

楼1感谢面向对象图片提供者:‭@【NB】问道 ‬‬‬,楼下开更。

评论列表

  • 加载数据中...

编写评论内容

明明哦
发表于 6个月前

Java的伪内存规划
为何称之为伪内存?因为真实的Java并不是这样规划,我们便于理解,将其进行简化,在看内存规划之前,我们先看一种数据结构:栈,数据结构就是存数据的结构,说白了就是如何存放数据,就像仓库里如何存放物品一样,数据就相当于物件,仓库相当于内存,只不过栈并不像仓库,它对数据存放有一定的要求(条件)。栈是一种基础的数据结构,它的特点为:先进后出,后进先出,类似于一个弹夹,我们在存放子弹的时候后存放的子弹是先打出去的,先放进去的子弹是后打出去的(图1)。介绍完了栈,我们来说一下堆,堆就类似于储物间,物品任意摆放,取也随意取(图2)。
我们暂且可以理解为Java逻辑内存中有这么几个区域,什么是逻辑内存?因为内存为一维空间,因此堆与栈并不是真实存在的,只是Java自己约定的这几个区域,是为了方便内存管理,以便于哪种类型的数据去哪个区域寻找,在我们开发中,大部分数据都是存储在堆中的,栈只是存储数据的地址,比如说我在堆中存储了一个数据,那么它在内存中会被分配到一个编号,在栈中存放一个指针去指向这个内存编号(内存编号也叫作内存地址),然后可以通过指针去寻找堆中内存的数据。以上是我们基础阶段所需要了解的内存知识。

评论列表

  • 加载数据中...

编写评论内容

acsa
发表于 6个月前

数组
笔者估计是全网第一个将数组存放在此节进行介绍的,大部分作者都是放在数据类型中进行介绍的,笔者放在这里介绍主要是为了更好的理解堆与栈。
也许读者看完第一节有点不知所云的感受,数组就是很好的利用了堆与栈的这种特性,相信笔者对数组的介绍可以加深大家对堆栈的理解。
下面来介绍一下数组,首先说一下为什么会有数组,原来你定义一个变量:int a = 2;,如果要将这一类数据规划处理(将其放在一起管理)呢?那么这样写起代码来成本巨大,因此数组就诞生了。数组主要是为了存储一类数据,比如10个int变量,20个float变量,等等。
介绍完了数组的由来下面我们来看一下数组如何定义:常用的数组定义方式有三种(图1)。
前两种是最常用的,首先介绍第一种,int[]代表定义了一个数组,[]是数组的标志,int代表这个数组中每个元素都是int类型,{1, 2}代表这个数组的初始化元素,也就是说这个数组中出生就带有两个元素,分别是1和2。
接下来说第二种,第二种方式,第二种方式是声明式定义,new代表新建一个数组,我们暂且这样理解,后面我们还会遇到,int[2]中的2代表初始化两个int变量,但是这两个int变量值缺不能确定,因此他们拥有一个默认值,我们来说一下常见数据类型的默认值:int默认值为0,double为0.0,boolean为false,String为null,null代表空,空就是什么都没有,没有为其分配内存,因此其不可使用,使用则会抛出异常,有关异常的概念我们后文介绍。
最后说一下第三种,第三种并不常用,它所能达到的效果与第一种相同,但是写法相当复杂,大部分情况下我们应当选择较为简化的写法(有关第三种与第一种详细区别及二维数组读者可以自己找资料阅读,不是重点,此处笔者不赘述)。

评论列表

  • 加载数据中...

编写评论内容

许之诺
发表于 6个月前

数组的本质
数组虽然可以极大的方便我们管理数据,但是具有一定的局限性,我们先探究一下数组的原理,看看数组是如何分配内存的,例如我们定义数组:int[] arr = new int[2];,它的内存该如何分配?先说结论:arr存放在栈中,实际数组中的元素存在堆中。我们定义数组内存分配过程如下:在堆中分配连续8字节(2个int,每个int4字节,图1中每个长方形代表1字节),在栈中定义一个指针,指向堆中的这部分内存,请注意:栈中定义的仅为指针,并不是整型数组类型,其实也非常好理解,如果定义小数数组或布尔数组,在栈中存放的资源仅仅用来指向堆中的实际数据位置,因此它们并没有必要占用多余的内存空间(图2)。
了解完数组的内存分配后,我们来说一下数组的约束:不可扩容,也就是说如果你定义了一个长度为3的数组,那么后续就不能将它变为长度为4的数组了。为什么不能扩展数组长度了?如图2,如果我们先在堆中分配了8字节的空间,那么在数组后方还是存在富裕空间的,如果过段时间我们发现数组长度不够用了,我们需要扩容,而数组的内存空间都是连续的,因此我们应该在数组的末尾追加内存,那么此时就没办法保证当前数组的后方还是否存在可用内存(在发现内存不够前是否在内存分配时恰好分配到了此数组后方),因此干脆禁止此项行为是最明智的选择,那么如果非要扩容该如何做呢?一个非常简单粗暴的办法就是再申请一个更大空间的数组,将原来数组的元素拷贝至新数组,那么此时就拥有富裕的空间,也就达到了扩容的功能,只不过是伪扩容,事实上Java有关数组的类库在扩容时也确实是这么做的,后文介绍到容器类库时会详细介绍。

评论列表

  • 加载数据中...

编写评论内容

悲漠
发表于 6个月前

热热帖子。

评论列表

  • 加载数据中...

编写评论内容

1797591992
发表于 6个月前

数组如何进行常用操作
我们所涉及到数组的常用操作,无非是增加数组元素、删除数组元素、修改数组元素、查询数组元素这几种,我们先介绍数组如何进行查询。
查询说白了就是如何获取数组中的操作,首先是获取数组中的某个元素(也称为通过数组下标获取),我们若定义一个长度为3的数组:int[] arr = {1, 2, 3};,常规情况下,我们认为第1个是1,第二个是2,第三个是3,但是在计算机中认为1不是起始数字,起始数字应当为0,那么就会演变为:第0个为1,第1个为2,第2个为3,那么第几个就是我们前文所指的下标,我们可以通过下标获取元素,比如我们获取第0个就可以通过:数组名[下标]的形式获取,如上文所说就可以通过arr[0]来获取第0个元素(图1)。
介绍完了获取某个元素后,我们来延伸一下,就是获取全部元素,有了前文铺垫,如果我们要遍历(取出数组所有元素的值看一遍)数组该怎么做?读者可以自己尝试挑战一下,笔者在这里直接上代码(图2)。
Java提供了一种简化方式来简化我们的遍历方式,这种方式称为foreach,它的形式如下:for (表达式1(数组的每个元素的数据类型 ) 表达式2(自定义变量名 ): 表达式3(数组名)) {// ...},首先来看表达式1,如果我们定义了一个int类型的数组,那么此处填写的就是int,即数组什么类型这里就填写什么类型;再来看表达式3,如果你定义了一个数组名称为arr,你需要遍历arr数组,那么此处就填写arr即可;最后是表达式2,什么是自定义变量名?foreach将表达式2视为数组中的每个元素,例如当Java取出第1数组个元素的时候,那么表达式2代表的就是第1个元素,在遍历第二个元素的时候它也代表了第二个元素,以此类推。介绍完了三个表达式,我们若需要遍历arr数组,我们可以采用如下代码(图3)。
介绍完了查询我们接下来介绍一下修改,修改比较简单,例如我们想要将第0个修改为10,那么可以这么做(图4)。
修改完毕是添加,我们先看如何在数组末尾添加元素,我们知晓当前数组中有几个元素,我们可以通过我们知晓的数组末尾下标来进行添加。如果我们需要在中间位置添加元素呢?例如前文的arr,我们如果在第1个前添加一个10,该如何?如果直接使用arr[1] = 10那么会将2改为10,达到的只是修改效果,唯一的办法就是将数组你要添加下标后每个元素向后移动1次,然后再将待添加元素替换掉原有元素即可(图5)。
代码为(图6),此处额外介绍了数组的length,length就是记录了数组的最大长度,也就是说若数组声明长度为4,那么length为4,非常简单。再来看程序,第一个for循环我们从后向前遍历,将倒数第2个元素拷贝至倒数第1个元素,直到待插入元素位置就结束循环,我们向第1个位置插入10,因此条件判断就是为1,移动完毕,我们将值写入数组,最后循环遍历,达成效果。
最后是删除元素,删除元素也很简单,如果我们删除最后一个元素,那么我们可以将其设置为默认值(修改边缘元素最好使用0或length),若修改中间元素该怎么办?读者可以当做小练笔做一下,笔者在此处就不进行代码编写了,如遇困难,欢迎留言讨论。
我们可以看到,如果操作数组的中间元素,那么数组的代码编写难度也有一定提升,并且性能也受限,但是不得不说我们如果访问数组的元素效率是非常高的,由于它们都是连续空间,因此它可以保证下一个内存空间是不是它的元素,如果我们需要访问某个元素也是直接访问,效率非常高,因此我们若需要多访问,少增删的场景,我们应当尽量选择数组作为我们首选的数据结构,与其类似数据结构还有很多,操作也相应的复杂起来,Java为了简化开发,帮我们简化了步骤,现阶段我们可以理解为是for与foreach的关系,后面我们学到Java框架可以看到Java强大的类库,这与Java的成功是密不可分的。

评论列表

  • 加载数据中...

编写评论内容

1008612
发表于 6个月前

哇,终于弄完了,累死了。

评论列表

  • 加载数据中...

编写评论内容

qingjiu
发表于 6个月前

总结
这节我们并没有将太多关于面向对象的知识,大多说了数组,虽说数组与面向对象并无关系,但是数组的内存分配对我们理解面向对象有极大的帮助,我们所说的面向对象,在内存分配上很大一部分借鉴了数组,可以说它们极为相似。
数组是较为简单的数据结构,作为程序员我们应当知晓每种数据结构所长,在适合场景选用适合的数据结构,这对程序的性能提升有质的帮助。

评论列表

  • 加载数据中...

编写评论内容

yangxu
发表于 6个月前

我觉得你可以把教程步骤分长一点

评论列表

  • 加载数据中...

编写评论内容

1769176
发表于 6个月前

这样还可以搞个精贴

评论列表

  • 加载数据中...

编写评论内容
LoginCan Publish Content