首页 专题 文章 代码 归档
Java 面向对象-继承
2020.02.26 19:51 2020.02.26 19:51

1. 继承

在java中,支持的是单继承,格式:class 子类 extends 父类{}

1.1. 特点

  1. 在java中只支持单继承,也就是说,一个类不允许继承多个类,但是可以层级的去继承,比如B继承A,C继承B,这样A就相当于继承了B和C
  2. 多个类可以同时继承一个父类,A继承B , C继承B
  3. 在java中子父类是相对概念,在2中,B是A的父类,但是B也是C的父类

1.2. 继承变量

  1. 子类只能获得父类的非私有(即非private修饰)的成员变量。
  2. 子父类中成员变量名字不一样(没有和父类成员变量一样的名字的变量),直接获取父类的成员变量(也即上面1所说的继承)
  3. 子父类中成员变量名字一养,就近原则
  4. super 可以获得父类的成员变量和方法,用法和this一样

1.3. 继承方法

  • 子类中没有这个方法,就可以调用父类的这个方法
  • 重写:方法在子类和父类中完全一样,包括返回值类型和参数列表,当子类重写了父类的方法之后,调用的就是子类的方法,父类的方法失效,起到了覆盖的作用。

注意:

1、不可以重写父类的私有方法,因为子类压根就看不到父类的私有成员

2、子类重写父类的方法,方法的修饰权限必须大于等于父类的方法

3、注解@override 方法重写,说明下面的方法是重写的父类的方法

重写与重载:

重写:必须父类中存在,且和父类方法完全一样,包括返回值类型和参数列表,在方法重写之后,父类方法失效。

重载:方法名一样,参数列表不一样。和返回值类型无关,且每个重载的方法有自己独立的功能。

1.4. this和super

  • super:父类对象的引用,调用分类的成员变量,成员方法,以及构造方法
  • this:子类对象的引用,调用子类的成员变量,成员方法,以及构造方法

2. 实例

这里定义了一个动物类

public class Animal {

    public String name = "动物";
    private int age = 10;

    // 打印名字
    public void printName() {
        System.out.println("父类中的打印名字:" + this.name);
    }

    // 打印年龄
    public void printAge() {
        System.out.println("父类中的打印年龄:" + this.age);
    }
}

再定义一个狗类,继承动物类

public class Dog extends Animal {


    @Override
    // @Override是注解,这个注解代表下面这个方式是重写父类方法了
    public void printName() {
        super.printName();// super代表父类,这里即调用父类的printName方法
        System.out.println("子类Dog中的打印名字(super调用):" + super.name);
        System.out.println("子类Dog中的打印名字(this调用):" + this.name);

    }

    @Override
    public void printAge() {
        super.printAge();

        // System.out.println("子类Dog中的打印名字:" + super.age);
        // 上面那行代码报错,因为父类中的age使用private修饰,子类不能继承
    }
}

结果:

截图-1582714517

2.1. 继承树

在Java中,所有类都默认继承自Object类,这是所有类的父类。

那Dog继承了Animal,并没有继承Object呀?不然违背了单一继承原则?

是的,Dog仅继承了Animal,但是Animal继承了Object呀!

所以,该继承树是:Object-->>Animal-->>Dog,也即代表Dog也继承了Object类。

2.2. 保护成员变量

前面说了,如果父类的成员变量和成员方法用private修饰,那子类是不会继承了。

但是又想子类可以继承,但又不使其他类可以修改/使用变量(当修饰符是public时,外部类可以通过"对象.属性"来修改/设置该属性),该如何做?

使用protected修饰符!

验证

首先,在Animal中age用Private修饰时,我们尝试用super.age调用看看:

@Override
public void printAge() {
    super.printAge();

    System.out.println("子类Dog中的打印名字:" + super.age);
}

毫无疑问,报错:

截图-1582715115

那么当使用protected呢?

public String name = "动物";
protected int age = 10;

截图-1582715155

正常运行!!

2.3. 向上转型

在上面的写法中,我们都是

Dog dog = new Dog();

new的什么类型,我们返回的就是什么类型(如上Dog

这是正常的,并无任何问题。


但是我们Dog是继承自Animal的,那么如果使用返回值类型是父类,也即如下写法:

Animal dog = new Dog();

有无问题呢?

并无问题:

截图-1582715602

这种把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)。

向上转型实际上是把一个子类型安全地变为更加抽象的父类型:

高到可直接弄成顶级父类:

Object d = dog;

但是,通过IDE的提示,我们可以看出并无我们定义在Dog的方法了:

截图-1582715708

2.4. 向下转型

和向上转型相反,如果把一个父类类型强制转型为子类类型,就是向下转型(downcasting)。例如:

Animal a1 = new Dog();//向上转型
Animal a2 = new Animal();

Dog d1 = (Dog) a1;//yes
Dog d2 = (Dog) a2;// 报错:java.lang.ClassCastException

为什么Dog d2 = (Dog) a2;报错呢?

因为子类肯定是会比父类拥有更多的功能的(方法,属性等等),因此,向下转型很可能会失败。失败的时候,Java虚拟机会报ClassCastException。


instanceof

为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型:

Dog d1 = new Dog();
System.out.println(d1 instanceof Dog);//true
System.out.println(d1 instanceof Animal);//true

Animal a1 = new Animal();


System.out.println(a1 instanceof Dog);//false
System.out.println(a1 instanceof Animal);//true

Animal a2 = null;

System.out.println(a2 instanceof Animal);// false
System.out.println(a2 instanceof Dog);//false

于是,我们就有如下代码:

Animal a1 = new Dog();
if (a1 instanceof Dog) {
    Dog a2 = (Dog) a1;
    a2.printName();
    a2.printAge();

}
本节阅读完毕! (分享
二维码图片 扫描关注我们哟