Java基础语法之抽象类和接口

news2024/7/4 6:03:48

抽象类

什么是抽象类

并不是所有的类都是用来描述对象的,这样的类就是抽象类

例如,矩形,三角形都是图形,但图形类无法去描述具体图形,所以它的draw方法无法具体实现,这个方法就可以没设计成抽象方法,这个类就是抽象类

抽象类语法

被abstract修饰的类是抽象类,abstract修饰的方法是抽象方法,抽象方法不用给出具体的实现体 

如上,draw方法没有具体实现体,也就是不用写代码块

继承了抽象类的子类必须重写所有的抽象方法。如下:

如何使用该抽象类及其子类

这就是一种使用方法,在main函数中调用了Test中的静态方法draw(注意一定要是静态的,在静态方法中不可以调用非静态方法,因为非静态方法的调用依赖对象,而静态方法不依赖对象,所以静态方法默认没有this参数,也就无法调用非静态方法)

draw方法的参数是Shape类,而我们传参时传的是其子类,这里就发生了向上转型;

在draw方法中调用shape.draw()时,又会发生动态绑定;

总之这也是多态。

抽象类的特性

1.不能实例化本抽象类的对象

但是可以用它实例化一个子类对象,也就是可以发生向上转型

因为向上转型后,对象的本质还是其子类,只不过用父类来接收了,并不代表实例化了一个父类对象。

2.抽象方法不可以是private权限,也不可以被final,static修饰

首先,子类重写方法的访问权限要大于等于父类;

其次,final修饰的方法是静态方法,static修饰的方法在方法区;

总之,被private,final,static修饰的方法不可以被重写

3.继承抽象类的子类必须重写其方法,否则子类也要被abstract修饰,然后它的继承者要将其父类以及父类的父类中所有的抽象方法重写

4.抽象类中不一定有抽象方法,但由抽象方法的一定是抽象类

5.抽象类中也可以有普通方法和成员

但它里面的普通成员变量及方法只能通过子类对象来调用

其中,shape引用是发生了向上转型的,而reck就是一个子类的引用,由于有继承关系,所以可以通过子类引用来调用抽象类的方法

6.抽象类中可以有构造方法,供子类创建对象时初始化父类的成员变量,但注意,构造方法不能是抽象方法,因为构造方法不可以被重写

抽象类的作用

很多工作不应由父类完成,而应由子类来完成,但如果是一个普通类,用父类的引用去调用某些方法时就不会报错,而要是抽象类就会报错

就比如画图,你要是用父类shape来调用draw方法,如果不是抽象类就能正常编译,但由于图形有很多种,所以shape这个引用画不出具体的图形;而要是抽象类就会及时报错,提醒你调用具体的子类来画图

接口

什么是接口

接口就是公共的行为标准,大家在实现时,只要符号规范标准就可以通用。

具体点说,接口就是多个类的公共规范,是一种引用数据类型。

语法规则

接口要用interface关键字定义,内部是抽象方法;例如:

接口的使用

有了接口就必须要有具体的类来使用这就要用到implements关键字

public class 类名 implements 接口名{

}

如果这个类还和其他类由继承关系,则应该:

public class 类名 extends 父类名 implements 接口名{

}   表示该子类继承了某个类并且还有某个功能

既然都有继承关系了,为什么还要有接口呢?一个父类可以引出多个子类,它们都是由共性的,但也有特性,这些特性就不适合写在父类里面,这时就可以提供接口,有什么功能就使用什么接口

举例:

接口的特性

1.接口默认是被abstract修饰的,因为它里面有抽象方法

 interface USB等价于abstract interface USB

2.接口中的方法默认是被public abstract修饰的(也只能是这种权限,其他的都会报错)

void func();  等价于public abstract void func();

这也好理解,接口是一种公共标准,一定得是公开的抽象的

3.接口是一种引用类型,但不能直接new一个接口对象

4.接口中可以有成员变量,但这些变量默认是public static final修饰的。既然默认被final修饰了,那么在定义的时候就必须初始化

首先  int a=10;等价于public static final int a=10;

其次,不可以直接int a;必须初始化

5.接口中一般不可以有普通方法,但被static default修饰的方法除外

但注意:static void func(){}等价于public static void func(){},不可以改成除public的其他权限,default也一样

对于被static修饰的方法,可以直接用接口名调用,而default修饰的方法只能用子类对象调用或者被重写

例如:

package Demo2;
interface A{
    void Testa();
    static void Testb(){
        System.out.println("A 的static方法");
    }
    default void Testc(){
        System.out.println("A的default方法");
    }
}
class B implements A{
    @Override
    public void Testa() {
        System.out.println("B重写的testa方法");
    }

    @Override
    public void Testc() {
        A.super.Testc();
    }
}

public class Test {
    public static void main(String[] args) {
        A.Testb();
        B b=new B();
        b.Testa();
        b.Testc();
    }
}

用static修饰的方法可以在main中直接用接口名调用,但default方法要么被重写,要么用子类实例化的对象来调用

6.重写接口中的抽象方法时,只可以设置为public权限,因为接口中的抽象方法默认是public的,所以子类的方法权限要大于等于public

7.接口中不能有静态代码块和构造方法

8.如果它的子类没有重写抽象方法,那这个类必须设置为抽象类

举例

abstract interface USB{
    void openDevice() ;

    void closeDevice();
}
class Mouse implements USB{
    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }
    public void click(){
        System.out.println("疯狂点击鼠标");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }
}
class KeyBoard implements USB{
    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }
    public void input(){
        System.out.println("疯狂打字");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }
}
class Computer {
    public void powerOn() {
        System.out.println("打开电脑");
    }
    public void powerOff() {
        System.out.println("关闭电脑");
    }
    public void useDevice(USB usb){
        usb.openDevice();

        if(usb instanceof Mouse){
            Mouse mouse=(Mouse) usb;
            mouse.click();
        }
        else if(usb instanceof KeyBoard){
            KeyBoard keyBoard=(KeyBoard) usb;
            keyBoard.input();
        }
        usb.closeDevice();

    }
}
public class Test {
    public static void main(String[] args) {
        Computer computer=new Computer();
        computer.powerOn();
        KeyBoard keyBoard=new KeyBoard();
        Mouse mouse=new Mouse();
        computer.useDevice(keyBoard);
        computer.useDevice(mouse);
        computer.powerOff();
    }
}

如上,电脑有usb接口,可以实现打开关闭键盘鼠标的操作

注意,电脑本身不需要usb接口来打开,所以电脑是一个独立的类,它通过自己的powerOn powerOff 方法来打开,然后它又有使用设备的方法,然后就是设备要通过usb接口来打开关闭,即usb.openDevice(); usb.closeDevice();但是在打开后,还要工作,可是只用USB类无法调用设备独有的功能,所以发生了向下转型,注意instanceof的判断和括号的类型强转

然后在main函数中,首先实例化一个电脑对象,将电脑打开,再实例化键盘和鼠标,然后就是电脑要开始使用设备了,调用useDevice函数(这里发生了向上转型,因为是用USB这个引用去接收各种需要USB的对象)。最后就关闭电脑

实现多个接口

在Java中,类和类之间只能单继承,一个类只能有一个父类,但是一个类可以有多个特殊的功能,即一个类可以有多个接口

class 类名 implements 接口1,接口2

例如:青蛙会跑也会游泳,鸭子会跑会游泳也会飞,如下

class Animal{
    String name;
    int age;
    public void eat(){
    }

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
 interface Running{
    public abstract void run() ;
}
interface Swimming{
    public abstract void swim();
}
interface Flying{
    public abstract void fly();
}
class Dog extends Animal implements Running{
    public Dog(String name, int age) {
        super(name, age);
    }
    @Override
    public void run() {
        System.out.println("正在用囧囧跑步步");
    }

    @Override
    public void eat() {
        System.out.println("吃狗粮");
    }
}
class Flog extends Animal implements Running,Swimming{
    @Override
    public void run() {
        System.out.println("正在用俩只大脚掌跑步步");
    }
    @Override
    public void swim() {
        System.out.println("正在用那小手手游泳");
    }
    public Flog(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("吃蛙粮");
    }
}
class Duck extends Animal implements Running,Flying,Swimming{
    @Override
    public void run() {
        System.out.println("正在小跑");
    }
    @Override
    public void swim(){
        System.out.println("正在划水");
    }
    @Override
    public void fly() {
        System.out.println("正在扇翅膀飞飞");
    }
    public Duck(String name, int age) {
        super(name, age);
    }

    @Override
    public void eat() {
        System.out.println("正在吃鸭粮");
    }
}

这就是一个典型的例子

那如何使用呢?

在main函数中可以调用这些static修饰的方法。在=========的上方,是多态的典型例子,将子类对象给到父类,再发生动态绑定,调用重写的方法,

在============的下方就是在使用接口了。注意public static void run(Running run),这个方法的参数是Running这个引用类型,当有此功能的对象给到这个引用类型时,就是发生了向上转型,所以在run.run()时,表面上是调用了接口的抽象方法,实际上是在调用子类的重写的方法

其实,只要是有这个功能的类,就可以实现这个接口

接口间的继承

在Java中,类和类之间是单继承的,但接口与接口可以多继承

package Demo4;
interface A {
    void testA();
}

interface B {
    void testB();
}
interface C extends A,B{
    void testC();
    void testA();
}
class TestDemo1 implements C {
    @Override
    public void testC() {

    }

    @Override
    public void testA() {

    }

    @Override
    public void testB() {

    }
}

public class Test {
}

接口举例

comparable

想要比较俩个学生的大小

单纯这样是不行的,我们要指定如何比较,拿什么比较,这就要用到comparable接口

这是一个接口,尖括号里面的T是要比较的数据的类型,如果要比较student,就在里面写student。这个接口里面有一个抽象方法,所以我们要重写这个抽象方法;以用年龄比较为例

注意,尖括号不能省略

但是,如果需求改变,又想用姓名比较,我们就又得把重写的方法改了,而不可以再写一个重写方法(抽象方法只可以被重写一份)很麻烦

所以就有了下面的比较器

comparator

它里面有很多抽象方法,但我们只用到了比较,所以重写比较即可

先看例子

这是年龄比较器,那要是名字比较器呢?不能直接返回o1.name-o2.name,因为name是String类型,不可以相加减。这时就需要用到String下面的compareTo方法

使用的时候就要实例化比较器这个对象

com

上面这个是String类下面的compareTo方法,其实是String类使用了comparable接口,所以重写了comepareTo方法

总结

comparable对类的侵入性比较强,是要比较哪个类,就要让哪个类implements它,是在类里面重写方法。一旦写死了,就只可以用这一种比较方法

comparator更加灵活,只要传入要比较的对象即可,只不过注意要实例化比较器对象因为comparator这是一个接口,里面的抽象方法无法调用,只有放到类里面才能发挥作用,需要我们按自己的意愿重写

再例如

我们可以这样对整型数组排序,那能否对学生数组排序呢?

显然是不可以的,来看一下源码

这里用到了Comparable接口,即将俩个对象强转为了comparable类型,所以要想成功排序,Student类就要使用这个接口,并且重写这个comepareTo方法

然后就可以正常排序了,但最好再自己重写一个toString方法,要不然就会出现上面的打印情况(上面这个最终是调用的object类的toString方法,而object类是所有子类的父类,所以当我们再Student类中重写了方法后就会调用我们自己的方法),如下

其实,sort有很多重载方法,比如下面这个:

它是有俩个参数,其中一个是要排序的数组,另一个就是Comparator这个接口(但不是说要传这个接口,而是要传实现这个接口的类型的引用)

那么我们就可以如下这种方式来排序

总结:

既然对于整型数组可以直接用arrays.sort排序,那就说明一个事实,Integer这个类一定implements了comparable这个接口,我们可以看一下源码:

自己写一个排序方法

因为排序不仅仅是为一个类提供,而是为多个类提供,所以参数要设置成Comparable类的数组,这样的话,只要是实现了这个接口的类,都可以用到这个排序方法。注意,里面的交换语句其实交换的是引用的指向(相当于C语言中指针的指向)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1317777.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

003 Windows用户与组管理

Windows用户管理 一、用户账户 1、什么是用户账户 不同用户身份拥有不同的权限每个用户包含了一个名称和一个密码每个用户账户具有唯一的安全标识符查看系统中的用户 net user 安全标识符(SID) whoami /user 使用注册表查看 打开注册表命令regedi…

Sentinel使用详解

组件简介 Sentinel是阿里开源的一套用于服务容错的综合性解决方案。它以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来保护服务的稳定性。Sentinel承接了阿里巴巴近10年的双十一大促流量的核心场景,例如秒杀、消息削峰填谷、集群流量控…

Java集合-12

Map的接口特点 key重复时会进行替换 package com.edu.map;import java.util.HashMap; import java.util.Map; import java.util.Set;SuppressWarnings({"all"}) public class Demo01 {public static void main(String[] args) {Map map new HashMap<>();map.…

并查集<基于ranks 的优化,基于Path Spliting的优化>

需求分析 假设有n个村庄&#xff0c;有些村庄之间有连接的路&#xff0c;有些村庄之间并没有连接的路 请你设计一个数据结构&#xff0c;能够快速执行2个操作 ◼ 查询2个村庄之间是否有连接的路◼ 连接2个村庄 首先思考在现有的数据结构能否实现上面的功能&#xff0c;数组、…

JVM学习之运行时数据区

运行时数据区 概述 内存 内存是非常重要的系统资源&#xff0c;是硬盘和CPU的中间桥梁&#xff0c;承载着操作系统和应用程序的实时运行。JVM内存布局规定了Java在运行过程中内存申请&#xff0c;分配&#xff0c;管理的策略&#xff0c;保证了JVM高效稳定运行。不同的JVM对于…

HTTP 302错误:临时重定向

在Web开发中&#xff0c;HTTP状态码是用于表示Web服务器响应的各种状态。其中&#xff0c;HTTP 302错误表示临时重定向&#xff0c;这意味着请求的资源已被临时移动到其他位置&#xff0c;并且服务器已经提供了新的URL&#xff0c;以便客户端可以重新发送请求。 了解HTTP 302错…

[计网01] 物理层 详细解析笔记,特性

计算机网络的物理层是网络协议栈中的第一层&#xff0c;负责传输原始的比特流&#xff08;bitstream&#xff09;通过物理媒介进行通信。物理层主要关注传输介质、信号的编码和调制、数据传输速率以及数据传输的物理连接等方面。 相关特性 机械特性&#xff08;Mechanical Ch…

网络安全—学习溯源和日志分析

日志分析的步骤&#xff1a; 判断是否为攻击行为 不是&#xff1a;不用处理 是&#xff1a;判断攻击是否成功或者失败 攻击失败&#xff1a;判断IP地址是否为恶意地址&#xff0c;可以让防火墙过滤IP地址 攻击成功&#xff1a;做应急处置和溯源分析 应急处置&#xff1a;网络下…

[楚慧杯 2023] web

文章目录 eaaevalupload_shell eaaeval 打开题目&#xff0c;源码给了用户密码 登陆后啥也没有&#xff0c;扫一下发现源码泄露www.zip <?php class Flag{public $a;public $b;public function __construct(){$this->a admin;$this->b admin;}public function _…

Python计算圆的面积,几何学技法大解析!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;我是彭涛&#xff0c;今天为大家分享 Python计算圆的面积&#xff0c;几何学技法大解析&#xff0c;全文3800字&#xff0c;阅读大约15分钟。 在本文中&#xff0c;将深入探讨如何使用 Python 计算圆的面积&…

用23种设计模式打造一个cocos creator的游戏框架----(十八)责任链模式

1、模式标准 模式名称&#xff1a;责任链模式 模式分类&#xff1a;行为型 模式意图&#xff1a;使多个对象都有机会处理请求&#xff0c;从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链&#xff0c;并沿着这条链传递该请求&#xff0c;直到有一个对象处…

『OPEN3D』1.5.1 动手实现点云暴力最近邻

本专栏地址: https://blog.csdn.net/qq_41366026/category_12186023.html?spm=1001.2014.3001.5482https://blog.csdn.net/qq_41366026/category_12186023.html?spm=1001.2014.3001.5482 1、暴力最近邻法 暴力最近邻法 (Brute-force Nearest Neighbour Search,BF 搜索) 是…

【数据结构】哈希表算法总结

知识概览&#xff08;哈希表&#xff09; 哈希表可以将一些值域较大的数映射到较小的空间内&#xff0c;通常用x mod 质数的方式进行映射。为什么用质数呢&#xff1f;这样的质数还要离2的整数幂尽量远。这可以从数学上证明&#xff0c;这样冲突最小。取余还是会出现冲突情况。…

ElasticSearch学习篇8_Lucene之数据存储(Stored Field、DocValue、BKD Tree)

前言 Lucene全文检索主要分为索引、搜索两个过程&#xff0c;对于索引过程就是将文档磁盘存储然后按照指定格式构建索引文件&#xff0c;其中涉及数据存储一些压缩、数据结构设计还是很巧妙的&#xff0c;下面主要记录学习过程中的StoredField、DocValue以及磁盘BKD Tree的一些…

【数据挖掘 | 相关性分析】Jaccard相似系数详解、关于集合的相关性(详细案例、附完详细代码实现和实操、学习资源)

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

《opencv实用探索·二十》点追踪技术

前言&#xff1a; 在学习点追踪技术前需要先了解下光流发追踪目标&#xff0c;可以看上一章内容&#xff1a;光流法检测运动目标 如果以光流的方式追踪目标&#xff0c;基本上我们可以通过goodFeaturesToTrack函数计算一系列特征点&#xff0c;然后通过Lucas-Kanade算法进行一…

Java智慧工地源码,智慧工地管理平台的技术架构和工作原理

智慧工地管理平台是将互联网的理念和技术引入建筑工地&#xff0c;从施工现场源头抓起&#xff0c;最大程度的收集人员、安全、环境、材料等关键业务数据&#xff0c;依托物联网、互联网&#xff0c;建立云端大数据管理平台&#xff0c;形成“端云大数据”的业务体系和新的管理…

考虑使用自定义的序列化形式

在Java中&#xff0c;有时候我们可能需要考虑使用自定义的序列化形式&#xff0c;以满足特定的需求或优化序列化过程。这通常涉及到实现Serializable接口的类&#xff0c;并自定义writeObject和readObject方法。以下是一个简单的例子&#xff0c;演示了如何使用自定义的序列化形…

货物数据处理pandas版

1求和 from openpyxl import load_workbook import pandas as pddef print_hi(name):# Use a breakpoint in the code line below to debug your script.print(fHi, {name}) # Press CtrlF8 to toggle the breakpoint.# Press the green button in the gutter to run the scr…

vue中2种取值的方式

1.url是这种方式的&#xff1a;http://localhost:3000/user/1 取得参数的方式为&#xff1a;this.$route.params.id 2.url为get方式用&#xff1f;拼接参数的&#xff1a;http://localhost:3000/user?phone131121123&companyId2ahttp://localhost:3000/ 取得参数值的方式…