13. 抽象类 & 接口
一、抽象类
// 抽象类Shape
public abstract class Shape
{
// 1. 成员变量
private String color; // 2. 初始化块
{
System.out.println("执行Shape的初始化块...");
} // 3. 构造器
public Shape() {}
public Shape(String color)
{
System.out.println("执行Shape的构造器...");
this.color = color;
} // 4. 方法:抽象方法和普通方法都可以
public abstract double calPerimeter();
public void setter(String color)
{
this.color = color;
} // 5. 内部类(接口、枚举)
}
1. 抽象方法
- 抽象方法没有方法体(只有方法签名,没有方法实现),只能由子类提供实现(即重写)
- 抽象方法必须能被其子类重写才有意义,即private和abstract不能同时修饰某个方法
- final修饰的方法不能被重写,因此final和abstract不能同时修饰某个方法
- 类方法不能被定义成抽象方法,即static和abstract不能同时修饰某个方法
- 含有抽象方法的类只能被定义成抽象类
2. 抽象类
- 抽象类不能被实例化(不能创建抽象类的实例),只能当作父类被其他子类继承
- final修饰的类不能被继承,因此final和abstract永远不能同时使用
- 子类只有实现抽象父类里的所有抽象方法,才能被定义成普通类
3. 抽象类的成员:成员变量、方法、初始化块、构造器、内部类(接口、枚举)
- 抽象类里可以没有抽象方法
- 抽象类中的初始化块和构造器都不是在创建该类对象时被调用的(抽象类的构造器不能用于创建实例),而是在创建其子类的实例时被调用
4. 利用抽象类可以发挥多态的优势,使程序更加灵活
// 下面定义一个Circle普通类,其必须实现Shape里的抽象方法
public class Circle extends Shape
{
private double radius;
public Circle(String color, double radius)
{
super(color);
this.radius = radius;
} public void setRadius(double radius)
{
this.radius = radius;
} // 重写Shape类的计算周长的抽象方法
public double calPerimeter()
{
return 2 * Math.PI * radius;
} public static void main(String[] args)
{
// 定义Shape类型的引用变量s,s指向Circle对象
Shape s = new Circle("黄色", 3); // 由于在Shape类中定义了calPerimeter()方法,所以程序可以直接调用变量s的calPerimeter()方法
// 无须将变量s强制转换为其子类类型
System.out.println(s.calPerimeter());
}
}
5. 抽象类的作用
不同Shape子类对周长的计算方法是不一样的,即Shape类无法准确地知道其子类计算周长的方法,故Shpe类不知道如何实现calPerimeter()方法。此时若不在Shape类中定义calPerimeter()方法,则Shape引用变量s虽然可以引用到Shape子类的实例,但该变量s却无法调用calPerimeter()方法,必须将其强制转换为其子类类型才行,这就降低了程序的灵活性。这种情况下,我们可以在Shape类中将calPerimeter()方法定义为抽象方法,其具体实现交给子类来完成。也就是说,抽象父类把不能实现的方法定义为抽象方法,留给其子类去实现。
- 从语义的角度来看,抽象类是从多个具体类中抽象出来的父类,它具有更高层次的抽象
- 从多个具有相同特征的类中抽象出一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性
- 抽象类体现的是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会大致保留抽象类的行为方式
二、接口
/*
* 示例:定义一个接口
*/
[修饰符] interface 接口名 extends 父接口1, 父接口2...
{
// 成员1:静态常量
[public static final int] MAX_SIZE = 50; // 成员2:抽象方法
[public abstract] void getData(String msg); // 成员3:默认方法
[public] default void test()
{
System.out.println("默认的test()方法");
} // 成员4:类方法
[public] String staticTest()
{
return "接口里的类方法";
} // 成员5:私有方法
private void foo()
{
System.out.println("foo私有方法");
}
private static void bar()
{
System.out.println("bar私有静态方法");
} // 成员6:内部类、内部接口、内部枚举
}
1. 定义接口概述
- 定义接口使用interface关键字
- 修饰符可以是public或省略,如果省略了public访问控制符,则默认采用包权限访问控制符
- 接口名应与类名采用相同的命名规则,通常使用形容词
- 接口只能继承接口,且接口支持多继承(可以有多个直接父接口)
- 一个Java源文件里最多只能有一个public接口,且如果某Java源文件里定义了一个public接口,则该文件名必须与public接口名相同
2. 接口的成员
由于接口里定义的是多个类共同的公共行为规范,因此接口里的成员(除了私有方法)都是public访问权限。
定义接口成员(除了私有方法)时,既可以显式使用public修饰符,也可以省略不写(此时系统会自动添加public修饰符)。
①静态常量
- 接口里的成员变量只能是静态常量,它们是接口相关的
- 在接口中定义成员变量时,不管是否使用public static final修饰符,接口里的成员变量总是使用这三个修饰符来修饰(系统自动添加)
- 由于接口里没有初始化块和构造器,因此接口里的静态常量只能在定义时指定默认值
②抽象方法(无方法体)
- 由于接口里定义的方法只能是抽象方法、类方法、默认方法和私有方法,因此如果不是定义后三种方法,系统将自动为普通方法增加abstract修饰符
- 定义接口里的普通方法时,不管是否使用public abstract修饰符,接口里的普通方法总是使用public abstract来修饰(系统自动添加)
③默认方法(带方法体)
- 默认方法必须使用default修饰,且不能使用static修饰,故不能直接使用接口来调用
- 定义接口里的默认方法时,不管是否使用public修饰符,接口里的默认方法总是使用public来修饰(系统自动添加)
- 接口的默认方法其实就是实例方法,只是多了个default修饰符而已
④类方法(带方法体)
- 类方法必须使用static修饰,且不能使用default修饰,可以直接使用接口来调用
- 定义接口里的类方法时,不管是否使用public修饰符,接口里的默认方法总是使用public来修饰(系统自动添加)
⑤私有方法(带方法体)
- Java9增加了私有方法,这是因为:Java8允许在接口中定义带方法体的默认方法和类方法,那么当两个默认方法(或类方法)中包含一段相同的实现逻辑时,程序必然考虑将这段实现逻辑抽取成工具方法,而工具方法时应该被隐藏的,故Java9增加了私有方法。
⑥内部类、内部接口、内部枚举
- 接口里的内部类、内部接口、内部枚举默认采用public static来修饰(系统自动添加)
3. 接口的作用
- 抽象类是从多个类中抽象出来的模板,如果将这种抽象进行得更彻底,那就得到了“接口”
- 接口定义了某一批类所需要遵守的规范,接口不关心这些类的内部状态数据和方法实现细节,它只规定这些类里必须提供哪些方法
- 接口是从多个相似类中抽象出来的规范,接口不提供任何实现,它体现的是规范和实现分离的设计哲学
- 接口作为系统与外界交互的窗口,它规定了实现者必须向外提供哪些服务,还规定了调用者可以调用哪些服务以及如何调用这些服务
- 当在一个程序中使用接口时,接口是多个模块间的耦合标准;当在多个程序之间使用接口时,接口是多个程序之间的通信标准
4. 抽象类 & 接口
- 抽象类体现的是一种模板式设计,它作为系统中多个子类的抽象父类,已经实现了系统的部分功能(那些已经提供实现的方法)
- 接口体现的是一种规范,它是系统的“总纲”,制定了系统各模块应该遵循的标准,故一个系统的接口不应该经常改变
三、面向接口编程
1. 接口的三大用途
- 定义引用类型变量,该变量必须引用到接口实现类的对象,该变量也可进行强制类型转换
- 调用接口中定义的常量
- 被实现类实现
2. 类实现接口
// 类实现接口的语法格式
[修饰符] class 类名 extends 父类 implements 接口1, 接口2...
{
}
- 类实现接口使用implements关键字
- 一个类可以继承一个父类,并同时实现一个或多个接口,implements部分必须放在extends部分之后
- 类实现接口时,该类将获得接口中定义的静态常量、方法等,故可以把实现接口理解为一种特殊的继承
- 类实现接口时,必须完全实现接口里所定义的全部抽象方法,否则,该类将保留从父接口那里继承到的抽象方法,该类也必须定义成抽象类
- 实现接口方法时,必须使用public访问控制修饰符,因为接口里的方法都是public的,故实现类实现接口里的方法时只能使用public访问权限
3. 模拟多继承
interface Output
{
// 静态常量,系统自动添加public static final修饰符
int MAX_CACHE_LINE = 50;
} interface Product
{
// 抽象方法,系统自动添加public abstract修饰符
int getProduceTime();
} // 让Printer类实现Output和Product接口
public class Printer implements Output, Product
{
public int getProduceTime()
{
return 45;
} public static void main(String[] args)
{
// ex1:创建一个Printer对象,当成Output使用
Output o = new Printer(); // ex2:创建一个Printer对象,当成Product使用
Product p = new Printer();
System.out.println(p.getProduceTime()); // ex3:所有接口类型的引用变量都可直接赋给Object类型的变量
Object obj = p;
}
}
- Printer类实现了Output接口和Product接口,故Printer对象既可直接赋给Output变量,也可直接赋给Product变量
- 彷佛Printer类既是Output类的子类,也是Product类的子类,这就是Java提供的模拟多继承,通过实现多个接口可以弥补单继承的不足
- 虽然接口不能显式继承任何类,但所有接口类型的引用变量都可直接赋给Object类型的引用变量
13. 抽象类 & 接口的更多相关文章
- 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait
[源码下载] 速战速决 (4) - PHP: 类基础, 抽象类, 接口, trait 作者:webabcd 介绍速战速决 之 PHP 类基础 抽象类 接口 trait 示例1.类的相关知识点 1(基础 ...
- python 全栈开发,Day21(抽象类,接口类,多态,鸭子类型)
一.昨日复习 派生方法和派生属性 super 只有在子父类拥有同名方法的时候, 想使用子类的对象调用父类的方法时,才使用super super在类内 : super().方法名(arg1,..) 指名 ...
- python面向对象 : 抽象类(接口类),多态,封装(私有制封装)
一. 抽象类(接口类) 与java一样, python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类, 它的特殊之处在于只能被继承, 不能被实例化. 从设计角度去看, 如果类是从现实对 ...
- 【学习笔记】--- 老男孩学Python,day18 面向对象------抽象类(接口类), 多态, 封装
抽象类,接口类 Python没有接口这个概念 抽象类(接口类): 目的是制定一个规范 要学会归一化设计,有重复的东西就要想把它们合并起来 from abc import ABCMeta, abstra ...
- “全栈2019”Java第一百零六章:匿名内部类与抽象类接口注意事项
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 面向对象 继承 抽象类 接口 static 权限修饰符
Day01 面向对象 继承 抽象类 接口 static 1.匿名对象是指创建对象时,只有创建对象的语句,却没有把对象地址值赋值给某个变量. 2.类的继承是指在一个现有类的基础上去构建一个新的类,构建出 ...
- python day - 19 抽象类 接口类 多态 封装
一. 抽象类接口类即制定一个规范 特点: 1.不可被实例化. 2.规范子类当中必须事先某个方法. 3.在python中有原生实现抽象类的方法,但没有原生实现接口类的方法. 例题:制定一个规范就是,子类 ...
- C# 你什么让程序员寂寞成酱紫 (男生版 娱乐中学习 抽象类 接口 继承 实现方法 )
你什么让程序员寂寞成酱紫 (男生版 娱乐中学习 抽象类 接口 继承 实现方法 ) 一个家庭 相当于 一个空间,这个空间里 有 很多元素,比如 爱,爱这个抽象事物,可能有很多动作,接吻.交流,对于一 ...
- 面向对象的理解 抽象类&接口
一.关于面向对象 1.什么是面向对象 在解释面向对象之前,先说说面向过程.学过C的同学都知道,C就是面向过程的一种语言.那什么是面向过程呢?比方说组装主机,对于面向过程,需要从0开始.买cpu ...
随机推荐
- Thymeleaf对象的使用:基本对象
Thymeleaf中有许多内置对象,可以在模板中实现各种功能.下面有几个基本对象.Web对象常用有:request.session.servletContext.Thymeleaf提供了几个内置变量p ...
- PHP 部分语法(二)
array() 创建数组: 1.数值数组:带数字 ID 键的数组 2.关联数组:带有指定键的数组,键关联一个值 3.多维数组:包含一个或多个数组的数组 $arr = array("Hello ...
- 使用 vs code 创建 Django 项目
操作流程: 1.前期准备工作 2.vs code配置Python环境 3.新建 Django 项目 4.vs code 配置 Debug Django 环境 5.浏览器查看效果 1.前期准备工作 安装 ...
- 001 C/C++ 选择排序法
简单选择排序: 选择排序法 是对 定位比较交换法(也就是冒泡排序法) 的一种改进. 选择排序的基本思想是:每一趟在n-i+1(i=1,2,…n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录 ...
- 题解:P2130 狂奔的Wzf
题目链接 solution 判断有无障碍的时候不需要以此枚举,利用前缀和,如果前缀为零证明没有障碍. 重复很多,写的很丑了,#3死活不过 #include<iostream> #inclu ...
- python中append的使用
没有系统地学习过python,最近在append的使用上遇到了大问题,吃到了苦头 之前一直单纯地认为通过append把数添加到list中,不需要提前开空间,非常便利,但却没有意识到这个过程并不是值传递 ...
- pytorch--基础类型之间的转换
在pytorch自己定义张量并进行计算的时候,往往会因为类型不匹配而报错,这里稍微记下pytorch之间的类型转换: 对tensor基础类型进行转换:比如说int().float().long().d ...
- angularjs 一篇文章看懂自定义指令directive
壹 ❀ 引 在angularjs开发中,指令的使用是无处无在的,我们习惯使用指令来拓展HTML:那么如何理解指令呢,你可以把它理解成在DOM元素上运行的函数,它可以帮助我们拓展DOM元素的功能.比如 ...
- 洛谷 P4999(数位DP)
###洛谷 P4999 题目链接 ### 题目大意:给你一个区间,求这段区间中所有数的,数位上的,数字之和. 分析: 这题与 洛谷 P2602 相似,稍微改一下就可以了. 求出 0 ~ 9 的个数,然 ...
- ansible小结(八)ansible-playbook简单使用
ansbile-playbook是一系统ansible命令的集合,其利用yaml 语言编写,运行过程,ansbile-playbook命令根据自上而下的顺序依次执行.同时,playbook开创了很多特 ...