Java入门姿势【面向对象编程10】抽象类和接口

本次课程我们来主要讲解一下抽象类以及接口,本期知识点在Java基础学习中非常重要,尤其是接口在今后的编程生涯中,这些知识点将会一直陪伴你,所以没有在这方面学习精通的同学记得要抓紧时间复习呀。

上期知识点:

Java入门姿势【面向对象9】三大特性之一多态性

学习教程推荐:

一、知识点:final关键字

final关键字的作用:

final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字。另外,Java中的String类就是一个final类。

  • 修饰变量: 被他修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。
    final int MAX_SPEED = 120;
  • 修饰方法:该方法不可被子类重写。但是可以被重载!
    final void study(){}
  • 修饰类: 修饰的类不能被继承。比如:Math、String、System等。
    final class A {}

了解以上使用“final关键字”,模拟实现Math类:

模拟实现Math类
public final class Maths {
    private Maths(){
    }
    public static final double PI = 3.14159;

    public  static final double  pow(int x,int y){
        double result = 1;
        for(int i=0;i<y;i++){
            result *= x;
        }
        return result;
    }
    public static  double abs(double num ){
        if(num >=0){
            return num;
        }else{
            return -num;
        }
    }
}

再次提醒注意:

  1. 注意:final不能修饰构造方法
  2. final修饰基本数据类型,值只能赋值一次,后续不能再赋值
  3. final修饰引用数据类型,final Dog dog = new Dog("亚亚");,不能变化的引用变量的值,可以变化的是对象的属性

示例练习:final关键字修饰引用变量

public class Dog {
    String name;
    public  Dog(String name){
        this.name = name;
    }
}
public class Test2 {
    public static void main(String[] args) {
        //final int NUM = 5;
        final int NUM;
        NUM = 5;
       // NUM = 6;
        System.out.println(NUM);
       //final Dog dog = new Dog();
        final Dog dog;
        dog = new  Dog("丫丫");
        dog.name = "欧欧";
        //dog = new Dog("菲菲");
    }
}

二、抽象类:

·抽象方法

使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。

·抽象类

使用abstract修饰的类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。

问题1:

Animal an = new Animal();没有一种动物,名称是Animal,所以Animal不能被实例化

解决:抽象类


问题2:

子类必须重写父类的某个方法,否则出现编译错误

解决:抽象方法

抽象类和抽象方法-代码示例:

public abstract class Animal {
    private String color;
    public Animal() {
    }
    public Animal(String color) {
        this.color = color;
    }
    public abstract  void shout();
    public abstract  void eat();
    public String toString() {
        return "Animal{"color='" + color + '\'' + '}';
    }
}

public class Dog extends Animal {
    private String nickName;
    public Dog() {
    }
    public Dog(String color, String nickName) {
        super(color);
        this.nickName = nickName;
    }
    public void shout() {
        System.out.println("旺旺旺");
    }
    public void eat() {    }
    public String toString() {
        return "Dog{nickName='" + nickName + '\'' +"} " + super.toString();
    }
}

抽象类主要要点:

1.有抽象方法的类只能定义成抽象类

2.抽象类不能实例化,即不能用new来实例化抽象类。

3.抽象类必须有构造方法,创建子类对象的时候使用

4.一个抽象类至少0个抽象方法,至多(所有的方法都是抽象方法)个抽象方法

5.子类必须重写父类的抽象方法,不重写就提示编译错误;或者子类也定义为抽象类

6.override 重写 implements 实现
父类的方法是抽象的,需要子类实现;父类的方法不是抽象的,子类可以重写

三、接口

接口就是规范,定义的是一组规则,体现了现实世界中“如果你是…则必须能…”的思想。如果你是天使,则必须能飞。如果你是汽车,则必须能跑。

接口的本质是契约,就像我们人间的法律一样。制定好后大家都遵守。

接口的声明格式:

[访问修饰符] interface 接口名 [extends 父接口1,父接口2…] {
常量定义; 
方法定义;
}

定义接口的详细说明:

  • 访问修饰符:只能是public或默认。
  • 接口名:和类名采用相同命名机制。
  • extends:接口可以多继承。
  • 常量:接口中的属性只能是常量,总是:public static final 修饰。不写也是。
  • 方法:接口中的方法只能是:public abstract。 省略的话,也是public abstract。

接口需要注意的点:

  • 子类通过implements来实现接口中的规范。
  • 接口不能创建实例,但是可用于声明引用变量类型。
  • 一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
  • JDK1.8(不含8)之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
  • JDK1.8(含8)后,接口中包含普通的静态方法、默认方法。

编写需求-训练接口:

需求:飞机、鸟、超人,导弹参加飞行表演。

思路1:定义一个父类Flyable,让飞机、鸟、超人,导弹都继承该类。不可以,因为继承表达的是is-a的关系,而飞机、鸟、超人不是一种事物,

思路2:使用接口,定义一个接口Flyable,让飞机、鸟、超人,导弹都实现该接口。接口表达是has-a的关系

代码示例:定义Flyable 接口:

public interface Flyable {
    //接口中只有常量,没有变量。常量都是全局静态常量
    public static final int NUM = 5;
    /**
     * 飞行
     * 接口的方法自动采用public abstract修饰。
     * 所有方法都是全局抽象方法(<JDK1.8之前)
     */
    public abstract void fly();
}

代码示例:实现Flyable 接口:

public  class Plane implements Flyable {
    @Override
    public void fly() {
        System.out.println("飞机在平流层平稳的飞行");
    }
}
public class Animal {
}

public class Bird extends  Animal implements Flyable{
    @Override
    public void fly() {
        System.out.println("鸟儿在空中展翅飞翔");
    }
}
public class SuperMan implements  Flyable {
    @Override
    public void fly() {
        System.out.println("超人能飞多高呢?");
    }
}

示例:测试Flyable 接口:

public class Test {
    public static void main(String[] args) {
        //new Flyable();
        //Flyable.num = 6;
       Flyable plane  = new Plane();
       showFly(plane);

       Bird bird = new Bird();
       showFly(bird);
       SuperMan sm = new SuperMan();
       showFly(sm);

       Flyable bird3 = new Plane();
       bird3.fly();
       Eatable eatable =  (Eatable)bird3;
       eatable.eat();
    }
    public static  void showFly(Flyable fly){
        fly.fly();
      }
}

总结1:接口的组成

  • 接口和数组、类、抽象类是同一个层次的概念
  • 成员变量:接口中所有的变量都使用public static final修饰,都是全局静态常量
  • 成员方法: 接口中所有的方法都使用public abstract修饰,都是全局抽象方法
  • 构造方法:接口不能new,也没有构造方法
  • 接口做方法的形参,实参可以该接口的所有实现类


总结2:接口和多继承

C++ 多继承

好处 :可以从多个父类继承更多的功能

缺点:不安全 有两个父类Father1,Father2,都有一个方法giveMoney(),子类如果重写了,没有问题,如果子类没有重写,调用giveMoney()是谁的

Java 单继承

好处:安全

缺点:功能受限
解决方案:既安全,功能又强大,采用接口。接口变相的使Java实现了C++的多继承,又没有C++多继承的不安全性
public class Bird extends Animal implements Flyable,Sleepable
必须先extends 再implements

四、接口应用:内部比较器Comparable

图书类、学生类、新闻类、商品类等是不同的类,可是却都需要有比较的功能,怎么办?共同的父类不可以,可以定义一个比较接口Comparable,其中定义一个实现比较的方法compareTo(Object obj)。让各个类实现该接口即可。Java中就是这么来实现的,下面就来模拟实现一下Comparable接口吧。

【示例】定义Comparable接口:

public interface Comparable {
    /**
     * 判断两个对象的大小
     * @param obj  另外一个对象
     * @return
     * > 0  正数  大于
     * =0   等于
     * <0   负数  小于     *
     */
    public int compareTo(Object obj);
}

【示例】实现Comparable接口:

public class Book implements  Comparable{
    private  String bookName;//书名
    private String author;//作者
    private String publisher;//出版社
    private double price;//
    @Override
    public int compareTo(Object obj) {
        Book other =(Book)obj;
        //return this.bookName.compareTo(other.bookName);//""
       if(this.price>other.price){
            return 1;
        } else if (this.price < other.price) {
            return -1;
        }else{
            return 0;
        }
    }
    public Book(String bookName, String author, String publisher, double price){
        this.bookName = bookName;
        this.author = author;
        this.publisher = publisher;
        this.price = price;       
    }
}
public class Person implements  Comparable{
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public int compareTo(Object obj) {
        Person other = (Person)obj;
        return this.age - other.age ;
    }
}

【示例】测试Comparable接口:

public class Test {
    public static void main(String[] args) {
        Book book1 = new Book("倚天屠龙记","金庸","清华大学出版社",35);
        Book book2 = new Book("倚天屠龙记","金庸","清华大学出版社",31);
        //book1.equals(book2)
        int result = book1.compareTo(book2);
        System.out.println(result);
        Person person1 = new Person("张三",24);
        Person person2 = new Person("李四",24);
        result = person1.compareTo(person2);
        System.out.println(result);
    }
}

五、JDK1.8的接口新特征:

JDK7及其之前:

接口的变量都是public final static 全局静态常量,无变化

接口中都是抽象abstract方法,不能有static方法(因为abstract和static、final、private不能共存)

JDK1.8及其之后:

  • 接口中可以添加非抽象方法(static),实现类不能重写,只能通过接口名调用。
  • 如果子类中定义了相同名字的静态方法,那就是完全不同的方法了,直接从属于子类。可以通过子类名直接调用
  • 接口中可以添加非抽象方法(default),实现类可以重写,只能通过对象名调用
  • 实现类可以直接使用default方法,可以重写default方法,但是必须去掉default。(default只能接口中使用)
  • 上级接口中default方法的调用:MyInterface.super.method2()

提供非抽象方法的目的

  • 为了解决实现该接口的子类代码重复的问题
  • 为了既有的成千上万的Java类库的类增加新功能,且不必对这些类重新进行设计。比如只需在Collection接口中增加default Stream<E> stream(),相应的Set和List接口以及它们的子类都包含此的方法,不必为每个子类都重新copy这个方法。

【代码示例】JDK8的接口新特征:

public interface MyInterface {
    public static final double PI = 3.14;
    public abstract void method1();
    public static void method2(){
        System.out.println("JDK1.8中的非抽象方法有两种,一种是static的");
    }
    public default void method3(){
        System.out.println("JDK1.8中的非抽象方法有两种,一种是default的");;
    }
    public static void main(String[] args) {
        MyInterface.method2();
    }
}
public class MyClass implements MyInterface {
    public void method1() {
        System.out.println("接口中的抽象方法,子类必须实现");
    }
    public void method3(){
        MyInterface.method2();
        MyInterface.super.method3();
        System.out.println("重写接口的default方法,须将default去掉");
    }
    public static void main(String[] args) {
        MyInterface  mi = new MyClass();
        mi.method1();
        MyInterface.method2();
        mi.method3();
    }
}

六、面向接口编程:

面向接口编程是面向对象编程的一部分。

为什么需要面向接口编程? 软件设计中最难处理的就是需求的复杂变化,需求的变化更多的体现在具体实现上。我们的编程如果围绕具体实现来展开就会陷入”复杂变化”的汪洋大海中,软件也就不能最终实现。我们必须围绕某种稳定的东西开展,才能以静制动,实现规范的高质量的项目。

接口就是规范,就是项目中最稳定的核心! 面向接口编程可以让我们把握住真正核心的东西,使实现复杂多变的需求成为可能。

通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高整个系统的可扩展性和和可维护性。

面向接口编程的概念比接口本身的概念要大得多。设计阶段相对比较困难,在你没有写实现时就要想好接口,接口一变就乱套了,所以设计要比实现难!

接口语法本身非常简单,但是如何真正使用?这才是大学问。我们需要后面在项目中反复使用,大家才能体会到。

学到此处,能了解基本概念,熟悉基本语法,就已经是非常棒的咯。 请继续努力!再请工作后,闲余时间再看看上面这段话,相信你会有更深的体会。

PDF-Java相关资料打包

感兴趣的同学快去试一下去~~

以上就是本章节所讲述的全部内容啦,稍后我在更新后续哦,喜欢的伙伴支持一下哦~

感谢观看~

Guess you like

Origin blog.csdn.net/LSFZ88888/article/details/120313995