在Java中我们知道静态变量会在类加载时机的“初始化”阶段得到赋值(编译器会收集类中的静态变量及静态代码块,然后在类构造方法
而在Kotlin中有所谓的伴随对象,用过的同学都知道,它的功能类似于Java中的静态变量,那它又是什么时候初始化的呢?来看一个例子,代码如下:
- package com.zfang.testapp
-
- class KConstructTest(val first: String, val second: Int) {
-
-
- init {// 111
- println("KConstructTest init")
- test(1)
- }
-
- companion object CC {
- init { // 222
- println("companion object init")
- }
-
- fun test(index: Int) {
- println("test, index = $index")
- }
- }
- }
一个简单的kotlin类,里面包含一个伴随对象CC,现在写一个测试类来看一个标记111和222这两个地方谁先初始化,测试代码如下:
- package com.zfang.testapp;
-
- class Test {
- public static void main(String[] args) {
- KConstructTest test = new KConstructTest("ttt", 1);
- KConstructTest.CC cc = test.CC;
- }
- }
一个简单的Java测试类,入口中直接new了一个KConstructTest对象,下面是程序输出:
- companion object init
- KConstructTest init
- test, index = 1
从输出结果中可以看出是伴随对象的init代码块先执行了,然后才是主类中的init代码块执行。下面我们反编译看下生存的java类是怎样的。结果如下:
- package com.zfang.testapp;
-
- import kotlin.Metadata;
- import kotlin.jvm.internal.DefaultConstructorMarker;
- import kotlin.jvm.internal.Intrinsics;
- import org.jetbrains.annotations.NotNull;
-
- @Metadata(
- mv = {1, 7, 1},
- k = 1,
- d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0002\b\u0007\u0018\u0000 \u000b2\u00020\u0001:\u0001\u000bB\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0007\u0010\bR\u0011\u0010\u0004\u001a\u00020\u0005¢\u0006\b\n\u0000\u001a\u0004\b\t\u0010\n¨\u0006\f"},
- d2 = {"Lcom/zfang/testapp/KConstructTest;", "", "first", "", "second", "", "(Ljava/lang/String;I)V", "getFirst", "()Ljava/lang/String;", "getSecond", "()I", "CC", "app_debug"}
- )
- public final class KConstructTest {
- @NotNull
- private final String first;
- private final int second;
- @NotNull
- public static final KConstructTest.CC CC = new KConstructTest.CC((DefaultConstructorMarker)null);
-
- @NotNull
- public final String getFirst() {
- return this.first;
- }
-
- public final int getSecond() {
- return this.second;
- }
-
- public KConstructTest(@NotNull String first, int second) {//与主构造方法对应
- Intrinsics.checkNotNullParameter(first, "first");
- super();
- this.first = first;
- this.second = second;
- String var3 = "KConstructTest init";//主类中的init代码块
- System.out.println(var3);
- CC.test(1);
- }
-
- static {//伴随对象中的init代码块
- String var0 = "companion object init";
- System.out.println(var0);
- }
-
- @Metadata(
- mv = {1, 7, 1},
- k = 1,
- d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0010\b\n\u0000\b\u0086\u0003\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u000e\u0010\u0003\u001a\u00020\u00042\u0006\u0010\u0005\u001a\u00020\u0006¨\u0006\u0007"},
- d2 = {"Lcom/zfang/testapp/KConstructTest$CC;", "", "()V", "test", "", "index", "", "app_debug"}
- )
- public static final class CC {//伴随对象类
- public final void test(int index) {
- String var2 = "test, index = " + index;
- System.out.println(var2);
- }
-
- private CC() {
- }
-
- // $FF: synthetic method
- public CC(DefaultConstructorMarker $constructor_marker) {
- this();
- }
- }
- }
额,反编译出来的Java代码看上去有点多的样子。不过逻辑还是很简单的,主要以下几点:
根据以上分析则可以得出结论:
所以如果项目中对主类Init块和伴随对象init块有初始化顺序要求的就需要注意相应的逻辑了。