抽象类概念
在面向对象的概念中,所有的对象都是通过类来描述的,但是并不是所有的类都描述了对象,有些类里面并没有包含足够的信息来描述对象,这些类被认为是抽象类。
抽象类与普通类的区别就在于抽象类不能被实例化,这就决定了抽象类必须有子类实现它的抽象方法
定义与使用
抽象类只是在普通类的基础上扩充了一些抽象方法而已,所谓的抽象方法指的是只声明而未实现的方法(即没有方法体)。所有抽象方法要求使用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明确指出使用父类哪个构造方法。
总结
-
抽象类不能被实例化(初学者很容易犯的错),如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
-
抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
-
抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
-
构造方法,类方法(用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
结果如下: