java构造函数调用另一个构造函数_ABAP,Java和JavaScript类的构造函数使用的一些陷阱...

8a94997c567a617434e3d079f1b6c5c9.png

This question draws my attention during a discussion with my colleague recently. I will explain it in ABAP, Java and JavaScript.

In ABAP

I have a super class and a sub class. Source code for super class:

class ZCL_SUPER definition
  public
  create public .
public section.
  methods CONSTRUCTOR .
  methods SET_I
    importing
      !IV_I type INT4 .
protected section.
private section.
  data MV_SUPER type INT4 .
ENDCLASS.
CLASS ZCL_SUPER IMPLEMENTATION.
  method CONSTRUCTOR.
    me->set_i( 100 ).
  endmethod.
  method SET_I.
    me->mv_super = iv_i.
  endmethod.
ENDCLASS.

In constructor of super class, method set_i is called to set the member attribute mv_super to 100. And here is my sub class:

class ZCL_SUB definition
  public
  inheriting from ZCL_SUPER
  final
  create public .
public section.
  methods PRINT .
  methods SET_I
    redefinition .
protected section.
private section.
  data MV_SUB type I value 1 ##NO_TEXT.
ENDCLASS.
CLASS ZCL_SUB IMPLEMENTATION.
  method PRINT.
    WRITE: / ' sub:' , mv_sub.
  endmethod.
  METHOD set_i.
    super->set_i( iv_i = iv_i ).
    me->mv_sub = iv_i.
    WRITE: / 'mv_sub assigned by: ' , iv_i.
  ENDMETHOD.
ENDCLASS.

The redefinition of set_i is done in order to fill the member attribute mv_sub in sub class. And now in report, use this line for test:

NEW zcl_sub( )->print( ). Originally I expect the following lines will be printed:
mv_sub assigned by: 100
sub: 100

I am wrong. The actual output:

sub: 1 When debugging into the code, we can find the reason:

in constructor, the redefinition of set_i done by sub class is not called at all, since the technical type of me reference points to super class. This makes sense since the execution context is constructor and the initialization of super class is not finished yet.

22523d51db654aa7444a52ceb9b3fb65.png

In Java

Let’s now see what will happen in Java. Super class:

public class SuperClass {

    private int mSuperX;

    public SuperClass() {
        setX(99);
    }

    public void setX(int x) {
        mSuperX = x;
    }
}

Sub class:

public class SubClass extends SuperClass {

    private int mSubX = 1;

    public SubClass() {}

    @Override
    public void setX(int x) {
        super.setX(x);
        mSubX = x;
        System.out.println("SubX is assigned " + x);
    }

    public void printX() {
        System.out.println("SubX = " + mSubX);
    }
}

test:

public static void main(String[] args) {
        SubClass sc = new SubClass();
        sc.printX();
    }

Output:

6e701ce9ffc1a202fba455197fd3aeb7.png

The constructor execution behavior is completely different in Java: in constructor, the redefinition of setX done in sub class now gets chance to be called. However, it is overwritten to default value 1 later. Use Javap to analyze the byte code. The magic of initialization lays in line 59: invokespecial #18.

895fe6fb1e62a10fdcef404fc1768565.png

the byte code clearly shows the Java code new SubClass() will invoke SubClass.””. The #18 represents the entry in constant pool with id 18, which is SubClass.:

f229871235b862b3353d70df5c9397d3.png

So use Javap to review byte code of sub class again.

Here explains why in the last line of output, subX is reset to 1, since the initialization of it is done AFTER super class’ constructor call.

71af13e6b5d9cbcd8b7e6c9d5efcf2ea.png

What logic is contained in SuperClass.””? Inspect SuperClass.”” again:

bfbdadd22f7302f68a79e97eeb60b536.png

12 points to SuperClass.setX:

65a65831e6153d915121492b3fba161c.png

Byte code of setX: line 59: put this reference to stack line 60: push parameter x of method setX to stack line 61: put stack top element to variable #20, which is SuperClass.mSuperX:

1f2a7758f5294aea5536c5ad18f98c41.png

908330353c7a42c05430d571b958966b.png

The execution sequence analyzed so far: SubClass.”” -> SuperClass.”” -> SubClass.setX(int) -> SuperClass.setX(int) Which could clearly be observed in callstack:

ee562b269cff1010c558ad8cb6684194.png

In JavaScript

I write an example via JavaScript to illustrate the execution logic in above example. Super class:

function SuperClass(){
    this.setX(99);
}

SuperClass.prototype = {
    mSuperX : 0,
    setX : function(x){
         this.mSuperX = x;
    }
};

Sub class:

function SubClass(){  
    SuperClass.call(this);  
    this.mSubX = 1;
}  

SubClass.prototype = new SuperClass();  

SubClass.prototype.setX = function(x){
        SuperClass.prototype.setX(x);
        this.mSubX = x;
        console.log("SubX is assigned " + x);
};

SubClass.prototype.print = function(){
    console.log("SubX: " + this.mSubX);
}

Test code:

var sub = new SubClass();
sub.print();

Let’s step into code of new SubClass():

(1) it will call SuperClass’ constructor:

c4fc8bb1fe50dfd6fddafe8183a78721.png

(2) In Super class constructor, since now this points to SubClass, so the redefinition of setX is called:

01be5830e4682b263d9bdebcd60e97a0.png

(3) In redefined setX in SubClass, line 23 and line 24 will set attribute mSuperX in SuperClass and mSubX in Sub class accordingly.

828d8f5aa1335293b078f1b6bfbed681.png

(4) finally:

2f7d83cae4b4fee08df952988a433c09.png

So we get the same output in Java:

354e3d2a4877725443b1ae57679e40ce.png

Conclusion

If you call a non-final method in constructor and that method is redefined by sub class, be careful that your code might not work as you expect when you create a new instance of sub class:

(1) In ABAP, the redefinition in sub class will NOT be called in constructor. (2) In Java, the constructor of super class is called BEFORE the initialization of sub class member attribute.

要获取更多Jerry的原创文章,请关注公众号"汪子熙":

f64ae366006aab4509dba63a629c47e2.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值