java编程思想——抽象类和接口的详解(实例)

文章目录

  • 一、抽象类的概念和作用
    • 1.抽象的定义
    • 2.抽象类
    • 3.抽象方法的特点
    • 4.抽象类的特点
      • 抽象类和接口为什么不能被实例化的原因
    • 二、理解抽象类和接口
      • java类为什么是单继承?
  • 参考资料:

一、抽象类的概念和作用

1.抽象的定义

抽象是把多个事物的共性的内容抽取出来,本质就是把我们关注的内容抽取出来。(比如:宝马、奔驰都属于汽车,汽车是抽象出来的概念)

2.抽象类

Java中可以定义没有方法体的方法,该方法由其子类来具体的实现。该没有方法体的方法我们称之为抽象方法,含有抽象方法的类我们称之为抽象类。

3.抽象方法的特点

(1)只有方法头没有方法体的方法称之为抽象方法。(即只有方法的声明,没有方法的实现)
(2)抽象方法用abstract关键字来修饰。
(3)抽象方法代表一种不确定的操作或行为。(由子类去具体实现)
(4)抽象方法不能被调用。

4.抽象类的特点

(1)定义中含有抽象方法的类叫做抽象类。
(2)抽象类用abstract关键字来修饰。
(3)抽象类代表一种抽象的对象类型。
(4)抽象类不能实例化。
(5)抽象类中可以有具体方法,可以没有抽象方法。(也就是说一个类中只要有一个方法是抽象方法那么这个类一定是抽象类,反过来,一个抽象类中可以没有抽象方法,可以带有具体
实现的方法),抽象方法用abstract修饰,普通方法不用abstract修饰
(6)一旦一个类中有抽象方法,那么这个类必须也要用abstract来修饰,代表这个类是抽象类,它是不能被实例化的。

抽象类和接口为什么不能被实例化的原因

二、理解抽象类和接口

用一个适配器的例子来理解抽象类和接口

没有使用适配器的时候,实现接口的类要实现接口的所有方法,即便只是对其中一个方法进行了实现

在这里插入图片描述

建立一个抽象类实现接口(适配器概念),再用抽象类的子类重写想要实现的方法

在这里插入图片描述

tips:对于接口,你需要继承接口里边所有的实现,而建立一个抽象类实现接口,那么可以用一个普通类实现这个抽象类就可以实现你需要的方法,方便了许多。

下面就是一个抽象类和接口引发的血案:

涉及到

1)掌握抽象类和接口的实例化操作。
2)掌握模板设计的作用。
3)掌握工厂设计模式的作用。
4)掌握代理设计模式的作用。
5)掌握适配器模式的作用。
6)掌握抽象类与接口的使用区别。

具体内容呢?
1、在java中,可以通过对象的多态性,为抽象类和接口实例化,这样再使用抽象类和接口的时候就可以调用本子类中所覆写过的方法。
  之所以抽象类和接口不能直接实例化,是因为其内部包含了抽象方法,抽象方法本身是未实现的方法,所以无法调用。
  通过对象多态性可以发现,子类发生了向上转型关系之后,所调用的全部方法,都是被覆写过的方法。如下:

abstract class A{    // 定义抽象类A
    public abstract void print() ;    // 定义抽象方法print()
};
class B extends A {    // 定义子类,继承抽象类
    public void print(){        // 覆写抽象方法
        System.out.println("Hello World!") ;
    }
};
public class AbstractCaseDemo01{
    public static void main(String args[]){
        A a = new B() ;        // 通过子类为抽象类实例化,向上转型。
        a.print() ;
    }
};
运行结果:
Hello World!

可以继续利用此概念,为接口实例化。

package com.zhijin;
interface A{    // 定义抽象类A
    public abstract void print() ;    // 定义抽象方法print()
};
class B implements A {    // 定义子类,继承抽象类
    public void print(){        // 覆写抽象方法
        System.out.println("Hello World!!!") ;
    }
};
public class ThisDemo06{
    public static void main(String args[]){
        A a = new B() ;        // 通过子类为抽象类实例化
        a.print() ;
    }
};

所以,如果想要使用抽象类和接口,则只能按照以上操作完成。
2、抽象类的实际使用场景–设计模式(模板设计)

super : 动物
子类:猫、狗

abstract class Animal{
    private String name ;        // 定义name属性
    private int age ;            // 定义age属性
    public Animal(String name,int age){
        this.name = name ;
        this.age = age ;
    }
    public String getName(){
        return this.name ;
    }
    public int getAge(){
        return this.age ;
    }
    public void say(){        // 人说话是一个具体的功能
        System.out.println(this.getContent()) ;    // 输出内容
    }
    public abstract String getContent() ;    // 说话的内容由子类决定
};
class Cat extends Animal{
    private String call;
    public Cat(String name,int age,String call){
        super(name,age) ;    // 调用父类中的构造方法
        this.call= call ;
    }
    public String getContent(){
        return    " 姓名:" + super.getName() + 
                ";年龄:" + super.getAge() + 
                ";叫声:" + this.call;
    }
};
class Dog extends Animal{
    private String call ;
    public Dog (String name,int age,String call){
        super(name,age) ;    // 调用父类中的构造方法
        this.call= call;
    }
    public String getContent(){
        return    "姓名:" + super.getName() + 
                ";年龄:" + super.getAge() + 
                ";叫声:" + this.call ;
    }
};
public class AbstractCaseDemo02{
    public static void main(String args[]){
        Animal per1 = null ;    // 声明Animal 对象
        Animal per2 = null ;    // 声明Animal 对象
      per1 = new Cat("猫儿",2,"喵儿") ;    
        per2 = new Dog("小狗",10,"wangwang") ;    
        per1.call() ;    
        per2.call() ;  
    }
};
运行结果:
姓名:张三;年龄:2;喵儿
姓名:李四;年龄:10;wangwang

tips:抽象类就相当于一个模板,现实的模板:

在这里插入图片描述

3、接口的实际应用–制定标准

比如说“U盘和打印机都可以插在电脑上使用,因为他们都实现了USB标准的接口”

interface USB{        // 定义了USB接口
    public void start() ;    // USB设备开始工作
    public void stop() ;    // USB设备结束工作
}
class Computer{
    public static void plugin(USB usb){    // 电脑上可以插入USB设备,向上转型,这里就相当于一个接口,只有符合这个接口的标准的类的对象(即只有继承这个接口的类的对象),才能被这个方法调用。
        usb.start() ;
        System.out.println("=========== USB 设备工作 ========") ;
        usb.stop() ;
    }
};
class Flash implements USB{
    public void start(){    // 覆写方法
        System.out.println("U盘开始工作。") ;
    }
    public void stop(){        // 覆写方法
        System.out.println("U盘停止工作。") ;
    }
};
class Print implements USB{
    public void start(){    // 覆写方法
        System.out.println("打印机开始工作。") ;
    }
    public void stop(){        // 覆写方法
        System.out.println("打印机停止工作。") ;
    }
};
public class InterfaceCaseDemo02{
    public static void main(String args[]){
        Computer.plugin(new Flash()) ;
      Computer.plugin(new Print()) ;
    }
};
运行结果:
U盘开始工作。
=========== USB 设备工作 ========
U盘停止工作。
打印机开始工作。
=========== USB 设备工作 ========
打印机停止工作。

4、工厂设计模式–接口应用

interface Fruit{    // 定义一个水果接口
    public void eat() ;    // 吃水果
}
class Apple implements Fruit{
    public void eat(){
        System.out.println("** 吃苹果。") ;
    }
};
class Orange implements Fruit{
    public void eat(){
        System.out.println("** 吃橘子。") ;
    }
};
public class InterfaceCaseDemo03{
    public static void main(String args[]){
        Fruit f = new Apple() ;    // 实例化接口
        f.eat() ;
    }
};
运行结果:
吃苹果。

看起来上边的代码没有什么大问题,但是扩展性呢?
JVM工作原理:程序-》JVM(相当于客户端)-》操作系统。

问题的解决:客户端通过过渡端,得到特定子类的接口实例,返回接口实例给客户端,接口实例调用接口中的方法。
此时就涉及到了工厂模式

interface Fruit{    // 定义一个水果接口
    public void eat() ;    // 吃水果
}
class Apple implements Fruit{
    public void eat(){
        System.out.println("** 吃苹果。") ;
    }
};
class Orange implements Fruit{
    public void eat(){
        System.out.println("** 吃橘子。") ;
    }
};
class Factory{    // 定义工厂类
    public static Fruit getInstance(String className){ //注意这里的方法是static修饰的,因为在主方法中是Factory调用
        Fruit f = null ;
        if("apple".equals(className)){    // 判断是否要的是苹果的子类
            f = new Apple() ;
        }
        if("orange".equals(className)){    // 判断是否要的是橘子的子类
            f = new Orange() ;
        }
        return f ;
    }
};
public class InterfaceCaseDemo04{
    public static void main(String args[]){
        Fruit f = Factory.getInstance(”apple“) ;    // 实例化接口
        f.eat() ;
    }
};
运行结果:
**吃苹果

5、代理模式–接口应用
这里说的代理模式有很多种,讨债公司代理要讨债的人,江歌案中(日本律师代理江母),下面我们讲一下我们经常使用的代理服务器;

interface Network{  
    public void browse() ;    // 浏览
}
class Real implements Network{  //真实上网操作类
    public void browse(){
        System.out.println("上网浏览信息") ;
    }
};
class Proxy implements Network{  //代理类。
   private Network network ;    // 代理对象
    public Proxy(Network network){  //初始化,把真实对象传给代理对象,向上转型操作。
        this.network = network ;
    }
    public void check(){
        System.out.println("检查用户是否合法。") ;
    }
    public void browse(){
        this.check() ;
        this.network.browse() ;    // 调用真实的主题操作,这里调用的是真实类里的对象。
    }
};
public class ProxyDemo{
    public static void main(String args[]){
        Network net = null ;
        net  = new Proxy(new Real()) ;//  指定代理操作,这里两次向上转型操作,第一次向上是实例化代理类,
第二次向上转型是括号中,把真实类对象传入,以便在代理类中调用真实类中的方法。

        net.browse() ;    // 客户只关心上网浏览一个操作
    }
};
运行结果:
检查用户是否合法
上网浏览信息

5、适配器模式–上边刚开始的案例
在Java中,一个子类实现一个接口,则肯定在子类中复写此接口中全部抽象方法,那么这就造成提供的抽象方法过多,而实际不需要,那么我们会选择一个中间过渡(也就是适配器模式)

在这里插入图片描述

interface Window{        // 定义Window接口,表示窗口操作
    public void open() ;    // 打开
    public void close() ;    // 关闭
    public void activated() ;    // 窗口活动
    public void iconified() ;    // 窗口最小化
    public void deiconified();// 窗口恢复大小
}
abstract class WindowAdapter implements Window{
    public void open(){} ;    // 打开
    public void close(){} ;    // 关闭
    public void activated(){} ;    // 窗口活动
    public void iconified(){} ;    // 窗口最小化
    public void deiconified(){};// 窗口恢复大小
};
class WindowImpl extends WindowAdapter{
    public void open(){
        System.out.println("窗口打开。") ;
    }
    public void close(){
        System.out.println("窗口关闭。") ;
    }
};
public class AdapterDemo{
    public static void main(String args[]){
        Window win = new WindowImpl() ;
        win.open() ;
        win.close() ;
    }
};
运行结果:

窗口打开。
窗口关闭。

6、内部类的扩展

abstract class A{    // 定义抽象类
    public abstract void printA() ;    // 抽象方法
    interface B{    // 定义内部接口
        public void printB() ;    // 定义抽象方法
    }
};
class X extends A{    // 继承抽象类
    public void printA(){
        System.out.println("HELLO --> A") ;
    }
    class Y implements B{    // 定义内部类实现内部接口
        public void printB(){
            System.out.println("HELLO --> B") ;
        }
    };
};
public class InnerExtDemo01{
    public static void main(String args[]){
        A.B b = new X().new Y() ;  //参考前面内部类的知识,实现内部类实例的方法:外部类.内部类  内部类对象= 外部类对象.new  内部类
        b.printB() ;
    }
};
运行结果:
HELLO-->B

含有内部接口的抽象类的子类,必须也要定义内部类继承该接口。

interface A{    // 定义接口
    public void printA() ;    // 抽象方法
    abstract class B{    // 定义内部抽象类
        public abstract void printB() ;    // 定义抽象方法
    }
};
class X implements A{    // 实现接口
    public void printA(){
        System.out.println("HELLO --> A") ;
    }
    class Y extends B{    // 继承抽象类
        public void printB(){
            System.out.println("HELLO --> B") ;
        }
    };
};
public class InnerExtDemo02{
    public static void main(String args[]){
        A.B b = new X().new Y() ;
        b.printB() ;
    }
};

含有内部抽象类的接口,对于继承他的子类来说,除了要覆写接口中的抽象方法,还要专门定义一个内部类继承抽象类,并覆写全部抽象类。
 从开发中,一般不推荐,看起来这些代码有点混乱。

总结:

在这里插入图片描述


Tips:在开发中,一个类永远不要去继承一个已经实现好的类,要么继承抽象类,要么实现接口,如果两个类同时可以使用的话,优先使用接口,避免单继承的局限性。

java类为什么是单继承?

1)抽象类和接口类的实例化,通过多态性(向上转型,向下转型)。
2)抽象类表示一个模板,接口表示一个标准。
3)常见的设计模式:模板设计,工厂设计,代理设计,适配器设计。


参考资料:

抽象类和接口的详解(实例)

热门文章

暂无图片
编程学习 ·

ClassName(类名)命名

ClassName命名 ClassName的命名应该尽量精短、明确,必须以字母开头命名,且全部字母为小写,单词之间统一使用下划线 “_” 连接。 eg:.nav_top 注意事项 ad、banner、gg、guanggao 等有机会和广告挂勾的字眠不建议直接用来做ClassName,因为有些浏览器插件(Chrome的广告拦截…
暂无图片
编程学习 ·

元注解

元注解 元注解时定义注解的注解,是Java提供的用于定义注解的基本注解注解 说明@Retention 是注解类,实现声明类Class,声明类别Category,声明扩展Extension@Target 放在自定义注解上的上边,表明该注解可以使用的范围@Inherited 允许子类继承父类的注解,在子类中可以获取使…
暂无图片
编程学习 ·

SQL存储过程

什么是存储过程,如何创建一个存储过程 * Stored Procedure * 存储过程=SQL语句+流控制语句定义存储过程定义 create procedure 存储过程名称(【参数列表】) begin 需要执行的语句 end. 创建CREATE PROCEDURE `get_hero_scores`( OUT max_max_hp FLOAT, OUT min_max_mp FLO…
暂无图片
编程学习 ·

Python中%r和%s的相同点和不同点

1、在处理布尔型或者数字型时,二者是没有区别的 (1)数字型 I am %r years old%22#%r ‘I am 22 years old’ I am %s years old%22#%s‘I am 22 years old’ This building is %r m tall%22.35#%r‘This building is 22.35 m tall’ This building is %s m tall%22.35#%s‘Thi…
暂无图片
编程学习 ·

ngrok内网穿透的使用

**有时候项目没有部署到服务器,需要给用户展示效果,就可以用ngrok做内网穿透来解决这个问题** **ngrok的用法:**进入ngrok官网 http://www.ngrok.cc/,注册登录进入 在隧道管理中,进行开通隧道(即购买免费的服务器)3.开通隧道,配置端口ip,进行添加开通。4.开通后,在隧道管…
暂无图片
编程学习 ·

例题 6-18 雕塑(Sculpture,ACM/ICPC NWERC 2008,UVa12171)

原题链接:https://vjudge.net/problem/UVA-12171 分类:图 备注:离散化;floodfill 紫书思路:利用离散化把三维图缩小,用floodfill求出外围空气体积和内表面积,总体积减去空气体积即所求体积,内表面积即所求表面积。 离散化知识参考:https://blog.csdn.net/tlonline/art…
暂无图片
编程学习 ·

计算机网络知识点梳理(上)

计算机网络知识点梳理(上) 参考资料:计算机网络(哈尔滨工业大学:李全龙、聂兰顺)MOOC目录计算机网络知识点梳理(上)1. 基础1.1. 计算机网络结构1.2. 应用模型:1.2.1. 客户/服务器应用模型1.2.2. 对等应用模型(P2P)1.3. 交换设备与交换网络1.4. 多路复用技术1.5. 性能1.5…
暂无图片
编程学习 ·

springboot aop 切到service层,不生效

今天发现一个问题,使用aop切到service层方法上,idea会有切成功的标志,编译也不报错,但就是不生效。研究了下发现,是因为我切的方法被同一个service中的其他方法调用,这样的话就不生效了,暂不清楚原因,解决方法时切到调用它的方法上,这只是切点不生效的一种情况,希望能…
暂无图片
编程学习 ·

使用python下载文件

使用requestspython 3.71 下载指定文件 import requestsurl = https://images.jjxsw.la/images/mijjxswcom.gif req = requests.get(url) with open(a.swf, wb) as code:code.write(req.content)
暂无图片
编程学习 ·

mmdetection训练出现:IndexError: list index out of range 错误

mmdetection训练出现:IndexError: list index out of range 错误文章目录:1 问题分析1.1 尝试解决错误:第一次1.2 尝试解决错误:第二次2 我的问题解决方式我的环境:Ubuntu18.04 TorchVision: 0.6.0 OpenCV: 4.2.0 MMCV: 0.5.5 MMDetection: 2.0.0+d9c8f14 MMDetection Com…
暂无图片
编程学习 ·

Spring-boot 使用undertow代替tomcat

Undertow是Red Hat公司的开源产品, 是一款灵活的高性能Web服务器,它完全采用Java语言开发,可以直接嵌入到Java项目中使用,支持阻塞IO和非阻塞IO。由于Undertow采用Java语言开发。 Undertow在高并发业务场景中,性能优于Tomcat,对于并发要求不高的情况下,二者差别不大。 Un…
暂无图片
编程学习 ·

模板字符串

模板字符串是在 JS 中创建字符串的一种新方法。我们可以通过使用反引号使模板字符串化。//ES5 Versionvar greet = Hi I\m Mark;//ES6 Versionlet greet = `Hi Im Mark`;在 ES5 中我们需要使用一些转义字符来达到多行的效果,在模板字符串不需要这么麻烦//ES5 Versionvar lastW…
暂无图片
编程学习 ·

【Linux】——I/O复用之poll

1、poll的概述 在上一篇文章中,我们详细的介绍了I/O复用技术中的select使用。这篇文章我们来主要介绍一下poll. poll系统调用和select类似,也是在指定事件内轮询一定数量的文件描述符,以测试其中是否有就绪的 本质都是统一监听,如果任意一个文件描述符上有关注的事件发生。…
暂无图片
编程学习 ·

kafka事务transactional.id相关

https://www.cnblogs.com/jingangtx/p/11330338.htmlhttps://blog.csdn.net/oTengYue/article/details/104727512/看了这两篇关于事务id的资料,我有些疑问:两篇文章里都讲了transactional.id是用户自己设置的,而且transactional.id与producerId在事务管理器中是一一对应关系…
暂无图片
编程学习 ·

查找 -- 7.1 Sear for a Range -- 图解

/********************************************************************************************************** 描述 Given a sorted array of integers, find the starting and ending position of a given target value. Your algorithm’s runtime complexity must be i…
暂无图片
编程学习 ·

HADOOP YARN原理及资源调度

文章目录1、ResourceManager2、NodeManager3、Applicacation Master4、yarn调度方式5、Yarn架构及各角色职责6、Yarn作业执行流程7、特点8、MapReduce在Yarn上的运转 1、ResourceManager ReaouceManager(资源管理器RM)常驻守护进程: 管理集群资源,负责全局资源的监控、分配和…
暂无图片
编程学习 ·

五人分

五人分🐟 """ A、B、C、D、E 五人在某天夜里合伙去捕鱼,到第二天凌晨时都疲惫不堪,于是各自找地方睡觉。 日上三杆,A 第一个醒来,他将鱼分为五份,把多余的一条鱼扔掉,拿走自己的一份。 B 第二个醒来,也将鱼分为五份,把多余的一条鱼扔掉拿走自己的一份…