深入理解java抽象类

抽象类概念

在面向对象的概念中,所有的对象都是通过类来描述的,但是并不是所有的类都描述了对象,有些类里面并没有包含足够的信息来描述对象,这些类被认为是抽象类。

抽象类与普通类的区别就在于抽象类不能被实例化,这就决定了抽象类必须有子类实现它的抽象方法

定义与使用

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

定义

下面我们写一个抽象类看看例子:

abstract class Person{
    private String name ; // 属性
    public String getName(){ // 普通方法
        return this.name;
    }
    public void setName(String name){
        this.name = name ;
    }
    // {}为方法体,所有抽象方法上不包含方法体
    public abstract void getPersonInfo() ; //抽象方法
}

可以看到抽象类前面要加abstract修饰,抽象方法也要加abstract关键字
需要注意的是:一个类中如果有抽象方法,那么这个类一定是抽象类,但是一个类如果是抽象类,它是可以没有抽象方法的。

使用原则

  • 所有的抽象类必须有子类。
  • 抽象类的子类必须覆写抽象类的所有抽象方法(子类不是抽象类)【方法覆写一定要考虑权限问题,权限尽量都用public】
  • 抽象类的对象可以通过对象多态性利用子类为其实例化
  • private与abstract不能同时使用。

下面我们写一个使用抽象类的代码:

abstract class Person{
    private String name ; // 属性
    public String getName(){ // 普通方法
        return this.name;
    }
    public void setName(String name){
        this.name = name ;
    }
    // {}为方法体,所有抽象方法上不包含方法体
    public abstract void getPersonInfo() ; //抽象方法
}
class Student extends Person{
    public void getPersonInfo(){//实现父类的抽象方法
        System.out.println("I am a student");
    }
}
public class Test{
    public static void main(String[] args) {
        Person per = new Student() ; //实例化子类,向上转型
        per.getPersonInfo() ; //被子类所覆写的方法
    }
}

抽象类的相关规定

在抽象类中也允许提供构造方法,并且子类也照样遵循对象实例化流程。实例化子类时一定先调用父类构造方法。例如:

abstract class Person{
    private String name ; // 属性
    public Person(){ //构造方法
        System.out.println("**********");
    }
    public String getName(){ // 普通方法
        return this.name;
    }
    public void setName(String name){
        this.name = name ;
    }
    // {}为方法体,所有抽象方法上不包含方法体
    public abstract void getPersonInfo() ; //抽象方法
}
class Student extends Person{
    public Student(){ //构造方法
        System.out.println("##########");
    }
    public void getPersonInfo(){
//空实现。
    }
}
public class Test {
    public static void main(String[] args) {
        new Student();
    }
}

结果如下:
在这里插入图片描述

所以说子类实例化是构造方法会先调用父类的构造方法。如果父类没有无参构造,那么子类构造必须使用super明确指出使用父类哪个构造方法。

总结

  • 抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。

  • 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    扫描二维码关注公众号,回复: 4384609 查看本文章
  • 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。

  • 构造方法,类方法(用static修饰的方法)不能声明为抽象方法。

  • 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类

挖坑练习

这道题我一开始也做错了,所以在这里整理一下:
看如下代码:输出结果是什么呢?

abstract class A{
    public A(){ //3.调用父类构造
        this.print() ; //4.调用被子类覆写的方法
    }
    public abstract void print() ;
}
class B extends A{
    private int num = 100 ;
    public B(int num) { //2.调用子类实例化对象
        super() ; //3.隐含一行语句,实际要先调用父类构造
        this.num = num ; //7.为类中属性初始化
    }
    public void print() { //5.此时子类对象的属性还没有被初始化
        System.out.println(this.num) ; //6.对应其数据类型的默认值
    }
}
public class Test{
    public static void main(String[] args) {
        new B(30) ; //1.实例化子类对象
        new B(30).print();
    }
}

这道题很容易入坑,main方法里面实例化了一个B的对象并传了30进去,那么在B类中首先会调用构造方法,在调用B的构造方法时,这时候又因为B继承于A所以又先调用了A的构造方法,A的构造方法里面又调用了A的print方法,但是A里面print方法时一个抽象方法,是由子类去实现的,所以这时候又来到了B类中的print方法,注意:此时num还并没有赋值,所以输出的应该是num类型的默认值0。
main方法中第二条语句在第一条语句的基础上又多了一个.print(),前面的步骤和第一条语句是一样的流程,只是后面.print()时,是指的B的print方法,而此时num是30,已经将值传进来了,所以第二条语句输出的结果应该是:
0
30

结果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40550018/article/details/84787676