美文网首页Java学习笔记Java学习笔记
读书笔记 | 《Think in Java》前言&Ⅰ对象

读书笔记 | 《Think in Java》前言&Ⅰ对象

作者: 寒食君 | 来源:发表于2018-04-02 23:23 被阅读13次
    信仰牌咖啡

    写在之前

    《Think in Java》被誉为“Java界的圣经”是当之无愧的。在我初学Java时,就有学长给我推荐这本书,但是无奈资质尚浅。

    这样一本巨著对于新手不太友好很正常。同样的,对编程语言本身或对Java缺少一些基本理解的新手去硬磕这本书,效率也会比较低,反而增加了学习的恐惧。

    现在,我在与Java有过一段接触,也和他配合去完成了几个项目。是时候回过头来阅读《Think in Java》,去更深入地理解前辈的思想。在阅读时,我想留下一些笔记分享给大家,同时也是为了方便自己与大家以后的查阅。在Java这条路上,我一直是初学者,欢迎与我交流。

    前言

    ---4.2更新---
    Java一开始仅仅被作者当成“又一种程序设计语言”,不过越深入,Java有着更核心的目的。

    作者提出,程序设计实质上就是对复杂性的管理,除了Java,作者在写此书时还提到Python已经非常接近“专注于克服开发与维护程序复杂性”这个目标了,并重申强调经常使用它解决问题。《Think in Java》的初版发行于1998年,写作此书的时间更早,在Python异常火爆的今天,可以说作者非常有预见性了。

    Java的发展,从举步维艰到起立鼓掌

    为了实现“减少开发健壮代码所需的时间以及困难”这个目标,Java的运行效率起初比较低。最终,Java解决了一系列相当大的复杂性问题:跨平台,动态代码修改,安全。极大地提升了程序员的生产力。

    Ⅰ. 对象导论

    1.1 抽象过程

    纯粹面向对象的程序设计方式:

    1. 万物皆为对象。
    2. 程序是对象的集合,他们通过发送消息来告知彼此所要做的。
    3. 每个对象都有自己的由其他对象所构成的存储。
    4. 每个对象都有其类型。
    5. 某一特定类型的所有对象都可以接收同样的消息。
    1.2 每个对象都有一个接口

    一旦类被建立,就可以随心所欲地创建类地任意对象,然后去操作他们。面向对象程序设计的挑战之一,就是在问题空间的元素和解空间的对象之间创建一对一的映射。

    这句话怎么理解呢?就是将问题抽象成模型,然后去创建对象去构造这个模型,而创建的对象和现实世界中的事物是一一对应的。

    学会使用UML来创建类与类之间的关系图。

    1.4 被隐藏的具体实现

    之所以将一些属性和方法封装成类,是为了让客户端程序员不能直接访问它们,被隐藏的部分往往是比较脆弱的部分,这有两个好处:一是类的创建者可以修改它们,而不会影响到客户端程序员;二是客户端程序员不需要了解其中的具体实现方式,为了不被随意毁坏或更改,可以减少Bug的出现,同时也可以知晓哪些对他们来说是重要的,哪些是不重要的。

    1.6 继承

    继承有一项缺陷:基类(父类)发生变动时,子类不可避免地也发生了变动。

    有一句话暂时还不太能理解,但是隐隐觉得很核心,暂时存疑。“事实上,对使用面向对象设计的人们来说,困难之一是从开始到结束过于简单。对于训练有素,善于寻找复杂解决方案的头脑来说,可能会在一开始被这种简单性给难倒。

    在子类中需要进行操作时可以有两个途径:1. 新增一个方法,不过同时也需要思考其他子类是否也需要这个方法,如果需要,那么最好迭代你的基类;(“是一个”关系,is-a)2. 重写继承的基类中的方法。(“像是一个”关系,is-like-a

    1.7 伴随多态的可互换对象

    ---4.3更新---
    多态是为了解决继承带来的问题:当一个父类有泛化的方法和多个子类时,如果要执行这个泛化的方法,编译器是不知道该去执行哪一段代码的。

    这是问题的关键所在:比如几何形有一个绘制的方法,作为子类,圆形,正方形,三角形都可以应用它,而对象会依据自身的具体类型来执行恰当的代码。

    在非OOP语言中,函数调用前存在着一个“前期绑定”,即运行时根据这个绑定,将这个调用解析到将要被执行的代码的绝对地址。

    但是在OOP中,程序直到运行时才才能确定所要执行的代码的地址,当消息发送到一个泛化的父类时,必须采用特殊的机制(“前期绑定”对于OOP肯定是不适用的)

    那么,OOP采取了一种叫做“后期绑定”的方式,由于被调用的代码需要直到运行时才能确定,所以编译器只要确保该方法存在,并且核对参数和返回值的类型(强类型为此提供保障)

    为了执“后期绑定”Java使用一小段特殊的代码来替代绝对地址的调用。这段代码使用在对象中存储的信息来计算方法体的地址(后文会详细讲述,现在理解有难度)这样,根据这一小段代码,每个对象都可以具有不同的行为表现。当向一个对象发消息时,该对象能够知道对这条消息应该做些什么。

    在一些OOP中,必须使用一些关键字来声明后期绑定,C++是使用virtual。在Java中,动态绑定是默认行为,不需要添加额外的关键字来实现多态。

    看到这里,可能大家对多态的理解还是会有偏差(这种误解从我刚学Java就一直存在,到如今初读此书才恍然大悟)

    假如Circle和Square都是几何形的子类。当他们都执行draw()方法时,并不是说“如果是Circle,请这样做;如果是Square,请那样做”(以前我的误解以为多态就是这样,惭愧。)因为,假如是通过这种实现方式,那么源码中肯定是杂乱不堪的。多态要表达的是“你是一个Circle,我知道你可以draw(),去做吧,但是要注意细节的准确性。”

    真的非常优雅。

    1.8 单根继承结构

    所有的类都继承自Object

    单继承的优点:

    单继承保证所有对象都具备某些功能。所以你知道在每个对象上都可以执行一些基本操作。比如getClass(),hashCode(),equals(),toString()等。

    所有的对象都会很容易地在堆上创建,而参数传递也得到了极大的简化。

    单继承结构使得垃圾回收器的实现变得容易得多,因为所有对象都保证具有其类型信息,因此不会因为无法确定对象而陷入僵局。

    1.9 容器

    提供不同种类容器的原因:

    1. 不同容器提供了不同类型的接口和外部行为。堆栈,队列,集合,列表等,都具备不同的接口和外部行为。

    2. 不同的容器对于某些操作具有不同的效率。最直接的例子:ArrayList,LinkedList。

    在Java5之前,容器中存储的都是Object,所以可以储存Object的容器可以储存任何东西。使得容器很容易被复用。

    将某一类型的对象存入容器时需要向上转型为Object,但是取出来时,是对Object对象的引用,而不是最早的类型对象的引用。这将会很不方便。

    向上转型是安全的,但是向下转型几乎是不安全的。向下转型也不是彻底危险的,向下转型运行时和检查需要额外的时间。

    所以这里引入了泛型,创建这样的容器,他知道自己所保存的对象的类型。这种解决方案被称为参数化类型。

    1.10对象的创建和生命期

    对象的数据置于何处?怎样控制对象的生命周期?
    C++注重效率控制,所以对象的存储空间和生命周期可以在编写程序时确定,这可以将对象放在栈或者静态储存区来实现。

    这在某些情况下非常有价值,但是牺牲了灵活性,因为必须在编写程序时就知道对象确切的数量,生命周期和类型。

    另外一种是在堆的内存池中动态地创建对象。Java完全采用了动态内存分配方式。在这种方式中,只有在程序运行到相关代码被执行地那一刻,才能确定对象地数量,生命周期,具体类型。需要用到什么对象,可以直接在堆中创建,这虽然远远大于在栈中创建存储空间的时间。

    因为在栈中创建和释放空间通常各只需要一条汇编命令即可,分别将栈顶指针向上向下移动,而在堆上创建存储空间的时间依赖于存储机制的设计。

    1.11 异常处理

    需要注意的是,异常虽然已经是一种对象,但是他不是面向对象的主要特征,因为他在OOP语言出现之前就已经存在了。

    1.12.4 备选方案

    微软的.NET平台大致相当于JVM和Java类库。C#毫无疑问和Java有类似之处。 这是微软在编程语言和编程环境这一块最出色的成果。因为Java是开源的,所以微软可以看看Java哪块做的很好,哪块还有欠缺。这也是Java遇到过的真正的竞争(彼时)。

    .NET不能跨平台,但也不是完全不能跨平台。Mono项目(www.go-mono.com)有一个在Linux上运行的.NET的部分实现。但是也仅仅是部分。按照我自己的理解,这可能仅仅是微软的一种作态。所以,在.NET完全支持跨平台之前,将其作为跨平台解决方案仍旧是高风险的赌博。

    扫一扫,关注公众号

    小白的成长探索之路。

    相关文章

      网友评论

      本文标题:读书笔记 | 《Think in Java》前言&Ⅰ对象

      本文链接:https://www.haomeiwen.com/subject/cphfhftx.html