Java抽象类&接口

本文详细讲解了Java中的抽象类和接口概念,包括抽象方法、抽象类的使用、接口的定义、默认方法、多实现、多继承以及接口与实现类的多态引用。重点阐述了接口作为规范的重要性,以及如何在实际项目中正确运用它们以提高代码的扩展性和可维护性。
摘要由CSDN通过智能技术生成

抽象类

抽象:即不具体、或无法具体

例如:当我们声明一个几何图形类:圆、矩形、三角形类等,发现这些类都有共同特征:求面积、求周长、获取图形详细信息。那么这些共同特征应该抽取到一个公共父类中。但是这些方法在父类中又无法给出具体的实现父类只知道你是一个矩形、三角形或者圆,但是并不知道你具体得长宽高面积等数据,无法具体实现),而是应该交给子类各自具体实现。那么父类在声明这些方法时,就只有方法签名,没有方法体,而没有方法体的方法就称为抽象方法。Java语法规定,包含抽象方法的类必须是抽象类

语法格式

  • 抽象方法 : 没有方法体的方法。
  • 抽象类:被abstract所修饰的类。

抽象类的语法格式

【权限修饰符】 abstract class 类名{
    
}
【权限修饰符】 abstract class 类名 extends 父类{
    
}

抽象方法的语法格式

【其他修饰符】 abstract 返回值类型  方法名(【形参列表】);

注意:抽象方法没有方法体

代码举例:

public abstract class Animal {
    public abstract void run()}
public class Cat extends Animal {
    public void run (){
      	System.out.println("小猫在墙头走~~~")}
}
public class CatTest {
 	 public static void main(String[] args) {
        // 创建子类对象
        Cat c = new Cat(); 
       
        // 调用run方法
        c.run();
  	}
}
输出结果:
小猫在墙头走~~~
abstract class HelloA{//创建一个抽象类
    //抽象类成员变量 
    String name;
    int age;
    //抽象方法
    abstract void run();
    abstract void sleep();
    public HelloA(String name,int age){
        //抽象类构造器
        this.age = age;
        this.name=name;
    }
}

/**
抽象类内抽象方法必须加上abstract关键字否则报错
 * 抽象注意事项:
 * 子类必须实现父类所有抽象方法,除非该子类也是抽象类
 * 抽象类不能创建对象,但是其非抽象的子类可以
 *抽象类有构造方法,供子类创建对象初始化父类成员变量使用
 *抽象类不一定包含抽象方法,包含抽象方法的类一定是抽象类
 */
class HelloB extends  HelloA{
    int high;//子类成员变量
    //子类构造器
    public HelloB(String name, int high) {
       //继承的父类属性
        //父类有成员变量时写法:
        super(name,age);
        //父类无成员变量时:
        //super();可写可不写
        this.high = high;
    }
	//实现父类抽象方法   重写
    public void run(){
        System.out.println("跑步");
    }
    public void sleep(){
        System.out.println("睡觉");
    }
}
class Test{
    public static void main(String[] args) {
        //创建子类对象同时使用子类和父类的成员属性
        HelloB b1 = new HelloB("沈阳",32);
        b1.run();
        b1.sleep();

    }
}

此时的方法重写,是子类对父类抽象方法的完成实现,我们将这种方法重写的操作,也叫做实现方法

注意事项

关于抽象类的使用,以下为语法上要注意的细节,虽然条目较多,但若理解了抽象的本质,无需死记硬背。

  1. 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

    理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

  2. 抽象类中,也有构造方法,是供子类创建对象时,初始化父类成员变量使用的。

    理解:子类的构造方法中,有默认的super()或手动的super(实参列表),需要访问父类构造方法。

  3. 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类。

    理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

  4. 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类

    理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。


接口

电脑边上提供了USB插槽,这个插槽遵循了USB的规范,只要其他设备也是遵循USB规范的,那么就可以互联,并正常通信。至于这个电脑、以及其他设备是哪个厂家制造的,内部是如何实现的,我们都无需关心。

	**这种设计是将规范和实现分离,这也正是Java接口的好处。Java的软件系统会有很多模块组成,那么各个模块之间也应该采用这种面向接口的低耦合,为系统提供更好的可扩展性和可维护性。**
  • 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的is-a关系,而接口实现则是 "能不能"的has-a关系。
  • 可以理解成是一个特殊的类,在接口中的定义必须是全局常量以及全部的公共抽象方法。
    • 例如:你能不能用USB进行连接,或是否具备USB通信功能,就看你是否遵循USB接口规范
    • 例如:Java程序是否能够连接使用某种数据库产品,那么要看该数据库产品有没有实现Java设计的JDBC规范

定义格式

接口的定义,它与定义类方式相似,但是使用 interface 关键字。它也会被编译成.class文件,但一定要明确它并不是类,而是另外一种引用数据类型

引用数据类型:数组,类,接口。

接口的声明格式
【修饰符】 interface 接口名{
    //接口的成员列表:
    // 静态常量   static final修饰
        接口中的变量声明,将隐式地声明为public static final 变量(并且只能是 public,用 private 修饰会报编译错误
    // 抽象方法   public void run();没有方法体的方法
    // 默认方法	  default修饰 
    // 静态方法   static方法
    // 私有方法   private方法

}

示例代码:

interface Usb3{
    //静态常量
	long MAX_SPEED = 500*1024*1024;//500MB/s
    
    //抽象方法
	void read();
    void write();
    
    //默认方法
    public default void start(){
        System.out.println("开始");
    }
    public default void stop(){
        System.out.println("结束");
    }
    
    //静态方法
    public static void show(){
        System.out.println("USB 3.0可以同步全速地进行读写操作");
    }
}

接口的成员说明

接口定义的是多个类共同的公共行为规范,这些行为规范是与外部交流的通道,这就意味着接口里通常是定义一组公共方法。

在JDK8之前,接口中只允许出现:静态变量和抽象方法

(1)公共的静态的常量:其中public static final可以省略

(2)公共的抽象的方法:其中public abstract可以省略

理解:接口是从多个相似类中抽象出来的规范,不需要提供具体实现

在JDK1.8时,接口中允许声明默认方法和静态方法

(3)公共的默认的方法:其中public 可以省略,建议保留,但是default不能省略

(4)公共的静态的方法:其中public 可以省略,建议保留,但是static不能省略

在JDK1.9时,接口又增加了:私有方法

(5)私有方法

除此之外,接口中不能有其他成员,没有构造器,没有初始化块,因为接口中没有成员变量需要初始化

实现接口

接口的使用,它不能创建对象,但是可以被实现(implements ,类似于被继承)。

类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类,也可以称为接口的子类。实现的动作类似继承,格式相仿,只是关键字不同,实现使用 implements关键字。

1、实现接口语法格式
【修饰符】 class 实现类  implements 接口{
	// 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
  	// 重写接口中默认方法【可选】
}

【修饰符】 class 实现类 extends 父类 implements 接口{
    // 重写接口中抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
  	// 重写接口中默认方法【可选】
}

注意:

  1. 如果接口的实现类是非抽象类,那么必须重写接口中所有抽象方法

  2. 默认方法可以选择保留,也可以重写。

    重写时,default单词就不要再写了,它只用于在接口中表示默认方法,到类中就没有默认方法的概念了

  3. 不能重写静态方法

示例代码:

class MobileHDD implements Usb3{

	//重写/实现接口的抽象方法,【必选】
	public void read() {
		System.out.println("读数据");
	}
    public void write(){
        System.out.println("写数据");
    }
	
	//重写接口的默认方法,【可选】
	//重写默认方法时,default单词去掉
	public void end(){
        System.out.println("清理硬盘中的隐藏回收站中的东西,再结束");
    }
}

如何调用对应的方法
  • 对于接口的静态方法,直接使用“接口名.”进行调用即可
    • 也只能使用“接口名."进行调用,不能通过实现类的对象进行调用
  • 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用
    • 接口不能直接创建对象,只能创建实现类的对象
public class TestInteface {
	public static void main(String[] args) {
		//创建实现类对象
		MobileHDD b = new MobileHDD();
		
		//通过实现类对象调用重写的抽象方法,以及接口的默认方法,如果实现类重写了就执行重写的默认方法,如果没有重写,就执行接口中的默认方法
		b.start();
		b.read();
		b.stop();
		
		//通过接口名调用接口的静态方法
		MobileHDD.show();
	}
}

/**
 * 接口就是一种规范,一种特殊的类,可以理解为100%抽象类
 * 接口中成员属性必须初始化------>全局常量
 *接口中的所有方法都默认是由public abstract修饰的(抽象方法)
 * 只能是 public abstract,其他修饰符都会报错,可以省略public和abstract
 * 接口不可实例化,即不能创建对象,也就没有构造器可言
 * 实现类:
 * 实现类为非抽象类必须重写所有抽象方法,实现类是抽象类可以不重写
 * 默认方法可重写也可不重写   默认方法重写就暴怒用再写default关键字了
 * 不能重写静态方法
 */
interface A{
    //接口成员
    String name ="12";
    int age = 0;
    //抽象方法
    void breath();
    void see();
    //默认方法---->default修饰
    public default void walk(){
        System.out.println("默认都会走路");
    }
    //静态方法
    public static void black(){
        System.out.println("黑色头发");
    }
}
class RealA implements A{//实现接口A
    @Override
    public void breath() {
        System.out.println("重写  会呼吸");
    }
    @Override
    public void see() {
        System.out.println("重写  有眼睛能看见");
    }
}

接口引用
    /**
 * 静态方法只能直接通过接口名调用:
 * 抽象方法、默认方法只能通过实现类对象调用
 * 接口不能创建对象
 */
interface A{
    String name = "张三";
    void breath();
    public static void run(){
        System.out.println("会走路");
    }
    public default void black(){
        System.out.println("黑色头发");
    }
}
class B implements A{
    @Override
    public void breath() {
        System.out.println("会呼吸");
    }
}
class Test00{
    public static void main(String[] args) {
        B b1 = new B();
        //默认方法
        b1.breath();
        b1.black();
        //静态方法
        A.run();
    }
}

接口的多实现

之前学过,在继承体系中,一个类只能继承一个父类。而对于接口而言,一个类是可以实现多个接口的,这叫做接口的多实现。并且,一个类能继承一个父类,同时实现多个接口

实现格式:

【修饰符】 class 实现类  implements 接口1,接口2,接口3。。。{
	// 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
  	// 重写接口中默认方法【可选】
}

【修饰符】 class 实现类 extends 父类 implements 接口1,接口2,接口3。。。{
    // 重写接口中所有抽象方法【必须】,当然如果实现类是抽象类,那么可以不重写
  	// 重写接口中默认方法【可选】
}

接口中,有多个抽象方法时,实现类必须重写所有抽象方法如果抽象方法有重名的,只需要重写一次

定义多个接口:

interface A {
    public abstract void showA();
    public abstract void show();
}

interface B {
    public abstract void showB();	
    public abstract void show();
}

定义实现类:

public class C implements A,B{
    @Override
    public void showA() {
        System.out.println("showA");
    }

    @Override
    public void showB() {
        System.out.println("showB");
    }

    @Override
    public void show() {
        System.out.println("show");
    }
}
interface A{
    String name = "1";
    void breath();
    static void black(){
        System.out.println("黑色头发");
    }
    default void body(){
        System.out.println("默认四肢健全");
    }
}
interface B{
    int age = 0;
    void see();
    void breath();
}
class Test implements A,B{
    @Override
    public void breath() {//    两接口重名方法重写一次即可
        System.out.println("都会喘气");
    }
    @Override
    public void see() {
        System.out.println("都能看见东西");
    }
}

class TestLast{
    public static void main(String[] args) {
        Test t1 = new Test();
        t1.body();//实现类未重写,调用接口该默认方法
        t1.breath();//调用实现类重写接口抽象方法
    }
}

默认方法冲突问题

亲爹优先原则

当一个类,既继承一个父类,又实现若干个接口时,父类中的成员方法与接口中的抽象方法重名,子类就近选择执行父类的成员方法。代码如下:

定义接口:

interface A {
    public default void methodA(){
        System.out.println("AAAAAAAAAAAA");
    }
}

定义父类:

class D {
    public void methodA(){
        System.out.println("DDDDDDDDDDDD");
    }
}

定义子类:

class C extends D implements A {
  	// 未重写methodA方法
}
class B extends D implements A{
    //当然也可以选择重写
    public void methodA(){
        System.out.println("BBBBBBBBBBBB");
    }
}

定义测试类:

public class Test {
    public static void main(String[] args) {
        C c = new C();
        c.methodA(); //调用父类的重名方法
        
        B b = new B();
        b.methodA();
    }
}
输出结果:
DDDDDDDDDDDD
BBBBBBBBBBBB
必须做出选择

当一个类同时实现了多个接口,而多个接口中包含方法签名相同的默认方法时,怎么办呢?

无论你多难抉择,最终都是要做出选择的。代码如下:

声明接口:

interface A{
	public default void d(){
		System.out.println("今晚7点-8点陪我吃饭看电影");
	}
}
interface B{
	public default void d(){
		System.out.println("今晚7点-8点陪我逛街吃饭");
	}
}

选择保留其中一个,通过“接口名.super.方法名"的方法选择保留哪个接口的默认方法。

class C implements A,B{
	@Override
	public void d() {
		A.super.d();
	}
}

选择自己完全重写:

class D implements A,B{
	@Override
	public void d() {
		System.out.println("自己待着");
	}
}

接口的多继承

一个接口能继承另一个或者多个接口,接口的继承也使用 extends 关键字,子接口继承父接口的方法。

定义父接口:

interface A {
    void a();
    public default void methodA(){
        System.out.println("AAAAAAAAAAAAAAAAAAA");
    }
}

interface B {
    void b();
    public default void methodB(){
        System.out.println("BBBBBBBBBBBBBBBBBBB");
    }
}

定义子接口:

interface C extends A,B{
    @Override
    public default void methodB() {
        System.out.println("CCCCCCCCCCCCCCCCCCCC");
    }
}

注意:

子接口重写默认方法时,default关键字可以保留。

子类重写默认方法时,default关键字不可以保留。

class D implements C{
 /**
     * 接口多继承
     * 写法一:interface C extends A,B{}    class TX implements C{}
     * 写法二:class TX implements A,B,C
     */
	@Override
	public void a() {
		System.out.println("xxxxx");
	}

	@Override
	public void b() {
		System.out.println("yyyyy");
	}
	
}
class E implements A,B,C{//效果和上面的D是等价的

	@Override
	public void b() {
		System.out.println("xxxxx");
	}

	@Override
	public void a() {
		System.out.println("yyyyy");
	}
	
}

接口与实现类对象的多态引用

实现类实现接口,类似于子类继承父类,因此,接口类型的变量与实现类的对象之间,也可以构成多态引用。通过接口类型的变量调用方法,最终执行的是你new的实现类对象实现的方法体。

public class TestInterface {
	public static void main(String[] args) {
		Flyable b = new Bird();
		b.fly();
		
		Flyable k = new Kite();
		k.fly();
	}
}
interface Flyable{
    //抽象方法
	void fly();
}
class Bird implements Flyable{

	@Override
	public void fly() {
		System.out.println("展翅高飞");
	}
	
}
class Kite implements Flyable{

	@Override
	public void fly() {
		System.out.println("别拽我,我要飞");
	}
	
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绿仔牛奶_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值