Kotlin中init代码块和构造方法以及伴生对象中代码的调用时机及执行顺序

在Kotlin中,除了主构造函数和次构造函数外,还给我们提供了init代码块,供我们做一些初始化操作。
那么kotlin init代码块是什么时候执行的呢,和构造方法以及伴生对象一起使用时它们的执行顺序又是怎样的呢?
下面我们通过一个小例子来看一下

我们先来看看kotlin中init代码块和构造方法的执行顺序

class Person() {

    /*属性*/
    private var gender: Boolean = true

    /*次构造方法*/
    constructor(name: String,gender: Boolean):this() {
        println("constructor")
    }

    /*初始化代码块*/
    init {
        println("Person init 2,gender:${gender}")
    }

    /*初始化代码块*/
    init {
        println("Person init 1")
    }

}

上面是一个简单的Person类,我们在类中写了一个次构造方法、两个init代码块和一个属性

此时,当我们调用 Person("yzq",false)时,Person类中代码的执行顺序是什么样的呢

打印结果如下

Person init 2,gender:true
Person init 1
constructor

可以看到,首先会按顺序执行类中init代码块,然后再执行构造方法里代码,并且我可以在init代码块中使用类声明的属性

下面我们来看一下翻译成java的代码

查看kotlin代码编译成java代码的方法
在这里插入图片描述

下面是由kotlin代码编译成java后的部分代码

public final class Person {
   private boolean gender;

   public Person() {
      this.gender = true;
      String var1 = "Person init 2,gender:" + this.gender;
      boolean var2 = false;
      System.out.println(var1);
      var1 = "Person init 1";
      var2 = false;
      System.out.println(var1);
   }

   public Person(@NotNull String name, boolean gender) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      this();
      String var3 = "constructor";
      boolean var4 = false;
      System.out.println(var3);
   }
}

我们可以简单的这样理解:

Kotlin中的init代码块就相当于Java中的普通代码块,在创建对象的时候代码块会先执行。注意是每次创建都会执行一遍

我们再来给类中加入个伴生对象来看看

class Person() {

    /*属性*/
    private var gender: Boolean = true

    /*次构造方法*/
    constructor(name: String, gender: Boolean) : this() {
        println("constructor")
    }

    companion object {
        val instance by lazy {
            Person("yzq",false)
        }

        /*伴生对象中的初始化代码*/
        init {
            println("companion init 1")
        }

        init {
            println("companion init 2")
        }
    }

    /*初始化代码块*/
    init {
        println("Person init 2,gender:${gender}")
    }

    /*初始化代码块*/
    init {
        println("Person init 1")
    }

}

加入伴生对象后,那肯定就是伴生对象中的代码先执行了。
我们调用一下 Person.instance来看一下结果

companion init 1
companion init 2
Person init 2,gender:true
Person init 1
constructor

和你预想的一样吗?

首先伴生对象中的代码是在类加载时就会执行,此时会先顺序的执行伴生对象中的init代码块,但是由于instance是懒加载的,所以只有当我们代码出现Person.instance时,才会执行instance中委托的代码。
此时会去调用指定的构造函数,而执行构造函数时就和最上面的那种执行顺序是一致的了,先执行类中的init代码块,再执行构造函数原本的代码

下面我们来看一下编译过后的java代码

public final class Person {
   private boolean gender;
   @NotNull
   private static final Lazy instance$delegate;
   public static final Person.Companion Companion = new Person.Companion((DefaultConstructorMarker)null);

   public Person() {
      this.gender = true;
      String var1 = "Person init 2,gender:" + this.gender;
      boolean var2 = false;
      System.out.println(var1);
      var1 = "Person init 1";
      var2 = false;
      System.out.println(var1);
   }

   public Person(@NotNull String name, boolean gender) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      this();
      String var3 = "constructor";
      boolean var4 = false;
      System.out.println(var3);
   }

   static {
      instance$delegate = LazyKt.lazy((Function0)null.INSTANCE);
      String var0 = "companion init 1";
      boolean var1 = false;
      System.out.println(var0);
      var0 = "companion init 2";
      var1 = false;
      System.out.println(var0);
   }

   @Metadata(
      mv = {1, 1, 15},
      bv = {1, 0, 3},
      k = 1,
      d1 = {"\u0000\u0014\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0005\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002R\u001b\u0010\u0003\u001a\u00020\u00048FX\u0086\u0084\u0002¢\u0006\f\n\u0004\b\u0007\u0010\b\u001a\u0004\b\u0005\u0010\u0006¨\u0006\t"},
      d2 = {"LPerson$Companion;", "", "()V", "instance", "LPerson;", "getInstance", "()LPerson;", "instance$delegate", "Lkotlin/Lazy;", "LeetCode"}
   )
   public static final class Companion {
      // $FF: synthetic field
      static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.Companion.class), "instance", "getInstance()LPerson;"))};

      @NotNull
      public final Person getInstance() {
         Lazy var1 = Person.instance$delegate;
         Person.Companion var2 = Person.Companion;
         KProperty var3 = $$delegatedProperties[0];
         boolean var4 = false;
         return (Person)var1.getValue();
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }
}

那如果伴生对象里的instance不是懒加载的

class Person() {

    /*属性*/
    private var gender: Boolean = true

    /*次构造方法*/
    constructor(name: String, gender: Boolean) : this() {
        println("constructor")
    }

    companion object {
        val instance = Person("yzq", false)


        /*伴生对象中的初始化代码*/
        init {
            println("companion init 1")
        }

        init {
            println("companion init 2")
        }
    }

    /*初始化代码块*/
    init {
        println("Person init 2,gender:${gender}")
    }

    /*初始化代码块*/
    init {
        println("Person init 1")
    }

}

调用Person.instance 则执行顺序如下:

Person init 2,gender:true
Person init 1
constructor
companion init 1
companion init 2

对于伴生对象,我们可以这样简单的理解

Kotlin中的伴生对象相当于Java中的Static关键字。
伴生对象里的init代码块就相当于Java中的静态代码块。在类加载的时候会优先执行且只会执行一次。

现在,init代码块和构造方法以及伴生对象中代码的执行顺序清楚了吗?


如果你觉得本文对你有帮助,麻烦动动手指顶一下,算是对本文的一个认可,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

  • 52
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喻志强(Xeon)

码字不易,鼓励随意。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值