侧边栏壁纸
博主头像
快乐江湖的博客博主等级

更多内容请点击CSDN关注“快乐江湖”

  • 累计撰写 127 篇文章
  • 累计创建 33 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

第十章:Java之接口基本概念、接口实现多继承、Object类

快乐江湖
2023-02-28 / 0 评论 / 0 点赞 / 19 阅读 / 17431 字

一:接口基本概念

(1)接口是什么

接口:接口就是公共的行为准则,实现时只要符合规范准则,就可以通用。在Java中,接口被看做是多个类的公共规范,是一种引用数据类型

(2)语法规则

语法规则:与定义类的格式一致,只需要将关键字class换成interface即可

interface Shape {
    void draw();
}

(3)接口使用及其特性

接口的使用:接口不可以直接使用,必须要有一个“实现类”来实现该接口的所有抽象方法,需要借助关键字implements。注意以下特性

  • 接口中的成员变量,默认会被public static final修饰
  • 接口中的成员方法,默认为抽象方法,也即被public abstract修饰
  • 接口中的普通成员方法,是不能有具体实现的(在jdk 8之后,加上default后可以有具体实现)
  • 接口中可以有静态成员方法
  • 接口中不可以有静态代码块和构造方法
  • 接口不可以被实例化
  • 接口虽然不是类,但是接口编译完成后字节码文件的后缀格式也是.class
  • 如果类没有实现接口中的所有的抽象方法,则类必须设置为抽象类
interface Shape {
    void draw();
}

class Rectangle implements Shape{
    @Override
    public void draw(){
        System.out.println("画一个矩形");
    }
}

class Triangle implements Shape{
    @Override
    public void draw(){
        System.out.println("画一个三角形");
    }
}
class Circle implements Shape{
    @Override
    public void draw(){
        System.out.println("画一个三角形");
    }
}
public class TestDemo {

    public static void drawing(Shape input){
        input.draw();
    }

    public static void main(String[] args) {
        drawing(new Circle());
    }
}

二:使用多个接口完成多继承

如下是一个多态体系

  • Animal为父类(抽象类),内含抽象方法eat()
  • DogBird分别继承自Animal
abstract class Animal{
    public String name;
    public int age;
    public Animal(String name, int age){
        this.name = name;
        this.age = age;
    }
    public abstract void eat();
}

class Dog extends Animal{
    public Dog(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name + "正在吃狗粮");
    }
}

class Bird extends Animal{
    public Bird(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name + "正在吃鸟食");
    }
}

public class TestDemo {
    public static void eating(Animal animal){
        animal.eat();
    }
    public static void main(String[] args) {
        eating(new Dog("旺财", 2));
        eating(new Bird("啾啾", 3));
    }
}

这里我们加入一个新的抽象方法fly(),用于描述“飞翔”这种行为。但是这里就会出现一个很难受的地方,所有的动物都会eat(),但不一定都会fly(),比如狗就不会飞翔。所以这个抽象方法fly()放到Animal中并不合适,那应该怎么解决呢?

(1)Java无法像C++那样直接实现多继承

由于C++中是支持多继承语法的,所以我们可以再建立一个新的类AnimlFly用于描述某些动物具有的飞翔行为

  • 注意:多继承是有很大弊端的,而且会使整个继承体系变得混乱不堪,所以Java中直接取消了这种语法
class Animal{
public:
    eat()
private:
    name;
    age;
}

class AnimalFly(){
public:
    fly();
private:
    name;
    age;
}

class Dog:public Animal{
    eat();
}

//多继承
class Bird:public Animal, public AnimalFly{
    eat();
    fly();
}

但这样的写法在Java中是会报错的

  • 纠错:下方的AnimalSpecial应该是AnimalFly

(2)接口实现多继承

在Java中,可以用接口完成这样的需求。建立一个接口AnimalFly,需要这种行为的类重写接口下的方法即可

abstract class Animal{
    public String name;
    public int age;
    public Animal(String name, int age){
        this.name = name;
        this.age = age;
    }
    public abstract void eat();
}

interface AnimalFly{
    void fly();
}

class Dog extends Animal{
    public Dog(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name + "正在吃狗粮");
    }
}

class Bird extends Animal implements AnimalFly{
    public Bird(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name + "正在吃鸟食");
    }

    @Override
    public void fly() {
        System.out.println(name + "正在天上飞");

    }
}

再比如DogBird都可以跑,所以可以建立一个接口AnimalRun

abstract class Animal{
    public String name;
    public int age;
    public Animal(String name, int age){
        this.name = name;
        this.age = age;
    }
    public abstract void eat();
}
//飞的行为
interface AnimalFly{
    void fly();
}
//跑的行为
interface AnimalRun{
    void run();
}

class Dog extends Animal implements AnimalRun{
    public Dog(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name + "正在吃狗粮");
    }
    @Override
    public void run(){
        System.out.println(name + "正在用四条腿跑路");
    }
}

class Bird extends Animal implements AnimalFly, AnimalRun{
    public Bird(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name + "正在吃鸟食");
    }

    @Override
    public void fly() {
        System.out.println(name + "正在天上飞");

    }
    @Override
    public void run(){
        System.out.println(name + "正在用两个脚丫跑路");
    }
}

(3)多继承使用

使用时,直接用接口作为形参接受对象即可,也即向上转型

abstract class Animal{
    public String name;
    public int age;
    public Animal(String name, int age){
        this.name = name;
        this.age = age;
    }
    public abstract void eat();
}
//飞的行为
interface AnimalFly{
    void fly();
}
//跑的行为
interface AnimalRun{
    void run();
}

class Dog extends Animal implements AnimalRun{
    public Dog(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name + "正在吃狗粮");
    }
    @Override
    public void run(){
        System.out.println(name + "正在用四条腿跑路");
    }
}

class Bird extends Animal implements AnimalFly, AnimalRun{
    public Bird(String name, int age){
        super(name, age);
    }
    @Override
    public void eat(){
        System.out.println(name + "正在吃鸟食");
    }

    @Override
    public void fly() {
        System.out.println(name + "正在天上飞");

    }
    @Override
    public void run(){
        System.out.println(name + "正在用两个脚丫跑路");
    }
}

public class TestDemo {
    //通用行为
    public static void eating(Animal animal){
        animal.eat();
    }
    //飞翔行为
    public static void flying(AnimalFly animalFly){
        animalFly.fly();
    }
    //跑路行为
    public static void running(AnimalRun animalRun){
        animalRun.run();
    }
    public static void main(String[] args) {
        Dog dog = new Dog("旺财", 2);
        Bird bird = new Bird("啾啾", 3);
        eating(dog);
        eating(bird);
        System.out.println("----------------------------");
        flying(bird);
        System.out.println("----------------------------");
        running(dog);
        running(bird);
    }
}

三:接口之间的继承

在Java中,接口也可以继承一个接口,达到复用的效果。接口之间的继承相当于把多个接口合并在一起

interface A{
    void A_method();
}
interface B{
    void B_method();
}
interface C extends A, B{

}

class D implements C{
    @Override
    public void A_method(){

    }
    @Override
    public void B_method(){

    }
}

四:接口使用示例

  • Java中为我们提供了很多的接口,大大简化了我们编写程序的困难,这里举一个个例子,展示一下它们的基本使用方法

如下是一个Student类,内含成员变量nameagescore,实例化两个对象student1student2

class Student{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score){
        this.name = name;
        this.age = age;
        this.score = score;
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Student student1 = new Student("张三", 27, 92.3);
        Student student2 = new Student("李四", 25, 96.3);
    }
}

对于基本数据类型,例如int a = 10int b = 20的大小比较,Java是很容易做到的。但是对于自定义类型,例如上面的student1student2是不能直接比较的,因为Java对于它们的比较“无从下手”(是比较age呢还是比较score呢?)。在C++中可以通过运算符重载达到,但是这个机制会显得代码十分臃肿,比如下面

bool operator==(Date& d1,Date& d2)//运算符重载函数
{
    return d1._year==d2.year
    &&d1.month==d2.month
    &&d3.day=d3.day;
}

而在Java则可以通过相关接口实现。在这里例子中需要借助接口Comparable,让Student类实现该接口

其中Comparable<T>中的<T>在Java泛型中会讲到,这里在<T>中填入你想要比较的类型即可,所以我们填入Student。接着在Student类中重写该接口的抽象方法compareTo

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score){
        this.name = name;
        this.age = age;
        this.score = score;
    }
    @Override
    public int compareTo(Student student){
        if(this.age > student.age){
            return 1;
        }else if(this.age == student.age){
            return 0;
        }else{
            return -1;
        }
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Student student1 = new Student("张三", 27, 92.3);
        Student student2 = new Student("李四", 25, 96.3);
        if(student1.compareTo(student2) > 0){
            System.out.println(student1.name + "的年龄大");
        }else if(student1.compareTo(student2) == 0){
            System.out.println(student1.name + "和" +student2.name + "的年龄一样的");
        }else{
            System.out.println(student2.name + "的年龄大");
        }
    }
}


上面这种方法其实并不好,因为它对类的“入侵”太过严重,在Student类已经写死了使用age比较。如果有一天需求换了,需要使用score比较,那么你又得重新更改

所以为了解决这样的问题,我们要借助Java设计模式中的适配器思想。就这个例子来说,可以实现两个比较器AgeComparatorScoreComparator专门用于agescore的比较。由于不在类的内部实现了,所以这里要引入一个新的接口Comparator<T>

它有很多抽象方法,这里我们使用int compare(T o1, T o2)这个方法。最终代码如下

class Student{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score){
        this.name = name;
        this.age = age;
        this.score = score;
    }
}

class AgeComparator implements Comparator<Student>{
    @Override
    public int compare(Student student1, Student student2){
        return student1.age - student2.age;
    }
}
class ScoreComparator implements Comparator<Student>{
    @Override
    public int compare(Student student1, Student student2){
        return (int)(student1.score - student2.score);
    }
}



public class TestDemo {
    public static void main(String[] args) {
        Student student1 = new Student("张三", 27, 92.3);
        Student student2 = new Student("李四", 25, 96.3);

        //使用age进行比较,那么就建立一个关于age的比较器
        AgeComparator ageComparator = new AgeComparator();
        int ret = ageComparator.compare(student1, student2);
        if(ret > 0){
            System.out.println(student1.name + "的年龄大");
        }else if(ret == 0){
            System.out.println(student1.name + "和" +student2.name + "的年龄一样的");
        }else {
            System.out.println(student2.name + "的年龄大");
        }
        System.out.println("--------------------------------------------------------");
        //使用score进行比较,那么就建立一个关于score的比较器
        ScoreComparator scoreComparator = new ScoreComparator();
        int ret1 = scoreComparator.compare(student1, student2);
        if(ret1 > 0){
            System.out.println(student1.name + "的成绩高");
        }else if(ret1 == 0){
            System.out.println(student1.name + "和" +student2.name + "的成绩一样");
        }else{
            System.out.println(student2.name + "的成绩高");
        }
    }
}

五:Object类

Object类:Java中,Object是所有类的父类,即任何一个类都默认继承Object。换句话说,所有类的对象都可以使用Object的引用来接收

class Person{
    String name = "人类";
    @Override
    public String toString(){
        return name;
    }

}
class Student{
    String name = "学生";
    @Override
    public String toString(){
        return name;
    }

}
public class TestDemo {
    public static void test(Object o){
        System.out.println(o.toString());
    }
    public static void main(String[] args) {
        test(new Person());
        test(new Student());
    }
}

下面挑选Object比较重要的两个方法进行讲解

(1)equals()方法:对象比较

Java中,如果使用==对自定义类型进行比较,那么它所比较的只是引用变量的地址是否相同,并不会去看你语义是否相等。比如下面有两个Person对象,他们的身份证号IdNum相同,所以按理说应该是相等的,因为是同一个人,但是如果用==比较你会发现结果是false

class Person{
    String IdNum;
    public Person(String IdNum){
        this.IdNum = IdNum;
    }

}
public class TestDemo {

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

因此这时必须重写Object类中的equals方法

class Person{
    String IdNum;
    public Person(String IdNum){
        this.IdNum = IdNum;
    }
    @Override
    public boolean equals(Object obj){
        //空对象
        if(obj == null){
            return false;
        }
        //自己和自己比较
        if(this == obj){
            return true;
        }
        //不是Person对象
        if(!(obj instanceof Person)){
            return false;
        }
        Person person = (Person)obj;//向下转型
        return this.IdNum.equals(person.IdNum);
    }

}
public class TestDemo {

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

(2)hashcode方法

接上面的例子,调用两个对象的hashcode方法,可以看到虽然两个对象在语义上是相等的,但是Java认为这两个对象是不一样的

public class TestDemo { 

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

所以我们可以重写hashcode方法,让其哈希值变得一样

class Person{
    String IdNum;
    public Person(String IdNum){
        this.IdNum = IdNum;
    }
    @Override
    public int hashCode(){
        return Objects.hash(IdNum);
    }


}
public class TestDemo {

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

0

评论区