在《Java中的抽象方法和接口》中,介绍了抽象方法与接口,以及做了简单的比较。

  这里我想详细探讨下抽象类。

  

  一、抽象类的定义

  被关键字“abstract”修饰的类,为抽象类。(而且,abxtract只能修饰类和方法)

  下面显示了一个最简单的空抽象类

public abstract class AbstractClass {
public static void main(String[] args) {
AbstractClass abstractClass=new AbstractClass();
}
}

  当对这个空的抽象类进行实例化时,编译器会报错:

  'AbstractClass' is abstract; cannot be instantiated'

  现在对这个抽象类进行扩展,添加属性和方法:

public abstract class AbstractClass {
public int constInt = 5; /*重载method()*/
public void method() { } //没有编译错误
public abstract void method(int a); public static void main(String[] args) {
AbstractClass abstractClass=new AbstractClass() {
@Override
public void method(int a) {
System.out.println("实例化抽象类");
}
};
System.out.println(abstractClass.constInt);
abstractClass.method(5);
}
} //运行结果
/*
  5
  实例化抽象类
*/

  在这个抽象类中我添加了一个实例属性,一个抽象方法,以及该抽象方法的重载实例方法,这些都是合法的。

  在main方法中,直接对抽象类通过new操作符进行实例化,出乎意料的是,IDE直接提示了这种操作——这里生成了一个匿名内部类(Anonymous Inner)。

  下面是关于匿名内部类的知识点:

  • 一种特殊的局部内部类,没有类名,没有class关键字,也不能使用extends和implements等关键字修饰。
  • 匿名内部类不能是抽象类(即不能拥有抽象方法),必须实现它的抽象父类或者接口的所有抽象方法。
  • 因为没有类名,所以匿名内部类只能被使用一次,通常用来简化代码编写。
  • 使用匿名内部类的前提条件:继承一个父类或者实现一个接口。

  第四点和第一点略有矛盾,其实就是使用的时候通过new操作符指定要继承的父类或要实现的接口(事实上,new是直接调用某个构造函数。new真的是个牛逼的操作符啊),然后再通过直接定义类体(类体中实现某些方法),构建新类的实例。

  所以,在我上面的代码中,生成了一个继承AbstractClass的新匿名内部类的实例,这个类中实现了父类的抽象method方法,获得的实例我们赋给了abstractClass,并通过实例调用了新的method(int 5)方法。

  二、抽象类与抽象方法

  抽象方法只有方法声明,没有方法体的方法。它将由子类(可能是抽象类,也可能是非抽象类)进行实现。

  通过上面空的抽象类的方法可知,拥有一个抽象方法并不是构建一个抽象类充分必要条件。

  那么,一个有抽象方法的普通类是合法的吗?大概率是不合法的,因为如果这样的设计是合法的又有什么意义呢?

  实际上,如果我们在一个非抽象类中定义一个抽象方法,IDE会提示:

  “Abstract method in non-abstract class”

  而如果我们一定要运行如下所示的一段错误代码:

public class AbstractTest {

    public abstract void test();

    public static void main(String[] args) {

    }
}

  编译器的报错信息为:

  Error:java: AbstractTest不是抽象的, 并且未覆盖biguo.classConstruct.AbstractTest中的抽象方法test()

  所以抽象方法只能存在于抽象类中。

  三、抽象方法可以是static的吗?

  在进行下面的测试时,突然想到一个很有意思的问题,抽象类中的抽象方法可以是静态的吗?

  先下结论:NO,这是的修饰符组合是不合法的——Error: java: 非法的修饰符组合: abstract和static

public abstract class AbstractTest {
 //非法的修饰符组合
public static abstract void test(); public static void main(String[] args) { }
}

  static成员方法意味着,不需要实例化可以使用(在类的内部或者通过类访问)。但是呢,也可以通过实例进行访问,这样做不会报错,但会得到IDE的警告,因为违反了static的设计语义;

  abstract方法意味着没有方法体(区别下“有方法体,但方法体为空”),即只是一个方法声明,需要被子类去实现。

  我们先要清楚,抽象类中是可以拥有static方法的,比如,我们把main方法放在一个抽象类中,程序是可以由此运行的。

  既然这样,一个“static abstract”组合的方法,对这个抽象类完全没有存在的意义了!!因为它没有方法体,无法通过类来使用。

  在StackOverFlow上有探讨,说完全可以允许这样的“static abstract”方法,因为在非抽象子类中,实现这个抽象方法后的子方法仍然是static,是子类的类方法。

  这样的说法有一点点意义,但是它仍然无法解决的是,“static abstract”方法对父类中“static”语义的违背

  static方法可以被子类重写吗?

  答案是:static方法不能被子类重写。(涉及到重写的定义)

  但是!我们确实又可以在子类中重新定义一个与父类static方法一模一样的方法,如下的test()方法。

package biguo.classConstruct;

public class AbstractTest {

    public static  void test(){
System.out.println("This is AbstractTest's static test!");
} public static void print(){
System.out.println("This is AbstractTest's static print!");
} public static void main(String[] args) {
AbstractTest.test();
AbstractTestSon.test();
AbstractTestSon.print();
System.out.println(); AbstractTestSon abstractTestSon=new AbstractTestSon();
abstractTestSon.print();
abstractTestSon.test();
}
} class AbstractTestSon extends AbstractTest{
public static void test(){
System.out.println("This is AbstractTest-Son's static test!");
}
} //输出
/*
This is AbstractTest's static test!
This is AbstractTest-Son's static test!
This is AbstractTest's static print! This is AbstractTest's static print!
This is AbstractTest-Son's static test!
*/ 

  通过输出结果可以看到:父类中未被子类重写的static方法是可以被子类及其对象访问到的,但是被子类重写过的方法,则子类及其对象只能调用自己的方法了。

  为什么这种情况不能叫做子类“重写”了父类的方法呢,而是叫”方法隐藏(method hidding)“?

  在www.geeksforgeeks.org的这篇文章中,对此做了解释:

  因为,方法重写(Overriding)是OOP语言的一种特性,在Java中,它是实现“运行时多态”一种方式!!而父类子类的相同签名的同名方法的情况,there won’t be any run-time polymorphism。

  还篇文章(”5 Java concepts explained: Overloading, overriding, shadowing, hiding, and obscuring“),解释了这些概念的差别,但是却没有提到上述的method hidding情况。(所以我以为隐藏只是继承关系的类中变量之间的行为)。

  四、抽象类的继承

  从抽象父类派生的子类如果不能实现所有的抽象方法,它也必须声明为抽象的。

  抽象类可以定义构造方法,且能被子类在构造方法中调用。

  一个非抽象类的子类,可以声明为抽象类。

  五、final与abstract的矛盾

  final关键字可以修饰类、方法、变量。

  final修饰的类不能被派生;final修饰的方法,禁止子类重写。

  所以我们可以看出,final和abstract就是冰火不容的~

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

  1. Java抽象类 详解

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

  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. Git 中无法忽略 .xcuserstate 的解决方法

    1.查看代码变化git status 2.接着输入 git rm –cached 刚才复制的地址 ,如下.git rm --cached RxSwift/Rx.xcodeproj/xcuserdata ...

  2. 2018-2019-2 20165210《网络对抗技术》Exp9 Web安全基础

    2018-2019-2 20165210<网络对抗技术>Exp9 Web安全基础 实验目的 本实践的目标理解常用网络攻击技术的基本原理. 实验内容 安装Webgoat SQL注入攻击 - ...

  3. 在SQLAlchemy ORM中动态变更表名

    在开发过程中,经常会遇到几张表结构相同,仅仅表名不一样.这在直接使用SQL语句进行查询的环境中处理起来很简单,但如果使用了SQLAlchemy ORM之后,因在model定义时就确定了表名,就需要用其 ...

  4. 常见Web攻击及解决方案

    DoS和DDoS攻击 DoS(Denial of Service),即拒绝服务,造成远程服务器拒绝服务的行为被称为DoS攻击.其目的是使计算机或网络无法提供正常的服务.最常见的DoS攻击有计算机网络带 ...

  5. cnpm与npm指定有什么区别?

    CNPM跟NPM用法完全一致,只是在执行命令时将故宫改为CNPM. 因为故宫安装插件是从国外服务器下载,受网络影响大,可能出现异常,如果故宫的服务器在中国就好了,所以我们乐于分享的淘宝团队干了这事来自 ...

  6. 阶段5 3.微服务项目【学成在线】_day04 页面静态化_20-页面静态化-静态化测试-填写页面DataUrl

    启动前端和后端.轮播图的数据url可以在这里修改. 找到列表页面的轮播图,然后点击编辑 随便更新一个地址测试 提交后数据再次编辑 发现url没有变化 在pageService里面update方法把更新 ...

  7. UICollectionview的头视图和尾视图

    UITableView有头视图和尾视图,那么UICollectionView有没有头视图和尾视图呢? 答案是有的. 1.新建一个类,必须继承自 UICollectionReusableView. 2. ...

  8. Laravel核心代码学习

    原文地址:https://github.com/kevinyan815/Learning_Laravel_Kernel

  9. Re-ranking Person Re-identification with k-reciprocal Encoding

    Re-ranking Person Re-identification with k-reciprocal Encoding Abstract In this paper, we propose a ...

  10. ES 数据类型

    官网数据类型网址 有价值的参考博客 本文 Elasticsearch 版本为 7.2 1. 核心数据类型 (1)字符串类型: text, keyword (2)数字类型:long, integer, ...