一、抽象类的基本概念

普通类是一个完善的功能类,可以直接产生实例化对象,并且在普通类中可以包含有构造方法、普通方法、static方法、常量和变量等内容。而抽象类是指在普通类的结构里面增加抽象方法的组成部分。

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

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

范例:定义一个抽象类

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

    public void fun(){//普通方法
System.out.println("存在方法体的方法");
} public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

二、抽象类的使用

我们先看范例。 
范例:直接实例化抽象类的对象

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();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

运行:

Exception in thread "main" java.lang.Error: Unresolved compilation problem:
Cannot instantiate the type A at com.wz.abstractdemo.TestDemo.main(TestDemo.java:15)
  • 1
  • 2
  • 3
  • 4
  • 5

从上可知,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();//被子类所覆写的过的方法
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

运行结果:

Hello World !
  • 1
  • 2

现在就可以清楚的发现: 
(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();//向上转型
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

执行结果:

*****A类构造方法*****
*****B类构造方法*****
  • 1
  • 2

(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();
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

执行结果

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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

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();
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

执行结果:

**********
  • 1

由此可见,外部抽象类不允许使用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();
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

运行结果:

Hello World !
  • 1
  • 2

(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();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

运行结果:

Hello World !
  • 1

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

例如,现在有三类事物: 
(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;
}
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

定义一个机器人的类:

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("机器人工作"); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

定义一个人的类:

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("人工作"); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

定义一个猪的类:

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() { } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

测试主类:

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);
} }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

运行结果:

机器人充电
机器人工作
人吃饭
人睡觉
人工作
猪进食
猪睡觉
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

Java抽象类 详解的更多相关文章

  1. Java 抽象类详解

    在<Java中的抽象方法和接口>中,介绍了抽象方法与接口,以及做了简单的比较. 这里我想详细探讨下抽象类. 一.抽象类的定义 被关键字“abstract”修饰的类,为抽象类.(而且,abx ...

  2. java抽象类详解

    前言 在没讲抽象类之前  我们先来看看 final关键字 final 修饰符 可以修饰 类.属性.方法 修饰类时  表示该类不能被继承   其他特征 跟普通的类一样 修饰 属性时 表示 改属性不能改变 ...

  3. java关键字(详解)

    目录 1. 基本类型 1) boolean 布尔型 2) byte 字节型 3) char 字符型 4) double 双精度 5) float 浮点 6) int 整型 7) long 长整型 8) ...

  4. android java 设计模式详解 Demo

    android java 设计模式详解 最近看了一篇设计模式的文章,深得体会,在此基础我将每种设计模式的案例都写成Demo的形式,方便读者研究学习, 首先先将文章分享给大家: 设计模式(Design ...

  5. Java接口 详解(二)

    上一篇Java接口 详解(一)讲到了接口的基本概念.接口的使用和接口的实际应用(标准定义).我们接着来讲. 一.接口的应用—工厂设计模式(Factory) 我们先看一个范例: package com. ...

  6. Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理

    本文非常详尽地介绍了Java中的三个集合类 ArrayList,Vector与Stack <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整 ...

  7. 「万字图文」史上最姨母级Java继承详解

    摘要:继承是面向对象软件技术中的一个概念.它使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用. 本文分享自华为云社区<「万字图文」史上最姨母级Java继承详解丨[奔跑吧!JAVA] ...

  8. Java 集合详解 | 一篇文章解决Java 三大集合

    更好阅读体验:Java 集合详解 | 一篇文章搞定Java 三大集合 好看的皮囊像是一个个容器,有趣的灵魂像是容器里的数据.接下来讲解Java集合数据容器. 文章篇幅有点长,还请耐心阅读.如只是为了解 ...

  9. Java内部类详解

    Java内部类详解 说起内部类这个词,想必很多人都不陌生,但是又会觉得不熟悉.原因是平时编写代码时可能用到的场景不多,用得最多的是在有事件监听的情况下,并且即使用到也很少去总结内部类的用法.今天我们就 ...

随机推荐

  1. c++学习笔记_3

    前言:本笔记所对应的课程为中国大学mooc中北京大学的程序设计与算法(三)C++面向对象程序设计,主要供自己复习使用,且本笔记建立在会使用c和java的基础上,只针对与c和java的不同来写 类和对象 ...

  2. 【ARM-Linux开发】ARM7 ARM9 ARM Cortex M3 M4 有什么区别

    ARM7 ARM9 ARM Cortex M3 M4 区别 arm7 arm9 可以类比386和奔腾, 不同代,arm9相比arm7指令集和性能都有所增强,arm7和arm9都有带mmu和无mmu的版 ...

  3. Java学习笔记-流程控制

    在Java中,最常见的就是顺序结构,另外,还有其他的一些的结构,选择,循环等,这些程序结构的加入,使得程序代码更有选择性 判断结构 if语句 三种格式: if(条件表达式) { 执行语句; } if( ...

  4. list集合的一些小见解

    关于LIst集合 前言: 第一次写博客,有些东西可能总结的到位,发表一下自己的一些观点,欢迎大佬们点评和指教 正文: list集合可以分为ArrayLlst和LinkedList. ArrayList ...

  5. nginx http跳https配置

    为了数据传输的安全性以及防止网页被恶意篡改,现在大多数网站都配置了https. 如何保证用户都是通过https进行访问呢? 如果有用到nginx,我们可以配置强制跳转. 在nginx配置中添加: se ...

  6. 最大流Dinic(模板)

    #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #include <cstdio>//sprintf islower isupp ...

  7. 网络流Dinic--模板

    #define IOS ios_base::sync_with_stdio(0); cin.tie(0); #include <cstdio>//sprintf islower isupp ...

  8. ios 输入框失去焦点,位置回调方法

    微信网页开发,ios 在input,textarea 失去焦点后,页面无法回调. 以下方法可解决: $("input,textarea").on("blur", ...

  9. vue开发环境配置跨域,一步到位

    本文要实现的是:使用vue-cli搭建的项目在开发时配置跨域,上线后不做任何任何修改,接口也可以访问,前端跨域解决方案 production:产品 生产环境 development:开发 开发环境 1 ...

  10. 第四章 MIZ701 ZYNQ制作UBOOT固化程序

      4.0难度系数★☆☆☆☆☆☆ 4.1是什么是固化 我们前几章将的程序都是通过JTAG先下载bit流文件,再下载elf文件,之后点击Run As来运行的程序.JTAG的方法是通过TCL脚本来初始化P ...