《java编程思想第四版完整中文高清版(免费).pdf》
欢迎下载这本书学习:
链接:https://pan.baidu.com/s/1OZZAysGVmcYkCDAVGC_Jpw
提取码:iar5
复制这段内容后打开百度网盘手机App,操作更方便哦
“问题空间”——》问题实际存在的地方
“方案空间”——》对实际问题进行建模的地方,如计算机
当我们进行面向对象的程序设计时,面临的最大一项挑战性就是:如何在“问题空间”(问题实际存
在的地方)的元素与“方案空间”(对实际问题进行建模的地方,如计算机)的元素之间建立理想的“一对
一”对应或映射关系
如何利用对象完成真正有用的工作呢?必须有一种办法能向对象发出请求,令其做一些实际的事情,比如完
成一次交易、在屏幕上画一些东西或者打开一个开关等等。每个对象仅能接受特定的请求。我们向对象发出
的请求是通过它的“接口”(Interface)定义的,对象的“类型”或“类”则规定了它的接口形式。“类
型”与“接口”的等价或对应关系是面向对象程序设计的基础
用一个简单的例子介绍下。
Light lt = new Light();
lt.on();
在这个例子中,类型/类的名称是 Light,可向 Light 对象发出的请求包括包括打开(on)、关闭(off)、
变得更明亮(brighten)或者变得更暗淡(dim)。通过简单地声明一个名字(lt),我们为 Light 对象创建
了一个“句柄”。然后用new关键字新建类型为 Light 的一个对象。再用等号将其赋给句柄。为了向对象发
送一条消息,我们列出句柄名(lt),再用一个句点符号(.)把它同消息名称(on)连接起来。从中可以看
出,使用一些预先定义好的类时,我们在程序里采用的代码是非常简单和直观的。
从根本上说,大致有两方面的人员涉足面
向对象的编程:“类创建者”(创建新数据类型的人)以及“客户程序员”(在自己的应用程序中采用现成
数据类型的人;注释④)。对客户程序员来讲,最主要的目标就是收集一个充斥着各种类的编程“工具
箱”,以便快速开发符合自己要求的应用。而对类创建者来说,他们的目标则是从头构建一个类,只向客户
程序员开放有必要开放的东西(接口),其他所有细节都隐藏起来。为什么要这样做?隐藏之后,客户程序
员就不能接触和改变那些细节,所以原创者不用担心自己的作品会受到非法修改,可确保它们不会对其他人
造成影响
有两方面的原因促使我们控制对成员的访问。第一个原因是防止程序员接触他们不该接触的东西——通常是
内部数据类型的设计思想。若只是为了解决特定的问题,用户只需操作接口即可,毋需明白这些信息。我们
向用户提供的实际是一种服务,因为他们很容易就可看出哪些对自己非常重要,以及哪些可忽略不计。
进行访问控制的第二个原因是允许库设计人员修改内部结构,不用担心它会对客户程序员造成什么影响。例
如,我们最开始可能设计了一个形式简单的类,以便简化开发。以后又决定进行改写,使其更快地运行。若
接口与实现方法早已隔离开,并分别受到保护,就可放心做到这一点,只要求用户重新链接一下即可。
Java 采用三个显式(明确)关键字以及一个隐式(暗示)关键字来设置类边界:public,private,protected 以及暗示性的friendly。若未明确指定其他关键字,则默认为后者。这些关键字的使用和含义都是相当直观的,它们决定了谁能使用后续的定义内容。“public”(公共)意味着后续的定义任何人均可使用。而在另一方面,“private”(私有)意味着除您自己、类型的创建者以及那个类型的内部函数成员,其他任何人都不能访问后续的定义信息。private在您与客户程序员之间竖起了一堵墙。若有人试图访问私有成员,就会得到一个编译期错误。“friendly”(友好的)涉及“包装”或“封装”(Package)的概念——即Java 用来构建库的方法。若某样东西是“友好的”,意味着它只能在这个包装的范围内使用(所以这一访问级别有时也叫作“包装访问”)。“protected”(受保护的)与“private”相似,只是一个继承的类可访问受保护的成员,但不能访问私有成员。继承的问题不久就要谈到。
简介说明如下:
public的类、类属变量及方法,包内及包外的任何类均可以访问;
protected的类、类属变量及方法,包内的任何类,及包外的那些继承了此类的子类才能访问;
private的类、类属变量及方法,包内包外的任何类均不能访问;
friendly如果一个类、类属变量及方法不以这三种修饰符来修饰,它就是friendly类型的,那么包内的任何类都可以访问它,而包外的任何类都不能访问它(包括包外继承了此类的子类),因此,这种类、类属变量及方法对包内的其他类是友好的,开放的,而对包外的其他类是关闭的
方案的重复利用是指我们要更多的组织(组织:在现有类的基础上组织一个新类。有时,我们也将组织称
作“包含”关系,比如“一辆车包含了一个变速箱,新类可由任意数量和类型的其他对象构成。无论如何,只要新类
达到了设计要求即可)类对象的组织具有极大的灵活性。新类的“成员对象”通常设为“私有”(Private),使用这个类的客户程序
员不能访问它们。这样一来,我们可在不干扰客户代码的前提下,从容地修改那些成员。也可以在“运行
期”更改成员,这进一步增大了灵活性。后面要讲到的“继承”并不具备这种灵活性,因为编译器必须对通
过继承创建的类加以限制
一个“克隆”类(正式名称叫作继承类或者子类)继承(继承是通过 extends关键字实现的)原始类(正式名称叫作基础类、超类或父类)的时候,需要体现子类和父类的不同,有两种方式可以区别,
1、添加新函数:最简单的区别子类和父类的不同的方法是:为衍生类添加新函数(功能),但是再添加之前还是要仔细调查自己的基础类是否真的需要这些额外的函数。如果这样做的话,子类和父类就会有所区分,这种情况子类由于和父类保留有相同的部分(共同的接口),所以认为子类和父类是相似的。
2、改善(覆盖override)旧(父类有的)旧函数:改变基础类一个现有函数的行为。我们将其称作“改善”那个函数。
为改善一个函数,只需为衍生类的函数建立一个新定义即可。我们的目标是:“尽管使用的函数接口未变,
但它的新版本具有不同的表现”,如果这么做的话可以认为子类和父类是等价的,即可替换
下面这个通过统一记号法展现的颠倒的树形图:
以上面的例子为基础,假设我们用 Java 写了这样一个函数,这个函数可与任何“几何形状”(Shape)通信,所以完全独立于它要描绘(draw)和删除(erase)的任何特定类型的对象。
void doStuff(Shape s) {
s.erase();
// ...
s.draw();
}
如果我们在其他一些程序里使用 doStuff()函数:在这儿,我们只需说:“你是一种几何形状,我知道你能
将自己删掉,即erase();请自己采取那个行动,并自己去控制所有的细节吧。
Circle c = new Circle();
Triangle t = new Triangle();
Line l = new Line();
doStuff(c);
doStuff(t);
doStuff(l);
那么对doStuff()的调用会自动良好地工作,无论对象的具体类型是什么。这实际是一个非常有用的编程技巧。请考虑下面这行代码:doStuff(c);此时,一个 Circle(圆)句柄传递给一个本来期待 Shape(形状)句柄的函数。由于圆是一种几何形状,所
以doStuff()能正确地进行处理。也就是说,凡是 doStuff()能发给一个 Shape的消息,Circle也能接收。
所以这样做是安全的,不会造成错误。
我们将这种把衍生类型当作它的基本类型处理的过程叫作“Upcasting”(上溯造型)。其中,“cast”(造
型)是指根据一个现成的模型创建;而“Up”(向上)表明继承的方向是从“上面”来的——即基础类位于
顶部,而衍生类在下方展开。所以,根据基础类进行造型就是一个从上面继承的过程,即“Upcasting”。
在面向对象的程序里,通常都要用到上溯造型技术。这是避免去调查准确类型的一个好办法
将一条消息发给对象时,如果并不知道对方的具体类型是什么,但采取的行动同样是正确的,这种情况就叫作“多形性”(Polymorphism)。对面向对象的程序设计语言来说,它们用以实现多形性的方法叫作“动态绑定”。编译器和运行期系统会负责对所有细节的控制;我们只需知道会发生什么事情,而且更重要的是,如何利用它帮助自己设计程序
类型 | abstract class | Interface |
---|---|---|
定义 | abstract class关键字 | Interface关键字 |
继承 | 抽象类可以继承一个类和实现多个接口;子类只可以继承一个抽象类 | 接口只可以继承接口(一个或多个);子类可以实现多个接口 |
访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 | 接口方法默认修饰符是public。你不可以使用其它修饰符 |
方法实现 | 可定义构造方法,可以有抽象方法和具体方法 | 接口完全是抽象的,没构造方法,且方法都是抽象的,不存在方法的实现 |
实现方式 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
作用 | 了把相同的东西提取出来,即重用 | 为了把程序模块进行固化的契约,是为了降低偶合 |