栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA 继承

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA 继承

概述

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。

其中,多个类可以称为子类,单独那一个类称为父类、超类(superclass)或者基类。

继承描述的是事物之间的所属关系,这种关系是: is -a 的关系。

定义

继承 :就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问从父类中继承过来的属性和行为。

好处
  1. 提高代码的复用性。
  2. 类与类之间产生了关系,是多态的前提。
格式

通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:

class 父类 {
...    
}
class 子类 extends 父类 {
...    
}
重点 多态与动态绑定的概念 多态

一个对象变量可以指示多种实际类型的现象称为多态。

动态绑定

在运行时能够自动地选择调用哪个方法的现象称为动态绑定。

继承中的访问权限修饰符

父类中的属性和方法能不能被子类继承,和访问权限修饰符有关
网上一搜索Java访问权限修饰符,很容易找到类似的一张图:

表格简单易懂,也容易记忆,大部分情况下也没什么问题,像我这种懒人以前也是靠着这张图来学习继承的。不过最近重新研究了下继承方面的知识,发现这张图还是有着部分缺漏。

  • private
    • 可见性:private修饰的方法和属性只能被本类访问,而不能被其他类访问
    • 继承性:private 修饰的属性和方法 子类不能继承
  • default
    • 可见性:默认的方法和属性可以在当前类中访问,也可以被与当前类在同一包下的所有其他类访问
    • 继承性:本包中的子类可以继承,其他包中的子类不能继承
  • protected
    • 可见性:protected修饰的方法和属性可以被当前类、与当前类在同一包下的其他类、所有子类访问(注意:protected修饰的属性和方法很特殊,在当前类与同一包中的类可以随意访问由protected修饰的方法和属性。但是,如果子类与父类不在同一个包下,子类仍然可以访问父类的protected修饰的方法和属性,却只能访问自己继承的属性和方法,而不能访问父类的其他子类继承的属性和方法)
    • 继承性:所有子类可以继承
  • public
    • 可见性:可以被所有类访问
    • 继承性:所有子类可以继承

理解protected:属性和方法的可见性应该结合继承性一起理解,这样更容易记住。看下面一段代码

package com.lmy.test.po;
public class Person {
    protected void say() {
        System.out.println("Person");
    }
}
=========================
package com.lmy.test.po;

public class Son1 extends Person {
    public static void main(String[] args) {
        new Son1().say();//ok
    }
}
=========================
package com.lmy.test.po;

public class Son2 extends Person {
    public static void main(String[] args) {
        new Son2().say();//ok
        new Son1().say();//ok
    }
}
=========================
package com.lmy.test.vo;

import com.lmy.test.po.Person;
import com.lmy.test.po.Son1;

public class Son3 extends Person {
    public static void main(String[] args) {
        new Son3().say();//ok
        new Son1().say();//error
        new Person.say();//error
    }
    public void test() {
        super.say();//ok
    }
}

可以看到,与Person类在同一个package下的子类Son2中,new Son1().say()是可以正确执行的,而与Person不在同一个包下的Son3中,new Son1().say()是错误的语法,甚至是new Person().say()也是被拒绝的。也许可以理解为protected修饰的属性和方法的可见性实际上是本类和同一个包下的其他类,同时子类可以继承protected属性,所以在于Person不同包下的子类只能访问自己继承的protected属性和方法,而不能访问其他子类继承的protected属性和方法,这一点在可以调用super.say()却不能调用new Person().say()体现得及其明显。(所以Object虽然定义了clone方法,但是由protected修饰,所有类只能调用自己的clone方法,无法调用其他类对象的clone方法)

注意:private修饰的方法不能被继承,构造函数不能被继承,初始化块也不能被继承,子类对象中是有父类private修饰的属性, 但是含有 不等于 继承(有但是不能访问)

ps:初始化块也没有继承的必要,在子类的构造函数中,第一行一定是调用父类的构造函数(隐式创建并初始化一个父类对象)。其实换句话说,子类能继承父类的一切,但是受到访问权限的影响,不是所有的都可以访问。

继承后的特点——成员方法 成员变量不重名

如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。

成员变量重名

如果子类父类中出现重名的成员变量,这时的访问是有影响的,子类的属性覆盖了父类的属性。在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字。

使用格式:

super.父类成员变量名
  • 小贴士:父类中的成员变量是非私有的,子类中可以直接访问。若父类中的成员变量私有了,子类是不能直接访问的。通常编码时,我们遵循封装的原则,使用private修饰成员变量,那么如何访问父类的私有成员变量呢?对!可以在父类中提供公共的getXxx方法和setXxx方法。
继承后的特点——成员方法 成员方法不重名

如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。

成员方法重名 ——重写(Override)

如果子类父类中出现重名的成员方法,这时的访问是一种特殊情况,叫做方法重写 (Override)。

  • 方法重写 :子类中出现与父类一模一样的方法时(方法签名相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。需要注意的是,重写的方法返回值必须与父类的兼容(相同或者是其子类),异常也必须兼容(相同或者是其子类),且方法的访问权限必须大于或者等于父类方法。

    例如:父类方法public double sayHell() throws IOException{}; 在子类中重写为:private String sayHello() throws Exception(); 是完全错误的,完全违反三个规定,无法通过编译。

重写详解

所谓方法重写,就是子类中定义与父类方法标签相同的方法(方法名、方法参数)。重写提现的是多态性,动态绑定。即将子类对象传递给父类变量,调用方法时将会调用子类重写的方法。

并不是所有方法都可以重写,只有动态绑定的方法能够重写。

动态绑定

支持运行时判断对象实际类型,从而调用对应的方法

public class Animal {
    public String like() throws IOException {
        System.out.println("animal");
        return "animal";
    }
    public final String look() throws IOException{
        System.out.println("animal");
        return "animal";
    }

    public static String speak() throws IOException {
        System.out.println("animal");
        return "animal";
    }
    private String say() throws IOException{
        System.out.println("animal");
        return "animal";
    }
}
public class Cat extends Animal {
    public String like() throws IOException {
        System.out.println("cat");
        return "cat";
    }
    public static String speak() throws IOException {
       System.out.println("cat");
       return "cat";
   }
    public void say() throws Exception{
       System.out.println("cat");
   }
}
public class Dog extends Animal {
    public String like() throws IOException {
        System.out.println("dog");
        return "dog";
    }
}
// 动态绑定	提现多态
Animal animal = new Cat();
animal.like();// cat
animal = new Dog();
animal.like();//dog
静态绑定

只会调用当前对象类型下的方法

Animal animal = new Cat();
animal.say();// animal
静态绑定的方法
  1. static 方法

    1. static方法允许子类存在标签相同的静态方法,子类不能定义标签相同的非静态方法。即子类不能存在标签与父类static方法相同的动态绑定的方法。

    2. 在调用static方法时,只会调用自身的static方法,即

      ...
      Animal animal = new Cat();
      animal.say(); // aniaml
      
  2. final 方法

    1. final方法不允许子类中存在标签相同的方法,任何方法都不行(final设计的初衷就是限制子类的行为,由父类规定行为方式)
  3. private 方法

    1. private方法允许子类中存在标签相同的方法,没有任何限制。但由于是静态绑定的方法,所以父类对象只会调用自身的该方法。

    2. 在Animal类中添加main方法:

      public static void main(String[] args) throws Exception{
              Animal animal = new Cat();
              animal.say();// animal
          }
      
方法重写

方法重写遵循一同两小一大原则:

  1. 一同:方法签名相同(方法名、参数列表相同)
  2. 两小:返回值类型、抛出异常类型 要与父类相同或者是其子类
  3. 一大:访问权限 要大于父类访问权限

将Animal类中的like()方法改为protected String like() throws IOException

Cat类中重写Animal中的like()方法

// 错误:访问权限变小
private String like() throws IOException{...}
// 错误:抛出异常与父类不兼容(非父类异常类或其子类)
protected  String like() throws Exception{...}
// 错误:返回值与父类不兼容(非父类返回值类型或其子类)
protected  Integer like() throws IOException{...}
// 正确写法之一:
public String like() throws IOException{...}

另一个角度理解一同两小一大原则:继承讲究一个置换法则,即任何父类出现的地方,都可以由其子类置换。例如

// 如果有 代码一:
try{
   String likeStr = new Animal().like();
}catch(IOException e){
    e.printStackTrace();
}
// 必须可以有 代码二:
try {
    String likeStr = new Cat().like();
}catch(IOException e) {
    e.printStackTrace();
}

假设上面的代码所在类所处的包与Animal、Cat所在包不同,那么在上面的代码二中,如果Cat类的like()方法返回值不是String或者String的子类,抛出的异常类型不是IOException类对象或其子类对象,访问权限如果比public低,就会出现异常,违反了置换法则。

继承后的特点——构造方法

子类是无法继承父类构造方法的。

  • 构造方法的作用是初始化成员变量的。**所以子类的初始化过程中,必须先执行父类的初始化动作(执行父类的构造方法)。**子类的构
    造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
public class Parent {
    private String name;
    private String sex;

    public Parent(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }
}
public class Son extends Parent {
    public Son(){
        //由于父类没有无参构造方法,所以子类必须显示声明给父类属性赋值
        super("zhangsan","男");
    }
}
继承中的对象初始化过程

创建子类对象的时候,会隐式的先创建一个父类对象

  • 父类对象的初始化

    • 给父类对象的实例变量分配空间、默认初始化
    • 声明时初始化、初始化块初始化
    • 调用父类的构造函数进行初始化
  • 子类对象的初始化

    • 给子类对象的实例变量分配空间、默认初始化
    • 声明时初始化、初始化块初始化
    • 调用子类的构造函数进行初始化


(图片来源于网络,来源太久远忘了,水印csdn自己加的,反正不是我自己画的,侵权请联系删除,十分抱歉)

继承的特点
  1. Java只支持单继承,不支持多继承。

  2. Java支持多层继承(继承体系)。

    class A{}
    class B extends A{}
    class C extends B{}
    
    • 顶层父类是Object类。所有的类默认继承Object,作为父类。
  3. 子类和父类是一种相对的概念。

编程入门菜鸟一位。以上说法如果有不对的地方,欢迎各路大佬留言指正。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/489084.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号