Java编程思想,读书笔记五(第7章 复用类)

       第7章 复用类

       复用代码是Java众多引人注目的功能之一。利用现有类型生成新类型,从而达到代码的复用,主要有组合和继承两种方式。

       组合。只需将对象引用置于新类中即可,将一个类的对象作为另一个类的成员变量(字段)。类中的字段为基本类型时,会初始化为0,boolean类型会初始化为false,String类型属于引用会初始化为null。当字段为对象引用时,会被初始化为null。要使用这个对象引用,必须完成初始化,否则运行时异常(空指针异常)。

       继承。继承是所有OOP语言和Java语言不可缺少的组成部分。当创建一个类时,总是在继承,因此除非已明确指出要从其他类中继承,否则就隐式的从Java的标准根类Object进行继承。继承通过使用关键字extends实现,子类会自动得到基类中所有的字段和方法。关于基类里关键字protected修饰的成员,在一个包里时,子类可以访问,其他类不可以访问。当基类与子类在不同包时,子类只可以访问基类的public修饰的成员。 继承并不只是复制基类的接口。子类在构造器中调用基类的构造器来执行初始化,Java会自动在子类的构造器中插入对基类构造器的调用。初始化顺序,是先基类后子类,在子类构造器访问基类之前,基类就已经完成了初始化。如果基类构造器不是默认的无参构造器,子类的构造器就需要用关键字super显示地编写调用基类构造器的语句,并且配以适当的参数列表。

      代理。代理是复用类的第三种实现方式,Java并没有提供对它的的直接支持。这是继承和组合之间的中庸之道,因为我们将一个成员对象置于所要构造的类中(就像组合),但与此同时我们在新类中暴露了该成员对象的所有方法(就像继承)。以太空船的控制模块为例,讲一下代理。第一种方式是不用代理,只使用继承,SpaceShip继承SpaceShipControls。

package use;

/**
 * 飞船控制模块,前后左右上下移动、加速
 * @author Administrator
 *
 */
public class SpaceShipControls {
	void up(int velocity) {};
	void down(int velocity) {};
	void left(int velocity) {};
	void right(int velocity) {};
	void forward(int velocity) {};
	void back(int velocity) {};
	void turboBoost(int velocity) {};

}
package use;

/**
 * 飞船类,继承控制模块类,可以使用控制模块的方法
 * @author Administrator
 *
 */
public class SpaceShip extends SpaceShipControls{
	private String name;
	public SpaceShip(String name) {
		this.name = name;
	}
	public String toString() {
		return name;
	}
	public static void main(String[] args) {
		SpaceShip protector = new SpaceShip("NSEA Protector");
		protector.forward(100);
	}
}
     这样,SpaceShipControls的所有方法会暴露给SpaceShip。第二种方式,使用代理,可以只暴露一部分方法。

package use;

/**
 * 飞船代理类,使用代理的方式使用控制模块类的方法,可以选择实现全部或者部分
 * @author Administrator
 *
 */
public class SpaceShipDelegation {
	private String name;
	private SpaceShipControls controls = new SpaceShipControls();

	public SpaceShipDelegation(String name) {
		this.name = name;
	}
	public void up(int velocity) {
		controls.up(velocity);
	}
	public void down(int velocity) {
		controls.down(velocity);
	}
	public void left(int velocity) {
		controls.left(velocity);
	}
	public void right(int velocity) {
		controls.right(velocity);
	}
	public void forward(int velocity) {
		controls.forward(velocity);
	}
	public void back(int velocity) {
		controls.back(velocity);
	}
	public void turboBoost(int velocity) {
		controls.turboBoost(velocity);
	}
	public static void main(String[] args) {
		SpaceShipDelegation protector = new SpaceShipDelegation("NSEA Protector");
		protector.forward(100);
	}
}

       同时使用组合和继承是很常见的事。关于子类的初始化,会先初始化基类,再初始化子类。虽然编译器强制初始化基类,并且要求我们在构造器里这么做,但并不会强制要求成员对象也初始化。在我们需要使用的时候,再对成员对象初始化也可以。

       正如第5章提到的那样,你并不知道垃圾回收器何时将会被调用,或者它是否被调用。如果要某个类清理一些东西,就必须显示的编写一个特殊的方法来做这件事,并在finally调用它。finally里的内容,无论发生什么都会被调用。在清理中,还必须注意对基类清理方法和成员对象清理方法的调用顺序,以防止某个子对象依赖另一个子对象的情形发生。最好的办法是,除了内存以外,不去依赖垃圾回收器做任何事,编写自己的清理方法,但不要使用finalize()。

       子类可以重载(覆盖,重写,override)基类的方法,可以添加注解@Override标明重载。@Override不是关键字,但是我们可以当做关键字使用。当你想要覆盖某个方法时,可以选择添加这个注解,当你不留心重载而并非覆写了该方法时,编译器就会产生一条错误信息,从而可以防止我们在不想重载时而意外地进行了重载。

       组合和继承都允许在新的类中放置子对象,组合是显式的这么做,而继承是隐式的这么做。组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形。即,在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口。为取得此效果,需要在新类中嵌入一个现有类的private对象。在继承的时候,使用某个现有类,并开发一个它的特殊版本,是为了某种特殊需要而将其特殊化。直观来说,“has-a”的关系用组合来表达,“is-a”的关系用继承来表达。

       向上转型。“为新的类提供方法”并不是继承技术中最重要的方面,其最重要的方面是用来表现新类和基类之间的关系。这种关系可以用“新类是现有类的一种类型”这句话加以概括。将子类引用转换为基类引用动作,我们称之为向上转型。在UML的类图中,可以看到基类在上方,子类在下方,其余类图也有类似的表示,所以将由子类转型为基类,称为向上转型。由于向上转型是一个从较专用类型向较通用类型的转换,所以是安全的。也就是说,子类是基类的一个超集,它至少具备基类中所含有的方法,而且可以拥有更多的方法。在面向对象编程的实际使用中,组合比继承广泛多了。到底使用组合还是继承,一个最清晰的判断方法是问一问自己是否需要从新类向基类进行向上转型。如果必须向上转型,则继承是必要的;但如果不需要,就要好好考虑是否需要继承。

       final关键字。根据上下文环境,final的含义有着细微的差别,但通常它是指“这是无法改变的”。final可以用到的三种情况:数据、方法和类。

       final数据,表示恒定不变的数据。例如,一个永不改变的编译时常量;或一个在运行时被初始化的值,而你不希望它被改变。一个既是static又是final字段称为编译时常量,只占据一段不能改变的存储空间。对于编译时常量,必须是基本数据类型,在定义时必须对其进行赋值,命名时全用大写字母,用下划线分割各个单词。对于final修饰的不是基本类型的对象引用,final使引用恒定不变,但对象自身是可以修改的。

       Java允许生成“空白的final”,即被指明为final而又未给定初值的值。无论是什么情况,编译器都确保空白final在使用前必须被初始化。一个类中的final域就可以做到根据对象而有所不同,且又保持其恒定不变的特性。必须在域的定义处或每个构造器中用表达式对final域进行赋值,这正是final域在使用前总是被初始化的原因所在。Java允许在参数列表中以声明的方式将参数指明为final,这意味着你无法在方法中改变参数引用所指向的对象。这一特性主要用来向匿名内部类传递数据。

     final方法。使用final方法的原因是把方法锁定,以防任何继承类修改它的含义。类中所有的private方法都隐式的指定为final。由于private方法无法触及而且能有效隐藏,所以除了把它看成是因为它所归属的类的组织结构的因为而存在外,其他任何事物都不需要考虑到它。

     final类。当将某个类的整体定义为final时,就表明了你不打算继承该类,而且不允许别人这么做。换句话说,出于某种考虑,你对该类的设计永不需要做任何变动,或者出于安全考虑,你不希望它有子类。final类中所有方法都隐式的指明为final,因为无法覆盖它们。

      要慎重使用final关键字,对于一个通用类,如果将一个方法指明为final,可能会妨碍其他程序员在项目中通过继承来复用你的类。

       下一篇:Java编程思想,读书笔记六(第8章 多态) - CSDN博客  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坚持是一种态度

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值