【Java】:抽象类和接口

情景构造

抽象类示例:动物王国

为了方便大家理解抽象类和接口,我们先创建一个情景:

抽象类示例:动物王国
想象有一个庞大的动物王国,其中有很多种不同类型的动物。这些动物有一些共通的特性,比如都有名字,但它们的行为(如吃、睡觉)可能各不相同。这时,我们可以定义一个抽象类来表示这些动物的共通特性。

 抽象类 Animal:

  • 有一个共通属性:name(所有动物都有名字)
  • 有抽象方法:eat() 和 sleep() (所有动物都会吃和睡觉,但具体怎么做各不相同)
public abstract class Animal {
    protected String name;
    
    public abstract void eat();
    public abstract void sleep();
    
}

在这个抽象类中,我们定义了动物的基本属性和它们应该有的行为,但没有给出具体的实现方法。这就是像在说:“所有的动物都会吃和睡觉,但没中动物的方式都不同,所以你们(子类)自己去实现吧。”

接口示例:技能接口

现在,假设在动物王国中,有些动物 具备特殊的技能,比如飞翔或者游泳。并不是所有的动物都会飞或游泳,而且这些节能的实现方式也各不相同。这时,我们可以使用接口来定义这些技能。

接口 Flyable:

  • 有一个方法:fly()(能飞的动物都会飞,但飞行方式各不相同)
public interface Flyable {  
    void fly();  
}

接口 Swimmable:

  • 有一个方法:swim()(能游泳的动物都会游泳,但游泳的方式各不相同)
public interface Swimmable {
    void swim();
}

接口在这里就像是一种“契约”。一个类实现了一个接口,就意味着它承诺提供接口中的方法的具体实现。我们的例子中,如果某个动物会飞,那么它就可以实现 Flyable 接口,并提供 fly() 方法的具体实现。同样,如果某个动物会游泳,它就可以实现 Swimmable 接口,并提供 swim() 方法的具体实现。

实现抽象类和接口

public class Bird extends Animal implements Flyable {  
    @Override  
    public void eat() {  
        System.out.println(getName() + " eats worms.");  
    }  
  
    @Override  
    public void sleep() {  
        System.out.println(getName() + " sleeps in a nest.");  
    }  
  
    @Override  
    public void fly() {  
        System.out.println(getName() + " flies with its wings.");  
    }  
}  
  
public class Fish extends Animal implements Swimmable {  
    @Override  
    public void eat() {  
        System.out.println(getName() + " eats algae.");  
    }  
  
    @Override  
    public void sleep() {  
        System.out.println(getName() + " sleeps at the bottom of the river.");  
    }  
  
    @Override  
    public void swim() {  
        System.out.println(getName() + " swims with its fins.");  
    }  
}

在这个例子中,Bird 类继承了 Animal 抽象类并实现了 Flyable 接口,因为它既能像其他动物一样吃和睡觉,也能飞。而 Fish 类继承了 Animal 抽象类并实现了 Swimmable 接口,因为它能游泳。通过这种方式,我们可以灵活地组合不同的行为和属性,来创建丰富多样的动物类型。

抽象类

抽象类的特性


  • 一个方法可以被修饰为abstract,此时代表这个方法可以不进行实现,此时被称为抽象方法
    • 若一个类中包含抽象方法,那这个类也必须是抽象类
    • 没有抽象方法,类也能被写为抽象类
  • 什么时候一个类可以被定义为抽象类?
    • 当一个类不能具体描述某个对象的时候
  • 抽象类中定义的成员可以和普通类中无区别
  • 抽象类不能实例化
  • 既然不能实例化,那存在的意义是什么?
    • 为了被继承
  • 当一个普通类继承抽象类后,必须重写抽象类中的抽象方法
  •  抽象类也能发生向上转型、动态绑定、重写
  • 此时抽象方法也需要满足重写的特点
    • 被 static 、final、private 修饰都是不能发生重载的
    • 可以理解为:abstract 和 final 是对立的
  • 当一个普通类 A 继承了一个抽象类,不想重写这个抽象类中的方法,此时可以把这个普通类 A 改为抽象类
  • 但是如果 A 这个抽象类再次被普通类 B 继承,此时 B 这个类需要重写所有未被重写的抽象方法(出来混,总是要还的)

作用


  • 其本身不能被实例化,若想使用,只能创建该抽象类的子类,然后让子类重写抽象类中的抽象方法

普通的类也能被继承呀,普通的方法也可以被重写呀,为什么非得用抽象类和抽象方法呢?

- 使用抽象类相当于多了一重编译器的校验

- 若实际工作不应该由父类完成,而是由子类完成,此时如果不小心误用成父类了,使用普通类编译器不会报错,但是父类是抽象类的话就会在实例化的时候提示错误,让我们可以尽早发现问题

Tips:

- 很多语法存在的意义都是为了“预防出错”,

- 例如我们使用过的 final 也是类似,若加上了 final 变量被不小心修改了,编译器就能及时提醒我们

- 充分利用编译器的校验,在实际开发中是非常有意义的

接口

概念

  • 接口就是公共的行为标准规范,大家在实现时,只要符合规范,就可以使用
  • 在 Java 中,接口可以看作是:多个类的公共规范,是一种引用数据类型

1. 接口的定义,可以使用 interface 定义(定义好后会生成一个单独的字节码文件)

创建接口时,接口的命名一般以大写字母 I 开头

  • 接口的命名一般使用“形容词”词性的单词
  • 阿里编码规范中约定,接口中的方法和属性不要加任何修饰符号,保持代码的简洁性

2. 接口当中的成员变量,默认为 public static final 修饰,定义的时候必须初始化,可以省略 怕public static final,

例如:public static final int a = 10;可以写为:int a = 10;

3. 接口中的方法默认是 public abstract 修饰的。你不写的时候,也是抽象方法,所以不能有具体的实现

4.接口当中,使用 default 修饰的方法和 static 修饰的方法是可以有具体实现的

public interface USB {
	double brand = 3.0; // 默认被:public static final修饰 
	void openDevice();
	void closeDevice(); 
} 

public class TestUSB implements USB{
	public static void main(String[] args) { 
		System.out.println(USB.brand); // 可以直接通过接口名访问,说明是静态的 
		
		USB.brand = 2.0; 
		//编译报错:Error:无法为最终变量brand分配值 
        //说明brand具有final属性 
	} 
}

5. 接口不能被实例化,它是抽象类,没有具体对象

public class TestUSB { 
	public static void main(String[] args) { 
		USB usb = new USB(); 
	} 
} // Error:USB是抽象的;无法实例化

6. 当接口需要被类实现,此时使用关键字 implements 来实现
例如:class TestUSB implements USB

7. 当一个类实现了一个接口,那么此时这个类就要重写接口中的方法

8.接口也可以发生向上转型、动态绑定、多态

接口的特性

1. 接口是一种引用类型,但不能直接 new 接口

2. 接口中每一个方法都是 public 的抽象方法,即接口中的方法会被隐式的指定为 public abstract(唯一的,其他修饰符都会报错)

3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现

4. 重写接口中的方法时,不能使用默认的访问权限

5. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量

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

7. 接口虽然不是类,但是接口编译完成后字节码文件的后缀也是 .class

8. 如果类没有实现接口中的所有抽象方法,则类必须被设置为抽象类

实现多个接口

  • 类和类之间是单继承的,一个类只能有一个父类,即java中不支持多继承,但一个类可以实现多个接口
interface IA {
    void testA();
}

interface IB {
    void testB();
}

public class TestDemo implements IA,IB{  //同时实现IA和IB接口
    //这个类同时具备了IA和IB接口的行为,需要重写每一个方法
    @Override
    public void testA() {
        System.out.println("testA()");
    }

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

    public static void main(String[] args) {
        TestDemo t = new TestDemo();
        t.testA();
        t.testB();
    }
}

接口间的继承

  • 接口间的继承就相当于把多个接口合并在了一起
interface IA {
    void testA();
}

interface IB {
    void testB();
}

interface IC extends IA,IB {
    void testC();
}

public class TestDemo implements IC{ 
//不仅具备自己testC功能,还扩展了IA和IB的功能
    @Override
    public void testA() {
        System.out.println("testA()");
    }

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

  • 29
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

高乐高有点矮

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

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

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

打赏作者

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

抵扣说明:

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

余额充值