目录
为什么需要有抽象类
当子类继承父类时,对于父类的方法,子类可以自由选择是否重写该方法,此时重写并不是强制发生的,父类不能强制地要求子类重写一个方法。
若是父类强制子类必须将方法重写,那么此时便可以将父类定义为抽象类。
抽象类的出现,允许父类强制子类重写自己的某个方法,此时子类别无选择只能重写该方法。
抽象类的作用
经过上面的描述,抽象类的作用便很清楚了:抽象类强制子类重写父类的方法。
抽象类的使用规则
- 抽象类使用abstract关键字定义
abstract class 类名 { }
- 抽象类不能直接使用new关键字
abstract class demo_1 { } public class demo_2 { public static void main(String[] args) { demo_1 d = new demo_1(); } }
这段代码是错误的,编译器会抛出错误提示,因为demo_1是一个抽象类,不允许直接使用new关键字创建对象
- 抽象类必须提供子类,子类使用extends继承一个抽象类
- 抽象类的子类必须重写抽象类的方法
abstract class class1 { abstract void method1(); } public class class2 extends class1{ @Override void method1() { System.out.println("method1"); } }
在上面的代码中,子类class2必须重写(或者说是实现可能更为恰当一些)class1的抽象方法
- 抽象类的对象实例化可以利用对象多态性向上转型来完成创建
抽象类的使用注意点
- 在定义抽象类时,不能使用final关键字进行修饰,因为抽象类是必须有一个子类的,而用final关键字修饰的类,没有子类
- 抽象类与普通类并没有很大的差别,只是抽象类在普通类的基础上提供了抽象方法
- 抽象类中允许不存在任何抽象方法(但是抽象类中没有抽象方法。。。。。那还是不要定义为抽象类比较好吧)
- 即使抽象类中不存在任何抽象方法,但是抽象类仍旧是抽象类,仍旧无法使用new关键字直接创建一个抽象类的对象(这边有情多态欺骗大师上场解决这个问题q(≧▽≦q))
- 可以在抽象类中使用static关键字进行修饰,静态方法是不受到抽象类的限制的
栗子1:利用对象多态性向上转型创建抽象类实例
抽象类不可以直接使用new关键字创建一个抽象类实例,但是可以利用多态性来向上转型创建抽象类
虽然规定是这么规定,但是也只是编译器认为创建了一个抽象类实例,但实际在运行时,执行的还是子类的方法
分析下面的代码段便可以验证
abstract class class1 {
abstract void method1();
void show() {
System.out.println("这是父类的show方法");
}
}
public class class2 extends class1 {
@Override
void method1() {
System.out.println("method1");
}
void show() {
System.out.println("这是子类的show方法");
}
}
public class class3 {
public static void main(String[] args) {
class1 c = new class2();
c.method1();
c.show();
}
}
class1是一个抽象类,类中有一个必须子类重写的method1()方法和一个子类可以选择重写也可以选择不重写的show()方法。
class2是抽象类class1的子类,在class2中实现了抽象方法method1(),重写了父类的show()方法。
class3中有一个main()方法以几句验证代码。
分析class3,首先利用对象的多态性,创建了一个变量c,c在编译时被认为是class1类,即抽象类/父类实例,在运行时被认为是class2类,即子类实例。总之在这方面,多态确实能称得上是一个欺骗大师(就逮着编译器欺骗了🤣🤣🤣)。
接着,调用了变量c的method()方法以及show()方法。
仔细想想,从这一步也是可以看出来一些端倪的,c是可以调用method1()方法的,但是在父类中,该方法是一个抽象方法,只有方法的声明,并没有方法的定义,只有在子类中才有具体的method1()方法定义,那么在这里便可以大胆的猜测,实际上c是class2类的对象。
当然,也可以调用c.getClass();来看一看c究竟是哪一个类的对象。(输出的结果是class2,上面的猜测是正确的)
运行程序后,得到的结果是这样的:
那么此时也成功验证了,利用对象多态性向上转型创建抽象类确实是可行的。
栗子2:使用一个内部类继承抽象类
①抽象类是无法直接使用new关键字直接创建一个抽象类对象
②抽象类的子类使用extends关键字继承抽象类
此时来折磨一下自己,若是:
- 非要使用new关键字
- 不想使用extends关键字
在这样的要求下,是否还可以好好地玩抽象类
答案是可以的,请看下面的代码段:
abstract class demo_1 {
abstract void show();
void show1() {
System.out.println("这是抽象类的show1");
}
void show2() {
}
}
public class demo_2 {
public static void main(String[] args) {
//这是一个匿名的demo_2的内部类
demo_1 d = new demo_1() {
void show() {
System.out.println("这是d对象的show");
}
void show2() {
System.out.println("这是d对象的show2");
}
};
d.show();
d.show1();
d.show2();
System.out.println(d.getClass());
//这是另一个匿名的demo_2的内部类
demo_1 d1 = new demo_1() {
void show() {
System.out.println("这是d1对象的show");
}
};
d1.show();
d1.show1();
System.out.println(d1.getClass());
}
}
接下来分析这一段代码:
有一个抽象类demo_1,类里面有一个亟待实现的抽象方法show(),还有一个普通方法show1()
有一个类demo_2,里面有一个main()方法,main方法中有一段代码
在上面的代码段中,有两段内容形式上有一些奇怪
//这是一个匿名的demo_2的内部类
demo_1 d = new demo_1() {
void show() {
System.out.println("这是d对象的show");
}
void show2() {
System.out.println("这是d对象的show2");
}
};
//这是另一个匿名的demo_2的内部类
demo_1 d1 = new demo_1() {
void show() {
System.out.println("这是d1对象的show");
}
};
但这两段代码的定义是正确的,它表明在demo_2类中构造了两个内部类,这两个内部类没有显式地给出类名,这是两个匿名内部类,并且这两个匿名内部类继承自demo_1类,即抽象类。
第一个匿名内部类中实现抽象方法show()以及重写了show2()方法
第二个匿名内部类中实现了抽象方法show()
但是需要注意的是,这两个匿名内部类继承自抽象类,因此,实现的方法必须是抽象类中所有的抽象方法,重写的方法必须是抽象类中已经存在的普通方法(实际上也可以在这两个匿名内部类中新增抽象类中没有的普通方法,编译器并不会报错,但是新增的普通方法是无法调用的)。因此若是加上下面这段内容,编译器便会报错。
demo_1 d = new demo_1() {
void show() {
System.out.println("这是d对象的show");
}
void show2() {
System.out.println("这是d对象的show2");
}
void show3() {
System.out.println("这是d对象的show3");
}
};
d.show3(); //这一句会报错,编译不通过
至于为什么会报错,这边可以吐槽一句多态不愧是专业欺骗编译器。
运行程序,可以得到下面的结果: