Java编程思想6——类复用(Reusing Classes)

通过创建新类来重复使用代码,但却用不着重新创建,可以直接使用别人已建好并调试好的现成类。

  • 第一个最简单:在新类里简单地创建原有类的对象。我们把这种方法叫作“组合”,因为新类由现有类的对象合并而成。
  • 第二种方法叫作“继承”(Inheritance),涉及的大多数工作都是由编译器完成的。

1.组合的语法

为进行组合,我们只需在新类里简单地置入对象句柄即可。

System.out.println("source = " + source) ;

试图将一个WaterSource添加给一个String对象("source =")。因为我们只能将一个字串“添加”到另一个字串,所以它会说:“我要调用toString(),把source转换成字串!”经这样处理后,它就能编译两个字串,并将结果字串传递给一个System.out.println()。每次随同自己创建的一个类允许这种行为的时候,都只需要写一个toString()方法。

在类内作为字段使用的基本数据会初始化成零。但对象句柄会初始化成null。
如希望句柄得到初始化,可在下面这些地方进行:

  • (1) 在对象定义的时候。这意味着它们在构建器调用之前肯定能得到初始化。
  • (2) 在那个类的构建器中。
  • (3) 紧靠在要求实际使用那个对象之前。这样做可减少不必要的开销——假如对象并不需要创建的话。

2.继承的语法

在类主体的起始花括号之前,需要放置一个关键字extends,在后面跟随“基础类”的名字。若采取这种做法,就可自动获得基础类的所有数据成员以及方法。

父类(基类)和子类都有scrub(),但在子类的scrub()里,不可只是简单地发出对scrub()的调用。那样便造成了递归调用,我们不愿看到这一情况。为解决这个问题,Java提供了一个super关键字,它引用当前类已从中继承的一个“超类”(Superclass)。所以表达式super.scrub()调用的是方法scrub()的基础类版本。

2.1 初始化基类

创建子类的一个对象时,它在其中包含了基础类的一个“子对象”。这个子对象就象我们根据基础类本身创建了它的一个对象。
在子类的构建器中,Java会自动插入对基础类构建器的调用。

2.1.1 含有自变量的构建器

如果类没有默认的自变量,或者想调用含有一个自变量的某个基础类构建器,必须明确地编写对基础类的调用代码。这是用super关键字以及适当的自变量列表实现的。

在子类构建器中,对基础类构建器的调用是必须做的第一件事情。

2.1.2 捕获基类构建器的异常

编译器会强迫我们在子类构建器的主体中首先设置对基础类构建器的调用。这意味着在它之前不能出现任何东西。这同时也会防止子类构建器捕获来自一个基础类的任何违例事件。显然,这有时会为我们造成不便。

3.组合与继承的结合

尽管编译器会强迫我们对基础类进行初始化,并要求我们在构建器最开头做这一工作,但它并不会监视我们是否正确初始化了成员对象。所以对此必须特别加以留意。

3.1 保证正确的清除

必须将这样的清除代码置于一个finally从句中,从而防范任何可能出现的异常事
件。

3.2 名字的隐藏(验证)

如果Java基础类有一个方法名被重载使用多次,在衍生类里对那个方法名的重新定义就不会隐藏任何基础类的版本。所以无论方法在这一级还是在一个基础类中定义,重载都会生效。
很少会用与基础类里完全一致的签名和返回类型来覆盖同名的方法,否则会使人感到迷惑。

4.到底选择组合还是继承

不能用一个车辆对象来合成一辆汽车——汽车并不“包含”车辆;相反,它“属于”车辆的一种类别。
“属于”关系是用继承来表达的,而“包含”关系是用合成来表达的。

5.protected

protected的意思是“它本身是私有的,但可由从这个类继承的任何东西或者同一个包内的其他任何东西访问”。

6.累积开发

继承的一个好处是它支持“累积开发”,允许我们引入新的代码,同时不会为现有代码造成错误。这样可将新错误隔离到新代码里。

继承是对一种特殊关系的表达,意味着“这个新类属于那个旧类的一种类型”。我们的程序不应纠缠于一些细树末节,而应着眼于创建和操作各种类型的对象,用它们表达出来自“问题空间”的一个模型。

7.向上转型(Upcasting)

继承:子类属于父类的一种类型。
向上转型:将子类句柄转换成一个父类句柄。

7.1 何谓向上转型?

之所以叫作这个名字,类继承图的画法是根位于最顶部,再逐渐向下扩展。
上溯造型(向上转型)肯定是安全的,因为我们是从一个更特殊的类型到一个更常规的类型。

7.1.1 再论组合与继承

使用继承时要特别慎重。只有在清楚知道继承在所有方法中最有效的前提下,才可考虑它。

为判断自己到底应该选用组合还是继承,一个最简单的办法就是考虑是否需要从新类上溯造型回基础类。若必须上溯,就需要继承。

8.final关键字

final声明“这个东西不能改变”。之所以要禁止改变,可能是考虑到两方面的因素:设计或效率。

8.1 final数据

常数主要应用于下述两个方面:

  • (1) 编译期常数,它永远不会改变
    在Java中,这些形式的常数必须属于基本数据类型(Primitives),而且要用final关键字进行表达。在对这样的一个常数进行定义的时候,必须给出一个值。
    但对于对象句柄,final会将句柄变成一个常数。进行声明时,必须将句柄初始化到一个具体的对象。而且永远不能将句柄变成指向另一个对象。然而,对象本身是可以修改的。这一限制也适用于数组,它也属于对象。

  • (2) 在运行期初始化的一个值,我们不希望它发生变化
    不能由于某样东西的属性是final,就认定它的值能在编译时期知道。
    将final值设为static和非static之间的差异。只有当值在运行期间初始化的前提下,这种差异才会揭示出来。因为编译期间的值被编译器认为是相同的。

8.1.1 Blank finals

尽管被声明成final,但却未得到一个初始值。无论在哪种情况下,空白final都必须在实际使用前得到正确的初始化。
现在强行要求我们对final进行赋值处理——要么在定义字段时使用一个表达 式,要么在每个构建器中。这样就可以确保final字段在使用前获得正确的初始化。

8.1.2 final自变量

意味着在一个方法的内部,我们不能改变自变量句柄指向的东西或其值(基本类型)。

8.2 final方法

之所以要使用final方法,可能是出于对两方面理由的考虑。

  • 第一个是为方法“上锁”,防止任何继承类改变它的本来含义。设计程序时,若希望一个方法的行为在继承期间保持不变,而且不可被覆盖或改写,就可以采取这种做法。

  • 采用final方法的第二个理由是程序执行的效率。将一个方法设成final后,编译器就可以把对那个方法的所有调用都置入“嵌入”调用里。只要编译器发现一个final方法调用,就会(根据它自己的判断)忽略为执行方法调用机制而采取的常规代码插入方法(将自变量压入堆栈;跳至方法代码并执行它;跳回来;清除堆栈自变量;最后对返回值进行处理)。相反,它会用方法主体内实际代码的一个副本来替换方法调用。这样做可避免方法调用时的系统开销。

通常,只有在方法的代码量非常少,或者想明确禁止方法被覆盖的时候,才应考虑将一个方法设为final。

类内所有private方法都自动成为final。

8.3 final类

不允许从这个类继承。
将类定义成final后,结果只是禁止进行继承——没有更多的限制。然而,由于它禁止了继承,所以一个final类中的所有方法都默认为final。

8.4 final的注意事项

我们很难预测一个类以后会以什么样的形式再生或重复利用。常规用途的类尤其如此。若将一个方法定义成final,就可能杜绝了在其他程序员的项目中对自己的类进行继承的途径。

9.初始化和类装载

每个对象的代码都存在于独立的文件中。除非真的需要代码,否则那个文件是不会载入的。

9.1 继承初始化

在装载过程中,装载程序注意它有一个基础类(即extends关键字要表达的意思),所以随之将其载入。无论是否准备生成那个基础类的一个对象,这个过程都会发生。

若基础类含有另一个基础类,则另一个基础类随即也会载入,以此类推。接下来,会在根基础类执行static初始化,再在下一个衍生类执行,以此类推。保证这个顺序是非常关键的,因为子类的初始化可能要依赖于对基础类成员的正确初始化。

此时,必要的类已全部装载完毕,所以能够创建对象。首先,这个对象中的所有基本数据类型都会设成它们的默认值,而将对象句柄设为null。随后会调用基础类构建器。在这种情况下,调用是自动进行的。但也完全可以用super来自行指定构建器调用。基础类的构建采用与衍生类构建器完全相同的处理过程。基础类构建器完成以后,实例变量会按本来的顺序得以初始化。最后,执行构建器剩余的主体部分。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • 窗外 只听见风在呜咽 偶尔有几片飘落的树叶 亲吻着窗子的玻璃 门缝里钻进来的风 有种凉飕飕的感觉 温度也如孩童的脸...
    文采乐阅读 443评论 11 20
  • 贵族不是用来仰望的,是用来超越的! 打破传统,方能创造未来!
    木子宇少阅读 392评论 0 0
  • 以前我总以为听取别人的意见会让自己变得更好 但当别人是想打击你时 我很痛苦 所以我现在觉得 我自己很了解我的不足 ...
    JANETTTTTTT阅读 77评论 0 0
  • 梦开始的地方,总有一些模糊,脑子里总有一些飞驰的画面,少年在春色盎然的平原上飞奔,那些白桦树的树叶婆娑,想大声唱歌...
    莫问庄主阅读 521评论 0 0
  • 深圳的台风天。风倒是没有多大,雨下得像是海水倒灌一样。早上一觉醒来,窗外噼里啪啦的大雨,朋友圈都被地铁大洪水刷屏了...
    山顶的黑狗兄阅读 172评论 0 0