《java编程思想》读书笔记——第一章1.7对象的创建和存在时间(java&c++对象创建原则及原因,结合,继承器,单根结构,向下转型,GC设计实例)

1.7对象的创建和存在时间

之前提到,对于面向对象的程序编程,我们需要的基础能力包括:
1.抽象数据类型(adt,也就是将客观存在描述为对象的能力)
2.对象的多态性
3.继承
同时,还有许多值得讨论的问题,其中,最为重要的便是对象的创建与破坏方式,对于一个对象,我们将其创建后存放于何处,又该如何管理它的存在时间?
该问题对于各种语言都有自己的选择,原书中同时讨论了C++和java的策略,对于c++我知之甚少,不多进行评价
在这里,我只在概念的层面对比两者

首先,对象的存放位置

c++给出的方案
存放于栈结构之中——快速,因为栈帧的存取本质只是移动指针而已。且栈帧提供的算法,能够精确的控制对象的存活时间以及具体释放顺序
然而对象的存活时间以及具体释放顺序在这里是必填的,因为栈帧的指针需要这些作为参数,这将导致代码设计上的笨重臃肿——你在写程序的时候必须精确的知道对象的数量,存在时间以及具体种类——举个例子,如果你需要设计一个仓库管理系统,具体会收入何种货品,多少数量,若将这些货品视为对象,那么这将对你的代码编写产生很大的限制
java给出的方案
存放于堆结构之中——没有具体存放时间、何种类型、具体数量的要求限制,然而其存储方式缓慢,因为本质是在堆结构先查找空闲位置在找到存放的位置

其次,对象的创建方式

java给出的方案:
在堆中动态的创建对象——何为动态,也就是java执行引擎除非进入到代码正式开始运行,否则到底要定义多少对象定义何种类型的对象该种对象究竟会存活多久都是不知道的——他所做的只是在代码运行是出现了需要一个新的对象的需求的同时,简单的将其创建即可
同时,这将带来一个新的问题——既然不知道这些对象何时被回收,那该由谁,在何时进行回收(如果看过我的sp2和3,就已经有明确的答案了)——没错,java将这一切任务交给了垃圾收集器进行处理。
这又带了一个问题,如果把垃圾回收交给垃圾收集器,那么我们就需要容忍所有的代码在运行时,垃圾回收器带来的额外开销,然而实际上,动态创建对象所需要花费的开销本来就很高,相对而言这些额外开销并不会造成太大的影响。
而这中方案带来的灵活性,让java在处理实际问题时更为有竞争力

c++给出的方案:
在运行前由程序员定义好所有的具体细节,再到栈中创建对象——实际上c++可以选择对象的创建的时机,甚至可以采取和java一样的在运行时动态的创建对象
其不灵活性的一切根源都来自于——c++希望将对象建立在栈帧之中
因为在堆中创建的对象想要进行回收,除了固定死具体的处理时间外,可以选择的方式只剩下垃圾收集器,而垃圾收集器带来的额外开销有悖与c++的设计宗旨

总的来说:

java: 动态的在堆中创建对象,把对象的生存时间的决定权交给垃圾收集器,并默许这将带来的额外开销,以获取代码的灵活性
c++: 与高速度较真到底,不能接受垃圾收集器带来的额外开销,因此代码的灵活性受到限制

1.7.1集合与集成器(Iterator)

在java的对象创建思路下,我们应该想到另外一个问题:若不知道对象的存放时间,具体类型,具体数量,撇开之前的清理问题,有另外一个问题更加惹眼——我们该如何存放他们?
除非进入运行区,我们永远不知道可能出现的这些对象具体所需要的空间到底有多大,对于这个问题,oop语言统一都提供了同一种解决方法——创建一个新类型的对象,该种对象即名为集合
集合对象中存放的只是指向其他对象的句柄,而且会根据需求进行扩容,因而,其理论上可以适应任何被置入的东西,因此,我们不需要预先知道这个集合到底要装哪些东西,而是创建一个集合,然后剩下的工作交给他即可
本质上,其定位类似于数组,但是不同的是,面向对象的语言可以配套一系列能实现不同需求的特殊集合,如java.lang.util中包含的map,list等等
我们在选择不同类型的集合时,通常源于两种原因:
一者,集合可以提供的外部接口不同;
二者,每个集合的底层实现是不同的,这将会导致他们对于不同的需求时的响应时间又较大差距,如arrayList底层为数组,LinkList底层则为链表,数组类型的集合在删除插入时由于要移位会导致大量额外开销,而链表类型则在取值时按照引用顺序进行查找会导致大范围的开销

然而使用集合时,将一个数据插入集合之中,方式显得十分明显——push或者add或者别的什么方法就能将数据放入,而其中数据的取出或者操纵就显得较为模糊,对于List这种有序集合或许可以使用一些单引用函数将其取出,但是对于set等无序函数时,我们明显需要一个有效的遍历器
在退推出类这种集合就已经说过了,oop类型的语言对于问题的解决方式总是为其设计出一种新的类,一种名为继承器(Iterator)的对象孕育而出,他为我们提供了一层抽象——无论是对于何种集合,他都将其抽象为一个序列,我们可以再不需在意集合的本质构造的基础上遍历他
而且,这一层抽象在设计上有更加深入的意义——我们已知不同的集合在不同的需求上会有截然不同的表现,而继承器是一个可以把它们当做同样的序列来进行处理的工具,因此,我们大可以在初期草率的随意选择一种集合,之后再根据需求改变,而不会影响其他的代码结构

1.7.2单根结构

**何为单根结构?**就是所有的对象都默认有一个相同的父类(实际上绝大多数的面向对象语言都实现了这样的方案,如在java中这种超级父类就是Object)
带来的红利:
1.可以令所有的对象都有共同的必须行为,这能让对象之间的参数传递更加简单
2.同时,这些共同行为中包括了GC执行时的一些命令,这可以简化垃圾收集器的负担
为什么c++不用:
c++为了先后兼容c,所以没有采用该种方法

1.73集合库与复用性

一个优秀的集合当然首要考虑的是如何让它能够装下各种类型的未知的对象,对于这种需求,继而出现了两种方案:
1.利用单根结构的特性,使得集合利用Object来作为集合使用的容器——这将会引出一个新的议题,万能父类自然能够装下所有类型的对象,但是集合中存储的数据将会因此进行向上转型,以Object类型的对象的形式存在,这使得内部存储的对象失去了原本类型特征——因而出现了新的需求,也就是向下转型
当然,和向上转型比起来,这必然是不安全的——你可以说三角形一定是一个形状,但是当你拿到一个形状时肯定无法断定这是一个三角形,因此,除非我们确切的知道对象的真面目,并且在代码中准确的管理,否则这将很容易成为隐患

2.参数化类型(泛型)
由于有不少面向对象并没有选择单根类型的设计方案(如c++),设计出一种能够达到需求的类型成为了迫切的需求,因而出现了“参数化类型” 这一设计,同样的,虽然对于有共同父类的java来说,这种设计略微鸡肋,但是由于向下转型可能存在的不安全性,且为了避免这种不安全性程序员将要付出许多额外的精力,java也在jdk5引入了泛型的概念

1.7.4对象清除时的困境

对于一个简单的问题,我们该在何时删除一个对象是显而易见的,在不用时删除即可,为了了解对象何时删除为何是值得讨论的问题,再此以一个简单的问题作为例子:
以一个空中交通的管理系统为例——这似乎非常简单,设计一个用于存放飞机对象的集合A,当飞机进入管制区域时,将其插入该集合,离开时,将其删除即可
但是若系统内除了这部分以外,还有飞行计划记录的管理功能——系统开辟一个额外的集合B对对象进行存储,并且将在系统空闲时对这些对象进行一些特殊的处理
此时,问题显得复杂了,B中的集合根本不知道到底该何时进行删除,甚至有可能会需要长期存在
JAVA对此类困境的解决方式是:对象的删除完全托付给垃圾收集器——他将能够动态的知道一个对象何时不再使用,并在那时再自动的杀死它,同时,默许它带来的额外开销
C++则是,将对象的具体删除时间完全交付给了程序猿,这使得变成变的格外复杂,但是其效率也因此大幅度提升——没有垃圾收集器造成的大额开销
——也因为C++的复杂性,JAVA因此成为了我们在进行程序设计时的首选

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值