抽象类
一、基本概念
普通类可以直接产生实例化对象,并且在普通类之中可以包含有构造方法、普通方法、static方法、常量、变量等内容。而所谓的抽象类就是指在普通类的结构里面增加抽象方法的组成部分。
所有的普通方法上面都会有一个“{}”,这个表示方法体,有方法体的方法一定可以被对象直接使用,而抽象方法指的是没有方法体的方法,同时抽象方法还必须使用abstract关键字进行定义。
拥有抽象方法的类一定属于抽象类,抽象类要使用abstract声明。
范例:定义抽象类
abstract class A {// 定义一个抽象类 public void fun() {// 普通方法 System.out.println("存在有方法体的方法!"); } // 此方法没有方法体声明,并且存在有abstract关键字,表示抽象方法 public abstract void print(); }
既然定义好了抽象类,那么下面来使用这个抽象类。
范例:直接产生对象??错误。
public class Demo { public static void main(String args[]) { A a = new A();// A是抽象的,无法实例化 a.fun(); a.print(); } }
这个时候发现根本就不能够直接对A类对象实例化。之所以不能够实例化原因很简单:当一个类的对象实例化之后,就意味着这个对象可以调用类中的属性或方法了,但是在抽象类里面存在有抽象方法,那么抽象方法没有方法体,没有方法体的方法又怎么可能去调用呢?既然不能调用方法,那么又怎么去产生实例化对象呢?
对于抽象类的使用原则定义如下:
· 抽象类必须有子类,即:每一个抽象类一定要被子类所继承;
· 抽象类的子类(不是抽象类的子类)必须要覆写抽象类之中的全部抽象方法(强制子类覆写);
· 抽象类的对象实例化需要依靠子类完成,采用向上转型的方式处理。
范例:正确使用抽象类
abstract class A {// 定义一个抽象类 public void fun() {// 普通方法 System.out.println("存在有方法体的方法!"); } // 此方法没有方法体声明,并且存在有abstract关键字,表示抽象方法 public abstract void print(); } // 一个子类只能够继承一个抽象类,属于单继承 class B extends A {// B类是抽象类的子类,并且是一个普通类 @Override public void print() {// 强制要求覆写的方法 System.out.println("Hello World!"); } } public class Demo { public static void main(String args[]) { A a = new B();// 向上转型 a.print();// 被子类所覆写过的方法 } }
总结:
- 抽象类继承子类里面会有明确的方法覆写要求,而普通类并没有;
- 抽象类只比普通类多了一些抽象方法的定义,其它的组成部分与普通类完全一样;
- 普通类对象可以直接实例化,但是抽象类的对象必须经过向上转型之后才可以得到实例化对象。
虽然一个子类可以去继承任意一个普通类,可是从开发的实际要求来讲,普通类不要去继承另外一个普通类,只能继承抽象类。
二、抽象类的相关限制
抽象类的组成和普通类的区别不大,但是由于有一点点使用以及定义上的区别,所以会有如下的概念可能会被大家忽略:
- 抽象类里面由于会存在一些属性,那么在抽象类之中一定会存在构造方法,目的:为属性初始化,并且子类对象实例化的时候,依然满足于先执行父类构造,再调用子类构造的情况。
- 抽象类不能够使用final定义:因为抽象类必须有子类,而final定义的类不能够有子类。
- 外部抽象类不允许使用static声明,而内部的抽象类允许使用static声明,使用static声明的内部抽象类就相当于是一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。
abstract class A {
static abstract class B {
public abstract void print();
}
}
class X extends A.B {
public void print() {
System.out.println("******");
}
}
public class Demo {
public static void main(String args[]) {
A.B ab = new X();
ab.print();
}
}
- 任何情况下,如果要执行类中static方法的时候,都可以在没有对象的时候直接调用,对于抽象类也是一样。
abstract class A {
public static void print() {
System.out.println("Hello World");
}
}
public class Demo {
public static void main(String args[]) {
A.print();
}
}
- 有些时候,由于抽象类只需要一个特定的系统子类操作,所以可以忽略掉外部子类。
abstract class A {
public abstract void print();
private static class B extends A {// 内部抽象类子类
public void print() {// 覆写抽象类的方法
System.out.println("Hello World !");
}
}
// 这个方法不受实例化对象的控制
public static A getInstance() {
return new B();
}
}
public class Demo {
public static void main(String args[]) {
// 此时取得抽象类对象的时候完全不需要知道B类这个子类的存在
A a = A.getInstance();
a.print();
}
}
这样的设计在系统类库之中会比较常见,目的:为用户隐藏不需要知道的子类。
对于之前有一个遗留的问题,下面来看一段代码,分析一下代码的执行结果。
abstract class A {
public A() {// 2、父类构造方法
this.print();// 3、调用print()方法
}
public abstract void print();
}
class B extends A {
private int num = 100;
public B(int num) {
this.num = num;
}
public void print() {// 4、调用覆写后的方法
System.out.println("num = " + num);// num还没初始化,内容是其对应数据类型的默认值
}
}
public class Demo {
public static void main(String args[]) {
new B(30);// 1、执行构造
}
}
解决思路:在任何一个类的构造执行完之前,所有属性的内容都是其对应数据类型的默认值,而子类构造执行之前,一定先执行父类构造,那么此时子类构造由于没有执行,所以num = 0;
三、抽象类应用——模板设计
例如,现在有三类事物:
· 机器人:充电、工作;
· 人:吃饭、工作、睡觉;
· 猪:吃饭、睡觉。
要求可以实现以上的操作控制,即:可以任意的控制人、机器人、猪的操作行为(吃、睡、工作)。
范例:定义的是一个行为类
abstract class Action { public static final int EAT = 1; public static final int SLEEP = 5; public static final int WORK = 7; public void command(int flag) { // switch只支持数值判断,而if支持条件判断 switch (flag) { case EAT: this.eat(); break; case SLEEP: this.sleep(); break; case WORK: this.work(); break; case EAT + WORK: this.eat(); this.work(); break; } } // 因为现在不确定子类的实现是什么样的 public abstract void eat(); public abstract void sleep(); public abstract void work(); }
范例:定义机器人的类
class Robot extends Action { @Override public void eat() { System.out.println("机器人补充能量"); } @Override public void work() { System.out.println("机器人正在努力工作"); } @Override public void sleep() { } }
范例:定义人的类
class Human extends Action { @Override public void eat() { System.out.println("人类正在吃饭"); } @Override public void work() { System.out.println("人类正在努力工作"); } @Override public void sleep() { System.out.println("人类正在休息"); } }
范例:定义猪的类
class Pig extends Action { @Override public void eat() { System.out.println("猪正在啃食槽"); } @Override public void work() { } @Override public void sleep() { System.out.println("猪在睡觉"); } }
这三个都属于行为的子类,所以都可以通过行为来控制。
public class Demo {
public static void main(String args[]) {
fun(new Robot());
fun(new Human());
}
public static void fun(Action act) {
act.command(Action.EAT);
act.command(Action.SLEEP);
act.command(Action.WORK);
}
}
这些不同的类型最终都在行为上成功的进行了抽象,即:如果要想使用行为操作,那么就必须按照Action类的标准来实现子类。