Java 抽象类 使用方法【非常详细】

一、抽象类的基本概念

普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法、普通方法、static方法、常量和变量等内容。

而抽象类是指在普通类的结构里面增加抽象方法的组成部分。

那么什么叫抽象方法呢?在所有的普通方法上面都会有一个“{}”,这个表示方法体,有方法体的方法一定可以被对象直接使用。而抽象方法,是指没有方法体的方法,同时抽象方法还必须使用关键字abstract做修饰。

而拥有抽象方法的类就是抽象类,抽象类要使用abstract关键字声明。

范例:定义一个抽象类

abstract class A{//定义一个抽象类

    public void fun(){//普通方法
        System.out.println("存在方法体的方法");
    }

    public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰

}

抽象类的特点:

1,抽象方法一定在抽象类中。
2,抽象方法和抽象类都必须被abstract关键字修饰。
3,抽象类不可以用new创建和实例化对象。因为抽象类本身就是不完整的。
4,抽象类中的抽象方法要被使用,必须由子类复写所有的抽象方法后,建立子类对象调用。
    如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。  

二、抽象类的使用

抽象类的注意事项:

  • 抽象类不能实例化 不能造对象(抽象类中一定也有构造器,用于创建子类对象时此构造器可以被调用)
  • 抽象类中不一定有抽象方法,但是抽象方法一定存在于抽象类中
  • 若子类重写了父类中所有的抽象方法,则意味着此子类方可进行实例化
  • 若子类没有重写父类中所有的抽象方法,意味着此子类是一个抽象方法,且不能进行实例化
  • 子类重写抽象方法 不光是直接父类的抽象方法 也包括间接父类的抽象方法

抽象方法的注意事项:

  • 抽象方法一定存在于抽象类中,抽象方法没有方法体
  • 子类如果继承了抽象类,且该子类不是一个抽象类,一定要重写抽象类中的所有方法


范例:直接实例化抽象类的对象

package com.wz.abstractdemo;

abstract class A{//定义一个抽象类

    public void fun(){//普通方法
        System.out.println("存在方法体的方法");
    }

    public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰

}

public class TestDemo {

    public static void main(String[] args) {
        A a = new A();
    }
}

运行:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Cannot instantiate the type A

    at com.wz.abstractdemo.TestDemo.main(TestDemo.java:15)

从上可知,A是抽象的,无法直接进行实例化操作。为什么不能直接实例化呢?当一个类实例化之后,就意味着这个对象可以调用类中的属性或者放过了,但在抽象类里存在抽象方法,而抽象方法没有方法体,没有方法体就无法进行调用。既然无法进行方法调用的话,又怎么去产生实例化对象呢。

抽象类的使用原则如下: 

(1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public; 
(2)抽象类不能直接实例化,需要依靠子类采用向上转型的方式处理; 
(3)抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类; 
(4)子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为为abstract类。);

范例:

package com.wz.abstractdemo;

abstract class A{//定义一个抽象类

    public void fun(){//普通方法
        System.out.println("存在方法体的方法");
    }

    public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰

}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类

    @Override
    public void print() {//强制要求覆写
        System.out.println("Hello World !");
    }

}
public class TestDemo {

    public static void main(String[] args) {
        A a = new B();//向上转型

        a.fun();//被子类所覆写的过的方法
    }
}

运行结果:

Hello World !

现在就可以清楚的发现: 

(1)抽象类继承子类里面有明确的方法覆写要求,而普通类可以有选择性的来决定是否需要覆写; 
(2)抽象类实际上就比普通类多了一些抽象方法而已,其他组成部分和普通类完全一样; 
(3)普通类对象可以直接实例化,但抽象类的对象必须经过向上转型之后才可以得到。

虽然一个类的子类可以去继承任意的一个普通类,可是从开发的实际要求来讲,普通类尽量不要去继承另外一个普通类,而是去继承抽象类。

三、抽象类的使用限制

(1)抽象类中有构造方法么? 
由于抽象类里会存在一些属性,那么抽象类中一定存在构造方法,其存在目的是为了属性的初始化。 
并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。

范例如下:

package com.wz.abstractdemo;

abstract class A{//定义一个抽象类

    public A(){
        System.out.println("*****A类构造方法*****");
    }

    public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰

}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类

    public B(){
        System.out.println("*****B类构造方法*****");
    }

    @Override
    public void print() {//强制要求覆写
        System.out.println("Hello World !");
    }

}
public class TestDemo {

    public static void main(String[] args) {
        A a = new B();//向上转型
    }

}

执行结果:

*****A类构造方法*****
*****B类构造方法*****

(2)抽象类可以用final声明么? 
不能,因为抽象类必须有子类,而final定义的类不能有子类;

(3)抽象类能否使用static声明? 
先看一个关于外部抽象类的范例:

package com.wz.abstractdemo;

static abstract class A{//定义一个抽象类

    public abstract void print();

}

class B extends A{

    public void print(){
        System.out.println("**********");
    }
}
public class TestDemo {

    public static void main(String[] args) {
        A a = new B();//向上转型
        a.print();
    }

}

执行结果

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Illegal modifier for the class A; only public, abstract & final are permitted

    at com.wz.abstractdemo.A.<init>(TestDemo.java:3)
    at com.wz.abstractdemo.B.<init>(TestDemo.java:9)
    at com.wz.abstractdemo.TestDemo.main(TestDemo.java:18)

再看一个关于内部抽象类:

package com.wz.abstractdemo;

abstract class A{//定义一个抽象类

    static abstract class B{//static定义的内部类属于外部类
        public abstract void print();
    }

}

class C extends A.B{

    public void print(){
        System.out.println("**********");
    }
}
public class TestDemo {

    public static void main(String[] args) {
        A.B ab = new C();//向上转型
        ab.print();
    }

}

执行结果:

**********

由此可见,外部抽象类不允许使用static声明,而内部的抽象类运行使用static声明。使用static声明的内部抽象类相当于一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。

(4)可以直接调用抽象类中用static声明的方法么? 
任何时候,如果要执行类中的static方法的时候,都可以在没有对象的情况下直接调用,对于抽象类也一样。 
范例如下:

package com.wz.abstractdemo;

abstract class A{//定义一个抽象类

    public static void print(){
        System.out.println("Hello World !");
    }

}

public class TestDemo {

    public static void main(String[] args) {
        A.print();
    }

}

运行结果:

Hello World !

(5)有时候由于抽象类中只需要一个特定的系统子类操作,所以可以忽略掉外部子类。这样的设计在系统类库中会比较常见,目的是对用户隐藏不需要知道的子类。 
范例如下:

package com.wz.abstractdemo;

abstract class A{//定义一个抽象类
    public abstract void print();

    private static class B extends A{//内部抽象类子类

        public void print(){//覆写抽象类的方法
            System.out.println("Hello World !");
        }
    }

    //这个方法不受实例化对象的控制
    public static A getInstance(){
        return new B();
    }

}

public class TestDemo {

    public static void main(String[] args) {

        //此时取得抽象类对象的时候完全不需要知道B类这个子类的存在
        A a = A.getInstance();
        a.print();
    }
}

运行结果:

Hello World !

四、抽象类的应用——模板设计模式

例如,现在有三类事物: 
(1)机器人:充电,工作; 
(2)人:吃饭,工作,睡觉; 
(3)猪:进食,睡觉。 
现要求实现一个程序,可以实现三种不同事物的行为。

先定义一个抽象行为类:

package com.wz.abstractdemo;

public abstract class Action{

    public static final int EAT = 1 ;
    public static final int SLEEP = 3 ;
    public static final int WORK = 5 ;

    public abstract void eat();
    public abstract void sleep();
    public abstract void work();

    public void commond(int flags){
      switch(flags){
        case EAT:
            this.eat();
            break;
        case SLEEP:
            this.sleep();
            break;
        case WORK:
            this.work();
            break;
        case EAT + SLEEP:
            this.eat();
            this.sleep();
            break;
        case SLEEP + WORK:
            this.sleep();
            this.work();
            break;
        default:
            break;
        }
    }
}

定义一个机器人的类:

package com.wz.abstractdemo;

public class Robot extends Action{

    @Override
    public void eat() {
        System.out.println("机器人充电");

    }

    @Override
    public void sleep() {

    }

    @Override
    public void work() {
        System.out.println("机器人工作");

    }

}

定义一个人的类:

package com.wz.abstractdemo;

public class Human extends Action{

    @Override
    public void eat() {
        System.out.println("人吃饭");

    }

    @Override
    public void sleep() {
        System.out.println("人睡觉");

    }

    @Override
    public void work() {
        System.out.println("人工作");

    }

}

定义一个猪的类:

package com.wz.abstractdemo;

public class Pig extends Action{

    @Override
    public void eat() {
        System.out.println("猪进食");

    }

    @Override
    public void sleep() {
        System.out.println("猪睡觉");

    }

    @Override
    public void work() {


    }

}

测试主类:

package com.wz.abstractdemo;

public class AbstractDemo {

    public static void main(String[] args) {

        fun(new Robot());

        fun(new Human());

        fun(new Pig());

    }

    public static void fun(Action act){
        act.commond(Action.EAT);
        act.commond(Action.SLEEP);
        act.commond(Action.WORK);
    }

}

运行结果:

机器人充电
机器人工作
人吃饭
人睡觉
人工作
猪进食
猪睡觉

所有的子类如果要想正常的完成操作,必须按照指定的方法进行覆写才可以,而这个时候抽象类所起的功能就是一个类定义模板的功能。

四、抽象类相关的设计模式

定义模板

这里写图片描述

abstract class Person{
    private String name ;       // 定义name属性
    private int age ;           // 定义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 void say(){      // 人说话是一个具体的功能
        System.out.println(this.getContent()) ; // 输出内容
    }
    public abstract String getContent() ;   // 说话的内容由子类决定
};
class Student extends Person{
    private float score ;
    public Student(String name,int age,float score){
        super(name,age) ;   // 调用父类中的构造方法
        this.score = score ;
    }
    public String getContent(){
        return  "学生信息 --> 姓名:" + super.getName() + 
                ";年龄:" + super.getAge() + 
                ";成绩:" + this.score ;
    }
};
class Worker extends Person{
    private float salary ;
    public Worker(String name,int age,float salary){
        super(name,age) ;   // 调用父类中的构造方法
        this.salary = salary ;
    }
    public String getContent(){
        return  "工人信息 --> 姓名:" + super.getName() + 
                ";年龄:" + super.getAge() + 
                ";工资:" + this.salary ;
    }
};
public class AbstractCaseDemo02{
    public static void main(String args[]){
        Person per1 = null ;    // 声明Person对象
        Person per2 = null ;    // 声明Person对象
        per1 = new Student("张三",20,99.0f) ; // 学生是一个人
        per2 = new Worker("李四",30,3000.0f) ;    // 工人是一个人
        per1.say() ;    // 学生说学生的话
        per2.say() ;    // 工人说工人的话
    }
};

分析:
1,说话这个功能,正常的是个人都有这个功能,所以可以抽取出来,在Person这个类中,定义一个say()方法,但是说话的内容,不同的类型的人讨论的话题不同,这个话题,不能写成具体的,需要定义成一个抽象的方法,在不同的子类中实现不同的内容。
2,一个人,无论是工人还是学生或者其他什么人,想说话么,想说话,就覆写我的getContent()方法,就是这样规定好了,这就是模板,

 

关注我们,更多技术干货:

2000多G的计算机各行业电子资源分享(持续更新)

2020年微信小程序全栈项目之喵喵交友【附课件和源码】

Spring Boot开发小而美的个人博客【附课件和源码】

Java微服务实战296集大型视频-谷粒商城【附代码和课件】

Java开发微服务畅购商城实战【全357集大项目】-附代码和课件

最全最详细数据结构与算法视频-【附课件和源码】

在这里插入图片描述

2021年JAVA 精心整理的常见面试题-附详细答案

https://mikejun.blog.csdn.net/article/details/114488339

2021年- 精心整理的 SpringMVC 常见面试题-【附详细答案】

https://mikejun.blog.csdn.net/article/details/114992529

2021年JAVA 面试题之--数据结构篇【附详细答案】

https://mikejun.blog.csdn.net/article/details/114647742

精心整理的计算机各类别的电子书籍【超全】

https://mikejun.blog.csdn.net/article/details/115442555

三天刷完《剑指OFFER编程题》--Java版本实现(第一天)

https://mikejun.blog.csdn.net/article/details/106996017

三天刷完《剑指OFFER编程题》--Java版本实现(第二天)

https://mikejun.blog.csdn.net/article/details/108098502

三天刷完《剑指OFFER编程题》--Java版本实现(第三天)

https://mikejun.blog.csdn.net/article/details/108253489
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值