`
grzrt
  • 浏览: 188676 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

JAVA中的继承分析

    博客分类:
  • JAVA
 
阅读更多

    为什么写这篇博客,之前对继承的理解知识大体理论上,最近有个同事问了个问题,发现对JAVA继承的底层实现相当模糊,结合《深入理解Java虚拟机:JVM高级特性与最佳实践》以及上网查的资料进行了一下深入学习。
   程序:现在又两个父子类如下

 

class Parent{
	public String str = "Parent";
	private int a = 10;
	public int getA() {
		return a;
	}
}

class Chield extends Parent {
	public String str = "Chield";
	private int a = 20;
	public int getA() {
		return a;
	}
}
 

  测试程序1:

public class TestInherit {
	public static void main(String[] args) {
		Parent p1 = new Parent();
		Parent c1 = new Chield();
		TestInherit.sayInherit(p1);
		TestInherit.sayInherit(c1);
		
		Parent p2 = new Parent();
		Chield c2 = new Chield();
		TestInherit.sayInherit(p2);
		TestInherit.sayInherit(c2);
	}
	
	public static void sayInherit(Parent p) {
		System.out.println("Call Parent");
	}
	
	public static void sayInherit(Chield c) {
		System.out.println("Call Chield");
	}
}

 结果是:
Call Parent
Call Parent
Call Parent
Call Chield

首先介绍方法调用的四条字节码指令:
invokevirtual 调用对象的实例方法,根据对象的实际类型进行分配,
invokeinterface 调用由接口实现的方法,在运行时对象中找到相应的实现;
invokespecial 调用需要特殊处理的实例方法,即实例的初始化<init>、private方法或超类的方法;
invokestatic  调用静态方法(static方法)。
其余字节码操作指令不作介绍。

 

代码:
Parent c1 = new Chield();
中Parent 称为静态类型,Chield称为实际类型。
静态绑定:如果是private,static或者final方法或者构造器,那么编译时就可以准备知道应该调用哪个方法,这种调用方式成为静态绑定。对应的字节码指令是:invokespecila,invokestatic(应用:overload,由于发生在编译期所以voreload不是由虚拟机来执行的)
动态绑定:与静态绑定相对,方法调用在运行时才能决定的,就是动态绑定。对应的指令为:invokevirtual(应用:override)
invokevirtual指令的运行时解析过程大致如下:
1)找到操作数栈顶的第一个元素所指向的对象的实际类型,    记作C
2)若果在类型C中找到常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束,不通过则返回java.lang.IllegalAccessError异常,
3)否则,按照继承关系从下到上依次对C的各个父类进行第2步的搜索和验证,
4)如果始终没找到合适的方法,则抛出java.lang.AbstractMethodError异常。
上边测试程序的子节码如下:

public class com.rt.TestInherit extends java.lang.Object{
public com.rt.TestInherit();
public static void main(java.lang.String[]);
  Code:
   0:   new     #16; //class com/rt/Parent
   3:   dup
   4:   invokespecial   #18; //Method com/rt/Parent."<init>":()V #初始化
   7:   astore_1	#局部变量表 索引为1的位置
   8:   new     #19; //class com/rt/Chield
   11:  dup
   12:  invokespecial   #21; //Method com/rt/Chield."<init>":()V #初始化
   15:  astore_2 #局部变量表 索引为1的位置
   16:  aload_1 #加载局部变量表 索引为1的位置reference类型值到 操作数栈 
   17:  invokestatic    #22; //Method sayInherit:(Lcom/rt/Parent;)V #采用静态绑定
   20:  aload_2 #加载局部变量表 索引为2的位置reference类型值到 操作数栈 
#采用静态绑定
,所以虽然c1变量的实际类型是Chiled,由于采用静态绑定方法参数是静态类型,因此输出“Call Parent”
   21:  invokestatic    #22; //Method sayInherit:(Lcom/rt/Parent;)V 
   24:  new     #16; //class com/rt/Parent
   27:  dup
   28:  invokespecial   #18; //Method com/rt/Parent."<init>":()V
   31:  astore_3
   32:  new     #19; //class com/rt/Chield
   35:  dup
   36:  invokespecial   #21; //Method com/rt/Chield."<init>":()V
   39:  astore  4
   41:  aload_3
   42:  invokestatic    #22; //Method sayInherit:(Lcom/rt/Parent;)V
   45:  aload   4
   47:  invokestatic    #26; //Method sayInherit:(Lcom/rt/Chield;)V
   50:  return

public static void sayInherit(com.rt.Parent);
  Code:
   0:   getstatic       #37; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #43; //String Call Parent
   5:   invokevirtual   #45; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

public static void sayInherit(com.rt.Chield);
  Code:
   0:   getstatic       #37; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #52; //String Call Chield
   5:   invokevirtual   #45; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return
}

 主要疑点在红色标记处。

 

测试程序2:

public class TestInherit {
    public static void main(String[] args) {
        Parent p1 = new Parent();
        Parent c1 = new Chield();

        System.out.println(p1.str);
        System.out.println(c1.str);
       
        System.out.println(p1.getA());
        System.out.println(c1.getA());
    }
}

  输出结果:
Parent
Parent
10
20

 

在Eclipse中,通过Debug查看c1中的属性如下图:

 

可以看出,子类中包含所有父类中的属性,这是由于在加载的时候会先加载父类。

System.out.println(p1.str);
System.out.println(c1.str);
对应字节码如下:

19:  aload_1 #从局部变量表中加载父类到操作数栈
   20:  getfield        #28; //Field com/rt/Parent.str:Ljava/lang/String;
   23:  invokevirtual   #32; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   26:  getstatic       #22; //Field java/lang/System.out:Ljava/io/PrintStream;
   29:  aload_2  #从局部变量表中加载子类类到操作数栈
   30:  getfield        #28; //Field com/rt/Parent.str:Ljava/lang/String;
#调用invokevirtual指令,会采用动态分配,找到实际类型子类对象(new Chield()生成)
#但是由于属性是静态绑定,所以导致输出的父类的属性
#如果调用的是方法,那么就会通过动态类型绑定到子类对象上
   33:  invokevirtual   #32; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   36:  getstatic       #22; //Field java/lang/System.out:Ljava/io/PrintStream;

 原因是:在Java中,属性绑定到类型,方法绑定到对象!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    java中与继承有关的程序内存分析

    当我们谈论“java中与继承有关的程序内存分析”时,主要关注的是在Java程序运行时,内存是如何为继承体系的实例分配的。 Java内存主要分为三个区域:栈(Stack)、堆(Heap)和方法区(Method Area)。在讨论继承时...

    Java面向对象之继承练习题.doc

    1. **类的继承**:在Java中,继承是面向对象编程的重要特性,允许一个类(子类)继承另一个类(父类)的属性和方法。例如,我们可以创建一个`Music`类作为其他音乐相关类的基类。 2. **构造方法**:构造方法用于...

    java中继承测试代码分析

    Java 中继承测试代码分析 Java 中继承测试代码分析是 Java 编程语言中的一种重要概念。继承是基于已经存在的类构造一个新类,可以复用这些类的方法和域。在 Java 中,继承关系的指定是通过关键字 "extends" 实现的...

    Java继承实例源代码

    在这个"Java继承实例源代码"的压缩包中,包含的文件可能提供了关于Java继承和多态性的实际应用示例。 首先,我们来看“继承”这一概念。在Java中,通过使用关键字`extends`,一个类可以声明其为另一个类的子类。...

    Java语言继承中多态的机理分析.pdf

    Java语言的继承和多态机理是面向对象程序设计的重要组成部分,它不仅仅是编程语言中的一个特性,更是实现...对于进一步想要深入研究Java继承和多态机理的读者,可以通过提供的资源链接获得更详细的理论分析和实例演示。

    java中多态的内存分析

    在深入探讨Java中多态的内存分析之前,我们需要先理解一些基本概念。 首先,了解Java内存模型至关重要。Java程序运行时主要涉及四种内存区域:程序计数器、虚拟机栈、本地方法栈、堆和方法区(在Java 8及以后版本中...

    用java编写的有关继承的程序

    Java继承和多态的程序设计 本节课我们将学习使用Java语言编写的有关继承和多态的程序设计。这个程序旨在帮助初学者更好地理解Java中的继承和多态机制。 继承是Java语言中的一种机制,它允许一个类继承另一个类的...

    java继承与多态

    ### Java继承与多态知识点详解 #### 一、引言 在面向对象编程语言中,继承和多态是非常重要的概念。它们提供了强大的机制来管理代码的复杂性,并且能够有效地复用现有代码。本文将详细介绍Java中的继承和多态概念...

    java 实验 继承与多态rectAngle 定义矩形类源代码

    java 实验 继承与多态rectAngle 定义矩形类,用户输入矩形的长与宽,程序计算其面积和周长;派生子类正方形类,定义一个接口Printable源代码

    JAVA子类与继承实验报告

    JAVA子类与继承实验报告 实验1 中国人与美国人 实验2 面积之和

    java有关继承的题目

    在Java编程语言中,继承是面向对象编程的一个核心概念,它允许我们定义类的层次结构,使得一个类可以从另一个类那里继承属性和方法。通过继承,我们可以创建更加具体和专用的类,同时避免代码重复,提高代码的复用性...

    论JAVA继承机制中父类与子类的关系

    ### 论JAVA继承机制中父类与子类的关系 #### 摘要 本文深入探讨了Java语言中的继承机制,并重点分析了父类与子类之间的关系。文章围绕子类继承父类成员的过程、成员变量及方法的赋值、重写、覆盖等问题展开讨论,...

    java 编写 决策分析程序

    下面我们将深入探讨Java在决策分析中的应用,并结合书中的经典案例进行解析。 首先,决策分析的核心在于数据处理。在Java中,我们可以使用内置的集合框架,如ArrayList、LinkedList、HashMap等,来存储和管理数据。...

    java的继承总结.doc

    下面将对Java继承的基本概念、语法格式、继承关系、方法重写、自动转型等进行详细的总结和分析。 一、继承的基本概念 继承是特殊的is-a关系,即子类继承父类,说明子类是一种特殊的父类,并且具有父类所不具有的...

    Java内部类继承问题的分析.pdf

    Java内部类继承问题的分析主要关注的是在Java编程中,当内部类(也称为嵌套类)参与继承时所面临的挑战和潜在问题。Java语言采用了单一继承机制,即一个类只能从一个父类继承,这有助于提高代码的可读性和可维护性。...

    java继承代码

    在Java编程语言中,继承是面向对象编程的一个核心特性,它允许一个类(子类)继承另一个类(父类...这个“java继承代码”示例为你提供了一个实践这些概念的机会,通过阅读和分析代码,你可以更好地理解Java的继承机制。

    面向对象抽象思维与java继承机制

    面向对象抽象和Java继承机制是现代软件开发中不可或缺的部分。通过抽象,我们可以将复杂的问题分解为更小、更易于管理的部分;而通过继承,我们可以复用现有的代码,并且更容易地扩展和维护我们的程序。理解和熟练...

    JAVA算法分析-很好的java思想

    排序算法是Java算法分析中的重要部分。Java标准库提供了多种排序算法实现,如Arrays.sort()内部采用的快速排序和归并排序,以及Collections.sort()默认使用的TimSort。理解这些排序算法的原理,有助于在实际开发中...

    Java中类的继承学习小结

    在Java编程语言中,类的继承是面向对象编程的一个核心概念。它允许我们创建一个新类,该类基于已存在的类(称为父类或超类),并在此基础上添加新的功能或者修改现有功能。通过继承,我们可以实现代码的重用,减少...

Global site tag (gtag.js) - Google Analytics