Java学习笔记——对象导论

时光荏苒,岁月穿梭,转眼间,我已走出了大学校门,踏上了职业的战场。作为一只初出茅庐的菜鸟,我发现自己要学习的东西真的很多很多。因此,我开始了自己的学习。渐渐地我发现,在解决一些具体的实际问题时,书本上往往找不到满意的答案,我只能在浩瀚的网络中苦苦找寻。幸运的是一些大大前辈们的博文为我提供了学习和解决问题的方法。但是,这些博文往往是一些高手们编写的,有时会忽略一些很小的细节,这往往会让我这只菜鸟痛苦不堪。同时,一些博文是基于一些个人的理解,逻辑结构不是很清晰,也对我的理解造成了影响。因此本人决定从自己一个菜鸟的角度整理撰写一系列博文——菜鸟看世界,在其中加入自己的思考和体会,加入自己的逻辑思维结构,以此记录自己的学习和一些体会,同时方便自己构建完整的知识体系、知识逻辑结构和日后的学习,也方便大家交流。由于本人属于菜鸟,个人水平和对问题的理解有限,在博文中一定会有一些不足,欢迎各位大大批评指正,本人在此表示万分感谢。


本文为Java编程思想第四版的学习笔记,在此感谢作者Bruce Eckel给我们带来这样一本经典著作,也感谢该书的翻译及出版人员。本文为原创笔记,转载请注明出处,谢谢。


言归正传,下面开始今天的学习,Java编程思想第一章——对象导论。


1.抽象过程

1.1.两种不同的抽象机制

1)针对机器模型和待解决问题进行抽象

特点:解决问题时,要基于计算机的结构,程序员必须建立起在机器模型(位于“解空间”内,这是你对问题建模的地方,例如计算机)和实际待解决问题的模型(位于“问题空间“内,这是问题存在的地方,例如一项业务)之间的关联。建立这种映射是费力的,而且这不属于编程语言所固有的功能,使得程序难以编写,并且维护代价高昂。

2)只针对待解决的问题进行抽象

1.2.成功的面向对象的设计语言的五个基本特征

1)万物皆为对象。理论上讲,你可以抽取待求解问题的任何概念化构建,将其表示为程序中的对象。

2)程序时对象的集合,他们通过发送消息来告知彼此所要做的。要想请求一个对象,就必须对该对象发送一条消息。更具体地说,可以把消息想象为对某个特定对象方法的调用请求。

3)每个对象都有自己的由其他对象所构成的存储。换句话说,可以通过创建包含现有对象的包的方式来创建新类型的对象。因此,可以再程序中构建复杂的体系,同时将其复杂性隐藏在对象的简单性背后。

4)每个对象都拥有其类型。每个对象都是某个类(class)的一个实例(instance),每个类最重要的区别于其他类的特性就是”可以发送什么样的消息给它"。

5)某一特定类型的所有对象都可以接收同样的消息。


2.每个对象都有接口

2.1面向对象方法通过创建类来构建仿真程序,但不是金局限于构建仿真程序。任何程序都是你所设计的系统的一种仿真,面向对象技术的应用确实可以将大量的问题很容易地降解为一个简单的解决方案。面向对象程序设计的挑战之一,就是在问题空间和解空间的对象之间创建一对一的映射。

2.2怎样才能获得有用的对象呢?必须有某种方式产生对对象的请求,是对象完成各种任务。每个对象都只能满足某些请求,这些请求由对象的接口所定义,决定接口的鞭尸类型。接口确定类对某一特定对象所能发出的请求。向某个对象“发送消息”(产生请求),这个对象遍知道此消息的目的,然后执行对应的程序代码。


3.每个对象都提供服务

3.1将对象看做是服务的提供者,因此,可以将问题分解为对象的集合。

3.2在良好的面向对象设计中,每个对象都可以很好地完成一项任务,但是他并不试图做更多的事。


4.被隐藏的具体实现

4.1将程序开发人员按照角色分为类的创建者(那些创建新数据类型的程序员)和客户端程序员。客户端程序员的目标是收集各种用来实现快速应用开发的类。类的创建者的目标是构建类,这种类只向客户端程序员暴露必须的部分,而隐藏其他部分。这意味着,类的创建者可以任意修改被隐藏的部分,而不用担心对其他任何人造成影响。

4.2具有关系所涉及的各方都遵守的边界是十分重要的事情。访问控制的第一个存在原因就是让客户端程序员无法解除他们不应该触及的部分;访问控制的第二个存在的原因就是允许库设计者可以改变类内部的工作方式而不用担心会影响到客户端程序员。

4.3Java中用三个关键字在类的内部设定边界:public、private、protected。

public:表示金穗其后的元素对任何人都是可用的

private:表示除了类型创建者和类型内部方法之外的任何人都不能访问的元素

protected:与private作用箱单,差别仅在于集成的类可以访问protected成员,但是不能访问private成员。

Java中还有一种默认的访问权限,当没有使用前面提到的任何访问指定词时,它将发挥作用。这种权限通常被称为包访问权限,因为在这种权限下,类可以访问在同一个包(构件库)中的其他类的成员,但是在包之外,这些成员如同指定了private一样。


5.复用的具体实现

5.1复用某个类的方式一般有三种:直接使用该类的一个对象、将那个累的一个对象置于某个新的类中(组合)和继承该类。


6.继承

6.1在创建一个类之后,即使创建另一个新类具有相似的功能,你还是得重新创建一个新类。如果我们能够以现有的类为基础,复制它,然后通过添加和修改这个副本来创建新类那就好多了。通过几成便可达到这样的效果,不过也有例外,当源类发生变动时,被修改的“副本”也会反映出这种变动。

6.2类型不仅仅只是描述了作用于一个对象集合上的约束条件,同时还有与其他类型之间的关系。两个类型可以有相同的特征和行为,但是其中一个类型可能比另一个含有更多的特性,并且可以处理更多的消息(或以不同的方式来处理消息)。继承使用基类型和导出类型的概念表示了这种类型之间的相似性。

6.3当继承现有类型时,也就创造了新的类型。这个新的类型不仅包括现有类型的所有成员(尽管private成员被隐藏起来,并且不可访问),而且更重要的是它付之类基类的接口。也就是说,所有可以发送给基类对象的消息同时也可以发送给导出类对象。通过几成而产生的类型等价性是理解面向对象程序设计方法内涵的重要门槛。

6.4有两种方法可以使基类与导出类产生差异。一种方法非常直接:直接在导出类中添加新方法。但是应该仔细考虑是否存在基类也需要这些额外的方法的可能性。第二种也是更重要的一种使导出类和基类之间产生差异的方法是改变现有基类的方法的行为,这被称之为覆盖(overriding,重写)。


7.伴随多态的可互换对象

7.1在处理类型的层次结构时,经常想把一个对象不当做它所属的特定类型来对待,而是将其当做其基类的对象来对待。这使得人们可以编写出不依赖于特定类型的代码。这样的代码是不会受添加新类型影响的。

7.2如果不需要知道那一段代码会被执行,那么当添加新的子类型时,不需要更改调用它的方法,它就能够执行不同的代码。因此,编译器无法精确地了解哪一段代码会被执行,那么该怎么办呢?这个问题的答案,也是面向对象程序设计的最重要的妙诀:编译器不可能产生传统意义上的函数调用。一个非面向对象编程的编译器产生的函数调用会引起所谓的前期绑定,这意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的绝对地址。面向对象程序设计语言使用了后期绑定的概念。当向对象发送消息时,被调用的代码知道运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值执行检查(无法提供此类保证的语言被称为是弱类型的),但是并不知道将被执行的确切代码。

7.3把导出类看做是它基类的过程成为向上转型。


8.单根继承结构

8.1在Java中(事实上还包括除C++意外的所有OOP语言),所有的类最终都继承自单一的类Object。

8.2在单根继承结构中所有对象都具有一个公用接口,所以它们归根到底都是相同的基本类型,单根继承结构能保证所有对象都具备某些功能,单根继承结构使垃圾回收的实现变得容易得多,而垃圾回收器正是Java相对C++的重要改进之一。


9.容器

9.1如果不知道在解决特定问题时需要多少个对象,或者他们将存活多久,那么就不可能知道如何存储这些对象。容器就是用来解决这个问题。容器持有对其他对象的引用,在任何需要时都可以扩充自己以容纳你至于其中的所有东西。

9.2需要对容器有所选择,这有两个原因:第一,不同容器提供了不同类型的接口和外部行为,如栈和队列。第二,不用的容器对于某些操作具有不同的效率,如LinkedList和ArrayList。

9.3参数化泛型

在Java SE5出现之前,容器存储的对象都只有Java中的通用类型:Object,所以当将对象引用置入容器时,它必须被向上转型为Object,因此它会丢失身份。当把它取回时,就获取了一个对Object对象的引用,而不是对置入时的那个类型的对象的引用。为了能将它变回先前置入容器中时的具有使用接口的对象,这里再度用了转型,这种转型方式成为向下转型。但是,除非确切地知道所要处理的对象的类型,否则向下转型几乎是不安全的。

因此,创建知道自己所保存的对象的类型的容器,从而不需要向下转型以及消除犯错误的可能,这是更有衣衣的做法,这种解决方案被成文参数化类型机制。参数化类型就是一个编译器可以自动定制作用于特定类型上的类。


10.对象的创建和声明周期

10.1对象的创建

在使用对象时,最关键的问题之一便是他们的生成和销毁方式。对象的数据位于何处?怎样控制对象的生命周期?C++认为效率控制是最重要的议题,为了追求最大的执行速度,对象的存储空间和生命周期可以在编写程序时确定,这可以通过将对象至于栈货静态存储区域来实现。这种方式将存储空间分配和释放至于优先考虑的位置,但是牺牲了灵活性,因为必须在编写程序时知道对象确切的数量、生命周期和类型。

第二种方式是在被称为堆的内存池中动态地创建对象。在这种方式中,知道运行时才知道需要多少对象,他们的生命周期如何,以及它们的具体类型是什么。因为存储空间是在运行时被动态管理的,所以需要大量的时间在堆中分配存储空间,这可能要远远大于在栈中创建存储空间。动态的方式有这样一个一般性的逻辑假设:对象趋于变得复杂,所以查找和释放存储空间的开销不会对对象的创建造成重大冲击。动态方式所带来的更大的灵活性正是解决一般化编程问题的要点所在。Java完全采用了动态内存的分配方式。

10.2对象的生命周期

对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁他。然而,如果是在堆上创建对象,编译器就会对它的生命周期一无所知。Java提供了被曾为”垃圾回收器”的机制,他可以自动发现对象何时不再被使用,并继而销毁它。垃圾回收器非常有用,因为它减少了所必须考虑的议题和必须编写的代码。更重要的是,垃圾回收器提供了更高层的保障,可以避免暗藏的内存泄露问题。


关于对象的创建和生命周期,本菜鸟认为是一个值得深入研究学习的议题,因此将会在之后有专门关于这方面的博文。


11.异常处理:处理错误

11.1异常是一种对象,它从出错地点被“抛出”,并被专门设计用来处理特定类型错误的相应异常处理器“捕获”。

11.2被抛出的异常不像方法返回的错误值和方法设置的用来表示错误条件的标志位那样可以被忽略。异常不能被忽略,所以它保证一定会在某处得到处理,如果没有编写正确处理异常的代码,那么就会得到一条编译时的出错消息。

11.3异常处理不是面向对象的特征——尽管在面向对象语言中异常尝尝被表示成为一个对象。异常处理在面向对象语言出现之前就已经存在了。


12.并发编程

12.1同时处理多任务,早起采用中断的方式,但这种方式难度大,程序也无法移植。有时,中断对于处理时间性强的任务是必须的,但是对于大量的其他问题,我们只是想把问题切分成多个可独立运行的部分(任务),从而提高程序的响应能力。在程序中,这些彼此独立运行的部分称之为线程,上述概念被称为“并发”。通常,线程只是一种为单一处理器分配执行时间的手段,但是如果操作系统支持多处理器,那么每个任务都可以被指派给不同的处理器,并且他们是真正并行执行。在语言级别上,多线程所带来的遍历之一便是程序员不用再操心机器上是由多个处理器还是只有有一个处理器。

12.2并发有一个隐患:共享资源。如果多个并行任务都要访问同意向资源,那么就会出问题。


13.Java与Internet

13.1Web是什么

1)客户/服务器计算技术

客户/服务器系统的核心思想是:系统具有一个中央信息存储池,用来存储各种数据,它通常存在于数据库中,你可以根据需要将他分发给某些人元货机器集群。客户/服务器概念的关键在于信息存储池的位置集中于中央,这使得他可以被修改,并且这些修改将被传播给信息消费者。总之,信息存储池、用于分发信息的软件以及信息与软件所驻留的机器货机群被总称为服务器。驻留在用户奇迹上的软件与服务器进行通信,以获取信息、处理信息,然后将他们显示在被称为客户机的用户机器上。

2)客户/服务器计算技术需要解决的三个问题:一、你只有单一的服务器,却要同时为多个客户服务。二、系统通常允许客户在服务器中插入新的信息,这意味着必须保证一个客户插入的新数据不会覆盖另一个客户出插入的新数据,也不会在将其添加到数据库的过程中丢失。三、可能在任意时刻都有成百上千的客户向服务器发出请求,所以任何小的延迟都会产生重大影响。为了将延迟最小化,程序员奴隶减轻处理器的负载,通常是分散给客户端及其处理,但有时也会使用所谓的中间件将负载分散给在服务器端的其他机器上。

3)Web就是一台巨型服务器

Web实际上就是一个句型客户/服务器系统,但稍微差一点,因为所有的服务器和客户机都同时共存于同一个网络中。


总结:本文内容为Java编程思想第四版第一章的学习笔记,其中摘录了该章内容的要点。本章主要结合Java语言,对面向对象相关的概念做了一个基本的、总结性的论述,旨在让读者初步形成面向对象的观念。同时随着后续的学习,将深化对本章内容的理解。


你可能感兴趣的:(学习笔记,JavaSE,菜鸟看世界)