抽象类 接口 多态

一 抽象类(abstract)

抽象类的概念
只抽取了很多类的方法的声明,方法声明用abstract修饰。
一个类如果有抽象方法,那么这个类必须是抽象类。
抽象类里边可以没有抽象方法,如果这么做只有一个目的:不让你创建这个类的对象。
抽象类不能被实例化,不能创建对象。
如果一个类继承抽象类,那么,它要么重写抽象类中的所有抽象方法,要么本身也是抽象类。

抽象类的成员
成员变量:可以是常量,也可以是变量。子类可以直接继承抽象类中的成员变量。
成员方法:可以是抽象的,也可以是非抽象的。抽象方法在子类中必须要被实现。普通方法可以被子类直接继承使用。
构造方法:抽象类不能被实例化创建对象,但抽象类是class,那么它就有构造方法,可以给子类实例化使用。

抽象关键字abstract不可以和哪些关键字共存?
private:私有的,外部直接无法访问。
static:static修饰后抽象方法就可以通过类名调用,但是这样是没有意义的。
final:final修饰的方法不能被重写,所以它和abstract冲突。

二 接口(interface)

接口的概念
接口是功能的集合,可看做是一种数据类型,一种只包含了功能声明的特殊类,是比抽象类更为抽象的”类”。
接口只描述所应该具备的方法,没有具体实现,具体实现由接口的实现类(相当于接口的子类)来完成。
当一个类中所有的方法都是抽象的时候,没必要定义为抽象类,定义为接口即可。

接口的作用
1 扩展了功能,将功能的定义与实现分离,优化程序设计,降低了耦合性,即设备与设备之间实现了解耦。
2 解决了java中只能单继承的问题。接口与接口可以是单继承,也可以是多继承。 extends

Java多继承会出现的问题
多继承时,当多个父类中有相同功能时,子类调用会产生不确定性,不确定运行父类中哪个功能主体。
接口多实现时,接口中的功能都没有方法体,由子类来明确,避免了多继承的问题。

接口的成员
接口只有成员变量和成员方法。
成员变量:必须是常量, 默认修饰符 public static final
成员方法:必须是公共访问的抽象方法,默认修饰符 public abstract
为便于阅读,建议手动加上修饰符。

定义格式
public interface 接口名 {
  抽象方法1;
  抽象方法2;
  抽象方法3;
}

接口的实现类
在类实现接口后,该类就会将接口中的抽象方法继承过来,此时该类需要重写该抽象方法,完成具体的逻辑。
子类必须覆盖掉接口中所有的抽象方法后,子类才可以实例化。否则子类是一个抽象类。

类实现接口的格式
class 类 implements 接口 {
  重写接口中方法
}

继承+实现接口
父类中定义的事物的基本功能,接口中定义的事物的扩展功能。子类通过继承父类来扩展功能,如果想继续扩展其他类中的功能,需要通过实现接口来完成。

接口和抽象类异同
相同点:
都位于继承的顶端,用于被其他类实现或继承;
都不能直接实例化对象;
都包含抽象方法,其子类都必须覆写这些抽象方法;
区别:
抽象类只能单继承;接口可以多实现。
抽象类为部分方法提供实现,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法;
抽象类是这个事物体系结构中的共性内容, 继承体系是is..a关系
接口是这个事物中的扩展功能,继承体系是like..a关系
包含成员区别
二者的选用:
优先选用接口,尽量少用抽象类;
需要定义子类的行为,又要为子类提供共性功能时才选用抽象类;

三 多态

多态的概念
Java作为面向对象的语言,可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类引用变量赋值,又可以给这个子类的父类(接口)变量赋值。如一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。
最终多态体现为父类引用变量可以指向子类对象。

使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。

方法重载(静态多态)
方法重写(动态多态,对象多态)

多态的前提
A:类与类(或接口)要有继承(或实现)关系。
B:一定要有方法的重写。
C:一定要有父类或者接口的引用指向子类的对象。
多态思想:可以指挥同一类型的一批对象做事情,多态的出现让我们复杂的问题简单化了。

类、抽象类、接口的多态调用
类的多态定义格式:
父类的引用变量指向子类对象
父类类型 变量名 = new 子类类型();
变量名.方法名();

class Fu {}
class Zi extends Fu {}
//类的多态使用
Fu f = new Zi();

抽象类多态定义格式
抽象类 变量名 = new 抽象类子类();

abstract class Fu {
  public abstract void method();
}
class Zi extends Fu {
  public void method(){
    System.out.println(“重写父类抽象方法”);
  }
}
//类的多态使用
Fu fu= new Zi();

接口多态定义的格式
接口 变量名 = new 接口实现类();

interface Fu {
  public abstract void method();
}
class Zi implements Fu {
  public void method(){
    System.out.println(“重写接口抽象方法”);
  }
}
//接口的多态使用
Fu fu = new Zi();

多态成员方法的变化
多态出现后会导致子父类中的成员变量有微弱的变化

class Fu {
    int num = 4;
}
class Zi extends Fu {
    int num = 5;
}
class Demo {
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);
        Zi z = new Zi();
        System.out.println(z.num);
    }
}         

>>>
4
5

多态成员变量
如果子父类中出现同名成员变量,多态调用该变量时:
编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。
运行时期:也是调用引用型变量所属的类中的成员变量。
简单记: 编译运行看左边。

class Fu {
    int num = 4;
    void show()    {
        System.out.println("Fu show num...");
    }
}
class Zi extends Fu {
    int num = 5;
    void show()    {
        System.out.println("Zi show num...");
    }
}
class Demo {
    public static void main(String[] args)     {
        Fu f = new Zi();
        f.show();
        System.out.println(f.num);

        Zi z = new Zi();
        z.show();
        System.out.println(z.num);
    }
}

>>>
Zi show num...
4
Zi show num...
5

多态成员方法
编译时期:参考引用变量所属的类,如果没有类中调用的方法,编译失败。
运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员方法。
简而言之:编译看左边,运行看右边。

Fu f = new Zi();
成员变量: 编译和运行都看Fu。
非静态方法:编译看Fu,运行看Zi。
静态方法: 编译和运行都看Fu。

举例:超人的例子:
person :走路();
SuperMan:走路();fly();

Person p = new SuperMan();//超人没变身之前就是普通人一个,只能调用Person里的方法
//在运行的时候发现有SuperMan这个子类继承了他,会去看里面是否有和你调用Person里相同的方法
//如果有运行就执行子类重写的方法(成员函数的特性,覆盖)
p.走路();

SuperMan sm= (SuperMan)p;//变身超人
sm.走路();
sm.fly();

总结:无论是向上转型还是向下转型,变化的都是子类对象,绝对不能把父类对象强转为子类类型

instanceof关键字
instanceof关键字用来判断对象是否属于某种数据类型。如学生的对象属于学生类,学生的对象也属于人类
格式:boolean b = 对象 instanceof 数据类型;

Person p1 = new Student();
boolean flag = p1 instanceof Student; //flag结果为true
boolean flag2 = p2 instanceof Teacher; //flag结果为false

多态转型
向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
使用格式:
父类类型 变量名 = new 子类类型();
Person p = new Student();

向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用转为子类引用,这个过程是向下转型。如果是直接创建父类对象,是无法向下转型的!
使用格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
Student stu = (Student) p; //变量p实际上指向Student对象

多态的好处和弊端

当父类的引用指向子类对象时,就发生了向上转型,即把子类类型对象转成了父类类型。
向上转型的好处是隐藏了子类类型,提高了代码的扩展性。
弊端是只能使用父类共性的内容,而无法使用子类特有功能,功能有限制。

看如下代码

//描述动物类,并抽取共性eat方法
abstract class Animal {
    abstract void eat();
}

// 描述狗类,继承动物类,重写eat方法,增加lookHome方法
class Dog extends Animal {
    void eat() {
        System.out.println("啃骨头");
    }

    void lookHome() {
        System.out.println("看家");
    }
}

// 描述猫类,继承动物类,重写eat方法,增加catchMouse方法
class Cat extends Animal {
    void eat() {
        System.out.println("吃鱼");
    }

    void catchMouse() {
        System.out.println("抓老鼠");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal a = new Dog(); //多态形式,创建一个狗对象
        a.eat(); // 调用对象中的方法,会执行狗类中的eat方法
        // a.lookHome();//使用Dog类特有的方法,需要向下转型,不能直接使用

        // 为了使用狗类的lookHome方法,需要向下转型
        // 向下转型过程中,可能会发生类型转换的错误,即ClassCastException异常
        // 那么,在转之前需要做健壮性判断
        if( !a instanceof Dog){ // 判断当前对象是否是Dog类型
                System.out.println("类型不匹配,不能转换");
                return;
        }
        Dog d = (Dog) a; //向下转型
        d.lookHome();//调用狗类的lookHome方法
    }
}

什么时候使用向上转型:
当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
Animal a = new Dog();
a.eat();
什么时候使用向下转型:
当要使用子类特有功能时,就需要使用向下转型。
Dog d = (Dog) a; //向下转型
d.lookHome();//调用狗类的lookHome方法
弊端:需要面对具体的子类对象;向下转型时容易发生ClassCastException类型转换异常,转换前必须用instanceof做类型判断。

多态举例:毕老师和毕姥爷的故事

class 毕姥爷 {
    void 讲课() {
        System.out.println("政治");
    }

    void 钓鱼() {
        System.out.println("钓鱼");
    }
}

// 毕老师继承了毕姥爷,就有拥有了毕姥爷的讲课和钓鱼的功能,
// 但毕老师和毕姥爷的讲课内容不一样,因此毕老师要覆盖毕姥爷的讲课功能
class 毕老师 extends 毕姥爷 {
    void 讲课() {
        System.out.println("Java");
    }

    void 看电影() {
        System.out.println("看电影");
    }
}

public class Test {
    public static void main(String[] args) {
        // 多态形式
        毕姥爷 a = new 毕老师(); // 向上转型
        a.讲课(); //这里表象是毕姥爷,其实真正讲课的仍然是毕老师,因此调用的也是毕老师的讲课功能
        a.钓鱼();//这里表象是毕姥爷,但对象其实是毕老师,而毕老师继承了毕姥爷,即毕老师也具有钓鱼功能

        // 当要调用毕老师特有的看电影功能时,就必须进行类型转换
        毕老师 b = (毕老师) a; // 向下转型
        b.看电影();
    }
}

原文地址:https://www.cnblogs.com/createtable/p/10589454.html

时间: 2024-10-31 01:59:07

抽象类 接口 多态的相关文章

JAVA笔记6__抽象类/接口/多态/instanceof关键字、父类设计法则

/** * 抽象类:很多具有相同特征和行为的类可以抽象为一个抽象类 * 1.抽象类可以没有抽象方法,有抽象方法的类必须是抽象类 * 2.非抽象类继承抽象类必须实现抽象方法[可以是空实现] * 3.抽象类可以有方法和属性 * 4.抽象类不能被实例化 * 5.抽象类不能声明为final * 6.抽象类可以有构造方法[不代表实例化对象] */ public class Main { public static void main(String[] args) { Goddess m1 = new Go

19 抽象类 接口类 封装. 多态

主要内容: 1.  抽象类(接口类): 制定一个规范  : from abc import ABCMeta,abstractmethod class Payment(metaclass=ABCMeta): @abstractmethod def pay(self):pass class Alipay(Payment): def __init__(self,money): self.money = money def pay(self): print('使用支付宝支付了%s' % self.mon

面向对象(接口 ,多态)

接口 (1)当抽象类中的方法都是抽象的时候,java就提供了一种新的表现形式:接口 ,接口是功能的集合 接口不能创建对象 (2)格式 父接口:public interface Tnter{ } 子类:public class interImp implements Itner{ } 接口的使用 1.接口不能创建对象 2.定义实现类来实现接口 实现的关键字 implements 3.重写抽象方法 4.创建实现类对象 调用方法 接口的特点 1.不需要被abstract 修饰 2.类实现接口,可以单实

韩顺平循序渐进学java 第13讲 抽象类.接口

13.1抽象类 13.1.1 概念 当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,称为抽象方法,用abstract来修饰该类,称为抽象类. 13.1.2 抽象类-深入讨论 抽象类是java中一个比较重要的类: 1.用abstract关键字来修饰一个类时,这个类就叫抽象类: 2.用abstract关键字来修饰一个方法时,这个方法就叫抽象方法: 3.抽象方法在编程中用的不是很多,但是在公司笔试时考的较多. 13.1.3 抽象类-注意事项 1.抽象类不能实例化: 2.抽象类不一

我的学习之路_第二章_接口/多态

接口 (1)当抽象类中的方法都是抽象的时候,java就提供了一种新的表现形式:接口.接口是功能的集合 接口不能创建对象 (2)格式: 父接口: public interface Inter { } 子类: public class InterImpl implements Inter { } (3)接口的特点: A:不需要被abstract修饰. B:类实现接口,可以单实现,还可以多实现. C:接口可以继承接口,可以单继承,也可以多继承. D:接口与父类的功能可以重复,均代表要具备某种功能. (

虚函数/纯虚函数/抽象类/接口/虚基类

1.多态 在面向对象语言中,接口的多种不同实现方式即为多态.多态是指,用父类的指针指向子类的实例(对象),然后通过父类的指针调用实际子类的成员函数. 在Java中,没有指针,就直接用父类实例化子类对象 多态性就是允许将子类类型的指针赋值给父类类型的指针,多态是通过虚函数实现的,多态可以让父类的指针有“多种形态”,这是一种泛型技术. 所谓泛型技术,就是试图使用不变的代码来实现可变的算法 2.虚函数 在基类的类定义中,定义虚函数的一般形式: Virtual 函数返回值类型 虚函数名(形参表){ 函数

面向对象的理解 抽象类&接口

一.关于面向对象 1.什么是面向对象 在解释面向对象之前,先说说面向过程.学过C的同学都知道,C就是面向过程的一种语言.那什么是面向过程呢?比方说组装主机,对于面向过程,需要从0开始.买cpu,显卡,主板,电源,风扇,把这些都通过主板链接到一起.我需要清楚的知道主机组装的每一个步骤. 介绍了面向过程,再说会正题,什么是面向对象?对于上面的装主机过程面向对象会把主机先抽象成一个机箱,机箱里有cpu,显卡,主板,电源.用主机的人,不关心里面是怎么工作的,也不需要知道内部逻辑,只知道插上电源就能用.面

Dart抽象类和多态

/* Dart中抽象类: Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口. 1.抽象类通过abstract 关键字来定义 2.Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法. 3.如果子类继承抽象类必须得实现里面的抽象方法 4.如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法. 5.抽象类不能被实例化,只有继承它的子类可以 extends抽象类 和 implements的区别: 1.如果要复用抽象类里面的方

Java抽象类/接口

一.抽象类: 1.关键字:abstract ;修饰抽象类,抽象方法: 2.注释: 2.1.抽象类不可以创建对象,但是可以被声明引用(强制被使用多态): 2.2.抽象类不一定包含抽象方法,包含抽象方法一定是抽象类: 抽象方法: 1.格式:abstract修饰,且没有{}方法体:因为必须被子类复写,则又方法体也没有意义: 2.注释:必须被子类复写: 3.注释:抽象方法不能用private修饰,因为抽象方法必须被实现: //父类 public abstract class father{ public