Java抽象类、接口知识总结

抽象类

什么是抽象类

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

public class Shape {
    public void draw(){
        System.out.println("什么都不干");
    }
}

语法规则

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

注意事项

  • 抽象类不能实例化(可以简单地理解为抽象抽象肯定不能实例化啦)
  • 在抽象类中可以拥有和普通类一样的属性和方法
  • 抽象类可以被继承(不然要你干啥)可以说抽象类的的最大意义就是为了继承
  • 当一个普通类A继承了一个抽象类,那么这个普通类一定要重写抽象类中的抽象方法,但当这个普通类A不想再在自己的内部实现父类抽象类的抽象方法,那么可以将这个普通类修改为抽象类A,此时便可以不实现抽象方法,但如果一个普通类B继承了这个抽象类A,此时便需要实现这个抽象方法了,但还不想实现的话就像上面一样设置为抽象类,但最后总归是要实现的(因为出来混总是要还的)
  • 抽象方法不能是private的(废话,因为你抽象方法就是要被重写的,你设成private还怎么重写,因为重写方法的话的话子类的访问权限要大于父类的访问权限)

接口

什么是接口

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

语法规则

在刚才的打印图形的示例中, 我们的父类 Shape 并没有包含别的非抽象方法, 所以也可以设计成一个接口

interface IShape { 
    void draw();
}

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

public class Test {
  public static void main(String[] args) { 
  IShape shape = new Rect(); 
  shape.draw();
 }
}

  • 使用interface定义一个接口
  • Cycle 使用 implements 继承接口. 此时表达的含义不再是 “扩展”, 而是 “实现”
  • 在调用的时候同样可以创建一个接口的引用, 对应到一个子类的实例. 接口不能单独被实例化.

扩展(extends) vs 实现(implements)

  • 扩展指的是当前已经有一定的功能了, 进一步扩充功能.
  • 实现指的是当前啥都没有, 需要从头构造出来.
interface A {
    void funcA();
}
interface B {
    void funcB();
}
//implements实现
class C implements A { 
   @Override
  public void funA() { 
  System.out.println("funA");
  }
}
//extends:扩展
interface D extends A,B{
    void funcD();
}
//此时D有funAfunBfunD三种方法

注意事项

  • 接口当中的方法不能具体实现(抽象方法
  • 接口中方法默认为public abstract可以省略
  • 接口当中的成员变量默认为public static final(定义的同时初始化)
  • 接口当中default修饰的方法,是当前接口的默认方法。
  • 接口不可以进行实例化
  • 接口也可以继承
  • 一个类可以继承一个类(抽象类或者普通类),但可以继承多个接口
  • 接口可以扩展多个接口,所以接口的出现就是为了解决Java的多继承

举例,下面这段代码包含和上边所需要注意的事项:

package 理解接口;
public interface IFlying {
    void fly();
    /*
    default void func(){     
   }*/
}
public interface IRunning {
    void run();
}
public interface ISwimming {
    void swim();
}
// 两栖的动物, 既能跑, 也能游 接口的继承
interface IAmphibious extends IRunning, ISwimming {

}


class Animal{
    protected String name;
    public Animal(String name){
        this.name=name;
    }
}

class Duck extends Animal implements IRunning,ISwimming,IFlying {
    public Duck(String name) {
        super(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 + " 正在游泳!!");
    }
}

class Robot implements  IRunning{
    @Override
    public void run() {
        System.out.println("机器人正在跑....");
    }
}
public class Test {
    public static void goFly(IFlying iFlying){
        iFlying.fly();
    }
    public static void goRun(IRunning iRunning){
        iRunning.run();
    }
    public static void goSwim(ISwimming iSwimming){
        iSwimming.swim();
    }

    public static void main(String[] args) {
        goFly(new Duck("唐老鸭"));
        goRun(new Duck("唐老鸭"));
        goSwim(new Duck("唐老鸭"));
        goRun(new Robot());

    }
    /*public static void main(String[] args) {
      IRunning iRunning=new Duck("唐老鸭");
      iRunning.run();
      IFlying iFlying=new Duck("唐老鸭");
      iFlying.fly();
      ISwimming iSwimming=new Duck("唐老鸭");
      iSwimming.swim();
    }*/
}

接口的实例(常见的接口)

Compareable

对于Comparable接口来说,一般用在类的内部修改类的本身底层是compareTo

举例

//Test.java类(一个类)
import java.util.*;

//对于Comparable接口来说,一般用再累的内部 修改类的本身
class Student implements Comparable<Student>{
    public String name;
    @Override
    //o传入的引用 默认从小到大排序
    public int compareTo(Student o) {
        if(this.score>o.score)
        {
            return 1;
        }else if(this.score==o.score)
        {
            return 0;
        }else{
            return -1;
        }
    }

    public int score;
    public Student(String name,int score){
        this.name=name;
        this.score=score;
    }
    //直接在类的内部修改
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
}
public class Test {
    public static void main(String[] args) {
        Student[] students=new Student[3];
        students[0] = new Student("bit",89);
        students[1] = new Student("abc",19);
        students[2] = new Student("htf",59);
        System.out.println(Arrays.toString(students));
        System.out.println("===============排序==============");
        Arrays.sort(students);
        System.out.println(Arrays.toString(students));

    }
}

Compareator

Compareato(比较器), 一般用在类外底层实现是Compare

举例

//NameComparator.java类
import java.util.Comparator;
public class NameComparator implements Comparator<Student> {
    @Override
    public int compare(Student2 o1, Student2 o2) {
        return o1.name.compareTo(o2.name);
    }
}
//ScoreComparator.java类
import java.util.Comparator;
public class ScoreComparator implements Comparator<Student> {
    @Override
    public int compare(Student2 o1, Student2 o2) {
        return o1.score-o2.score;
    }
}
//Test.java类
import java.util.*;
//比较器 一般用在类外
class Student {
    public String name;
    public int score;

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

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

}
public class Test {

    public static void main(String[] args) {
        Student[] students = new Student[3];
        students[0] = new Student("bit",89);
        students[1] = new Student("abc",19);
        students[2] = new Student("htf",59);
        System.out.println(Arrays.toString(students));
        System.out.println("===============根据分数排序==============");
        ScoreComparator scoreComparator=new ScoreComparator();
        Arrays.sort(students,scoreComparator);
        System.out.println(Arrays.toString(students));
        System.out.println("===============根据姓名进行排序==============");
        NameComparator nameComparator = new NameComparator();
        Arrays.sort(students,nameComparator);
        System.out.println(Arrays.toString(students));
    }
}

Cloneable(实现深拷贝)

这里就不具体说了,之后会详细写到深拷贝和浅拷贝(有兴趣的话可以看看)
Object 类中存在一个 clone 方法, 调用这个方法可以创建一个对象的 “拷贝”. 但是要想合法调用 clone 方法, 必须要先实现 Clonable 接口, 否则就会抛出 CloneNotSupportedException 异常.

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

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

     @Override
     protected Object clone() throws CloneNotSupportedException {
         return super.clone();
     }
 }
public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person personOdd = new Person("bit");
        personOdd.m.money=666;
        Person personNew = (Person)personOdd.clone();
        System.out.println(personOdd.name);//bit
        System.out.println(personNew.name);//bit
        
        System.out.println("============修改name============");
        personNew.name = "zjc";

        System.out.println(personOdd.name);//bit
        System.out.println(personNew.name);//zjc
    }
}
  • 此时拷贝了一份旧的personOdd给新的personNew,因为string不是自定义的类型,所以修改personNew的名字,原来personOdd的名字不会改变

在这里插入图片描述

但当自定义类型中还有自定义类型的话,这样拷贝的话就属于浅拷贝了,因为他们指向的的引用属于同一个引用,修改新的数值的话会影响原数值。

class Money {
    public double money=666;
}
 class Person implements Cloneable{
     public String name;
     public Money m=new Money();
     public Person(String name){
         this.name=name;
     }
     @Override
     public String toString() {
         return "Person{" +
                 "name='" + name + '\'' +
                 '}';
     }

     @Override
     protected Object clone() throws CloneNotSupportedException {    
         return super.clone();
     }
 }
public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person personOdd = new Person("bit");
        personOdd.m.money=666;
        Person personNew = (Person)personOdd.clone();

        System.out.println(personOdd.name);//bit
        System.out.println(personNew.name);//bit
        System.out.println(personOdd.m.money);//666
        System.out.println(personNew.m.money);//666
        System.out.println("============修改money============");
        personNew.m.money=888;
        System.out.println(personOdd.m.money);//888
        System.out.println(personNew.m.money);//888
    }
}
输出结果为
888
888

在这里插入图片描述
所以我们的Money需要也需要继承Conleable,并重写clone方法,且在Person类中进行重写clone的时候创建新的引用m,personNew的m指向新的引用而不是原有personOdd的m,这样的话修改其personNew的money值便不会影响personOdd的money值了(深拷贝)。

class Money implements Cloneable{
    public double money=12.8;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
 class Person implements Cloneable{
     public String name;
     public Money m=new Money();
     public Person(String name){
         this.name=name;
     }

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

     @Override
     protected Object clone() throws CloneNotSupportedException {
         Person personNew = (Person)super.clone();
         personNew.m = (Money) this.m.clone();
         return personNew;
         //return super.clone();
     }
 }
public class Test2 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person personOdd = new Person("bit");
        personOdd.m.money=666;
        Person personNew = (Person)personOdd.clone();
        System.out.println(personOdd.m.money);//666
        System.out.println(personNew.m.money);//666
        System.out.println("============修改money============");
        personNew.name = "zjc";
        personNew.m.money=888;
        System.out.println(personOdd.m.money);//666
        System.out.println(personNew.m.money);//888
    }
}
此时输出的结果为
666
888

在这里插入图片描述

总结

抽象类和接口都是 Java 中多态的常见使用方式. 都需要重点掌握. 同时又要认清两者的区别

  • 核心区别: 抽象类中可以包含普通方法和普通字段, 这样的普通方法和字段可以被子类直接使用(不必重写), 而接口中不能包含普通方法, 子类必须重写所有的抽象方法.
  • 如之前写的 Animal 例子. 此处的 Animal 中包含一个 name 这样的属性, 这个属性在任何子类中都是存在的. 因此此处的 Animal 只能作为一个抽象类, 而不应该成为一个接口.
class Animal {
  protected String name;
  public Animal(String name) { 
   this.name = name;
  }
}

好了,如果有错误或者补充,欢迎大家留言评论。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值