【0基础学Java第九课】-- 抽象类和接口

9. 抽象类和接口

  • [9.1 抽象类](#9.1 抽象类)
    • [9.1.1 抽象类概念](#9.1.1 抽象类概念)
    • [9.1.2 抽象类语法](#9.1.2 抽象类语法)
    • [9.1.3 抽象类的特性](#9.1.3 抽象类的特性)
    • [9.1.4 抽象类的作用](#9.1.4 抽象类的作用)
  • [9.2 接口](#9.2 接口)
    • [9.2.1 接口的概念](#9.2.1 接口的概念)
    • [9.2.2 语法规则](#9.2.2 语法规则)
    • [9.2.3 接口使用](#9.2.3 接口使用)
    • [9.2.4 接口特性](#9.2.4 接口特性)
    • [9.2.5 实现多个接口](#9.2.5 实现多个接口)
    • [9.2.6 接口的继承](#9.2.6 接口的继承)
    • [9.2.9 抽象类和接口的区别](#9.2.9 抽象类和接口的区别)
  • [9.3 Object类](#9.3 Object类)
    • [9.3.1 获取对象方法](#9.3.1 获取对象方法)
    • [9.3.1 对象比较equals方法](#9.3.1 对象比较equals方法)
    • [9.3.2 hashcode方法](#9.3.2 hashcode方法)

9.1 抽象类

9.1.1 抽象类概念

在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类


在打印图形例子中, 我们发现, 父类 Shape 中的 draw 方法好像并没有什么实际工作, 主要的绘制图形都是由 Shape的各种子类的 draw 方法来完成的. 像这种没有实际工作的方法, 我们可以把它设计成一个 抽象方法(abstract method), 包含抽象方法的类我们称为** 抽象类(abstract class)**.

9.1.2 抽象类语法

包含抽象方法的类,必须也拿abstract修饰 ,此时这个类也叫抽象类

java 复制代码
abstract class Shape {
    // 抽象方法
    public abstract void draw();
}

9.1.3 抽象类的特性

  1. 抽象类不能被实例化
  2. 如果一个普通类继承了一个抽象类,那么此时这个普通类 必须重写这个抽象方法
java 复制代码
class Cycle extends Shape {
    // 一定要重写父类的这个抽象方法
    @Override
    public void draw() {
        System.out.println("⚪");
    }
}
  1. 在一个普通类继承了抽象类,如果再被继承,那这个普通类必须同时重写这两个类
java 复制代码
abstract class A extends Shape {
    public abstract void testA();

}

class B extends A {
    @Override
    public void testA() {
    
    }

    @Override
    public void draw() {

    }
}
  1. 抽象类和 普通类 的区别在于:
  • 可以和普通类一样 有成员变量、成员方法
  • 多了抽象方法
  • 多了不能实例化
  1. 什么情况下 要设计为抽象类

    如果这个类 不能描述一个而具体的对象,那么就可以设置为抽象类

    比如:Animal这个类

  2. 抽象类当中可以包含构造方法,这个构造方法并不是实例化这个抽象类的时候使用,因为他就不能被实例化。那么这个构造方法,主要是在子类当中让子类调用,帮助父类进行初始化

java 复制代码
abstract class Person {
    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
class Student extends Person {
    public Student() {
        super("zhangsan", 10);
    }
}
  1. 抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类

9.1.4 抽象类的作用

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

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

使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了, 使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题。

9.2 接口

9.2.1 接口的概念

在现实生活里,接口的例子比如有:笔记本上的USB口,电源插座等。

而USB口可以插 U盘、鼠标、键盘等所有符合USB协议的设备。
接口就是公共的行为规范标准,大家在实现时,只要符合规范标准,就可以通用在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型

9.2.2 语法规则

  1. 定义一个接口的时候使用关键字interface来定义

提示:

  1. 创建接口时, 接口的命名一般以大写字母 I 开头.
  2. 接口的命名一般使用 "形容词" 词性的单词.
  3. 阿里编码规范中约定, 接口中的方法和属性不要加任何修饰符号, 保持代码的简洁性.

9.2.3 接口使用

接口不能直接使用,必须要有一个"实现类"来"实现"该接口,实现接口中的所有抽象方法

IUSB接口:

java 复制代码
public interface IUSB {
    void openDevice();
    void closeDevice();
}

Mouse类:

java 复制代码
public class Mouse implements IUSB{
    @Override
    public void openDevice() {
        System.out.println("打开鼠标");
    }

    public void click() {
        System.out.println("点击鼠标");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭鼠标");
    }
}

Keyboard类:

java 复制代码
public class KeyBoard implements IUSB{
    @Override
    public void openDevice() {
        System.out.println("打开键盘");
    }

    public void inPut() {
        System.out.println("键盘输入");
    }
    @Override
    public void closeDevice() {
        System.out.println("关闭键盘");
    }
}

Computer类:

java 复制代码
public class Computer {
    public void powerOn() {
        System.out.println("打开电脑");
    }
    public void powerOff() {
        System.out.println("关闭电脑");
    }

    public void useDevice(IUSB iusb) {
        iusb.openDevice();
        // instanceof :测试它左边的对象是否是它右边的类的实例 ,返回boolean类型
        // A(对象) instanceof B(类)
        if (iusb instanceof Mouse) {
            Mouse mouse = (Mouse) iusb;
            mouse.click();
        }else if(iusb instanceof KeyBoard) {
            KeyBoard keyBoard = (KeyBoard) iusb;
            keyBoard.inPut();
        }
        iusb.closeDevice();
    }
}

Test类:

java 复制代码
public class Test {
    public static void main(String[] args) {
        Computer computer = new Computer();
        computer.powerOn();

        computer.useDevice(new Mouse());
        computer.useDevice(new KeyBoard());

        computer.powerOff();
    }
}

9.2.4 接口特性

  1. 接口当中的方法 如果没有被实现, 那么他默认就是一个抽象方法
  2. 在接口当中的方法不能有具体的实现
  3. 如果有具体的实现,那么必须是由default修饰或者是static修饰
java 复制代码
interface Ishape {
    public int a = 10;
    public abstract void draw();

    //在接口当中的方法不能有具体的实现
    //如果有具体的实现,那么必须是由default修饰或者是static修饰
    public default void test() {
        System.out.println("ds");
    }

    public static void func() {

    }
}
  1. 接口当中定义成员变量 默认都是public static final的
java 复制代码
    public int a = 10;
    public static final int b = 100;
    int aa = 10; // 可以不加public static final 直接定义int aa = 10;
    int bb = 20;
  1. 接口当中的抽象方法 默认都是public abstract修饰的
java 复制代码
    public abstract void draw();
    void fun1();
  1. 接口类型是一种引用类型,是不可以被实例化
  2. 类和接口之间的关系 可以使用implements来关联
java 复制代码
interface IShape {
    void draw();
}

class Rect implements IShape{
    @Override
    public void draw() {
        System.out.println("矩形");
    }
}

class Cycle implements IShape {
    @Override
    public void draw() {
        System.out.println("⚪");
    }
}

class Flower implements IShape {
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

public class Test {
    public static void func(IShape iShape) {
        iShape.draw();
    }
    public static void main(String[] args) {
        //IShape iShape = new IShape();
        IShape iShape1 = new Flower();
        IShape iShape2 = new Rect();
        IShape iShape3 = new Cycle();

        func(iShape1);
        func(iShape2);
        func(iShape3);

        IShape[] iShapes = {iShape1,iShape2,iShape3};
    }
}
  1. 接口也是可以产生字节码文件的(.class)
  2. 接口中不能有静态代码块和构造方法
java 复制代码
public interface USB {
	// 编译失败
	public USB(){
	}
	{} // 编译失败
	void openDevice();
	void closeDevice();
}
  1. 一个类 可以继承一个抽象类/普通类 同时还可以实现这个接口
java 复制代码
abstract class AA {

}
class CC extends AA implements IUSB {

}

9.2.5 实现多个接口

在Java中,类和类之间是单继承的,一个类只能有一个父类,即Java中不支持多继承,但是一个类可以实现多个接口。下面通过类来表示一组动物.

java 复制代码
public abstract class Animal {
    public String name;

    public Animal(String name) {
        this.name = name;
    }
    public abstract void eat();
}

提供一组接口,分别表示会飞,会跑,会游泳

会飞的接口:

java 复制代码
public interface IFly {
    void fly();
}

会跑的接口:

java 复制代码
public interface IRun {
    void run();
}

会游泳的接口:

java 复制代码
public interface ISwim {
    void swim();
}

创建鸟,狗,鸭子,机器人这个类:

鸟:

java 复制代码
public class Bird extends Animal implements IFly,IRun{
    public Bird(String name) {
        super(name);
    }

    @Override
    public void fly() {
        System.out.println(this.name+" 正在用两个翅膀飞");
    }

    @Override
    public void eat() {
        System.out.println(this.name + "正在吃鸟粮");
    }

    @Override
    public void run() {
        System.out.println(this.name+ " 正在用两个小腿跑");
    }
}

狗:

java 复制代码
public class Dog extends Animal implements ISwim,IRun{
    public Dog(String name) {
        super(name);
    }

    @Override
    public void swim() {
        System.out.println(this.name+" 正在用4条腿游泳");
    }

    @Override
    public void eat() {
        System.out.println(this.name+ " 正在吃狗粮");
    }

    @Override
    public void run() {
        System.out.println(this.name+" 正在用4条腿跑");
    }
}

鸭子:

java 复制代码
public class Duck extends Animal implements IFly,IRun,ISwim{

    public Duck(String name) {
        super(name);
    }

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

    @Override
    public void fly() {
        System.out.println(this.name+" 正在用鸭翅膀飞");
    }

    @Override
    public void run() {
        System.out.println(this.name+"正在用鸭腿跑");
    }

    @Override
    public void swim() {
        System.out.println(this.name+"正在用鸭腿游泳");
    }
}

机器人:

java 复制代码
public class Robot implements IRun{
    @Override
    public void run() {
        System.out.println("机器人在跑");
    }
}

Test类:

java 复制代码
public class Test {
    public static void func1(Animal animal) {
        animal.eat();
    }
    public static void testFly(IFly iFly) {
        iFly.fly();
    }
    public static void testSwim(ISwim iSwim) {
        iSwim.swim();
    }
    public static void testRun(IRun iRun) {
        iRun.run();
    }

    public static void main(String[] args) {
        func1(new Duck("小黄鸭"));
        testFly(new Duck("小黄鸭"));
        testSwim(new Duck("小黄鸭"));
        testFly(new Bird("布谷"));
        func1(new Dog("旺财"));
        // testFly(new Dog("旺财"));  // 报错 狗没有Ifly接口
        testRun(new Robot());
    }
}

注意:一个类实现多个接口时,每个接口中的抽象方法都要实现,否则类必须设置为抽象类。一个类继承一个父类,同时实现多种接口。

9.2.6 接口的继承

在Java中,类和类之间是单继承的,一个类可以实现多个接口,接口与接口之间可以多继承。即:用接口可以达到多继承的目的。

接口可以继承一个接口, 达到复用的效果. 使用 extends 关键字.

java 复制代码
// 两栖的动物, 既能跑, 也能游
public interface IAmphibious extends IRun,ISwim {
    void test1();
}

再创建一个Frog类接口实现run方法和swim方法:

java 复制代码
public class Frog extends Animal implements IAmphibious{

    public Frog(String name) {
        super(name);
    }
    @Override
    public void eat() {

    }
    @Override
    public void test1() {

    }
    @Override
    public void run() {

    }
    @Override
    public void swim() {
    }
}

接口间的继承相当于把多个接口合并在一起

9.2.9 抽象类和接口的区别

  1. 抽象类当中,可以包含和普通类一样的成员变量和成员方法,但是接口 当中的成员变量只能是public static final 的,方法只能是public abstract
  2. ** 一个类只能继承一个抽象类,但是能够同时实现多个接口**,所以解决了Java当中不能进行多继承的特性

9.3 Object类

Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的。默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收。

Object是所以类的父类,意味着可以发生向上转型,能接受所以类的对象。

java 复制代码
class Person {
    public String name;
}

class Student extends Person {

}
public class Test {
    public static void main(String[] args) {
        Person person = new Person();

        //Object 是所以类的父类,意味着可以发生向上转型
        Object obj = new Person();
        Object obj1 = new Student();
    }
}

9.3.1 获取对象方法

如果要打印对象中的内容,可以直接重写Object类中的toString()方法、

java 复制代码
class Person {
    public String name = "haha";

    @Override
    public String toString() {
        return "name: "+ name;
    }
}

class Student extends Person {

}
public class Test {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person);
        //Object 是所以类的父类,意味着可以发生向上转型
        /*Object obj = new Person();
        Object obj1 = new Student();*/
    }
}

9.3.1 对象比较equals方法

在Java中,进行比较时:
a.如果左右两侧是基本类型变量,比较的是变量中值是否相同

b.如果==左右两侧是引用类型变量,比较的是引用变量地址是否相同

c.如果要比较对象中内容,必须重写Object中的equals方法,因为equals方法默认也是按照地址比较的:

java 复制代码
// Object类中的equals方法
public boolean equals(Object obj) {
	return (this == obj); // 使用引用中的地址直接来进行比较
}
java 复制代码
class Person {
    public String name;

    public Person(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return "name: "+ name;
    }

    @Override
    public boolean equals(Object obj) {
        //发生动态绑定
        Person tmp = (Person) obj;
        return tmp.name.equals(this.name);
    }
}


public class Test {

    public static void main(String[] args) {
        Person person1 = new Person("zhangsan");
        Person person2 = new Person("zhangsan");
        System.out.println(person1 == person2);

        //调用了object方法,所以要在Person类重写equals方法
        System.out.println(person1.equals(person2));
        
        String str1 = "zhangsan";
        String str2 = "zhangsan";
        System.out.println(str1.equals(str2));
    }
}

比较对象中内容是否相同的时候,一定要重写equals方法。

9.3.2 hashcode方法

重写hashCode之后哈希值就会相同

java 复制代码
import java.util.Objects;

class Person {
    public String name;

    public Person(String name){
        this.name = name;
    }

    @Override
    public String toString() {
        return "name: "+ name;
    }

    /*@Override
    public boolean equals(Object obj) {
        //发生动态绑定
        Person tmp = (Person) obj;
        return tmp.name.equals(this.name);
    }*/

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }
}

public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("zhangsan");
        Person person2 = new Person("zhangsan");

        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());

        System.out.println(person1.equals(person2));
    }

注意:

  1. hashcode方法用来确定对象在内存中存储的位置是否相同
  2. 事实上hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
相关推荐
喵叔哟9 分钟前
重构代码之移动字段
java·数据库·重构
喵叔哟9 分钟前
重构代码之取消临时字段
java·前端·重构
fa_lsyk12 分钟前
maven环境搭建
java·maven
远望清一色18 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
何曾参静谧26 分钟前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化
Prejudices30 分钟前
C++如何调用Python脚本
开发语言·c++·python
Daniel 大东31 分钟前
idea 解决缓存损坏问题
java·缓存·intellij-idea
wind瑞37 分钟前
IntelliJ IDEA插件开发-代码补全插件入门开发
java·ide·intellij-idea
HappyAcmen37 分钟前
IDEA部署AI代写插件
java·人工智能·intellij-idea
马剑威(威哥爱编程)43 分钟前
读写锁分离设计模式详解
java·设计模式·java-ee