当前位置: 首页 > 图灵资讯 > 技术篇> 【Java】你真的了解抽象类和接口?

【Java】你真的了解抽象类和接口?

来源:图灵教育
时间:2023-05-30 09:35:33

一、抽象类

在Java中,如果一个类被录用 abstract 修饰称为抽象类,抽象类被称为抽象类 abstract 修饰方法称为抽象方法**,抽象方法不需要给出具体的实现体**。

public class TestDemo {    public static void main(String[] args){        Circle c = new Circle();        c.setR(5);        c.getArea();        Squre s = new Squre();        s.setLength(10);        s.getArea();    }}////抽象类abstracttracttact class Shape{    private int size;    ///抽象方法    abstract public void getArea();}class Circle extends Shape{    private int r;    public int getR() {        return r;    }    public void setR(int r) {        this.r = r;    }    //重写抽象方法    @Override    public void getArea() {        double  area = r*r*r*4.0/3;        System.out.println("这个圆形的面积是: "+area);    }}class Squre extends Shape{    private int length;    //重写抽象方法    @Override    public void getArea() {        double area = length*length;        System.out.println("这个正方形的面积是: "+area);    }    public int getLength() {        return length;    }    public void setLength(int length) {        this.length = length;    }}
  • 抽象特征
  • 抽象类采用abstract修饰和抽象方法,没有具体实现。抽象类可以包含普通类可以包含的成员。抽象类存在的最大意义是继承。
  • 抽象方法不能是私有的。如果一个普通的类继承了抽象类,那么抽象类中的抽象方法必须重写,不能由static和final修改,因为抽象方法应该由子类继承。
  • 抽象方法不一定包含在抽象类中,但抽象方法必须包含在抽象类中,抽象类之间的相互继承不需要重写抽象方法。
二、接口
  • 接口的定义

界面的定义格式与定义格式基本相同,用class关键字代替class关键字 interface 定义一个接口的关键字。

  • 接口的使用

 

///界面定义interface USB {    void openDevice();    void closeDevice();}////实现接口classss Mouse implements USB {    @Override    public void openDevice() {        System.out.println(打开鼠标);    }    @Override    public void closeDevice() {        System.out.println(关闭鼠标);    }    public void click(){        System.out.println(鼠标点击);    }}////实现接口classss KeyBoard implements USB{///实现界面中的抽象类    @Override    public void openDevice() {        System.out.println(“打开键盘”);    }    @Override    public void closeDevice() {        System.out.println(关闭键盘);    }    public void inPut(){        System.out.println(键盘输入);    }}
  • 注意事项
  • ❗ 界面不能直接使用,必须有一个类来实现界面,并实现界面中的所有抽象方法

【Java】你真的了解抽象类和接口?_后端

  • ❗ 子类和父类之间的extends 类与接口之间的继承关系 implements 实现关系
  • 接口中的每一种方法都是public的抽象方法, 即接口中的方法将被隐式指定为 public abstract(只能是public abstract,所有其他修饰符都会报错。

【Java】你真的了解抽象类和接口?_后端_02

  • ❗ 接口中的方法不能在接口中实现,只能通过实现接口类来实现 ,JDK1.8 开始允许有可实现的方法,但这种方法只能是default 修改后,类别在实现接口时不需要重写默认方法。

具体作用: 当我们扩展业务时,我们需要在接口中添加新的方法。如果新方法写成普通方法,则需要在接口的所有实现类别中重写。如果新方法定义为default类型,则无需在所有实现类别中重写default方法。哪个实现类别需要添加该方法,并在哪个类别中实现

【Java】你真的了解抽象类和接口?_后端_03

  • 重写界面中的方法时,不能使用default访问权限进行修改
  • 变量可以包含在接口中,但接口中的变量将被隐式指定 public static final 变量
  • 接口中没有静态代码块和结构方法
  • 虽然界面不是类,但字节码文件的后缀格式在界面编译完成后也是如此.class

【Java】你真的了解抽象类和接口?_抽象类_04

  • 如果接口中的抽象方法没有实现,则必须将该类设置为抽象类
  • 一类实现多个接口
interface USB {     void openDevice();    void closeDevice();}interface ULine{    void lineInsert();}class Mouse implements USB,ULine{    @Override    public void openDevice() {        System.out.println(打开鼠标);    }    @Override    public void closeDevice() {        System.out.println(关闭鼠标);    }    @Override    public void lineInsert() {        System.out.println(插入鼠标线);    }}

当一个类实现多个接口时,应实现每个接口中的抽象方法,否则类必须设置为抽象类

  • 一个类继承一个父类,实现多个接口
public class TestDemo3 {    public static void main(String[] args) {        Duck duck = new Duck("yaya");        walk(duck);        Brid brid = new Brid("gugu");        walk(brid);    }    public static void walk(IRunning running) {        System.out.println(“散步”);        running.run();    }} class Animal {        protected String name;        public Animal(String name) {            this.name = name;       }}interface IFlying {        void fly();}interface IRunning {      void run();}interface ISwimming {        void swim();}class Duck extends Animal implements IFlying,IRunning,ISwimming{    public Duck(String name) {        super(name);    }    @Override    public void fly() {        System.out.println(“飞飞”;    }    @Override    public void run() {        System.out.println(鸭子嘎嘎跑);    }    @Override    public void swim() {        System.out.println(“游”;    }}class Brid extends Animal implements IRunning,ISwimming,IFlying{    public Brid(String name) {        super(name);    }    @Override    public void  fly() {        System.out.println(鸟儿飞);    }    @Override    public void run() {        System.out.println(鸟儿跑);    }    @Override    public void swim() {        System.out.println(鸟儿游);    }}
  • 接口中的多态
public class TestDemo3 {    public static void main(String[] args) {        Duck duck = new Duck("yaya");        walk(duck);        Brid brid = new Brid("gugu");        walk(brid);    }    public static void walk(IRunning running) {        System.out.println(“散步”);        running.run();    }}

有了接口后, 类用户不必关注特定类型,而只关注某一类是否具有特定的能力.

  • 接口之间的继承

一个类可以实现多个接口,多个接口可以在接口之间继承。也就是说,多个继承的目的可以通过接口来实现。接口可以继承一个接口, 达到复用效果. 使用 extends 关键字

interface IRing { void run();} interface ISing { void swim();} interface IAmphibious extends IRunning, ISwimming {}class Frog implements IAmphibious { @Override public void run() { System.out.println(“跑啊跑”; } @Override public void swim() { System.out.println(“游啊游”; }}

接口之间的继承相当于将多个接口结合在一起.

✅抽象和界面的区别???

区别

抽象类(abstract)

接口(interface)

1

结构组成

普通+抽象方法

抽象法+全局变量

2

权限

各种权限

public

3

子类使用

使用extends关键字继承抽象类

使用implements关键字实现接口

4

关系

一个抽象类可以实现多个接口

接口不能继承抽象类,但可以用extends关键字继承多个接口

5

子类限制

子类只能继承一个抽象类

一个子类可以实现多个接口

三、Object类

Object是Java默认提供的一个类别。除了Object,Java中的所有类别都有继承关系。默认情况下,Object父类将被继承。也就是说,所有类别的对象都可以通过Object的引用来接收。

public class Testdemo5 {    public static void main(String[] args) {        function(new Person());        function(new Student());    }    public static void function(Object obj){        System.out.println(obj);    }}class Person{    private int age;    private String name;}class Student{    private int grade;    private String sno;}

Object类提供的一些默认方法

3.1 toString()方法
实现///Object类tostring()方法:public String toString() {     return getClass().getName() + "@" + Integer.toHexString(hashCode());}

toString()方法一般需要重写后使用。

3.2 hashcode()方法
  • 返回对象的hash代码值

源码:

public native int hashCode();

重写hashCode() 方法

class Per{    public String name;    public int age;    public Per(String name, int age) {        this.name = name;        this.age = age;    }    @Override    public int hashCode() {        return Objects.hash(name, age);    }}public class Testdemo6 {    public static void main(String[] args) {        Per per1 = new Per("gaobo",20);        Per per2 = new Per("gaobo", 20);        System.out.println(per1.hashCode());       /*         注:哈希值是一样的。        结论:        1、hashcode方法用于确定对象在内存中的存储位置是否相同        2、事实上,hashCode() 只有在散列表中才有用,在其他情况下才无用。        结论:        1、hashcode方法用于确定对象在内存中的存储位置是否相同        2、事实上,hashCode() 只有在散列表中才有用,在其他情况下才无用。        hashCode()在散列表中 其功能是获取对象的散列码,然后确定对象在散列表中的位置。        */        System.out.println(per2.hashCode());    }}

 

【Java】你真的了解抽象类和接口?_抽象类_07

3.3 equals()方法
  • 比较地址
// objectequals方法publict boolean equals(Object obj){     return (this == obj);   // 使用引用中的地址直接进行比较   }

 

【Java】你真的了解抽象类和接口?_Java_08

要比较对象中的内容,必须重写object中的equals方法,因为equals方法默认也是根据地址比较的

重写equals()方法

@Override public boolean equals(Object obj) {     //判断是否为空        if (obj == null) {            return false ;        } if(this == obj) {            return true ;        }        // 不是Person的对象        if (!(obj instanceof Per)) {            return false ;        }        Per per = (Per) obj ; // 向下转型,比较属性值        return this.name.equals(per.name) && this.age==per.age ;    }    /*  @Override    public boolean equals(Object obj) {        Per per = (Per)obj;        //String类调用自己的equals,        // S1和S2的比较规则是根据String类重写后的equals方法进行比较,        //显然,String类的比较规则是根据值进行比较的,因此结果将输出True。        if(this.name.equals(per.name)&&this.age == per.age){                return true;        }        return false;    }}*/

 

【Java】你真的了解抽象类和接口?_后端_09

hashcode()和equals()方法由编译器自动生成重写

@Overridepublic boolean equals(Object o) {        if (this == o) return true;        if (o == null || getClass() != o.getClass()) return false;        Per per = (Per) o;        return age == per.age &&                Objects.equals(name, per.name);    }    @Override    public int hashCode() {        return Objects.hash(name, age);   }

object类,hashcode()方法是本地方法,返回对象的地址值,object类中的equals()方法也是两个对象的地址值。**如果equals()相等,说明两个对象的地址值相等,当然hashcode()相等。.**但是hashcode() 相同时,equals()不一定相同

✅✅还必须重写hashcode()方法才能重写equals方法吗?

答:必须,hashCode 和 equals 这两种方法用于协同判断两个对象是否相等。这样做的原因是当重写时,可以提高程序插入和查询的速度equals有必要在方法结束后进行hashCode该方法也被重写,以确保它不会违反它hashCode“相同的对象必须具有相同的哈希值”的协议。

✅✅ == 和 equals 有什么区别?

答:

基本类型和引用类型 == 效果不同,如下所示:

  • 基本类型:比较值是否相同;
  • 引用类型:比较引用是否相同

String x = "string";String y = "string";String z = new String("string");System.out.println(x==y); // trueSystem.out.println(x==z); // falseSystem.out.println(x.equals(y)); // trueSystem.out.println(x.equals(z)); // true

equals() 根据源码可以知道方法 : equals() 本质上是truee

public boolean equals(Object obj) {return (this == obj);}

因此,equals()方法 默认情况下是引用比较,但重写的类别很多 equals 方法,比如 String、Integer 等它变成值比较,所以一般情况下 equals 比较值是否相等。

四、常用接口4.1 Comparable接口(比较)

在学习数组时,Arrays类中的sort方法可以对对对象数组进行排序 , 下面的对象数组能用Arrays吗?.sort排序呢?

class Student {      String name;    int age;    public Student(String name, int age) {        this.name = name;        this.age = age;    }    @Override      public String toString() {         return "Student{" +                "name='" + name + '\'' +                ", age=" + age +                '}';    }}public class test4 {    public static void main(String[] args) {        Student[] students = new Student[] {               new Student("zhangsan", 13),              new Student("lisi", 23),              new Student("able", 17),        };        Arrays.sort(students);        System.out.println(Arrays.toString(students));       }}

 

【Java】你真的了解抽象类和接口?_抽象类_10

此时,编译器不知道它是按姓名还是年龄排序的。当sort方法对对象的类别进行排序时,对象的类别必须实现comparable接口。根据参考文件,comparable接口中只有一种抽象方法。

【Java】你真的了解抽象类和接口?_Java_11

【Java】你真的了解抽象类和接口?_抽象类_12

然后我们可以实现comparable接口,实现和重写compareto方法

class Student implements Comparable<Student>{    public int age;    public String name;        public Student(int age, String name) {        this.age = age;        this.name = name;    }    @Override    public String toString() {        return "Student{" +                "age=" + age +                ", name='" + name + '\'' +                '}';    }    ///重写compareto方法    @Override    public int compareTo(Student o) {        if (this.age - o.age > 0)            return 1;        else        if (this.age - o.age < 0)            return -1;        else            return 0;    }     public static void main1(String[] args) {        Student student = new Student(16, "liba");        Student student1 = new Student(13, "zhangsan");        System.out.println(student.toString());        System.out.println(student1.toString());        if (student.compareTo(student1) > 0) {            System.out.println("student > student1”;        } else {            System.out.println("student < student1”;        }    }}

此时可以得到按年龄排序的结果:

【Java】你真的了解抽象类和接口?_Java_13

我们知道在Arrays.sort(students); 其中有一个学生对象数组。在调用Arays对象数组进行排序时,我们实际上在Comparable接口中调用CompareTo方法对数组的每个元素进行排序和比较,并在Java中对引用数据类型进行比较或排序,Comparable接口中的compareto()通常用于使用 方法

按姓名排序时,重写compareto方法

@Override  public int compareTo(Student o) {   // this.代表引用当前对象,o.代表参数对的引用        if (this.name.compareTo(o.name) > compareto方法在/String类中重写,可直接使用             return 1;          else if (this.name.compareTo(o.name) < 0)              return -1;        else              return 0;    }    //如果当前对象应排在参数对象之前, 返回小于 0 的数字;    //如果当前对象应排在参数对象之后, 返回大于 0 的数字;    //如果当前对象和参数对象不分顺序, 返回 0;