【Java复健指南12】OOP高级03-抽象类与接口
抽象类
引出
问题:
父类方法有时候具有不确定性
小结:
当父类的某些方法,需要声明,但是又不确定如何实现
时,可以将其声明为抽象方法,那么这个类就是抽象类
例子:
public class Abstract01 {
public static void main(String[] args) {
}
}
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
//思考:这里eat 这里你实现了,其实没有什么意义
//即: 父类方法不确定性的问题
//===> 考虑将该方法设计为抽象(abstract)方法
//===> 所谓抽象方法就是没有实现的方法
//===> 所谓没有实现就是指,没有方法体
//===> 当一个类中存在抽象方法时,需要将该类声明为abstract类
//===> 一般来说,抽象类会被继承,有其子类来实现抽象方法.
// public void eat() {
// System.out.println("这是一个动物,但是不知道吃什么..");
// }
public abstract void eat() ;
}
介绍
1)用abstract关键字来修饰一个类时,这个类就叫抽象类
访问修饰符abstract类名{
}
2)用abstract关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型 方法名(参数列 表);//没有方法体
3)抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类()
4)抽象类,是考官比较爱问的知识点,在框架和设计模式使用较多
注意事项
1)抽象类不能被实例化
public class AbstractDetail01 {
public static void main(String[] args) {
//抽象类,不能被实例化
//new A();
}
}
abstract class A {
}
2)抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
//抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法,还可以有实现的方法。
abstract class A {
public void hi() {
System.out.println("hi");
}
}
3)一旦类包含了abstract方法,则这个类必须声明为abstract【反之不一定成立】
//一旦类包含了abstract方法,则这个类必须声明为abstract
abstract class B {
public abstract void hi();
}
4)abstract只能修饰类和方法,不能修饰属性和其它的。
//abstract 只能修饰类和方法,不能修饰属性和其它的
class C {
// public abstract int n1 = 1;
}
5)抽象类可以有任意成员【因为抽象类本质还是类】,比如:非抽象方法、构造器、静态属性等等
//抽象类的本质还是类,所以可以有类的各种成员
abstract class D {
public int n1 = 10;
public static String name = "jk";
public void hi() {
System.out.println("hi");
}
public abstract void hello();
public static void ok() {
System.out.println("ok");
}
}
6)抽象方法不能有主体,即不能实现。
abstract void aaa()//{}
7)如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
abstract class E {
public abstract void hi();
}
abstract class F extends E {
}
class G extends E {
@Override
public void hi() { //这里相等于G子类实现了父类E的抽象方法,所谓实现方法,就是有方法体
}
}
8)抽象方法不能使用private、final和static来修
饰,因为这些关键字都是和重写相违背的。
抽象模板模式
需求
1)有多个类,完成不同的任务job
2)要求能够统计各自完成任务的时间
3)请编程实现
直觉
先用最容易想到的方法
例如有TestTemplate、AA、BB三个类
在AA和BB中分别定义两种计算任务(比如1加到100和1乘到100)
以AA为例,那么其中会有一个job方法
该方法定义了具体的计算任务,并调用诸如System.currentTimeMillis()这样的模块计算运行时间
public void job() {
//得到开始的时间
long start = System.currentTimeMillis(;long num = 0;
for (long i = 1; i <= 800000; i++) {
num += i;
}
//得的结束的时间
long end = System.currentTimeMillis(;
System.out.println("执行时间"+(end - start));
}
BB同理
问题
按上面的思路,AA和BB都会有一个job方法,里面计算时间的代码是重复的【重复点1】
这个好办,把这些代码抽象为一个父类即可,该父类中有一个例如calculateTime()的方法,专门计算运行时间
那么这个方法在该父类中应该类似下面的形式
public void calculateTime(){//实现一个方法去调用job
long start = System.currentTimeMillis();
job();//触发动态绑定机制,是aa调用就跳到AA,bb同理
//获取结束时间
long end = System.currentTimeMillis();
System.out.println("AA执行时间:"+(end-start));
}
现在又有一个问题,job()是AA和BB都有的方法,那么抽象到父类中,父类也肯定有一个job()
但是AA和BB中,job方法内的计算任务是不一样的,他们有可能都重写父类的方法,也可能不写
无论是哪种情况,父类都必须实例化一个默认的job()方法,这又造成代码的冗余【重复点2】
那么抽象类就有用了
将父类设计为一个抽象类,那么再写一个抽象方法job,抽象父类无需初始化它,子类用的时候再各自重写即可
优雅
解决
设计一个抽象类(Template),能完成如下功能:
1)编写方法calculateTime().可以计算某段代码的耗时时间
2)编写抽象方法job()
3)编写一个子类Sub,继承抽象类Template,并实现job方法。
4)编写一个测试类TestTemplate,看看是否好用。
抽象类Template
abstract public class Template {//抽象类-模板设计模式
public abstract void job();//抽象方法
public void calculateTime(){//实现一个方法去调用job
long start = System.currentTimeMillis();
job();//触发动态绑定机制,是aa调用就跳到AA,bb同理
//获取结束时间
long end = System.currentTimeMillis();
System.out.println("AA执行时间:"+(end-start));
}
}
子类AA
public class AA extends Template{
//计算任务
//1加到10000
public void job(){//实现父类Template的抽象方法job
int num = 0;
for (int i = 0; i <= 800000; i++) {
num += i;
}
}
}
子类BB
public class BB extends Template{
//计算任务
public void job(){
int num = 0;
for (int i = 0; i <= 8000000; i++) {
num *= i;
}
}
}
测试
public class TestTemplate {
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime();
BB bb = new BB();
bb.calculateTime();
}
}
=================================================
AA执行时间:2
BB执行时间:15
接口
定义
接口就是给出一些没有实现的方法,封装到一起,到某个
类要使用的时候,在根据具体情况把这些方法写出来。
语法
interface 接口名{
//属性
//方法
// 1.抽象方法
// 2.默认实现方法
// 3.静态方法
}
class 类名 implements 接口{
自己属性;
自己方法;
必须实现的接口的抽象方法;
}
小结:
1.在Jdk7.0前,接口里的所有方法都没有方法体。
2 Jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现。
接口初体验
例如,在实际开发中,项目经理定义一些接口,由程序员去具体实现。【假设需求是连接数据库】
接口类
public interface DBInterface { //项目经理负责写的
public void connect();//连接方法
public void close();//关闭连接
}
MySQL类
具体去实现项目经理定义的接口
//假设这是A程序员的工作
public class MysqlDB implements DBInterface {
@Override
public void connect() {//改一点名字都不行,必须与接口定义的一样
System.out.println("连接mysql");
}
@Override
public void close() {
System.out.println("关闭mysql");
}
}
Oracle类
//假设这是B程序员的工作,连接Oracle
public class OracleDB implements DBInterface{
@Override
public void connect() {
System.out.println("连接oracle");
}
@Override
public void close() {
System.out.println("关闭oracle");
}
}
使用
public class Interface03 {
public static void main(String[] args) {
MysqlDB mysqlDB = new MysqlDB();
t(mysqlDB);
OracleDB oracleDB = new OracleDB();
t(oracleDB);
}
public static void t(DBInterface db) {
db.connect();
db.close();
}
}
=====================================
连接mysql
关闭mysql
连接oracle
关闭oracle
总结
在开发中使用接口可以统一程序员的工作成果,提高效率。
细节与注意事项
1)接口不能被实例化。
- 因为接口本身是希望别的类来实现它,然后再创建实现了接口的具体类的实例
- 因此接口不能实例化
2)接口中所有的方法是public方法。接口中抽象方法可以不用abstract修饰。
3)一个普通类实现接口,就必须将该接口的所有方法都实现。
4)抽象类实现接口,可以不用实现接口的方法。
5)一个类同时可以实现多个接口
//一个类同时可以实现多个接口
class Pig implements IB,IC {
@Override
public void hi() {
}
@Override
public void say() {
}
}
6)接口中的属性只能是final的,而且是public
static final修饰符。
比如:int a=1;
实际上是public static final int a=1;(必须初始
7)接口中属性的访向形式:
接口名.属性名
8)一个接口不能继承其它的类,但是可以继承多个别的接口
//接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB,IC {
}
9)接口的修饰符只能是public和默认,这点和类的修饰
符是一样的。
小练习
public class InterfaceExercise01 {
public static void main(String[] args) {
B b = new B();//ok
System.out.println(b.a); //b是对象实例,访问一个public的属性a,没问题,23
System.out.println(A.a); //接口.static类型的属性,没问题,23
System.out.println(B.a); //B实现了A接口,因此可以使用接口中的属性,没问题,23
}
}
interface A {
int a = 23; //接口中的属性等价于 public static final int a = 23;
}
class B implements A {//A接口没有抽象方法,因此此处语法正确
}
接口与继承
简单来说,接口是对Java单继承机制的一种补充
看下面的例子
LittleMonkey通过继承父类Monkey得到了climbing()方法
再通过接口,实现了swimming()和flying()。无形之中拓展了LittleMonkey的功能
public class ExtendsVsInterface {
public static void main(String[] args) {
LittleMonkey wuKong = new LittleMonkey("悟空");
wuKong.climbing();
wuKong.swimming();
wuKong.flying();
}
}
//猴子
class Monkey {
private String name;
public Monkey(String name) {
this.name = name;
}
public void climbing() {
System.out.println(name + " 会爬树...");
}
public String getName() {
return name;
}
}
//接口
interface Fishable {
void swimming();
}
interface Birdable {
void flying();
}
//继承
//小结: 当子类继承了父类,就自动的拥有父类的功能
// 如果子类需要扩展功能,可以通过实现接口的方式扩展.
// 可以理解 实现接口 是 对java 单继承机制的一种补充.
class LittleMonkey extends Monkey implements Fishable,Birdable {
public LittleMonkey(String name) {
super(name);
}
@Override
public void swimming() {
System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳...");
}
@Override
public void flying() {
System.out.println(getName() + " 通过学习,可以像鸟儿一样飞翔...");
}
}
- 接口和继承解决的问题不同
继承的价值主要在于:解决代码的复用性和可维护性。
接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。- 接口比继承更加灵活
接口比继承更加灵活,继承是满足 is - a 的关系【如猫是动物】, 而接口只需满足 like - a 的关系【如猴子像人】接口在一定程度上实现代码解耦
接口多态特性
多态数组
定义一个接口以及对应的实现接口的类
interface Usb{
void work();
}
class Phone_ implements Usb {
public void call() {
System.out.println("手机可以打电话...");
}
@Override
public void work() {
System.out.println("手机工作中...");
}
}
class Camera_ implements Usb {
@Override
public void work() {
System.out.println("相机工作中...");
}
}
在main方法中
public class InterfacePolyArr {
public static void main(String[] args) {
//多态数组 -> 接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone_();
usbs[1] = new Camera_();
/*
给Usb数组中,存放 Phone 和 相机对象,Phone类还有一个特有的方法call(),
请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外,
还需要调用Phone 特有方法 call
*/
for(int i = 0; i < usbs.length; i++) {
usbs[i].work();//动态绑定机制
//和前面一样,我们仍然需要进行类型的向下转型,即类型判断
//因为接口类型数组中有不同的类型【Phone和Camera】
if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
((Phone_) usbs[i]).call();
}
}
}
}
多态参数
public class InterfacePolyParameter {
public static void main(String[] args) {
//接口的多态体现
//接口类型的变量 if01 可以指向 实现了IF接口类的对象实例
IF if01 = new Monster();//IF if01 = new IF();是错误的
if01 = new Car();
//继承体现的多态
//父类类型的变量 a 可以指向 继承AAA的子类的对象实例
AAA a = new BBB();
a = new CCC();
}
}
interface IF {}
class Monster implements IF{}
class Car implements IF{}
class AAA {
}
class BBB extends AAA {}
class CCC extends AAA {}
接口多态传递
展示多态传递现象
/**
* 演示多态的传递
*/
public class InterfacePolyPass {
public static void main(String[] args) {
//接口类型的变量可以指向,实现了该接口的类的对象实例
IG ig = new Teacher();
//如果IG 继承了 IH 接口,而Teacher 类实现了 IG接口
//那么,实际上就相当于 Teacher 类也实现了 IH接口.
//这就是所谓的 接口多态传递现象.
IH ih = new Teacher();
}
}
interface IH {
void hi();
}
interface IG extends IH{ }
class Teacher implements IG {
@Override
public void hi() {
}
}
接口练习题
代码有没有错误,有错误就改,改好后,看输出?
interface A{
int x = 0; }//等价public static final int x=0
class B{
int x = 1;}
class C extends B implements A {
public void pX(){
System.out.printin(x);//没有指明是父类的x还是接口的x,编译器会报错
public static void main(String[] args) {
new C().pX();
}
}
修改后
public class InterfaceExercise02 {
public static void main(String[] args) {
}
}
interface A {
int x = 0;
} //想到 等价 public static final int x = 0;
class B {
int x = 1;
} //普通属性
class C extends B implements A {
public void pX() {
//System.out.println(x); //错误,原因不明确x
//可以明确的指定x
//访问接口的 x 就使用 A.x
//访问父类的 x 就使用 super.x
System.out.println(A.x + " " + super.x);
}
public static void main(String[] args) {
new C().pX();
}
}
至此,类的五大成员已经学习了4个
即:
- 属性
- 方法
- 构造器
- 代码块
还差一个难点:**内部类
【Java复健指南12】OOP高级03-抽象类与接口的更多相关文章
- Java第五次作业--面向对象高级特性(抽象类与接口)
Java第五次作业--面向对象高级特性(抽象类与接口) (一)学习总结 1.在上周完成的思维导图基础上,补充本周的学习内容,对Java面向对象编程的知识点做一个全面的总结. 2.汽车租赁公司,出租汽车 ...
- 【Java复健指南09】项目练习全解--房屋出租系统
一个基于文本界面的综合练习,主要用于串联和回忆知识点,比较简单 各个界面的设计样式 主菜单 =============房屋出租系统菜单============ 1 新 增 房 源 2 查 找 房 屋 ...
- 【Java复健指南15】链表LinkedList及其说明
链表LinkedList by Java 之前有写过一些记录(引用),但是忘了乱了,现在重新梳理一遍 链表是Java中List接口的一种实现 定义(引用) 链表(linked list)是一种物理存储 ...
- Java基础学习笔记十二 类、抽象类、接口作为方法参数和返回值以及常用API
不同修饰符使用细节 常用来修饰类.方法.变量的修饰符 public 权限修饰符,公共访问, 类,方法,成员变量 protected 权限修饰符,受保护访问, 方法,成员变量 默认什么也不写 也是一种权 ...
- Java C++ 比较 – 虚函数、抽象函数、抽象类、接口
[转自]原文 Java – 虚函数.抽象函数.抽象类.接口 1. Java虚函数 虚函数的存在是为了多态. C++中普通成员函数加上virtual关键字就成为虚函数 Java中其实没有虚函数的概念,它 ...
- Java学习日记基础篇(六)—— 抽象类、接口、final
抽象类 为什么要有抽象类? 因为父类方法有不确定性,我们在Animal中定义了一个方法,但是它会被子类的方法覆盖掉,我们就不知道这个方法原本是做什么的 public class test1 { pub ...
- 面向对象 OOP中的抽象类,接口以及多态
[抽象类与抽象方法] 1.什么是抽象方法? 没有方法体{}的方法,必须使用abstract关键字修饰,这样的方法,我们称之为抽象方法. abstract function say() 2.什么是抽象类 ...
- Java(20)参数传递之类名、抽象类、接口
作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15201632.html 博客主页:https://www.cnblogs.com/testero ...
- Java编程的逻辑 (20) - 为什么要有抽象类?
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...
- java面向对象(三)之抽象类,接口,向上转型
java类 java类分为普通类和抽象类,接口,上一节我大概讲了java类的一般格式,今天将抽象类和接口.同时讲一下它们是怎样存储的. 最重要的是理解为什么要有抽象和接口,这样学下来你猜不会迷茫,才能 ...
随机推荐
- [转帖]看6大国产CPU加速替代,谁才是“王者”选手?
https://baijiahao.baidu.com/s?id=1761150458273739276&wfr=spider&for=pc 2023-03-23 17:33湖北匠心计 ...
- Sysbench的简单学习-编译与安装
sysbench的简单学习-编译与安装 摘要 github上面获取一下最新版本 https://github.com/akopytov/sysbench 注意现在 2023.2.17 最新版是 sys ...
- vue3中provide和inject的使用
1.provide 和 inject 的讲解 provide和inject可以实现嵌套组件之间进行传递数据. 这两个函数都是在setup函数中使用的. 父级组件使用provide向下进行传递数据: 子 ...
- 动态添加input,然后获取所有的input框中的值
今天遇见一个问题. 点击按钮,动态添加input框(可以添加多个) 然后搜集用户在input中输入的值. 我刚刚在纠结,给input框中注入事件. 但是这样会很麻烦. 经过同事的指点. 我直接去拿v- ...
- 程序员必备!10款实用便捷的Git可视化管理工具
前言 俗话说得好"工欲善其事,必先利其器",合理的选择和使用可视化的管理工具可以降低技术入门和使用的门槛.我们在团队开发中统一某个开发工具的使用能够大大降低沟通成本,提高协作沟通效 ...
- svn忽略某个目录后update出现fetching
忽略某个子目录 在svn udpate一个大目录时忽略特定的子目录,主要是子目录下内容已经单独拉取过,并且这个大目录对于程序来说,可以是只读的. 操作方法:选中要忽略的目录,右键 svn - Unve ...
- MybatisPlus对Mysql数据库关键字作为列名的处理--SQLSyntaxErrorException: You have an error in your SQL syntax;
说明: 在设计数据库时,使用mysql关键字作为列名(比如order用于排序),就会报错:java.sql.SQLSyntaxErrorException: You have an error in ...
- 小白学k8s(1)二进制部署k8s
二进制部署k8s 前言 准备工作 关闭防火墙 关闭 swap 分区 关闭 SELinux 更新系统时间 秘钥免密码 设置主机名称 服务器角色 安装etcd 创建证书 生成证书 部署Etcd 在Node ...
- 基于无监督训练SimCSE+In-batch Negatives策略有监督训练的语义索引召回
基于无监督训练SimCSE+In-batch Negatives策略有监督训练的语义索引召回 语义索引(可通俗理解为向量索引)技术是搜索引擎.推荐系统.广告系统在召回阶段的核心技术之一.语义索引模型的 ...
- 从github上下载代码到本地
相关链接: 码云(gitee)配置SSH密钥 码云gitee创建仓库并用git上传文件 git 上传错误This oplation equires one of the flowi vrsionsot ...