前面介绍了类的常见用法,令人感叹面向对象的强大,几乎日常生活中的所有事物,都可以抽象成Java的基类及其子类。然而抽象操作也有副作用,就是某个抽象而来的行为可能是不确定的,比如半夜鸡叫,如果是公鸡则必定“喔喔喔”地叫,如果是母鸡则必定“咯咯咯”地叫,可要是不能确定这只鸡是公鸡还是母鸡抑或小鸡,系统怎么知道它会怎么叫?落实到鸡类Chicken的定义代码中,它的call方法便无法给出具体的叫声了,尽管鸡类能够派生出公鸡类和母鸡类,再在公鸡类和母鸡类重写call方法,但是外部仍然可以创建鸡类的实例,接着调用鸡类实例的call方法,此时该期望这只鸡发出什么叫声呢?不管是让鸡类胡言乱语、语无伦次,还是让鸡类默不作声、噤若寒蝉,显然都与真实情况有很大出入。
由此可见,某些类其实并不能拿来直接使用,充其量只能算半成品,必须经过进一步的加工,形成最终的成品方能给外部调用。鉴于前述的鸡类存在叫唤这个不确定的方法,故而理应将它归入半成品之列;至于由鸡类派生而来的公鸡类与母鸡类,因为包括叫唤在内的每个方法都是明确的,所以它们才成为机能鉴权的完整类。在Java编程中,功能不确定的方法被称作抽象方法,而包含抽象方法的类受到牵连就变成了抽象类。在类的定义代码里面,通过关键字abstract来标识抽象方法及抽象类;凡是被abstract修饰的抽象方法,由于方法的具体实现并不明确,因此抽象方法没有花括号所包裹着的方法体;凡是被abstract修饰的抽象类,由于包含了至少一个抽象方法,因此不允许外部创建抽象类的实例,否则就会出现鸡类实例不知如何叫唤的尴尬。除此之外,抽象类还有下列两点需要注意:
1、abstract只能用来修饰抽象方法和抽象类,不可用于修饰成员属性,因为属性值本身就允许通过赋值来改变,无所谓抽象不抽象。
2、虽然抽象类依旧可以拥有构造方法,但它的构造方法并不能被外部直接调用,因为外部不允许通过构造方法来创建抽象类的实例,抽象类的构造方法只能提供给它的子类调用。
絮絮叨叨了这么多抽象概念,接着尝试把之前的鸡类改写成抽象类,修改后的抽象鸡类定义代码示例如下:

//演示抽象类的定义
abstract public class Chicken { // 定义一个名称属性
public String name;
// 定义一个性别属性
public int sex; // 定义一个抽象的叫唤方法。注意后面没有花括号,并且以分号结尾
abstract public void call(); // 即使抽象类定义了构造方法,外部也无法创建它的实例
public Chicken() {
} // Java只有抽象类和抽象方法,没有抽象属性的说法
//abstract public String cry;
}

然后分别编写继承自鸡类的公鸡类和母鸡类,其中鸡类的抽象方法call是必须在子类中重写的,只有这样,派生而来的子类才具备所有完善的行为动作;否则的话,这个子类仍旧是个尚未完工的半成品,依然属于抽象类的行列。下面是重写了call方法的公鸡类代码例子:

//定义一个继承自抽象鸡类的公鸡类
public class Cock extends Chicken { public Cock() {
// 公鸡的性别固定为雄性
sex = 0;
} // 重写了公鸡的叫唤方法。如果不重写父类的抽象方法,那么该子类仍旧为抽象类
public void call() {
System.out.println("喔喔喔");
}
}

同样重写了call方法的母鸡类代码如下所示:

//定义一个继承自抽象鸡类的母鸡类
public class Hen extends Chicken { public Hen() {
// 母鸡的性别固定为雌性
sex = 1;
} // 重写了母鸡的叫唤方法。如果不重写父类的抽象方法,那么该子类仍旧为抽象类
public void call() {
System.out.println("咯咯咯");
}
}

最后轮到外部调用各种鸡类了,对于外部而言,唯一的区别是外部不能创建抽象类的实例,其它子类的调用则跟从前一样没有变化。具体的外部调用代码见下:

		// 不能创建抽象类的实例,因为抽象类是个尚未完工的类
//Chicken chicken = new Chicken();
// 创建一个公鸡实例,公鸡类继承自抽象类Chicken
Cock cock = new Cock();
cock.call(); // 调用公鸡实例的叫唤方法
// 创建一个母鸡实例,母鸡类继承自抽象类Chicken
Hen hen = new Hen();
hen.call(); // 调用母鸡实例的叫唤方法

运行上面的调用代码,得到以下的日志结果,可见子类重写后的call方法正常工作。

喔喔喔
咯咯咯

  

更多Java技术文章参见《Java开发笔记(序)章节目录

Java开发笔记(五十七)因抽象方法而产生的抽象类的更多相关文章

  1. Java开发笔记(十七)各得其所的多路分支

    前面提到条件语句的标准格式为“if (条件) { /* 条件成立时的操作代码 */ } else { /* 条件不成立时的操作代码 */ }”,乍看之下仿佛只有两个分支,一个是条件成立时的分支,另一个 ...

  2. Java开发笔记(序)章节目录

    现将本博客的Java学习文章整理成以下笔记目录,方便查阅. 第一章 初识JavaJava开发笔记(一)第一个Java程序Java开发笔记(二)Java工程的帝国区划Java开发笔记(三)Java帝国的 ...

  3. Java开发笔记(三十七)利用正则串分割字符串

    前面介绍了处理字符串的常用方法,还有一种分割字符串的场景也很常见,也就是按照某个规则将字符串切割为若干子串.分割规则通常是指定某个分隔符,根据字符串内部的分隔符将字符串进行分割,例如逗号.空格等等都可 ...

  4. Java开发笔记(五十八)简单接口及其实现

    前面介绍了抽象方法及抽象类的用法,看似解决了不确定行为的方法定义,既然叫唤动作允许声明为抽象方法,那么飞翔.游泳也能声明为抽象方法,并且鸡类涵盖的物种不够多,最好把这些行为动作扩展到鸟类这个群体,于是 ...

  5. Java开发笔记(五十九)Java8之后的扩展接口

    前面介绍了接口的基本用法,有心的朋友可能注意到这么一句话“在Java8以前,接口内部的所有方法都必须是抽象方法”,如此说来,在Java8之后,接口的内部方法也可能不是抽象方法了吗?之所以Java8对接 ...

  6. Java开发笔记(六十五)集合:HashSet和TreeSet

    对于相同类型的一组数据,虽然Java已经提供了数组加以表达,但是数组的结构实在太简单了,第一它无法直接添加新元素,第二它只能按照线性排列,故而数组用于基本的操作倒还凑合,若要用于复杂的处理就无法胜任了 ...

  7. Java开发笔记(六十七)清单:ArrayList和LinkedList

    前面介绍了集合与映射两类容器,它们的共同特点是每个元素都是唯一的,并且采用二叉树方式的类型还自带有序性.然而这两个特点也存在弊端:其一,为啥内部元素必须是唯一的呢?像手机店卖出了两部Mate20,虽然 ...

  8. Java开发笔记(九十五)NIO配套的文件工具Files

    NIO不但引进了高效的文件通道,而且新增了更加好用的文件工具家族,包括路径组工具Paths.路径工具Path.文件组工具Files.先看路径组工具Paths,该工具提供了静态方法get,输入某个文件的 ...

  9. Java开发笔记(八十七)随机访问文件的读写

    前面介绍了字符流读写文件的两种方式,包括文件字符流和缓存字符流,但是它们的写操作都存在一个问题:不管是write方法还是append方法,都只能从文件开头写入,而不能追加到文件末尾或者在文件中间某个位 ...

随机推荐

  1. Python练手例子(9)

    49.使用lambda来创建匿名函数. #python3.7 MAXIMUM = lambda x,y : (x > y) * x + (x < y) * y MINIMUM = lamb ...

  2. window server 2008 安装Oracle10g

    oracle安装都大同小异. 开始安装步骤 输入完之后点击下一步 这时候稍等一会儿. 这时候也要稍等一会儿. 直接安装. 设置口令管理,设置SCOTT的密码为tiger就好了. 这时候稍等一会儿. o ...

  3. JavaEE开发之基于Eclipse的环境搭建以及Maven Web App的创建

    本篇博客就完整的来聊一下如何在Eclipse中创建的Maven Project.本篇博客是JavaEE开发的开篇,也是基础.本篇博客的内容干货还是比较多的,而且比较实用,并且都是采用目前最新版本的工具 ...

  4. CoreProfiler升级到.NetStandard 2.0

    致所有感兴趣的朋友: CoreProfiler和相应的Sample项目cross-app-profiling-demo都已经升级到.NetStandrard 2.0和.NetCore 2.0. 有任何 ...

  5. Python函数声明以及与其他编程语言数据类型的比较

    1.函数声明 与其它大多数语言一样 Python 有函数,但是它没有像 C++ 一样的独立的头文件:或者像 Pascal 一样的分离的  interface / implementation 段.在需 ...

  6. [Swift]LeetCode970.强整数 | Powerful Integers

    Given two non-negative integers x and y, an integer is powerful if it is equal to x^i + y^j for some ...

  7. django启动server报错Error: That port is already in use.

    这种一般是端口错误,一般是要把端口关掉,这里提供了两种方法. 方法一:直接命令: sudo lsof -t -i tcp:8000 | xargs kill -9 方法二:脚本:名字manage.py ...

  8. 使用google搜索时的10个小技巧!

    为大家分享一些google的技巧,很多工作了好几年的同学还不知道如何高效的利用这些技巧,希望同学们掌握!此为google的技巧,百度现在也基本上都实现了这些功能.   使用搜索引擎的10个搜索技巧   ...

  9. Gin框架源码解析

    Gin框架源码解析 Gin框架是golang的一个常用的web框架,最近一个项目中需要使用到它,所以对这个框架进行了学习.gin包非常短小精悍,不过主要包含的路由,中间件,日志都有了.我们可以追着代码 ...

  10. Zabbix系列之七——添加磁盘IO监测

    zabbix给我们提供了一些较常用的监控模板,但现在我们如果想要监控我们磁盘的IO,这时候zabbix并没有给我们提供这么一个模板,所以我们需要自己来创建一个模板来完成磁盘IO的监控. 1. [roo ...