Java开发笔记(五十七)因抽象方法而产生的抽象类
前面介绍了类的常见用法,令人感叹面向对象的强大,几乎日常生活中的所有事物,都可以抽象成Java的基类及其子类。然而抽象操作也有副作用,就是某个抽象而来的行为可能是不确定的,比如半夜鸡叫,如果是公鸡则必定“喔喔喔”地叫,如果是母鸡则必定“咯咯咯”地叫,可要是不能确定这只鸡是公鸡还是母鸡抑或小鸡,系统怎么知道它会怎么叫?落实到鸡类Chicken的定义代码中,它的call方法便无法给出具体的叫声了,尽管鸡类能够派生出公鸡类和母鸡类,再在公鸡类和母鸡类重写call方法,但是外部仍然可以创建鸡类的实例,接着调用鸡类实例的call方法,此时该期望这只鸡发出什么叫声呢?不管是让鸡类胡言乱语、语无伦次,还是让鸡类默不作声、噤若寒蝉,显然都与真实情况有很大出入。
由此可见,某些类其实并不能拿来直接使用,充其量只能算半成品,必须经过进一步的加工,形成最终的成品方能给外部调用。鉴于前述的鸡类存在叫唤这个不确定的方法,故而理应将它归入半成品之列;至于由鸡类派生而来的公鸡类与母鸡类,因为包括叫唤在内的每个方法都是明确的,所以它们才成为机能鉴权的完整类。在Java编程中,功能不确定的方法被称作抽象方法,而包含抽象方法的类受到牵连就变成了抽象类。在类的定义代码里面,通过关键字abstract来标识抽象方法及抽象类;凡是被abstract修饰的抽象方法,由于方法的具体实现并不明确,因此抽象方法没有花括号所包裹着的方法体;凡是被abstract修饰的抽象类,由于包含了至少一个抽象方法,因此不允许外部创建抽象类的实例,否则就会出现鸡类实例不知如何叫唤的尴尬。除此之外,抽象类还有下列两点需要注意:
1、abstract只能用来修饰抽象方法和抽象类,不可用于修饰成员属性,因为属性值本身就允许通过赋值来改变,无所谓抽象不抽象。
2、虽然抽象类依旧可以拥有构造方法,但它的构造方法并不能被外部直接调用,因为外部不允许通过构造方法来创建抽象类的实例,抽象类的构造方法只能提供给它的子类调用。
絮絮叨叨了这么多抽象概念,接着尝试把之前的鸡类改写成抽象类,修改后的抽象鸡类定义代码示例如下:
//演示抽象类的定义 abstract public class Chicken { // 定义一个名称属性 public String name; // 定义一个性别属性 public int sex; // 定义一个抽象的叫唤方法。注意后面没有花括号,并且以分号结尾 abstract public void call(); // 即使抽象类定义了构造方法,外部也无法创建它的实例 public Chicken() { } // Java只有抽象类和抽象方法,没有抽象属性的说法 //abstract public String cry; }
然后分别编写继承自鸡类的公鸡类和母鸡类,其中鸡类的抽象方法call是必须在子类中重写的,只有这样,派生而来的子类才具备所有完善的行为动作;否则的话,这个子类仍旧是个尚未完工的半成品,依然属于抽象类的行列。下面是重写了call方法的公鸡类代码例子:
//定义一个继承自抽象鸡类的公鸡类 public class Cock extends Chicken { public Cock() { // 公鸡的性别固定为雄性 sex = 0; } // 重写了公鸡的叫唤方法。如果不重写父类的抽象方法,那么该子类仍旧为抽象类 public void call() { System.out.println("喔喔喔"); } }
同样重写了call方法的母鸡类代码如下所示:
//定义一个继承自抽象鸡类的母鸡类 public class Hen extends Chicken { public Hen() { // 母鸡的性别固定为雌性 sex = 1; } // 重写了母鸡的叫唤方法。如果不重写父类的抽象方法,那么该子类仍旧为抽象类 public void call() { System.out.println("咯咯咯"); } }
最后轮到外部调用各种鸡类了,对于外部而言,唯一的区别是外部不能创建抽象类的实例,其它子类的调用则跟从前一样没有变化。具体的外部调用代码见下:
// 不能创建抽象类的实例,因为抽象类是个尚未完工的类 //Chicken chicken = new Chicken(); // 创建一个公鸡实例,公鸡类继承自抽象类Chicken Cock cock = new Cock(); cock.call(); // 调用公鸡实例的叫唤方法 // 创建一个母鸡实例,母鸡类继承自抽象类Chicken Hen hen = new Hen(); hen.call(); // 调用母鸡实例的叫唤方法
运行上面的调用代码,得到以下的日志结果,可见子类重写后的call方法正常工作。
喔喔喔 咯咯咯
更多Java技术文章参见《Java开发笔记(序)章节目录》