JavaSE学习笔记

子类对象是包含整个父类对象仍是仅仅拥有父类对象的引用?

2017-09-04  本文已影响0人  Aruforce

1.问题描述:

子类对象是包含整个父类对象仍是仅仅拥有父类对象的引用?
个人描述:扩展类的实例对象在内存中包含的是基类的一个实例还是实例的引用?

2.问题解释:

首先,这个问题有一个假设:任意一个类的实例必定至少有一个或多个对象组成;
第二,其结构方式类似于列表(包含基类对象)或者链表(持有引用);
总结,扩展类对象与基类对象的关系就是 ExtendsClassObj has a BaseClassObj

3.个人回答:

首先,类的扩展关系和基类实例与扩展类实例的关系是不具有相关性的,类的扩展目的在于代码的复用,而扩展类的对象与基类的对象之间没有任何关系(除非扩展类显式的声明扩展类对象持有一个基类对象的引用)。
所以扩展类实例化并不会实例化一个基类的对象,但是会加载基类并对基类初始化,原因很简单:需要复用基类的属性和方法。
第二,扩展类实例在进行实例方法调用或者对实例属性读写(方法或者属性来自于基类)的时,如果ExtendsClass没有重写BaseClass的方法,那么是不会包含BaseClass的方法信息(可以javap -c ExtendsClass 反编译字节码),那么他是怎么执行的呢?每个类在JVM内有自己的Class(Java用于描述类的类)对象(这个class对象里边有所属父类索引信息,自己的方法表,属性表等信息):在方法执行的时候,会首先检测引用的类型再检测其所指向对象的实际类型(Base base = new ExtendsBase()或者BaseExtends baseExtends = new BaseExtends())1,如果方法在扩展类的方法区里找到就执行扩展类的,2,如果在扩展类的方法中没有找到就会自动寻找它的基类的方法区3,重复1/2步骤;
tip: JVM对于静态方法和实力方法的invoke指令有区分,分别为invokestatic和invokevirtual;

4.关于this(aload_0)和super:

tip1:this指代当前线程操作的本类实例。由编译器在后台默认的的加在形参列表第一个位置上) obj.test(Object...objs) 可以理解为Class.test(obj,..objs) 在方法内用this指代obj(由JVM aload_0 载入);
tip2;super指代基类链表,super.getClass().getName()可以翻译为:从基类链的各自的方法区找到getClass()方法(Object final的不可被重写)然后执行,返回Class对象再getName();

//源代码:
public class Base {
    private String name;

    public Base() {
        super();
        // TODO Auto-generated constructor stub
    }
    public Base(String aname) {
        super();
        name = aname;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Base getInstance(){
        return this;
    }
    public static void test(Base base,String s,int i){
        System.out.println("test Base");
    }
}
//反编译:
Compiled from "Base.java"
public class oop.Base {
  public oop.Base();
    Code:
       0: aload_0  //将在本类construtor前的全部局部变量压入栈顶(this) 请注意getInstance方法的反编译
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: return

  public oop.Base(java.lang.String);
    Code:
       0: aload_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: aload_1  //构造参数压入栈顶
       6: putfield      #17                 // Field name:Ljava/lang/String;
       9: return

  public java.lang.String getName();
    Code:
       0: aload_0
       1: getfield      #17                 // Field name:Ljava/lang/String;
       4: areturn

  public void setName(java.lang.String);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #17                 // Field name:Ljava/lang/String;
       5: return

  public oop.Base getInstance();
    Code:
       0: aload_0
       1: areturn

  public static void test(oop.Base, java.lang.String, int);
    Code:
       0: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream; 获取静态方法 
       3: ldc           #33                 // String test Base 载入字符串
       5: invokevirtual #35                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V执行实例方法
       8: return
}
//源代码:
public class BaseExtends extends Base{
    private String sex;
    public BaseExtends() {
        super();
        sex = "ChildSex";
    }

    public BaseExtends(String asex) {
        super();
        sex = asex;
    }
    public BaseExtends(String aname,String asex) {
        super(aname);
        sex = asex;
    }
    public String getSex() {
        return sex;
    }

    public void setSex(String asex) {
        sex = asex;
        super.getName();
    }
    public void test(String s){
        String str = sex;
    }
    public static void test(Base base ,String s,int i){
        System.out.println("test BaseExtends");
    }
}
//反编译
public class oop.BaseExtends extends oop.Base {
  public oop.BaseExtends();
    Code:
       0: aload_0
       1: invokespecial #10                 // Method oop/Base."<init>":()V  在执行基类的初始化代码
       4: aload_0 
       5: ldc           #12                 // String ChildSex
       7: putfield      #14                 // Field sex:Ljava/lang/String;
      10: return 

  public oop.BaseExtends(java.lang.String);
    Code:
       0: aload_0
       1: invokespecial #10                 // Method oop/Base."<init>":()V
       4: aload_0
       5: aload_1
       6: putfield      #14                 // Field sex:Ljava/lang/String;
       9: return

  public oop.BaseExtends(java.lang.String, java.lang.String);
    Code:
       0: aload_0
       1: aload_1
       2: invokespecial #23                 // Method oop/Base."<init>":(Ljava/lang/String;)V
       5: aload_0
       6: aload_2
       7: putfield      #14                 // Field sex:Ljava/lang/String;
      10: return

  public java.lang.String getSex();
    Code:
       0: aload_0
       1: getfield      #14                 // Field sex:Ljava/lang/String;
       4: areturn

  public void setSex(java.lang.String);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #14                 // Field sex:Ljava/lang/String;
       5: aload_0
       6: invokespecial #29                 // Method oop/Base.getName:()Ljava/lang/String;
       9: pop
      10: return

  public void test(java.lang.String);
    Code:
       0: aload_0
       1: getfield      #14                 // Field sex:Ljava/lang/String;
       4: astore_2
       5: return

  public static void test(oop.Base, java.lang.String, int);
    Code:
       0: getstatic     #36                 // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #42                 // String test BaseExtends
       5: invokevirtual #44                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

5.实例的构造过程(JVM 指令大体顺序):

new someClass //根据someClass的属性表开辟内存空间,并对各个属性初始化(赋予JVM默认值0啊,null啊等等 )返回内存空间的对应的“地址”。对于基类和扩展类有重复的属性这种情况(比如都分别声明了private String name这个属性,虽然是不合理不应该的设计),扩展类的实例在开辟内存空间时,name会分别的开辟不同的地址(基类和扩展类)。name对应的两个地址是JVM 指令getField或者putField的参数)
invokespecial //调用自己写的初始化代码
retunr aload_0的值;

上一篇 下一篇

猜你喜欢

热点阅读