【阿里云】Java面向对象开发课程笔记(十六)——抽象类


1 抽象类的定义与使用

实际开发中不要使用一个类去继承一个已经实现好的类而只能继承抽象类和接口。
对象多态性的核心本质:方法的覆写。如果自子类没有进行指定方法的覆写,这样的操作不合乎要求。如果要对子类的方法进行强制的要求,就必须采用抽象类解决。

1.1 抽象类基本概念

抽象类只是在普通类的基础上扩充了一些抽象方法而已。抽象方法指的是只是声明而未实现(没有方法体)的方法,所有的抽象方法要求使用abstract关键字定义,并且抽象方法所在的类也必须使用abstract定义类,表示抽象类。
范例:定义一个抽象类

代码

abstract class A {
  private String msg = "www";  // 属性
  public void print(){  // 普通方法
      System.out.println(msg);
  }
  // { }为方法体,所有抽象方法不包含方法体
  public abstract void fun(); // 抽象方法
}

抽象类就是比普通类多了一些抽象方法而已。
抽象类中包含有抽象方法,而抽象方法没有方法体,即:不知道方法的实现,而如果现在产生了实例化对象,则意味着可以调用类中的所有操作。
抽象类适用原则:

  • 所有抽象类必须要有子类;
  • 抽象类的子类(不是抽象类)必须覆写抽象类中的全部抽象方法;
    • 方法覆写一定要考虑权限问题:抽象方法可以使用任意权限,尽量都使用public。
  • 抽象类的对象可以通过对象多态性,利用子类为其实例化。

范例:使用抽象类

代码

abstract class A {
  private String msg = "www";  // 属性
  public void print(){  // 普通方法
      System.out.println(msg);
  }
  // { }为方法体,所有抽象方法不包含方法体
  public abstract void fun(); // 抽象方法
}
// 一个子类只能够利用extends来继承抽象类,所以依然存在有单继承
class B extends A{ // 定义抽象类的子类
    public void fun(){
        System.out.println("hello world!");
    }
}

public class TestDemo {
    public static void main(String[] args) {
         A a = new B(); // 实例化子类对象
        a.fun(); // 被子类所覆写过得方法
    }
} // 输出结果:hello world!

以上操作没有任何问题,而且也是一种使用最多的形式。
说明:如下使用形式

代码

abstract class A {
    private String msg = "www";  // 属性

    public void print() {  // 普通方法
        System.out.println(msg);
    }

    public static A getInstance() {
        class B extends A {
            public void fun() {
                System.out.println("hello world!");
            }
        }
        return new B();
    }
    public abstract void fun(); // 抽象方法
}

public class TestDemo {
    public static void main(String[] args) {
        A a = A.getInstance();
        a.fun(); // 被子类所覆写过得方法
    }
} // 输出结果:hello world!

这种模式属于非正常模式,但是对于一些封装性是有好处的。

1.2 抽象类使用限制

1.抽象类只是比普通类多了一些抽象方法的定义而已,所以在抽象类之中允许提供有构造方法,并且子类也会遵守子类对象的实例化流程。实例化子类对象前一定要先去调用子类构造方法。

范例:在抽象类中定义构造

代码

abstract class Person {
    private String name;
    private int age;
    public Person(){
        System.out.println("**********************");
    }
    public abstract String getInfo(); // 抽象方法
}

class Student extends Person{
    private String school;
    public Student(){
        System.out.println("######################");
    }
    @Override
    public String getInfo() {
        return null;
    }
}

public class TestDemo {
    public static void main(String[] args) {
        new Student();
    }
}

输出结果

**********************
######################

如果父类中没有无参构造,那么子类就必须通过super()指明要调用的父类构造方法。

代码

abstract class Person {
    private String name;
    private int age;
    public Person(String name ,int age){
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return this.name;
    }
    public int getAge(){
        return this.age;
    }
    public abstract String getInfo(); // 抽象方法
}

class Student extends Person{
    private String school;
    public Student(String name,int age, String school){
        super(name,age); // 子类构造必须明确的调用父类构造方法
        this.school = school;
    }
    @Override
    public String getInfo() {
        return "[Student] name = " + super.getName() + ", age = " + super.getAge() + ", school = " + this.school;
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Person per = new Student("Jack",25,"NWAFU");
        System.out.println(per.getInfo());
    }
}

输出结果

[Student] name = Jack, age = 25, school = NWAFU

抽象类中有属性,所有属性在对象进行实例化时开辟空间,对象实例化必须使用构造方法,所以抽象类里有构造方法。

对象实例化核心步骤:

  • 进行类的加载;
  • 进行类对象的空间开辟;
  • 进行类对象中的属性初始化(构造方法)。

代码

abstract class A{
    public A(){  // 4. 调用父类构造方法
        this.print(); // 5. 调用被子类覆写过的构造方法
    }
    public abstract void print();  // 抽象方法
}

class B extends A{
    private int num = 100;
    public B(int num){ // 2. 调用子类实例化对象
        super(); // 3. 隐含一行语句,实际要想调用父类构造
        this.num = num; // 8. 为类中的属性初始化
    }
    public void print(){  // 6. 此时子类对象的属性还没有被初始化
        System.out.println(this.num); // 7. 内容为其对应数据的默认值
    }
}

public class TestDemo {
    public static void main(String[] args) {
       new B(30); // 1. 实例化子类对象
    }
}
 // 输出结果:0

结论:如果构造方法没有执行,那么对象中的属性一定都是其对应数据的默认值。
2.抽象类中允许不定义任何的抽象方法,但此时抽象类对象依然无法进行直接实例化处理。
3.抽象类一定不能使用final进行声明,因为使用final定义的的不能够有子类,而抽象类一定有子类。

  • 抽象方法不能够使用private进行定义,因为抽象方法必须被覆写

4.抽象类也分为内部抽象类和外部抽象类,抽象类可以使用static定义描述为外部抽象类。

范例:观察内部抽象类

代码

abstract class A{
   public abstract void printA();
   static abstract class B{
       public abstract void printB();
   }
}
class X extends A{
    public void printA(){};
    class Y extends B {
        public void printB(){}
    }
}

如果外部抽象类上使用了static就是语法错误,可内部抽象类上允许使用static。

1.3 抽象类实际应用——模板设计模式

抽象类最大特点在于规定了子类的实现结构,抽象类更多的情况下还可以起到一个模板的作用。

  • 人= 吃饭+睡觉+工作;
  • 猪=吃饭+睡觉;
  • 机器人=吃饭+工作。

假设有一个按钮控制(方法),一旦传入了某些指令之后就可以进行相应处理。

代码

abstract class Action{ // 描述的是一个抽象行为
   public static final int EAT = 1;
   public static final int SLEEP = 5;
   public static final int WORK = 10;
   public void command(int cmd){
        switch (cmd){
            case EAT :
               this.eat();
               break;
            case SLEEP :
                this.sleep();
                break;
            case WORK :
                this.work();
                break;
            case EAT + SLEEP + WORK :
                this.eat();
                this.work();
                this.sleep();
                break;
        }
   }
   // 不确定具体的实现,但是行为应该定义好
   public abstract void eat();
   public abstract void sleep();
   public abstract void work();
}
class Humen 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(){}
}
class Robbot extends Action{
    public void eat(){
        System.out.println("机器人要充电");
    }
    public void sleep(){}
    public void work(){
        System.out.println("机器人不停工作");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        fun(new Humen());
        fun(new Pig());
        fun(new Robbot());

    }
    public static void fun (Action action){
        action.command(Action.EAT + Action.SLEEP + Action.WORK);
    }
}
 ```
> 输出结果
```shell
人吃熟食,干净
人要拼搏
人困了就睡
猪在槽子里吃
猪圈里睡
机器人要充电
机器人不停工作
  • 抽象类在实际的使用过程之中会定义一些固化的模式,它只能接收几种特定的指令
  • 每种指令的具体实现由子类负责完成,父类只做了方法的约定。

2 总结

  1. 抽象类虽然定义了子类必须要做的事情,但抽象类依然存在单继承局限。
  2. 抽象类的使用必须通过子类进行对象实例化处理。

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com