Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Java子类与父类之间的对象转换(说明继承)

Java子类与父类之间的对象转换(说明继承)

作者头像
用户7886150
修改于 2020-12-10 02:51:51
修改于 2020-12-10 02:51:51
3.9K0
举报
文章被收录于专栏:bit哲学院bit哲学院

参考链接: 父类和子类在Java中具有相同的数据成员

在使用Java的多态机制时,常常使用的一个特性便是子类和父类之间的对象转换。从子类向父类的转换称为向上转换(upcasting),通过向上转换,我们能够在编写程序时采用通用程序设计的思想,在需要使用子类对象的时候,通过把变量定义为父类型,我们可以通过一个变量,使用该父类型的所有子类型实例;从父类型向子类型的转换称为向下转换(downcasting),通过向下转换,我们能在必要的时候,将父类型变量转换成子类型变量,使用一些通过子类型才能够使用的方法。以下是我对于对象转换的一些个人理解,如有不对,欢迎指正,虚心向大神们请教。

   首先是从子类向父类的向上转换。向上转换比较直观,总是能够将一个子类的实例转换为一个父类的对象,从继承链的角度,这个特性很容易理解:继承是一种“是一种”的关系,从父类派生出的子类,我们都能理解为,子类总是父类的一个实例。比如说,Fruit类派生出了Orange类,Apple类,Orange和Apple都是Fruit;Animal类派生出了Tiger类和Lion类,Tiger和Lion都是Animal。因此,从子类向父类的转换不需要什么限制,只需直接将子类实例赋值给父类变量即可,这也是Java中的多态的实现机制。

//Test.java

public class Test {

    public static void main(String args[]) {

        Animal tiger = new Tiger();

        Animal lion = new Lion();

    }

}

class Animal {

    String name;

    Animal() {

        name = "animal";

    }

    Animal(String name) {

        this.name = name;

    }

}

class Tiger extends Animal {

    Tiger() {

        super("tiger");

    }

}

class Lion extends Animal {

    Lion() {

        super("lion");

    }

}

    然而从父类向子类的向下转换就稍微复杂一些了。在讲述向下转换之前,也许有些刚学java的朋友会有点不解为什么要使用向下转换,使用多态和动态绑定机制通过父类型变量使用子变量不就可以了么(比如我就曾对此感到疑惑)。这就要考虑到,在继承关系中,有一些方法是不适合由父类定义并由子类继承并重写的,有些方法是子类特有的,不应该通过继承得到,且子类可能也会有自己特有的成员变量,那么在使用多态机制的时候,若我们要通过父类型变量使用到这些子类特有的方法和属性的话,就需要将父类型变量转换成对应的子类型变量。一个典型例子便是标准库中的数据类型包装类:Integer类,Double类,Long类等,它们都继承自Number类,且它们都有一个方法叫做compareTo用于比较两个同样的类型。然而这个方法是这些子类通过实现Comparable接口来实现的,在Number类中并没有该方法的实现,因此若要通过Number类型变量来使用compareTo方法,就要先将Number类转换成子类的对象。

从父类向子类的转换就有限制了。首先,父类变量向子类转换必须通过显式强制类型转换,采取和向上转换相同的直接赋值方式是不行的,;并且,当把一个父类型变量实例转换为子类型变量时,必须确保该父类变量是子类的一个实例,从继承链的角度来理解这些原因:子类一定是父类的一个实例,然而父类却不一定是子类的实例;比如说,Fruit未必是Orange,它可能是Apple;Animal也不一定是Tiger,它可能是Lion。用代码来解释一下:

Animal tiger = new Tiger();

Animal lion = new Lion();

在前面向上转换的代码示例当中,main方法中的这两行代码,意思就是父类型变量tiger是子类Tiger的一个实例,lion是Lion的一个实例。 也就是说,如果要把tiger转换为Tiger类型,必须保证tiger本身是Tiger的一个实例,在上例中,如果要把tiger转换成Lion类型,或是把Lion类型转换为Tiger类型,都是行不通的,在运行时,这会抛出一个运行异常ClassCastException,表示类转换异常。因此,在进行父类向子类的转换时,一个好的习惯是通过instanceof运算符来判断父类变量是否是该子类的一个实例:

Tiger t = null;

if(tiger instanceof Tiger)

    t = (Tiger)tiger;

如果要通过父类调用子类变量的方法,那么要注意要将父类型变量和强制转换用括号括起来:

Number i = new Integer(3);

System.out.println(

    ((Integer)i).compareTo(new Integer(4))

                  );

因为成员访问运算符.的优先级大于类型转换,所以要用括号括起来保证类型转换先于成员访问进行运算。 前面说到用instanceof判断父类是否是子类的一个实例是一个好习惯,如果不养成这个习惯的话很容易出问题,请看下面这段代码:

Animal animal = new Tiger();

Lion lion = (Lion)tiger;

前面说到,这段代码会在运行时抛出ClassCastException异常,然而,这段代码却是能够编译成功的。原因是因为,Java编译器并没有聪明到能够在编译阶段就知道父类型变量是哪一个子类的实例,所以,将animal转换为Lion类型的代码:(Lion)animal是能够编译通过的,即使事实上我们能看到animal是Tiger的一个实例,因为Animal类型确实能转换成Lion类型,所以这条语句是合法的。所以,如果没有使用instanceof防止不同子类型之间的对象转换,而又不能指望编译器检查出这种转换逻辑错误的话,就很容易犯错了。

一、父类引用指向子类对象时

1、若子类覆盖了某方法,则父类引用调用子类重新定义的新方法

2、若子类未覆盖某方法,则父类引用调用父类本身的旧方法

3、若子类覆盖了某属性,但父类引用仍调用父类本身的旧属性

4、若子类未覆盖某属性,则父类引用调用父类本身的旧属性

5、父类引用不能访问子类新定义的属性和方法

二、子类引用指向自身对象时

1、若子类覆盖了某方法,则子类引用调用子类重新定义的新方法

2、若子类未覆盖某方法,则子类引用调用父类本身的旧方法

3、若子类覆盖了某属性,则子类引用调用子类重新定义的新属性

4、若子类未覆盖某属性,则子类引用调用父类本身的旧属性

5、子类引用可以访问子类新定义的方法

三、示例代码

 B.java

    [java] 

    view plain

     copy

  public class B {        int a = 1;      int b = 2;        void f1() {          System.out.println("B.f1()");      }        void f2() {          System.out.println("B.f2()");      }    }  

 C.java

    [java] 

    view plain

     copy

  public class C extends B {        int a = 3;        @Override      void f1() {          System.out.println("C.f1()");      }        void f3() {          System.out.println("C.f3()");      }        public static void main(String[] args) {            B b = new C();// 父类引用指向子类对象          b.f1();// 子类覆盖了该方法,所以父类引用调用新方法          b.f2();// 子类未覆盖该方法,所以父类引用调用旧方法          // b.f3();此行去掉注释会报错,父类引用不能访问子类新定义方法          System.out.println(b.a);// 子类覆盖了该属性,但父类引用仍旧访问旧属性          System.out.println(b.b);// 子类未覆盖该属性,父类访问旧属性            System.out.println();            C c = new C();// 子类引用指向自身对象          c.f1();// 子类覆盖了父类方法,所以调用新方法          c.f2();// 子类未覆盖父类方法,所以调用旧方法          c.f3();// 子类调用自己新定义的方法          System.out.println(c.a);// 子类覆盖了该属性,所以访问新属性          System.out.println(c.b);// 子类未覆盖该属性,所以访问旧属性      }  }  

 输出:

    [html] 

    view plain

     copy

  C.f1()  B.f2()  1  2    C.f1()  B.f2()  C.f3()  3  2

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java面向对象编程高级
在main方法中定义一个变量count,当一个小孩加入游戏后count++,最后个count 就记录有多少小孩玩游戏 。
timerring
2023/04/21
1.6K0
Java面向对象编程高级
面向对象的三大特征-继承
继承是面向对象的核心特性,是面向对象的学习重点。同时继承是代码复用的重要方式,可以表示类与类之间的关系,是所有面向对象语言不可缺少的组成部分。
星哥玩云
2022/09/08
5270
面向对象的三大特征-继承
Java面向对象三大特性(封装、继承、多态)
OOP 语言:也就是面向对象编程。 面向对象的语言有三大特性:封装、继承、多态。三大特性是面向对象编程的核心。下面就来介绍一下面向对象的三大特性。 如果想了解面向对象可以看一下这一篇博客类和对象
全栈程序员站长
2022/09/17
3380
Java面向对象三大特性(封装、继承、多态)
Java基础(八):封装、继承、多态性
概述:私有化类的成员变量,提供公共的get和set方法,对外暴露获取和修改属性的功能
冬天vs不冷
2025/01/21
1330
Java基础(八):封装、继承、多态性
新手小白学JAVA 面向对象之多态
多态是面向对象程序设计(OOP)的一个重要特征,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
全栈程序员站长
2022/09/09
2470
新手小白学JAVA 面向对象之多态
Java Review (十、面向对象----继承)
使用继承,人们可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域。在此基础上,还可以添加一些新的方法和域, 以满足新的需求。这是 Java 程序设计中的一项核心技术。
三分恶
2020/07/16
5130
疯狂Java笔记之对象及其内存管理
类体内定义的变量被称为成员变量〔英文是Field)。如果定义该成员变量时没有使用static 修饰,该成员变量又被称为非静态变量或实例变量;如果使用了static修饰,则该成员变量又可被称为静态变量或类变量
HelloJack
2018/08/28
4340
疯狂Java笔记之对象及其内存管理
详解 Java 对象与内存控制(上)
不管是类变量还是实例变量,你都不能引用一个还没有定义的变量,或者在引用之前没有定义的变量,如下图所示:
CoderJed
2018/09/13
4720
详解 Java 对象与内存控制(上)
java的类和对象(三)
多态是Java中另一个重要的面向对象编程概念。多态的意思是同一个方法调用,由于对象不同可能会产生不同的行为。
堕落飞鸟
2023/04/02
3780
Java学习笔记——面向对象编程(核心)
面向对象是相对于面向过程而言的。面向过程,强调的是功能行为。面向对象,将功能封装进对象,强调具备了功能的对象。 面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
梦飞
2022/06/23
4250
【11】JAVASE-面向对象-多态【从零开始学JAVA】
Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。
用户4919348
2024/05/25
600
【11】JAVASE-面向对象-多态【从零开始学JAVA】
java编程——从jvm角度看懂类初始化、方法重写、重载
类的声明周期可以分为7个阶段,但今天我们只讲初始化阶段。我们我觉得出来使用和卸载阶段外,初始化阶段是最贴近我们平时学的,也是笔试做题过程中最容易遇到的,假如你想了解每一个阶段的话,可以看看深入理解Java虚拟机这本书。
慕容千语
2019/06/11
5230
Java Review (十一、面向对象----多态)
Java 引用变量有两个类型 :一个是编译时类型,一个是运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定,如果编译时类型和运行时类型不一致,就可能出现所谓的多态(Polymorphism)。
三分恶
2020/07/16
4670
类与对象(一)—— 类(继承)
大西瓜:“类是构造对象的模板,由类构造对象的过程称为创建类的实例。可以说对象是类的实例!”
东边的大西瓜
2022/05/05
3950
类与对象(一)—— 类(继承)
Java学习【类与对象—继承与多态】
当我们去定义一个student类和techer类时会发现里面有重复的属性,那如果我们相要写其他一些工作人员的类时,每一个类都要写这些重复的属性
2的n次方
2024/10/15
1110
Java学习【类与对象—继承与多态】
从jvm角度看懂类初始化、方法重载、重写。
类的声明周期可以分为7个阶段,但今天我们只讲初始化阶段。我们我觉得出来使用和卸载阶段外,初始化阶段是最贴近我们平时学的,也是笔试做题过程中最容易遇到的,假如你想了解每一个阶段的话,可以看看深入理解Java虚拟机这本书。
帅地
2018/08/30
6580
从jvm角度看懂类初始化、方法重载、重写。
Java 小白成长记 · 第 6 篇「为什么说要慎用继承,优先使用组合」
在代码的编写过程中,避免冗余代码的出现是非常重要的,大段大段的重复代码必然不能够称之为优雅。所谓减少冗余代码,通俗来说就是实现一段代码多处使用,「在不污染源代码的前提下使用现存代码」,也就是代码「复用」,避免重复编写。然而,对于像 C 语言等面向过程的语言来说,复用通常指的仅仅只是「复制代码」,任何语言都可通过简单的复制来达到代码复用的目的,显然这样做的效果并不好。
飞天小牛肉
2021/02/26
1K0
Java 小白成长记 · 第 6 篇「为什么说要慎用继承,优先使用组合」
Java面向对象OOP
如果想要返回多个结果,则需要返回一个数组,将结果封装到一个数组中去 (即将返回值设为int[]等数组类型)
h3110_w0r1d
2024/02/19
1980
Java面向对象OOP
Java核心-面向对象(下)
之前说完了类、对象、方法以及面向对象的三大特性封装、继承和多态,现在来了解一下接口、代码块和一些常见的类如抽象类、包装类等。
reload
2024/02/21
1450
Java核心-面向对象(下)
Java基础系列1:Java面向对象
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架。
说故事的五公子
2019/11/02
4840
相关推荐
Java面向对象编程高级
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档