Java多态与抽象类接口详解

目录

一.多态

Override方法重写

重写Object类方法

 多态作用

二.Abstract抽象类

抽象方法

抽象类          

面向抽象设计

小结

三.interface接口

接口的定义

抽象类和接口的区别

 Comparable接口

总结


一.多态

Override方法重写

         在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为重写(Override)。

class Person {
    public void run() {
        System.out.println("Person.run");
    }
}
class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

 注:verride重写Overload重载不同的是:如果方法签名不同,就是Overload重载Overload重载方法是一个新方法;如果方法签名相同,并且返回值也相同,就是Override 重写。

方法名相同,方法参数相同,但方法返回值不同,也是不同的方法。在Java程序中,出现这种情况,编译器会报错。

class Person {
    public void run() { … }
}

class Student extends Person {
    // 不是Override,因为参数不同:
    public void run(String s) { … }
    
    // 不是Override,因为返回值不同:
    public int run() { … }
}

 加上@Override注解可以让编译器帮助检查是否进行了正确的覆写。希望进行覆写,但是不小心写错了方法签名,编译器会报错。

Java的实例方法调用是基于运行时的实际类型的动态调用,而非变量的声明类型。在面向对象编程中称之为多态

重写Object类方法

Java中,所有的class类最终都继承自Object,而Object定义了几个重要的方法:

  • toString():把对象转换为String字符串
  • equals():判断两个对象是否相等(内存地址是否相等)
  • hashCode():计算一个对象的哈希值(哈希码)

在必要的情况下,我们可以覆写Object的这几个方法。例如:

class Person {
    ...
    // 显示更有意义的字符串:
    @Override
    public String toString() {
        return "Person:name=" + name;
    }

    // 比较是否相等:
    @Override
    public boolean equals(Object o) {
        // 当且仅当o为Person类型:
        if (o instanceof Person) {
            Person p = (Person) o;
            // 并且name字段相同时,返回true:
            return this.name.equals(p.name);
        }
        return false;
    }

    // 计算hash:
    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
}

 多态作用

        // 多态
        /*
        1.有继承
        2.有向上转型
        3.有方法重写
        多态好处:
        1.类的使用者对类的学习成本降低,可以让类的调用者连这个是什么类都不必要知道
        2.降低圆圈复杂度
        3.可拓展性比较强
         */

二.Abstract抽象类

抽象方法

        如果父类的方法本身不需要实现任何功能,仅仅是为了定义方法签名,目的是让子类去覆写它,那么,可以把父类的方法声明为抽象方法

        在定义一个方法时使用abstract关键字,表示它是一个抽象方法,本身没有实现任何方法语句。因为这个抽象方法本身是无法执行的,所以,Person类也无法被实例化。编译器会告诉我们,无法编译Person类,因为它包含抽象方法。      

抽象类          

如果一个class中定义了某个方法,该方法没有具体执行代码(方法体)。那么,这个方法就是抽象方法,抽象方法使用abstract修饰。因为无法执行抽象方法,因此这个类也必须定义为抽象类(abstract class )。也可以理解为:抽象方法必须定义在abstract class 抽象类中。

注意,抽象类中,却不是一定存在抽象方法

使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类:

Person p = new Person(); // 编译错误

面向抽象设计

        这种尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象设计,在很多核心类库、框架技术或应用系统的底层架构设计中,比较多见。

面向抽象设计的本质是:

  • 上层代码只定义规范(例如:abstract class Person
  • 不需要子类就可以实现业务逻辑(正常编译)
  • 具体的业务逻辑由不同的子类实现,调用者并不关心
package test.abstract0111;

public class Demo02 {
    public static void main(String[] args) {
        // 面向抽象设计--向上转型
        // 优点:让类的调用者不必关注具体的类型是什么
        // 关注本领能跑的
        IRunning r1 = new Dog("旺财");
        IRunning r2 = new Dog("王子");
        IRunning r3 = new Dog("可达鸭");
        r1.run();
        r2.run();
        r3.run();
// duddddddddddddddddddddddddddddddddddddddddddd
//        // 此处强调游泳本领
//        ISwimming s1 = new Frog("王子");
//        ISwimming s2 = new Duck("可达鸭");
//        s1.swim();
//        s2.swim();
//        IFlying f1 = new Duck("可达鸭");
//        f1.fly();
//        System.out.println("=================");
//        // f1.run ()//因为对外声明的本领是IFlying,所以不能使用RUn方法
//        // 如果想要使用当前对象独有的本领,不是对外声明的这种类型,需要进行向下转型
//        if(f1 instanceof IRunning){
//            IRunning d1 = (IRunning) f1;
//            d1.run();
//        }
    }
}

小结

  • 通过abstract定义的方法是抽象方法,它只有方法的基本定义,没有具体实现(方法体),并且抽象方法必须定义在抽象类中。
  • 子类在继承父类时,必须实现的抽象父类定义的抽象方法。如果不实现,那么子类必须是抽象类。
  • 存在抽象方法的class必须被定义为抽象类(抽象方法必须定义在一个抽象类里)。
  • 抽象类中可以包含构造方法。
  • 抽象类不能被实例化,但是可以定义“引用”,该引用可以指向任意一个“实现子类”对象。
  • 面向抽象编程,使得调用者只关心抽象方法的定义,不关心子类的具体实现。

三.interface接口

接口的定义

package test.abstract2;
/*
当一个类中没有成员变量,且所有的方法为抽象方法时,可以定义为接口
接口定义:
修饰符 interface I接口名{
1.常量
2.抽象方法()
}
接口无法实例化对象
接口中的方法和常量权限修饰符为public
在java中接口的方法默认使用费public abstract 关键字进行修饰
在java中接口中所有的常量默认使用public static final 关键字进行修饰
 */
public interface IShape {
    // 默认使用public static final修饰
    double PI  = 3.14;
    // 接口中不能有构造方法
    // public IShape(){}
    void draw();
    // 接口中不能有方法体的方法
    // public void test(){}
}
// 实现类
// 修饰符 类名 implements 接口名{
// }
// 要求实现接口中所有的抽象方法,除非这个类是抽象类
class Circle implements IShape{
    @Override
    public void draw(){
        System.out.println("O");
    }

}
abstract class Traingle implements IShape{

}
class RuiTraingle extends Traingle{
@Override
    public void draw(){
    System.out.println("三角形");
}
}
package test.abstrict;

public class Demo01 {
    public static void main(String[] args) {
        // 抽象方法无法实例化对象
        // Shape s1 = new Shape();

        // 向上转型--面向上层设计
        Shape c1 = new Circle();
        c1.draw();
        c1.test();// 子类进行test重写,编译看声明的是否有,运行的时候看new出来的类型是否重写
        System.out.println(c1.age);
        System.out.println("===========");
        // 向上转型
        Shape t1 = new RuiTriangle();
        t1.test();// 调用父类中的test方法
        t1.draw();// 调用自己重写的draw方法
    }
}

注:类和接口之间可以多实现,多个实现之间用逗号分割

一个interface可以继承自多个interface(接口之间允许多重继承):

// Smart接口继承AutoDriver接口、ConnectNet接口、AutoCheck接口
// 如果一个类实现Smart接口,则需要实现所有接口中定义的抽象方法
interface Smart extends AutoDriver,ConnectNet,AutoCheck{
	public void sari();
}

// 自动驾驶接口
interface AutoDriver{
	
	public void start();
	
	public void stop();
	
}

// 连接网络
interface ConnectNet{
	
	public void connected();
	
	public void disconnect();
	
}


// 自动检测
interface AutoCheck{
	
	public void checkHealth();
}

 default方法(在JDK 1.8及其更高版本中,接口中可以定义default方法。)

public class Main {
    public static void main(String[] args) {
        Person p = new Student("Xiao Ming");
        p.run();
    }
}

interface Person {
    String getName();
    default void run() {
        System.out.println(getName() + " run");
    }
}

class Student implements Person {
    private String name;

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

    public String getName() {
        return this.name;
    }
}

实现类可以不必覆写default方法。default方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。

default方法和抽象类的普通方法是有所不同的。因为interface没有字段,default方法无法访问字段,而抽象类的普通方法可以访问实例字段。

抽象类和接口的区别

abstract class 抽象类

interface接口

使用 abstract class关键字定义抽象类

使用 interface关键字定义接口

只能extends继承一个class

可以implements多个interface接口

并且接口之间直接允许多重extends继承

可以定义实例字段(成员变量)

不能定义实例字段(成员变量)

使用final关键字定义常量

无需任何关键字,只能定义常量

可以定义抽象方法

可以定义抽象方法

可以定义非抽象方法(普通方法)

可以定义default方法

可以有构造方法

不允许有构造方法

可以使用任意访问修饰符

只能使用public访问修饰符

接口是对行为的抽象,它是抽象方法的集合,利用接口可以达到行为定义和 业务实现分离的架构设计目的,并且接口的多重实现以及多重继承等特性, 在架构设计上更灵活,易扩展。

抽象类大多用于抽取相关子类的共用行为方法实现或者是共同成员变量,然后通过继承的方式达到代码复用的目的。同时,抽象类中的抽象方法还可以 用于约束子类对于某种行为必须进行实现。例如:Java的核心类库中,很多子 类的通用部分就被抽取成为抽象类。比如集合框架中的 java.util.AbstractList、字符串的java.lang.AbstractStringBuilder等;

 Comparable接口

Comparable接口用于进行对象之间的比较,主要用于排序方法。

class Book implements Comparable<Book>{
	
	private String bookName;
	private int pageSize;
	private double price;
	
    // 构造方法
	public Book(String bookName, int pageSize, double price) {
		this.bookName = bookName;
		this.pageSize = pageSize;
		this.price = price;
	}
    
    // 实现Comparable接口的compareTo()方法
	// 比较当前对象this与另外一个Book对象的大小
	// this > another 返回 大于零的值
	// this == another 返回 零
	// this < another 返回小于零的值
	@Override
	public int compareTo(Book another) {
		// 按照页数比较(升序)
		// return this.getPageSize() - another.getPageSize();
		
		// 按照书名比较(升序)
		return this.getBookName().compareTo(another.getBookName());
	}
}

package test;

import java.util.Arrays;

public class Demo0222 {
    public static void main(String[] args) {
        Person[] persons = new Person[5];
        persons[0]  = new Person("高科",20,89);
        persons[1] = new Person("是懵然",21,9);
        persons[2] = new Person("软秦安",34,99);
        persons[3] = new Person("戴菲菲",44,99);
        persons[4] = new Person("肉肉肉肉肉",22,100);
        System.out.println("排序前:");
        System.out.println(Arrays.toString(persons));
        // 对persons数组使用工具类方法排序
        Arrays.sort(persons);
//         sortArr(persons);

        System.out.println("排序后:");
        System.out.println(Arrays.toString(persons));
        // 自定义排序规则 ,理解Arrays.sort
    }
    public static void sortArr(Comparable[] com){
        for (int i = 0; i<com.length-1;i++){
            for (int j = 0; j < com.length-1-i; j++) {
                if (com[j+1].compareTo(com[j])<0){
                    Comparable c = com[j];
                    com[j] = com[j+1];
                    com[j+1] = c;
                }

            }
        }
    }
}

总结

  • Java的接口(interface)用于定义纯粹的抽象行为(方法)规范。
  • 一个类只能extends继承自另一个类(单继承)。但是,一个类可以implements实现多个interface接口(多重实现)。
  • 一个interface可以继承自多个interface(接口之间允许多重继承)。
  • 一个类实现interface接口时,要求必须实现该接口中所有的抽象方法。
  • 接口也是一种“高层”的数据类型,可以用于定义“引用”的类型,也适用于"向上转型"和"向下转型"。
  • 接口不允许实例化。
  • 接口的所有方法都是抽象方法,接口不能定义实例字段(成员变量)。
  • 接口可以定义default方法(JDK版本 >= 1.8),default方法可以包含方法体。
  • 实现Comparable接口的类,该类的对象之间可以进行“比较”,用于“排序”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值