抽象类相当之重要,不会抽象类与接口,相当于没学过Java。
若在一个类中要定义一个没有方法体的方法,可以利用abstract关键字进行抽象方法定义,而包含抽象方法的类就可以使用abstract来定义为抽象类。
为什么会出现抽象类的概念?有什么作用?
类的核心组成包括属性和方法,但是学习完继承后,可知子类存在一种覆写父类方法的机制,这一机制与对象的多态性相关。但是这样会出现一个问题,假设现在使用的是普通类,类中有个print()方法。但类设计之初有个要求,希望继承它的方法一定要覆写print()方法,但是这个只是一个普通方法,子类是否覆写没有任何要求,这时候会出现一个漏洞,即父类无法强制要求子类必须覆写方法。所以需要抽象类和抽象方法来解决此类问题。
【举例】:定义抽象类
abstract class A{
public void fun(){
System.out.println("hello");
}
public abstract void print();//没有方法体,使用abstract声明
}
抽象方法 的特点:一个是使用abstract关键字定义,一个是方法后面没有{},即没有方法体。
【举例】:错误的使用抽象类
以上可知,抽象类不能直接进行实例化操作,因为一旦类的对象实例化了,意味着可以调用类中的方法,但是抽象类是没有方法体的。所以,在实际开发中,抽象类的使用原则:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
A a = new B();
a.print();
}
}
abstract class A{
public void fun(){
System.out.println("hello");
}
public abstract void print();//没有方法体,使用abstract声明
}
class B extends A{
@Override
public void print() {
System.out.println("子类必须覆写的方法");
}
}
抽象类与普通类相比,最大的好处就是强制定义了子类的实现要求,本质上讲,抽象类就是比普通类多了一些抽象方法的定义而已。实际开发设计中,父类的设计最重要,普通类相比于抽象类,抽象类的约束更加严格,所以实际开发中,几乎不会出现普通类定义子类的情况,大多数都是继承抽象类。
学到这里,整个设计结构中多了抽象类的定义,需要与原始的结构进行下对比:
1)抽象类不能使用final关键字定义,因为抽象类必须要有子类,而final不能有子类;
2)抽象类就是比普通类多了抽象方法,必须用abstract关键字声明,但是普通类中的所有结构抽象类都可以定义,包括:属性、普通方法、构造方法、常量等,且子类对象也符合对象实例化流程,默认先调用父类中的无参构造方法,再执行子类自己的构造。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
A a = new B();
a.print();
}
}
abstract class A{
public static final String INFO = "HELLO";
public A(){
this.fun();
}
public void fun(){
System.out.println("hello");
}
public abstract void print();//没有方法体,使用abstract声明
}
class B extends A{
@Override
public void print() {
System.out.println("子类必须覆写的方法");
}
}
【举例】:观察如下一个有意思的大坑思考题(了解即可)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
A a = new B(10);
}
}
abstract class A{
public A(){ //2.默认调用父类构造
this.print();//3.调用该方法
}
public abstract void print();//没有方法体,使用abstract声明
}
class B extends A{
private int num=1;
public B(int num) { //1.传递内容,子类对象实例化前先要实例化父类对象
this.num = num;
}
@Override
public void print() { //4.调用此方法执行,但是此时子类对象还未实例化,内容默认值为0
System.out.println(this.num); //5.只能够输出对应数据类型的默认值
}
}
3)抽象类中可以没有抽象方法,但是仍不能使用关键字new直接进行抽象类的实例化操作;
4)外部抽象类不允许使用static声明,但是内部抽象类中可以,这样表明的是一个外部抽象类;
【举例】:定义普通的内部抽象类
abstract class A{
public abstract void printA();
abstract class B{
public abstract void printB();
}
}
class X extends A{
@Override
public void printA() {}
class Y extends B{
@Override
public void printB() {
}
}
}
【举例】:在内部抽象类中使用static
abstract class A{
public abstract void printA();
static abstract class B{
public abstract void printB();
}
}
class X extends A.B{
@Override
public void printB() {
}
}
5)抽象类中可以存在static方法,static方法不受实例化控制
【举例】:直接通过抽象类产生实例化对象
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
A a = A.getInstance();
a.printA();
}
}
abstract class A{//考虑日后应用需要有子类
public abstract void printA();
private static class B extends A{ //在A类中直接定义实现的子类
@Override
public void printA() {
System.out.println("Hello");
}
}
public static A getInstance(){
return new B();
}
}
若发现系统类库中抽象类有直接用static方法取得实例化对象,可能就是用的以上方式。
以上出现的几种形式,有的是后面讲解系统类库中会出现的问题,目前了解即可。
抽象类与普通类具体有什么区别?打个比方,有三个物种:
现在要求实现一种命令模式,不管何种物种,只有传递指定的指令就可以进行操作。
【举例】:实现程序操作
1)行为的父类:
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){
case EAT:
this.eat();
break;
case SLEEP:
this.sleep();
break;
case WORK:
this.work();
break;
default:
break;
}
}
public abstract void eat(); //交由子类,根据实际情况完成
public abstract void sleep();
public abstract void work();
}
2)定义子类:
class Robot extends Action{
@Override
public void eat() {
System.out.println("机器人补充能量");
}
@Override
public void sleep() {}
@Override
public void work() {
System.out.println("机器人正在工作");
}
}
class Person extends Action{
@Override
public void eat() {
System.out.println("人在吃饭");
}
@Override
public void sleep() {
System.out.println("人在休息");
}
@Override
public void work() {
System.out.println("人在工作");
}
}
class Pig extends Action{
@Override
public void eat() {
System.out.println("猪在吃饭");
}
@Override
public void sleep() {
System.out.println("猪在睡觉");
}
@Override
public void work() {}
}
3)程序测试:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fun(new Robot());
System.out.println("********************");
fun(new Person());
System.out.println("********************");
fun(new Pig());
System.out.println("********************");
}
public static void fun(Action act){
act.eat();
act.sleep();
act.work();
}
}
现在的程序中,某一类事物需要实现特定的功能,那么就必须按照Action定义的方法进行覆写,子类必须按照父类提供的模板进行代码设计,所以这种设计叫做模板设计。
1)抽象类的设计是在普通类之上的抽象,普通类描述的是具体事物,抽象类描述了多个类之间的共同点;
2)抽象类关键的问题是约定了子类必须覆写的抽象方法;
3)抽象类的使用原则: