抽象类的基本概念
如果说现在在一个类之中需要定义一个没有方法体的方法,可以用abstract关键字来进行抽象方法定义,而包含有抽象方法的类就可以使用abstract来定义成为抽象类。
类的核心组成:属性、方法,但是在学习完继承操作之后,会发现子类存在有一种覆写父类方法的机制,而且这一机制直接与对象的多态性有关。于是这样就会出现这样一个问题:假设现在使用的是普通类,并且在这个类里面有一个print()方法。
但是这个时候子类完全可以选择是否需要覆写方法。但是由于只是一个普通方法,所以对于子类是否覆写没有任何的要求,于是这样就会出现一个漏洞,父类无法强制有求子类覆写方法。
如果只依靠普通类的继承,那么根本就不能够对子类产生限制,所以就可以利用抽象类和抽象方法来解决而此类问题。
范例:定义抽象类
抽象方法的特点:一是使用了abstract关键字定义,另一个是方法的后面没有“{}”,表示没有方法体。
本处直接采用了关键字new实例化了抽象类对象,但是编译的时候就会出现如下的错误提示信息。
抽象类是不能够直接进行对象实例化操作的。因为一旦类的对象实例化了,就意味着可以调用类中的所有方法了,但是抽象方法只是一个声明,并没有具体的方法体,所以在实际的开发之中对于抽象类的使用规则如下:
- 抽象类必须有子类,子类利用extends关键字来继承抽象类,一个子类只能够继承一个父类;
- 抽象类的子类(如果不是抽象类),那么必须要覆写抽象类中的全部抽象方法;
- 抽象类可以利用对象的向上转型机制,通过子类对象进行实例化操作。
抽象类与普通类相比最大的好处是强制定义了子类的实现要求。
本质上讲抽象类就是比普通类多了一些抽象方法的定义而已。
在实际的设计之中,父类的设计是最重要的,明显抽象类的约束更加的严格,所以在实际的开发之中,几乎不会出现普通类定义子类的情况,大多数都是继承抽象类。
抽象类的相关说明
抽象类的使用限制:
- 不能使用final关键字定义;
- 抽象类就是比普通类多了抽象方法而已,普通类的所有结构抽象类都可以定义,而且子类也符合对象实例化流程,默认先调用父类中的无参构造,而后再调用子类自己的构造。
范例:观察
abstract class A{
public static final String INFO="Hello";
public A(){
this.fun();
}
public void fun(){
System.out.println("*********************");
}
public abstract void print(); //没有方法体,使用abstact声明
}
class B extends A{
public void print(){
System.out.println("这个方法是强制子类要覆写的方法。");
}
}
public class Hello{
public static void main(String args[]){
A a = new B();//向上转型
a.print();//被子类所覆写过的方法
}
}
范例:思考题
abstract class A{
public A(){
this.print();
}
public abstract void print(); //没有方法体,使用abstact声明
}
class B extends A{
private int num = 50;
public B(int num){
this.num=num;
}
public void print(){
System.out.println("num="+this.num);
}
}
public class Hello{
public static void main(String args[]){
new B(100);
}
}
本程序的关键解决思路,子类对象实例化前一定要先实例化父类对象,也就是此时子类对象的属性都没有内容。
abstract class A{
public A(){//2.默认调用父类构造
this.print();//3.调用print()方法
}
public abstract void print(); //没有方法体,使用abstact声明
}
class B extends A{
private int num = 50;
public B(int num){//1.传递内容过来,在子类对象实例化前先实例化父类对象
this.num=num;
}
public void print(){//4.调用此方法执行,但是此时子类对象还未实例化,内容没有赋值
System.out.println("num="+this.num);//5.只能够输出对应数据类型的默认值
}
}
public class Hello{
public static void main(String args[]){
new B(100);
}
}
- 抽象类中可以没有抽象方法,但是依然不可能使用关键字new进行对象的实例化操作;
abstract class A{
}
public class Hello{
public static void main(String args[]){
A a = new A();
}
}
因为类A上有abstract关键字,所以此处无法进行对象的直接实例化。
- 外部抽象类上不允许使用static抽象类声明,但是内部抽象类中可以使用static声明,这样表明的是一个外部抽象类。
范例:定义普通的内部抽象类
abstract class A{
public abstract void printA();
abstract class B{
public abstract void printB();
}
}
class X extends A{
public void printA(){}
class Y extends B{
public void printB(){}
}
}
范例:在内部抽象类中使用static关键字
abstract class A{
public abstract void printA();
static abstract class B{
public abstract void printB();
}
}
class X extends A.B{
public void printB(){}
}
- 抽象类中可以定义static方法,而且static方法不受实例化对象的控制。
范例:直接通过抽象类产生实例化对象
abstract class A{ //此类设计之初没有子类,但考虑到之后会需要子类的定义
public abstract void printA();
private static class B extends A{//在A类中直接定义实现的子类
public void printA(){
System.out.println("Hello World!");//Hello World!
}
}
public static A getInstance(){
return new B();
}
}
class C extends A{
public void printA(){
System.out.println("************************");
}
}
public class Hello{
public static void main(String args[]){
A a = A.getInstance();
a.printA();
A a1 = new C();//留给用户做的
a1.printA();
}
}
日后如果发现,在系统类库中有某个抽象类可以直接利用一个static方法取得取得实例化对象的时候不要觉得陌生。
以上出现的几种形式有一些是在后面讲解系统类库终会出现的问题,现阶段看看就好。
抽象类的实际应用——模板设计模式
抽象类与普通类到底有哪些区别?
范例:实现程序操作
abstract class Action{//定义的是行为,行为一定不是具体的
public static final int Eat=1;
public static final int Sleep=2;
public static final int Work=5;
public void command(int flag){//执行命令
switch(flag){//数值用switch判断最好
case Eat :{
this.eat();
break;
}
case Sleep :{
this.sleep();
break;
}
case Work :{
this.work();
break;
}
}
}
public abstract void eat();//因为这些具体的行为如何执行不知道
public abstract void sleep();//交由子类根据自己的实际情况完成
public abstract void work();
}
范例:定义子类
class Robot extends Action{
public void eat(){
System.out.println("机器人补充能量");
}
public void sleep(){}
public void work(){
System.out.println("机器人正在工作");
}
}
class Person extends Action{
public void eat(){
System.out.println("人在吃饭");
}
public void sleep(){
System.out.println("人在睡觉");
}
public void work(){
System.out.println("人在工作");
}
}
class Pig extends Action{
public void eat(){
System.out.println("猪在啃食槽");
}
public void sleep(){
System.out.println("猪在养肉肉");
}
public void work(){}
}
不同的子类有着自己不同的操作支持。
范例:程序测试
public class Hello{
public static void main(String args[]){
fun(new Pig());
System.out.println("****************************************");
fun(new Robot());
System.out.println("****************************************");
fun(new Person());
System.out.println("****************************************");
}
public static void fun(Action act){//接收的是行为
act.eat();
act.sleep();
act.work();
}
}
现在在整个程序之中,如果某一事物需要实现特定的功能,那么就必须按照Action定义的方法进行覆写。这个时候子类必须按照父类提供的模板编写代码。