栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【Java SE系列】抽象类与接口

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【Java SE系列】抽象类与接口

内容介绍
  • 1 抽象类
    • 1.1 语法规则
    • 1.2 抽象类的作用
  • 2 接口
    • 2.1 语法规则
    • 2.2 接口使用实例
    • 2.3 克隆接口

1 抽象类 1.1 语法规则

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

abstract class Shape { 
     abstract public void draw(); 
}
  • 在 draw 方法前加上 abstract 关键字, 表示这是一个抽象方法. 同时抽象方法没有方法体(没有 { }, 不能执行具体代码).
  • 对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类.

注意事项

  1. 抽象类不能实例化

    2.抽象方法不能是 private 的()

3.抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用.(抽象类除了包含抽象方法和不能实例化之外和普通类相似)
4.final修饰的抽象方法也不能被重写,因为final修饰就代表方法不能进行修改即不能重写.

打印图形栗子:

abstract class Printf{
    String name = "ant";
     abstract public  void printf();
}
class Rect extends Printf{
    @Override
    public void printf() {
        System.out.println("△");
    }

}
class Cur extends Printf{
    @Override
    public void printf() {
        System.out.println("○");
    }

}
class Flower extends Printf{
    @Override
    public void printf() {
        System.out.println("❀");
    }
}
public class Test {
    public static void printY(Printf pintf){

        pintf.printf();
    }
    public static void main(String[] args) {
        printY(new Cur());
        printY(new Rect());
        printY(new Flower());
    }
}

1.2 抽象类的作用

抽象类存在的最大意义就是为了被继承.
抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法.
使用抽象类的场景就如上面的代码, 实际工作不应该由父类完成, 而应由子类完成. 那么此时如果不小心误用成父类了,使用普通类编译器是不会报错的. 但是父类是抽象类就会在实例化的时候提示错误, 让我们尽早发现问题.

小知识:
使用抽象类相当于多了一重编译器的校验.

2 接口

接口是抽象类的更进一步. 抽象类中还可以包含非抽象方法, 和字段. 而接口中包含的方法都是抽象方法, 字段只能包含静态常量.

2.1 语法规则

我们仍然通过打印图案的栗子来看:

interface Printf{
     public static final String name = "ant";
     abstract public  void printf();
}
class Rect implements Printf{
    @Override
    public void printf() {
        System.out.println("△");
    }

}
class Cur implements Printf{
    @Override
    public void printf() {
        System.out.println("○");
    }

}
class Flower implements Printf{
    @Override
    public void printf() {
        System.out.println("❀");
    }
}
public class Test {
    public static void printY(Printf printf){

        printf.printf();
    }
    public static void main(String[] args) {
        printY(new Cur());
        printY(new Rect());
        printY(new Flower());
    }
}

知识点:
接口中的方法一定是抽象方法, 因此可以省略 abstract。
接口中的方法一定是 public, 因此可以省略 public。
接口中可以有static方法。
抽象方法默认是public abstract的
接口中成员变量默认是public static final修饰的
在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例。
接口不能单独被实例化
当一个类实现了一个接口后,重写方法必须加上public。
一个类可以通过关键字extends继承一个抽象类或普通类,但是只能继承一个类,同时可以通过implement实现多个接口,接口之间用逗号隔开。

我们来看一个错误案例:

interface IShape {
    abstract void draw() ; // 即便不写public,也是public 
}
class Rect implements IShape {
    void draw() {
        System.out.println("□") ; //权限更加严格了,所以无法覆写。
    }
}

这里报错就是因为接口中方法默认是public的,而Rect中的draw方法是包访问修饰符修饰的,作用范围比public小,由于重写方法的作用范围要大于等于父类的方法,所以程序就报错了。

2.2 接口使用实例

接下来我们用接口来实现给对象数组排序
方法一:实现Comparator接口

Comparator接口中含有比较方法,所以我们只需要创建几个类然后实现Comparator接口并重写compara方法就能实现排序功能。

代码实现:

package demo3;

import java.util.Arrays;
import java.util.Comparator;


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

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

   

}
class AgeComparator implements Comparator{
    @Override
    public int compare(Student o1, Student o2) {

        return o1.age-o2.age;
    }
}
class NameComparator implements Comparator{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}
class ScoreComparator implements Comparator{
    @Override
    public int compare(Student o1, Student o2) {
        return (int)(o1.score-o2.score);
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] student = new Student[3];
        student[0] = new Student("zhangsan",12,98.9);
        student[1] = new Student("lisi",6,95.9);
        student[2] = new Student("wangwu",18,88.9);
        AgeComparator ageComparator = new AgeComparator();
        NameComparator nameComparator = new NameComparator();
        ScoreComparator scoreComparator = new ScoreComparator();
        System.out.println(Arrays.toString(student));
        Arrays.sort(student,ageComparator);
        System.out.println(Arrays.toString(student));
        Arrays.sort(student,nameComparator);
        System.out.println(Arrays.toString(student));
        Arrays.sort(student,scoreComparator);
        System.out.println(Arrays.toString(student));
    }
}


方法一:实现Comparable接口

在 sort 方法中会自动调用 compareTo 方法.所以我们只需要在Student类中实现Comparable接口并重写compara To方法即可实现排序。

package demo3;

import java.util.Arrays;
import java.util.Comparator;


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

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + ''' +
                ", age=" + age +
                ", score=" + score +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] student = new Student[3];
        student[0] = new Student("zhangsan",12,98.9);
        student[1] = new Student("lisi",6,95.9);
        student[2] = new Student("wangwu",18,88.9);
        System.out.println(Arrays.toString(student));
        Arrays.sort(student);
        System.out.println(Arrays.toString(student));
    }
}

注意:
实现Comparable接口有个局限性,即只能排序指定的类型,若要根据其他规则去排序则需要重新修改compara To函数的内容。

2.3 克隆接口

前面我们已经知道创建对象的方法有new,克隆的方式就是第二种创建对象的方式。

Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出CloneNotSupportedException 异常.

代码实现:

class Person implements Cloneable{
    int age;

    public Person(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Test {
    public static void main(String[] args)throws CloneNotSupportedException {
        Person person = new Person(20);
        Person person1 = (Person) person.clone();
        System.out.println(person.age);
        System.out.println(person1.age);
    }
}

注意:
1.类必须要实现克隆接口才能进行克隆
2.类中必须要重写clone方法
3.主函数中必须要进行异常抛出
4.当前克隆属于浅拷贝(不会克隆引用指向的对象)后面会实现深拷贝的克隆


深拷贝克隆实现:

class Money implements Cloneable{
    int num = 10;


    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
class Person extends Money implements Cloneable{
    int age;
    Money money = new Money();


    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person tep = (Person) super.clone();
        tep.money = (Money)super.clone();
        return tep;
    }
}
public class Test {
    public static void main(String[] args)throws CloneNotSupportedException {
        Person person = new Person();
        Person person1 = (Person) person.clone();
        person1.money.num = 13;
        System.out.println(person.money.num);
        System.out.println(person1.money.num);
    }
}

此时Person和Person1中的money引用指向不同的对象。

小知识:
深拷贝浅拷贝不是方法自带,是看编程人员如何去设计的。

总结:
1.抽象类可以有普通成员函数,接口一般只存在public abstract方法(jdk1.8中接口也可以有静态方法)。
2.抽象类中的成员变量可以是各种类型的,接口中只能是public static final类型。
3.抽象类只能继承一个,接口可以实现多个。
4.抽象类和接口都不能实例化。
5.抽象类不一定有抽象方法(但是声明为抽象类没有抽象方法也不能实例化),但是包含抽象方法的类一定为抽象类。
6.抽象类是对类本质的抽象,表达的是 is a的关系,抽象类包含并实现子类的通用特性,将子类存在差异化的地方进行抽象,交给子类去实现。
7.接口是对行为的抽象,表达的是 like a的关系,比如鸟可以像飞行器一样飞,但是其本质还是鸟。接口的核心是定义行为即功能,也就是实现类可以做什么,至于主体是谁、如何实现接口都不关心。

作者水平有限,若文章有任何问题欢迎私聊或留言,希望和大家一起学习进步!!!
创作不易,再次希望大家支持下,谢谢大家

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/571402.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号