广告
返回顶部
首页 > 资讯 > 后端开发 > JAVA >Java中继承图文详解
  • 938
分享到

Java中继承图文详解

Java 2019-09-16 13:09:57 938人浏览 猪猪侠
摘要

java继承与合成基本概念继承:可以基于已经存在的类构造一个新类。继承已经存在的类就可以复用这些类的方法和域。在此基础上,可以添加新的方法和域,从而扩充了类的功能。合成:在新类里创建原有的对象称为合成。这种方式可以重复利用现有的代码而不更改

java继承与合成基本概念

继承:可以基于已经存在的类构造一个新类。继承已经存在的类就可以复用这些类的方法和域。在此基础上,可以添加新的方法和域,从而扩充了类的功能。

合成:在新类里创建原有的对象称为合成。这种方式可以重复利用现有的代码而不更改它的形式。

相关视频教程推荐:java视频教程

1.继承的语法

关键字extends表明新类派生于一个已经存在的类。已存在的类称为父类或基类,新类称为子类或派生类。例如:

class Student extends Person {

}

类Student继承了Person,Person类称为父类或基类,Student类称为子类或派生类。

2.合成的语法

合成比较简单,就是在一个类中创建一个已经存在的类。

class Student {
    Dog dog;
}

上溯造型

1.基本概念

继承的作用在于代码的复用。由于继承意味着父类的所有方法亦可在子类中使用,所以发给父类的消息亦可发给衍生类。如果Person类中有一个eat方法,那么Student类中也会有这个方法,这意味着Student对象也是Person的一种类型。

class Person {
    public void eat() {
        System.out.println("eat");
    }

    static void show(Person p) {
        p.eat();
    }
}
public class Student extends Person{
    public static void main(String[] args) {
        Student s = new Student();
        Person.show(s);     // ①
    }
}

【运行结果】:
eat

在Person中定义的show方法是用来接收Person句柄的,但是在①处接收的却是Student对象的引用。这是因为Student对象也是Person对象。在show方法中,传入的句柄(对象的引用)可以是Person对象以及Person的衍生类对象。这种将Student句柄转换成Person句柄的行为成为上溯造型。

2.为什么要上溯造型

为什么在调用eat是要有意忽略调用它的对象类型呢?如果让show方法简单地获取Student句柄似乎更加直观易懂,但是那样会使衍生自Person类的每一个新类都要实现专属自己的show方法:

class Value {
    private int count = 1;

    private Value(int count) {
        this.count = count;
    }

    public static final Value
            v1 = new Value(1),
            v2 = new Value(2),
            v3 = new Value(3);
}

class Person {

    public void eat(Value v) {
        System.out.println("Person.eat()");
    }
}

class Teacher extends Person {
    public void eat(Value v) {
        System.out.println("Teacher.eat()");
    }
}

class Student extends Person {
    public void eat(Value v) {
        System.out.println("Student.eat()");
    }
}

public class UpcastingDemo {
    public static void show(Student s) {
        s.eat(Value.v1);
    }

    public static void show(Teacher t) {
        t.eat(Value.v1);
    }

    public static void show(Person p) {
        p.eat(Value.v1);
    }

    public static void main(String[] args) {
        Student s = new Student();
        Teacher t = new Teacher();
        Person p = new Person();
        show(s);
        show(t);
        show(p);
    }
}

这种做法一个很明显的缺陷就是必须为每一个Person类的衍生类定义与之紧密相关的方法,产生了很多重复的代码。另一方面,对于如果忘记了方法的重载也不会报错。上例中的三个show方法完全可以合并为一个:

public static void show(Person p) {
     p.eat(Value.v1);
}

动态绑定

当执行show(s)时,输出结果是Student.eat(),这确实是希望得到的结果,但是似乎没有按照我们希望的形式来执行,再来看一下show方法:

public static void show(Person p) {
     p.eat(Value.v1);
}

它接收的是Person句柄,当执行show(s)时,它是如何知道Person句柄指向的是一个Student对象而不是Teacher对象呢?编译器是无从得知的,这涉及到接下来要说明的绑定问题。

1.方法调用的绑定

将一个方法同一个方法主体连接在一起就称为绑定(Binding)。若在运行运行前执行绑定,就称为“早期绑定”。上面的例子中,在只有一个Person句柄的情况下,编译器不知道具体调用哪个方法。Java实现了一种方法调用机制,可在运行期间判断对象的类型,然后调用相应的方法,这种在运行期间进行,以对象的类型为基础的绑定称为动态绑定。除非一个方法被声明为final,Java中的所有方法都是动态绑定的。

用一张图表示上溯造型的继承关系:

1.jpg

用代码概括为:

Shape s = new Shape();

按照继承关系,将创建的Circle对象句柄赋给一个Shape是合法的,因为Circle属于Shape的一种。

当调用其中一个基础类方法时:

Shape s = new Shape();

此时,调用的是Circle.draw(),这是由于动态绑定的原因。

class Person {
    void eat() {}
    void speak() {}
}
class Boy extends Person {
    void eat() {
        System.out.println("Boy.eat()");
    }
    void speak() {
        System.out.println("Boy.speak()");
    }
}
class Girl extends Person {
    void eat() {
        System.out.println("Girl.eat()");
    }
    void speak() {
        System.out.println("Girl.speak()");
    }
}
public class Persons {
    public static Person randPerson() {
        switch ((int)(Math.random() * 2)) {
        default:
        case 0:
            return new Boy();
        case 1:
            return new Girl();
        }
    }
    public static void main(String[] args) {
        Person[] p = new Person[4];
        for (int i = 0; i < p.length; i++) {
            p[i] = randPerson();    // 随机生成Boy或Girl
        }
        for (int i = 0; i < p.length; i++) {
            p[i].eat();
        }
    }
}

对所有从Person衍生出来的类,Person建立了一个通用接口,所有衍生的类都有eat和speak两种行为。衍生类覆盖了这些定义,重新定义了这两种行为。

在主类中,randPerson随机选择Person对象的句柄。**上诉造型是在return语句里发生的。**return语句取得一个Boy或Girl的句柄并将其作为Person类型返回,此时并不知道具体是什么类型,只知道是Person对象句柄。

在main方法中调用randPerson方法为数组填入Person对象,但不知具体情况。当调用数组每个元素的eat方法时,动态绑定的作用就是执行对象的重新定义了的方法。

然而,动态绑定是有前提的,绑定的方法必须存在于基类中,否则无法编译通过。

class Person {
    void eat() {
        System.out.println("Person.eat()");
    }
}
class Boy extends Person {
    void eat() {
        System.out.println("Boy.eat()");
    }
    void speak() {
        System.out.println("Boy.speak()");
    }
}
public class Persons {
    public static void main(String[] args) {
        Person p = new Boy();
        p.eat();
        p.speak();  // The method speak() is undefined for the type Person
    }
}

如果子类中没有定义覆盖方法,则会调用父类中的方法:

class Person {
    void eat() {
        System.out.println("Person.eat()");
    }
}
class Boy extends Person {
}
public class Persons {
    public static void main(String[] args) {
        Person p = new Boy();
        p.eat();
    }
}

【运行结果】:
Person.eat()

2.静态方法的绑定

将上面的方法都加上static关键字,变成静态方法:

class Person {
    static void eat() {
        System.out.println("Person.eat()");
    }
    static void speak() {
        System.out.println("Person.speak()");
    }
}
class Boy extends Person {
    static void eat() {
        System.out.println("Boy.eat()");
    }
    static void speak() {
        System.out.println("Boy.speak()");
    }
}
class Girl extends Person {
    static void eat() {
        System.out.println("Girl.eat()");
    }
    static void speak() {
        System.out.println("Girl.speak()");
    }
}
public class Persons {
    public static Person randPerson() {
        switch ((int)(Math.random() * 2)) {
        default:
        case 0:
            return new Boy();
        case 1:
            return new Girl();
        }
    }
    public static void main(String[] args) {
        Person[] p = new Person[4];
        for (int i = 0; i < p.length; i++) {
            p[i] = randPerson();    // 随机生成Boy或Girl
        }
        for (int i = 0; i < p.length; i++) {
            p[i].eat();
        }
    }
}

【运行结果】:

Person.eat() 
Person.eat() 
Person.eat() 
Person.eat()

观察结果,对于静态方法而言,不管父类引用指向的什么子类对象,调用的都是父类的方法。

更多java相关文章请关注Java基础教程栏目。

--结束END--

本文标题: Java中继承图文详解

本文链接: https://www.lsjlt.com/news/1703.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java中继承图文详解
    java继承与合成基本概念继承:可以基于已经存在的类构造一个新类。继承已经存在的类就可以复用这些类的方法和域。在此基础上,可以添加新的方法和域,从而扩充了类的功能。合成:在新类里创建原有的对象称为合成。这种方式可以重复利用现有的代码而不更改...
    99+
    2019-09-16
    Java
  • 详解Java的继承
    目录继承:继承的好处:继承的坏处:继承的特点:继承和成员变量之间的关系:this关键字和super关键字的区别方法的重写:总结继承: 把多个类相同的内容提取到另外一个类中,然后使用关...
    99+
    2022-11-12
  • Java中的继承详情
    目录一. 继承1.1 继承的实现1.2 继承的好处和弊端二. 继承中的成员访问特点2.1 继承中变量的访问特点2.2 super2.3 继承中构造方法的访问特点2.4 继承中成员方法...
    99+
    2022-11-13
  • 【Java】还不理解继承?一篇文章看懂继承|继承入门
    作者:努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享:算法、数据结构、Java等相关知识。 博主主页: @是瑶瑶子啦 所属专栏: Java岛冒险记【从小白到大佬之路】...
    99+
    2023-09-03
    java 开发语言 jvm
  • C++图文并茂讲解继承
    目录一、生活中的例子二、惊艳的继承三、继承的意义四、小结一、生活中的例子 组合关系∶整体与部分的关系 下面看一个组合关系的描述代码: #include <iostream&g...
    99+
    2022-11-13
  • 详解如何继承Mybatis中Mapper.xml文件
    目录引言修改扩展Ext文件修改命名空间this.currentNamespace 设置修改applicationContext.xml,让Mapper.java不被扫描引言 最近在写...
    99+
    2022-11-13
  • Java集合继承体系详解
    Java的集合类是一种特别有用的工具,它可以用于存储数量不等的多个对象,并可以实现常用的数据结构,如栈、队列等。Java集合还可以用于板寸具有映射关系的关联数组。java集合就像是一个容器,我们可以把多个对象(实际上是对象的引用,习惯上叫对...
    99+
    2023-05-30
    java 集合继承 ava
  • 详解Java面向对象中的继承
    继承的概念 继承是面向对象编程中的一个概念,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法。子类在继承父类的同时也可以添加自己的属性和方法,从而实现代码的...
    99+
    2023-05-19
    Java 面向对象 Java 继承
  • 详解C++中单继承与多继承的使用
    目录前言1.继承的概念和定义(1)继承的概念(2)继承的定义方法(2)继承后子类的成员类型2.基类与派生类的赋值转换(1)派生类赋值给基类(2)基类给派生类3.继承中的作用域(1)隐...
    99+
    2022-11-13
  • Java超详细讲解类的继承
    目录写在前面1.子类的创建1.1子类的创建方法1.2调用父类中特定的构造方法2.在子类中访问父类成员3.覆盖3.1覆盖父类中的方法3.2用父类的对象访问子类的成员4.不可被继承的成员...
    99+
    2022-11-13
  • Java三大特性之继承详解
    目录概述由来定义好处继承的格式继承后的特点—成员变量成员变量不重名成员变量重名继承后的特点—成员方法成员方法不重名成员方法重名—重写(Overri...
    99+
    2022-11-13
    Java 特性 继承 Java 继承
  • Java语言之包和继承详解
    目录一、包包名类的导入与静态导入在包中添加类包访问权限二、继承类、超类与子类重写方法(override)this与super的区别:子类构造器protected关键字阻止继承:fin...
    99+
    2022-11-12
  • C++ 继承,虚继承(内存结构)详解
    目录普通的公有继承多重继承虚继承虚继承(菱形继承)总结普通的公有继承 class test1 { public: test1(int i) :num1(i) {} pri...
    99+
    2022-11-12
  • C#中的类继承详解
    目录前言类的继承注意事项寄语总结前言 在日常的程序编码工作中,我们的最终目标是完成公司交给自己的开发任务,核心目标是写出好代码。 那么什么是好代码? 高内聚,低耦合 想必从事编码工作...
    99+
    2022-11-13
  • python的继承详解
    目录1、单继承:子类只继承一个父类2、多继承:子类继承多个父类3、子类重写父类的同名属性和方法4、子类调用父类同名属性和方法5、 6、调用父类方法super()总结1、单继...
    99+
    2022-11-12
  • Java图文分析之继承内存布局
    目录一、初步了解继承的内存布局(1) 继承内存布局初探(2) Object 类(3) 同名的成员变量二、更复杂的继承的内存布局一、初步了解继承的内存布局 (1) 继承内存布局初探 看...
    99+
    2022-11-13
  • C++详细讲解继承与虚继承实现
    目录继承的概念及定义概念:定义:继承关系和访问限定符总结基类和派生类对象赋值转换继承中的作用域派生类的默认成员函数继承与友元继承与静态成员复杂的菱形继承及菱形虚拟继承虚继承原理继承的...
    99+
    2022-11-13
  • 详解Java语言中的抽象类与继承
    目录一、实验目的二、实验要求三、实验报告要求四、实验小结一、实验目的 1、掌握抽象类的设计; 2、掌握抽象方法方法的实现; 3、熟悉类的向下向上转型,以及子类实例化父类对象的基本要求...
    99+
    2022-11-13
    Java抽象类 继承 Java抽象类 Java 继承
  • JavaScript组合继承详解
    目录1、前言2、原型链继承3、构造函数继承4、组合继承1、前言 首先学习继承之前,要对原型链有一定程度的了解。 不了解可以去先阅读我另一篇文章,里面对原型链有一个较为详细的说明:Ja...
    99+
    2022-11-12
  • C++继承模式详解
    目录继承继承的概念继承的定义继承关系和访限定符继承方式父类和子类对象赋值转化继承中的作用域子类的默认成员函数继承与友元继承与静态成员复杂的菱形继承虚继承继承的总结继承 继承的概念 ...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作