Java继承和多态


Java继承和多态

继承

定义

继承就是利用现有类创建新类的过程,现有的类称为 父类(基类),新类称为 子类(派生类)。比如现实中:儿子继承了父亲遗产一样。面向对象程序设计中的继承,就是代码重用。
Java中所有的类都继承自 Object 这个父类。

实现

在Java中实现继承需要使用到extends关键字;

[访问修饰符] class 派生类名 extends 基类名 {
        成员列表
    }

如:

class Student extends Person
{
    ……
}

访问修饰符 请参看之前章节的内容。

例子

例:

定义抽象类:Person。

public abstract class Person {  // 抽象类
    private String name;  // 私有变量
    
    public String getName() {  // Getter方法
        return name;
    }
    public void setName(String name) {  //Setter方法
        this.name = name;
    }
        
    public Person(String name) {  // 构造函数,用于初始化name
        super();
        this.name = name;
    }
    public abstract String getDesc();  // 抽象类中的抽象方法。  只有声明,没有具体实现。
  
      public String toString(){      // toString方法覆盖了Object类的toString方法
         return name + getDesc();
      }
}

public class Student extends Person {  // 继承类
    private String major; // 新增加的数据    
    
    public String getMajor() {
        return major;
    }
    
    public void setMajor(String major) {
        this.major = major;
    }    
    
    public Student(String name,String major) { // 构造函数用于初始化
        super(name);   // 调用超类构造函数
        this.major = major;
    }
    
    @Override
    public String getDesc() {  // 必须实现超类中的抽象方法
        // TODO Auto-generated method stub
        return " : a student, major is " + major;
    }

继承的作用

  • 当今软件设计的特征
    软件规模越来越大
    软件设计者越来越多
    软件设计分工越来越细
  • 引入继承,实现了代码重用;
  • 引入继承,实现了递增式的程序设计。
  • 继承是能自动传播代码和重用代码的有力工具;
  • 继承能够在某些比较一般的类的基础上建造、建立和扩充新类;
  • 能减少代码和数据的重复冗余度,并通过增强一致性来减少模块间的接口和界面,从而增强了程序的可维护性;
  • 能清晰地体现出类与类之间的层次结构关系。

注意事项

  • 继承是单方向的,即派生类可以继承和访问基类中的成员,但基类则无法访问派生类中的成员; 父类不能访问子类方法
  • 在Java中只允许 单一继承 方式,即一个派生类只能继承于一个基类,而不能象C++中派生类继承于多个基类的多重继承方式。

继承中的构造方法

  • 父类中的构造方法可以被重载,但不能被子类继承,即便它是public的;
  • 父类的构造方法负责初始化属于它的成员变量,而子类的构造方法则只需考虑属于自己的成员变量,不必去关注父类的情况。
  • 当实例化子类的对象时,必须先执行父类的构造方法,然后再执行子类的构造方法;
  • 如果父类还有更上级的父类,就会先调用最高父类的构造方法,再逐个依次地将所有继承关系的父类构造方法全部执行;
  • 如果父类的构造方法执行失败,那么子类的对象也将无法实例化。

class ParentClass {  //定义父类
  public ParentClass() {  //构造方法
    System.out.println("这是父类的构造方法。");
  }
}

class ChildClass extends ParentClass {  //子类继承于父类
  public ChildClass() {  //构造方法
    System.out.println("这是子类的构造方法。");
  }
}

public class ConstructorTest {  //该类用于容纳main方法
  public static void main(String[] args) {
    ChildClass cc = new ChildClass();  //实例化子类对象
  }
}

super

如果子类需要调用父类的方法或者属性怎么办?

  • 在子类的构造方法中,super关键字可以显式地调用父类的构造方法,用于将参数传递给它;需要注意的是: 该语句必须是子类构造方法的第一条语句

如:

class Point  //定义"点"类
{
  protected float mX, mY;  //x轴坐标和y轴坐标

  public Point(float x, float y)  //构造方法
  {
    mX = x;
    mY = y;
  }
  ……
}

class Circle extends Point  //定义"圆"类继承于"点"类
{
  protected float mRadius;    //半径

  public Circle(float x, float y, float r)  //构造方法
  {
    super(x, y);  //显式调用父类构造方法,必须是第一条语句
    mRadius = r;
  }
  ……
}
  • 如果父类和子类中有同名成员,在子类中默认访问是属于自己的那一个成员;super关键字可以明确地指定要访问父类中的成员;但前提条件是:父类中的该成员不是private的

多态

方法覆盖

定义
  • 在类的继承体系结构中,如果子类中出现了与父类中有同原型的方法,那么认为子类中的方法覆盖了父类中的方法(也称为方法重写);
  • 通过子类的实例调用被覆盖的方法时,将总是调用子类中的方法,而父类中的方法将被隐藏。

如:

/*如果不但名称相同,而且连方法原型也完全相同的话,则构成方法覆盖*/
class ParentClass {  //定义父类
  public void fun() {
    System.out.println("这是父类中的方法。");
  }
}

class ChildClass extends ParentClass {//子类继承于父类
  public void fun() {  //子类覆盖父类中的方法
    System.out.println("这是子类中的方法。");
  }
}

class OverriddenTest {  //用于容纳main方法
  public static void main(String[] args) {
    ParentClass parObj = new ParentClass();
    parObj.fun();  //父类的实例调用此方法

    ChildClass chiObj = new ChildClass();
    chiObj.fun();  //子类的实例调用此方法
  }
}
方法覆盖的注意事项
  • 子类中重写的方法,其访问权限不能比父类中被重写方法的访问权限更低
  • 在子类中重写方法时要保持方法的签名与父类中方法的签名一致

引用转型

  • 基类(父类)的引用可以指向派生类(子类)的对象。

如:

BaseClass obj = new DerivedClass();
  • 如果存在方法覆盖,那么将会调用其派生类中的方法

如:

class Shapes {  //基本形状类
  public void draw() {  //绘图的方法
    System.out.println("绘制了一个基本形状。");
  }
}
class Circle extends Shapes {  //圆形类继承于基本形状类
  public void draw() {  //覆盖父类的绘图方法
    System.out.println("绘制了一个圆形。");
  }
} 
class Square extends Shapes {  //正方形类继承与基本形状类
  public void draw() {  //覆盖父类的绘图方法
    System.out.println("绘制了一个正方形。");
  }
}
public class polymorphismDemo {
  public static void main(String[] args) {
    Shapes obj = new Shapes();  //父类的引用指向父类的实例
    obj.draw();                 //调用绘图方法
    obj = new Circle();         //父类的引用指向子类的实例
    obj.draw();                 //调用绘图方法
    obj = new Square();         //父类的引用指向子类的实例
    obj.draw();                 //调用绘图方法
  }
}

多态

在Java中,使用父类的引用,调用同一个方法,却可以得到不同的调用结果,这就是多态。即: 同一函数,多种形态。
实际上多态包括 动态多态静态多态

静态多态
  • 静态多态也称为编译时多态,即在编译时决定调用哪个方法;
  • 静态多态一般是指方法重载;
  • 只要构成了方法重载,就可以认为形成了静态多态的条件;
  • 静态多态与是否发生继承没有必然联系。
动态多态

动态多态也称为运行时多态,即在运行时才能确定调用哪个方法。
形成动态多态必须具体以下条件:

  • 必须要有继承的情况存在;
  • 在继承中必须要有方法覆盖;
  • 必须由基类的引用指向派生类的实例,并且通过基类的引用调用被覆盖的方法;

由上述条件可以看出,继承是实现动态多态的首要前提。

抽象

抽象方法

基类无法(或者没有必要)提供被覆盖方法的具体实现,那么就可以将这个方法定义为 抽象方法

[访问权限] abstract 返回值类型 方法名(参数列表){
}

如:

public abstract void draw(){
}
抽象类

如果某个类包含抽象方法,那么这个类就必须定义成 抽象类

[访问权限] abstract class 类名{
    成员列表
}

如:

public abstract class Shapes{
    public abstract void draw();
}
抽象类的注意事项
  • 抽象类不可以直接实例化,只可以用来继承;
  • 抽象类的派生子类应该提供对其所有抽象方法的具体实现;
  • 如果抽象类的派生子类没有实现其中的所有抽象方法,那么该派生子类仍然是抽象类,只能用于继承,而不能实例化;
  • 抽象类中也可以包含有非抽象的方法;
  • 构造方法和静态方法不可以修饰为abstract。

如:

abstract class Shapes {  //基本形状类,抽象类
    public abstract void draw();  //绘图方法,抽象方法
}
class Circle extends Shapes {  //圆形类继承于基本形状类
    public void draw() {  //实现抽象父类的抽象绘图方法
        System.out.println("绘制了一个圆形。");
    }
}
class Square extends Shapes {  //正方形类继承与基本形状类
    public void draw() {  //实现抽象父类的抽象绘图方法
        System.out.println("绘制了一个正方形。");
    }
}
public class abstractDemo {  //该类用于容纳main方法
    public static void main(String[] args) {
        Shapes obj;
        obj = new Circle();         //父类的引用指向子类的实例
        obj.draw();                 //调用绘图方法
        obj = new Square();         //父类的引用指向子类的实例
        obj.draw();                 //调用绘图方法
    }
}

接口

接口定义

如果某个类中所有的方法都是 抽象方法,那么可以考虑将该类定义为接口。

[访问权限] interface 接口名{
    成员列表
}

如:

public interface IMyInterface{
    public void doIt();
}
实现接口

接口只能用于实现,不能实例化(new)

[访问权限] class 类名 implements 接口名 {
    成员列表
}

如:

public class MyClass implements IMyInterface {
    public void doIt(){
    }
}
接口的注意事项
  • 接口中不能定义非抽象方法,也就是说接口中不能包含有函数实体。
    接口中的所有方法都默认为抽象方法,无需在每个方法前加abstract关键字。
  • 接口的实现类应该提供对接口中所有抽象方法的具体实现,否则将成为抽象类。
    与抽象类和它的继承类相似,也可以使用接口的引用指向其实现类的对象,从而达到动态多态的效果。
  • Java只支持单继承,而不能象C++那样可以多重继承,接口正是为了弥补这一点。
  • Java中还允许一个接口继承于另一个接口,即由父接口派生出子接口。

如:

public interface 子接口名 extends 父接口名 {
    成员列表
}

final关键字

final修饰变量

如果将某个变量修饰为final,那么该变量就成为 常量
如:

final double PI = 3.14159;

☆ 常量在声明时必须初始化。

final修饰方法

如果将某个成员方法修饰为final,则意味着该方法 不能被子类覆盖
如:

public final void fun() {
    ……
}
//如果在派生类中出现同原型的方法,将会报错。
final修饰类

如果将某个类修饰为final,则说明该类 无法被继承
如:

public final class MyClass {
    ……
}
//任何类想继承于MyClass类都将报错。

类和类之间的关系

  • 有——>has
    汽车有轮子。一般来说这种关系对应的是 属性

  • 是——>is
    圆形是个形状。一般来说这种关系对应的是 方法

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-java.h...
    eddy_wiki阅读 1,228评论 0 5
  • 继承和多态 1. 继承的优缺点 优点:(1)子类可以灵活地改变父类中的已有方法;(2)能够最大限度的实现代码重用。...
    MinoyJet阅读 666评论 0 0
  • (一)Java部分 1、列举出JAVA中6个比较常用的包【天威诚信面试题】 【参考答案】 java.lang;ja...
    独云阅读 7,146评论 0 62
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,803评论 18 399
  • 昨天,我的大学室友给我发来一屏幕的微信语音 大致的意思是,她有一个又丑又丑的同学,通过整容把自己变成白富美,还有一...
    呼啦来了阅读 506评论 0 0