基本上在讲基本的面向对象思想,准备记录一下不是很了解的地方。

一、接口的意义

我之前一直在使用接口,可以说是养成了这个习惯。

但是偶然我也会注意到,有一些类(我习惯写一个名为tools的包,然后把一些常用工具类放进去,比如说日期处理,ip获取,正则表达式…)并不一定需要接口,直接调用就好。

之前一直没有思考过,为什么提出了接口这个概念?

使用接口有什么好处?

(1)接口是什么?

建好一个类后,可根据情况生成许多对象。随后,可将那些对象作为要解决问题中存在的元素进行处理。

如何利用对象完成真正有用的工作呢?必须有一种办法能向对象发出请求,令其做一些实际的事情,比如完成一次交易、在屏幕上画一些东西或者打开一个开关等等。

每个对象仅能接受特定的请求。我们向对象发出的请求是通过它的“接口”(Interface)定义的,对象的“类型”或“类”则规定了它的接口形式。“类型”与“接口”的等价或对应关系是面向对象程序设计的基础。

我的理解:

我们设计了类,之后可以建立类的对象,而我们可以使用这个对象的类型和类创建一个接口,用来实现对对象的控制和访问。

(2)接口的控制

“接口”(Interface)规定了可对一个特定的对象发出哪些请求。然而,必须在某个地方存在着一些代码,以便满足这些请求。这些代码与那些隐藏起来的数据便叫作“隐藏的实现”。

有两方面的原因促使我们控制对成员的访问。

第一个原因是防止程序员接触他们不该接触的东西——通常是内部数据类型的设计思想。若只是为了解决特定的问题,用户只需操作接口即可,毋需明白这些信息。我们向用户提供的实际是一种服务,因为他们很容易就可看出哪些对自己非常重要,以及哪些可忽略不计。

第二个原因是允许库设计人员修改内部结构,不用担心它会对客户程序员造成什么影响。例如,我们最开始可能设计了一个形式简单的类,以便简化开发。以后又决定进行改写,使其更快地运行。若接口与实现方法早已隔离开,并分别受到保护,就可放心做到这一点,只要求用户重新链接一下即可。

我的理解:

典型的接口如下:

若只是为了解决特定的问题,用户只需操作接口即可,毋需明白这些信息。

我个人认为,接口已经是明确定义的,有控制权限(public),有返回结果(returnPojo),有类中具体方法的名称(insertRole),有需要的参数(insertRolePojos),用户只需要找到自己需要的服务即可。如果没有接口,只有实现类,用户就要亲自看这个类中具体是怎么实现的。

允许库设计人员修改内部结构,不用担心它会对客户程序员造成什么影响。

我个人认为,接口已经定义好了类中的实现方法了,如果要实现这个接口,实现类就要按照接口的规范来,不管你怎么实现,只要达到接口的效果就好。(规范作用)

(3)再举几个例子

1、接口最主要是写给编译器看的

Java是强类型语言,在调用方法时,编译器会检查这个方法是否存在,当定义了接口时,就确保该方法一定会存在。

如果有接口,只要调用接口中的方法,编译器就会直接去实现类去找对应的实现方法了。如果直接实现实现类,那么编译器只能通过反射去实现类里找,这个方法存在吗?使用反射执行效率就会变差了。

2、A和B一起合作写代码,一个负责写功能,一个负责写实现

那么,A只要把接口写好了,也就是说把我需要的东西定好了,B你只要实现这个接口就可以了,随便你怎么实现,反正传入的参数就是这个,我要得到的结果就是这个。所以B的工作就很直观了,只要根据接口写个实现类,方便团队配合。

3、A和B一起合作写代码,B需要更改A的实现

A的工作很重要,他写了一个接口名叫interfaceA,interfaceA中有一个methodA方法

结果在看需求的时候,A发现这个方法的实现挺麻烦的,但是methodA是个重要方法,是别的代码中的一个环节。不实现methodA,其他环节不好继续。所以这个时候A就可以简单写一个临时实现类classA,直接出数据。

这样A就可以实现这个方法:

简单来说,A实现了一个冒牌货,没有真正的功能,但是他的工作可以继续下去。

那么B的工作来了,为了完善interfaceA中的这个简单方法,他独立实现了一个真正的实现类classB:

然后B通知A,把接口的实现类一改:

冒牌货就变成真货了。如果没有接口,A可能就要在实现methodA这个方法上耽搁好久。

4、这里我觉得要补充一个情景,就是不要过度设计

比如说一个pojo,只有get和set方法,那就完全没必要新建一个接口了。只要写一个实体类即可。

二、对象的创建和存在时间

java中对象的整个生命流程如何?此前我只是有个模糊的印象,现在可以有稍微深入一点的了解。

为了完全了解这个问题,似乎看《深入了解java虚拟机》会比较好些。think in java在第一章只是提到了对象的生命流程的一些概念。而且使用了c++作比较。

对象需要的数据位于哪儿,如何控制对象的“存在时间”呢?针对这个问题,解决的方案是各异其趣的。

(1)c++对象的创建

C++认为程序的执行效率是最重要的一个问题,所以它允许程序员作出选择。

为获得最快的运行速度,存储以及存在时间可在编写程序时决定,只需将对象放置在堆栈(有时也叫作自动或定域变量)或者静态存储区域即可。

这样便为存储空间的分配和释放提供了一个优先级。某些情况下,这种优先级的控制是非常有价值的。然而,我们同时也牺牲了灵活性,因为在编写程序时,必须知道对象的准确的数量、存在时间、以及类型。

如果要解决的是一个较常规的问题,如计算机辅助设计、仓储管理或者空中交通控制,这一方法就显得太局限了。

我的理解:

作为c++的开发人员,必须非常熟练并且小心翼翼地使用内存,知道每一个对象在内存中的详细信息,才能避免这个对象出现问题,这对c++的开发人员有很高的要求。但是有时候为了解决一些常规问题,要求未免太过于严格。

(2)java对象的创建

第二个方法是在一个内存池中动态创建对象,该内存池亦叫“堆”或者“内存堆”。

若采用这种方式,除非进入运行期,否则根本不知道到底需要多少个对象,也不知道它们的存在时间有多长,以及准确的类型是什么。这些参数都在程序正式运行时才决定的。若需一个新对象,只需在需要它的时候在内存堆里简单地创建它即可。

由于存储空间的管理是运行期间动态进行的,所以在内存堆里分配存储空间的时间比在堆栈里创建的时间长得多(在堆栈里创建存储空间一般只需要一个简单的指令,将堆栈指针向下或向下移动即可)。

由于动态创建方法使对象本来就倾向于复杂,所以查找存储空间以及释放它所需的额外开销不会为对象的创建造成明显的影响。除此以外,更大的灵活性对于常规编程问题的解决是至关重要的。

我的理解:

java希望让java程序员可以在大部分情况下忽略与内存管理相关的细节,只专注于业务逻辑。但是并不是说这样内存就不会出问题。往往让人觉得java程序员不懂内存,而事实很多java程序员也是如此(包括我也是)。jvm动态实现内存分配,可以让java程序变得灵活,但是动态创建毕竟是复杂的,所以性能上往往没有c++优秀。

三、关于内存对象的销毁

若在堆栈或者静态存储空间里创建一个对象,编译器会判断对象的持续时间有多长,到时会自动“破坏”或者“清除”它。

程序员可用两种方法来破坏一个对象:用程序化的方式决定何时破坏对象,或者利用由运行环境提供的一种“垃圾收集器”特性,自动寻找那些不再使用的对象,并将其清除。

当然,垃圾收集器显得方便得多,但要求所有应用程序都必须容忍垃圾收集器的存在,并能默许随垃圾收集带来的额外开销。但这并不符合C++语言的设计宗旨,所以未能包括到C++里。但Java确实提供了一个垃圾收集器。

我的理解:

c++需要自己回收不用的资源,对程序有很高的要求。而java有自带的gc,然而必须承担gc的不稳定和gc的额外开销。

四、集合的概念

针对一个特定问题的解决,如果事先不知道需要多少个对象,或者它们的持续时间有多长,那么也不知道如何保存那些对象。既然如此,怎样才能知道那些对象要求多少空间呢?事先上根本无法提前知道,除非进入运行期。

在需要的时候,集合会自动扩充自己,以便适应我们在其中置入的任何东西。所以我们事先不必知道要在一个集合里容下多少东西。只需创建一个集合,以后的工作让它自己负责好了。

我的理解:

  1. 集合是为了解决有多个对象存在的情况而存在的。
  2. 集合也是一个对象。

五、违例控制:解决错误

从最古老的程序设计语言开始,错误控制一直都是设计者们需要解决的一个大问题。对大多数错误控制方案来说,最主要的一个问题是它们严重依赖程序员的警觉性,而不是依赖语言本身的强制标准。如果程序员不够警惕——若比较匆忙,这几乎是肯定会发生的——程序所依赖的错误控制方案便会失效。

“违例控制”将错误控制方案内置到程序设计语言中,有时甚至内建到操作系统内。这里的“违例”(Exception)属于一个特殊的对象,它会从产生错误的地方“扔”或“掷”出来。随后,这个违例会被设计用于控制特定类型错误的“违例控制器”捕获。在情况变得不对劲的时候,可能有几个违例控制器并行捕获对应的违例对象。

由于采用的是独立的执行路径,所以不会干扰我们的常规执行代码。这样便使代码的编写变得更加简单,因为不必经常性强制检查代码。除此以外,“掷”出的一个违例不同于从函数返回的错误值,也不同于由函数设置的一个标志。那些错误值或标志的作用是指示一个错误状态,是可以忽略的。但违例不能被忽略,所以肯定能在某个地方得到处置。

最后,利用违例能够可靠地从一个糟糕的环境中恢复。此时一般不需要退出,我们可以采取某些处理,恢复程序的正常执行。显然,这样编制出来的程序显得更加可靠。

Java的违例控制机制与大多数程序设计语言都有所不同。因为在Java中,违例控制模块是从一开始就封装好的,所以必须使用它!如果没有自己写一些代码来正确地控制违例,就会得到一条编译期出错提示。这样可保证程序的连贯性,使错误控制变得更加容易。

我的理解:

java因为有错误类和错误捕获机制,所以可以对程序抛出的错误做一些基本的控制处理。这个机制可以将程序员必须时刻注意错误的捕获并且进行处理中解放出来,当然一些自定义错误还是需要程序员自己来实现。

六、多线程

在计算机编程中,一个基本的概念就是同时对多个任务加以控制。

许多程序设计问题都要求程序能够停下手头的工作,改为处理其他一些问题,再返回主进程。可以通过多种途径达到这个目的。最开始的时候,那些拥有机器低级知识的程序员编写一些“中断服务例程”,主进程的暂停是通过硬件级的中断实现的。尽管这是一种有用的方法,但编出的程序很难移植,由此造成了另一类的代价高昂问题。

有些时候,中断对那些实时性很强的任务来说是很有必要的。但还存在其他许多问题,它们只要求将问题划分进入独立运行的程序片断中,使整个程序能更迅速地响应用户的请求。在一个程序中,这些独立运行的片断叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。多线程处理一个常见的例子就是用户界面。利用线程,用户可按下一个按钮,然后程序会立即作出响应,而不是让用户等待程序完成了当前任务以后才开始响应。

最开始,线程只是用于分配单个处理器的处理时间的一种工具。但假如操作系统本身支持多个处理器,那么每个线程都可分配给一个不同的处理器,真正进入“并行运算”状态。

从程序设计语言的角度看,多线程操作最有价值的特性之一就是程序员不必关心到底使用了多少个处理器。程序在逻辑意义上被分割为数个线程;假如机器本身安装了多个处理器,那么程序会运行得更快,毋需作出任何特殊的调校。

根据前面的论述,大家可能感觉线程处理非常简单。但必须注意一个问题:共享资源!如果有多个线程同时运行,而且它们试图访问相同的资源,就会遇到一个问题。举个例子来说,两个进程不能将信息同时发送给一台打印机。为解决这个问题,对那些可共享的资源来说(比如打印机),它们在使用期间必须进入锁定状态。所以一个线程可将资源锁定,在完成了它的任务后,再解开(释放)这个锁,使其他线程可以接着使用同样的资源。

Java的多线程机制已内建到语言中,这使一个可能较复杂的问题变得简单起来。对多线程处理的支持是在对象这一级支持的,所以一个执行线程可表达为一个对象。Java也提供了有限的资源锁定方案。它能锁定任何对象占用的内存(内存实际是多种共享资源的一种),所以同一时间只能有一个线程使用特定的内存空间。为达到这个目的,需要使用synchronized关键字。其他类型的资源必须由程序员明确锁定,这通常要求程序员创建一个对象,用它代表一把锁,所有线程在访问那个资源时都必须检查这把锁。

七、总结

第一章主要讲了一些概念问题。如果有新发现,将继续补充。

发表评论

电子邮件地址不会被公开。 必填项已用*标注