父类引用指向子类对象的理解

本文详细解释了Java中父类引用指向子类对象的概念及其实现方式,包括如何进行强制类型转换并保持对象属性不变。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

虽然接触java已经好长时间了,但对于一些知识点,还是有些模糊,就比如今天说的这个。

接触java的人都知道,继承是面向对象的三个特征之一(封装、继承、多态),继承中有一对概念,即父类和子类。他们通过关键字extends关联。

eg:

父类:parent

子类:son

son extends parent


parent  par = new son();

关于这句,说明两点,等号左边是关于父类的一个引用,等号右边是关于子类的一个实例对象。即概括出来就是:父类引用指向子类对象。

看下面这句

son  s = (son)par;

这句是强制类型转换,即将父类强制转换为子类。

观看这个过程我们知道,par指向的实例对象本来就是一个son,它指向的是内存中的一个已经实例化过的对象,所以这个对象里存的,还是son里面该有的东西,并没有把son中不属于parent的自定义的东西丢掉。

接下来经过强制转换,让子类引用指向子类对象,所以重新转换后,对象里的东西并没有丢失,只是用父类引用时,对象中的某些东西(子类中定义的)使用父类调用不到。

思考:

1 par.getclass().getname()与s.getclass().getname()的结果一样,都是子类 son。



### Java 多态的原理及父类引用指向子类对象的工作机制 Java 中的多态是一种重要的面向对象特性,它允许可变数量的不同类型的对象以统一接口的形式呈现出来。以下是关于父类引用指向子类对象工作机制的具体解析。 --- #### 1. **多态的核心概念** 多态指的是在继承关系下,当父类子类拥有相同签名的方法时,运行时会调用实际对象所属类中的方法而非声明类型中的方法[^1]。这种行为依赖于动态绑定(Dynamic Binding),也称为后期绑定或运行时绑定。 - 动态绑定意味着 JVM 在程序运行期间决定应该调用哪个具体实现。 - 只有实例方法支持动态绑定,静态方法和变量则不参与多态[^3]。 --- #### 2. **JVM 的内存模型分析** 为了更好地理解父类引用指向子类对象的行为,可以从 JVM 的内存管理角度出发: ##### (1)**栈区与堆区的作用** - 当创建一个对象时,JVM 在 **堆区** 创建了完整的子类对象,其中包括子类自身的属性、方法以及从父类继承来的所有成员[^3]。 - 同时,在 **栈区** 创建了一个父类类型的引用变量,用来保存堆中对象的地址。 ##### (2)**引用对象的关系** - 栈中的父类引用仅能访问父类定义范围内的方法和字段。 - 若子类重写了父类的方法,则运行时会优先调用子类中的实现。 - 如果尝试访问子类特有的方法或字段,则需显式地将父类引用向下转型为子类类型[^2]。 --- #### 3. **代码示例及其解释** 以下是一个典型的多态场景演示: ```java class Animal { public void makeSound() { System.out.println("Animal makes a sound"); } } class Dog extends Animal { @Override public void makeSound() { System.out.println("Dog barks"); } public void wagTail() { System.out.println("Dog wags its tail"); } } public class TestPolymorphism { public static void main(String[] args) { // 父类引用指向子类对象 Animal animal = new Dog(); // 调用了子类重写的 makeSound 方法 animal.makeSound(); // 输出: Dog barks // 尝试调用子类特有方法会报错 // animal.wagTail(); // 编译错误! // 下转型后可访问子类特有方法 ((Dog) animal).wagTail(); // 输出: Dog wags its tail } } ``` - 上述代码展示了父类引用 `animal` 指向子类对象 `Dog`。 - 运行时调用了子类重写的 `makeSound()` 方法[^1]。 - 若要调用子类独有的方法 `wagTail()`,必须先将其强转回子类类型[^2]。 --- #### 4. **方法覆盖 vs 方法隐藏的区别** 需要注意的是,只有非静态方法才能体现多态性,因为它们会被动态绑定到实际对象上。而对于静态方法而言,编译期就已经决定了调用哪一个版本,这被称为方法隐藏而不是覆盖。 举例如下: ```java class Parent { public static void display() { System.out.println("Parent's static method"); } public void test() { System.out.println("Parent's instance method"); } } class Child extends Parent { public static void display() { System.out.println("Child's static method"); } @Override public void test() { System.out.println("Child's instance method"); } } public class StaticVsInstance { public static void main(String[] args) { Parent p = new Child(); p.display(); // 输出: Parent's static method 【静态方法未被覆盖】 p.test(); // 输出: Child's instance method 【动态绑定生效】 Child c = new Child(); c.display(); // 输出: Child's static method 【直接调用本类静态方法】 } } ``` --- #### 5. **总结工作流程** 基于以上讨论,可以归纳出父类引用指向子类对象的工作机制如下: 1. 声明阶段:定义一个父类类型的引用变量。 2. 初始化阶段:让该引用指向一个具体的子类对象实例。 3. 访问控制:通过父类引用只能访问父类可见的部分;若子类重写了某些方法,则运行时会选择子类的实际实现。 4. 特殊需求处理:如需操作子类独占的功能,可通过强制类型转换恢复原始类型[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值