--- 
title: 变量初始化顺序
categories:
- Scala
tags: [迁移自简书]
---

Scala与Java有些许不同点，但是主要机制遵循了Java虚拟机。
Int默认值 0，对象类型默认值null，以此类推。

Java 类加载机制顺序是，加载 → 链接（验证＋准备＋解析）→ 初始化（使用前的准备）
静态变量在准备阶段被初始化为默认值，初始化时才被设置为用户传入的值，而final的静态变量在准备阶段就被设置为用户传入的值。

scala中object中属性都是静态的，类似Java静态属性，但是final仅起到了不能被覆盖的作用，并没有让变量/常量在准备阶段就被初始化为实际值，这是因为object继承了App特质，被认为是主函数，声明的静态量在主函数执行时才会被初始化。scala final也是可以修饰var，而var又是众所周知的变量，可见final对变量常量的赋值是无影响的，而是不能被覆盖，如果有object继承该object，那么这个值是不能被重写的。（object不能继承object，能继承特质、类，所以在此final是无用的）
这里lazy这个懒加载起到了类似Java final的作用，因为lazy是懒求值，只有在使用时才会获取实际值，而这时类肯定是被加载完成了，所以才能拿到真正的实际值，不受App特质影响。

### 现在在object中有以下6种变量/常量

```scala
   //lazy var 是不允许的，
  //常量，不可被覆盖，不可修改，懒值
  private final lazy val default1 = Seq(1, 2, 3, 4)

  //变量，可被修改不可被覆盖
  private final var default2 = Seq(1, 2, 3, 4)

  //常量，不可变，懒值
  private lazy val default3 = Seq(1, 2, 3, 4)

  //常量，不可变，不可覆盖
  private final val default4 = Seq(1, 2, 3, 4)

  //普通常量
  private val default5 = Seq(1, 2, 3, 4)

  //普通变量
  private var default6 = Seq(1, 2, 3, 4)
```
### 在object中继承 extends App 并执行

很显然是可以打印出值，完整代码如下
```scala
/**
 * @author 梦境迷离
 * @version 1.0, 2019-06-20
 */
object MainObject1 extends App {

  //lazy var 是不允许的，
  //常量，不可被覆盖，不可修改，懒值
  private final lazy val default1 = Seq(1, 2, 3, 4)
  println("final lazy val 初始化 =>>>>> " + default1)

  //变量，可被修改不可被覆盖
  private final var default2 = Seq(1, 2, 3, 4)
  println("final var 初始化 =>>>>> " + default2)

  //常量，不可变，懒值
  private lazy val default3 = Seq(1, 2, 3, 4)
  println("lazy val 初始化 =>>>>> " + default3)

  //常量，不可变，不可覆盖
  private final val default4 = Seq(1, 2, 3, 4)
  println("final val 初始化 =>>>>> " + default4)

  //普通常量
  private val default5 = Seq(1, 2, 3, 4)
  println("val 初始化 =>>>>> " + default5)

  //普通变量
  private var default6 = Seq(1, 2, 3, 4)
  println("var 初始化 =>>>>> " + default6)
}
```
输出，很正常结果
```
final lazy val 初始化 =>>>>> List(1, 2, 3, 4)
final var 初始化 =>>>>> List(1, 2, 3, 4)
lazy val 初始化 =>>>>> List(1, 2, 3, 4)
final val 初始化 =>>>>> List(1, 2, 3, 4)
val 初始化 =>>>>> List(1, 2, 3, 4)
var 初始化 =>>>>> List(1, 2, 3, 4)
```
### 将打印函数写在方法中

代码如下
```
 def getData() = {
    println("方法调用1 =>>>>>> " + default1)
    println("方法调用2 =>>>>>> " + default2)
    println("方法调用3 =>>>>>> " + default3)
    println("方法调用4 =>>>>>> " + default4)
    println("方法调用5 =>>>>>> " + default5)
    println("方法调用6 =>>>>>> " + default6)
  }
```
完整代码
```scala
package cn.edu.jxnu.other

/**
 * @author 梦境迷离
 * @version 1.0, 2019-06-20
 */
object MainObject1 extends App {

  //lazy var 是不允许的，
  //常量，不可被覆盖，不可修改，懒值
  private final lazy val default1 = Seq(1, 2, 3, 4)
  println("final lazy val 初始化 =>>>>> " + default1)

  //变量，可被修改不可被覆盖
  private final var default2 = Seq(1, 2, 3, 4)
  println("final var 初始化 =>>>>> " + default2)

  //常量，不可变，懒值
  private lazy val default3 = Seq(1, 2, 3, 4)
  println("lazy val 初始化 =>>>>> " + default3)

  //常量，不可变，不可覆盖
  private final val default4 = Seq(1, 2, 3, 4)
  println("final val 初始化 =>>>>> " + default4)

  //普通常量
  private val default5 = Seq(1, 2, 3, 4)
  println("val 初始化 =>>>>> " + default5)

  //普通变量
  private var default6 = Seq(1, 2, 3, 4)
  println("var 初始化 =>>>>> " + default6)

  def getData() = {
    println("方法调用1 =>>>>>> " + default1)
    println("方法调用2 =>>>>>> " + default2)
    println("方法调用3 =>>>>>> " + default3)
    println("方法调用4 =>>>>>> " + default4)
    println("方法调用5 =>>>>>> " + default5)
    println("方法调用6 =>>>>>> " + default6)
  }
  //调用
  getData
}
```
结果如下，也是正常的
```
final lazy val 初始化 =>>>>> List(1, 2, 3, 4)
final var 初始化 =>>>>> List(1, 2, 3, 4)
lazy val 初始化 =>>>>> List(1, 2, 3, 4)
final val 初始化 =>>>>> List(1, 2, 3, 4)
val 初始化 =>>>>> List(1, 2, 3, 4)
var 初始化 =>>>>> List(1, 2, 3, 4)
方法调用1 =>>>>>> List(1, 2, 3, 4)
方法调用2 =>>>>>> List(1, 2, 3, 4)
方法调用3 =>>>>>> List(1, 2, 3, 4)
方法调用4 =>>>>>> List(1, 2, 3, 4)
方法调用5 =>>>>>> List(1, 2, 3, 4)
方法调用6 =>>>>>> List(1, 2, 3, 4)
```
### 在其他object中调用getData方法
代码如下
```scala
/**
 * 测试 初始化顺序
 *
 * @author 梦境迷离
 * @version 1.0, 2019-06-20
 */
object MainObject2 extends App {

  MainObject1.getData

}
```
执行上述代码，得到结果
```
方法调用1 =>>>>>> List(1, 2, 3, 4)
方法调用2 =>>>>>> null
方法调用3 =>>>>>> List(1, 2, 3, 4)
方法调用4 =>>>>>> null
方法调用5 =>>>>>> null
方法调用6 =>>>>>> null
```

### 总结

如果被调用方继承了App特质，则相当于Java main方法，如果不执行main，则在main的值不会被初始化为实际值，只会被初始化为默认值（例如Int被初始化为0，而不是你设置的值），而去掉MainObject1继承的特质则会得到正确的值。
```
  private final lazy val default1 = Seq(1, 2, 3, 4)
  private final var default2 = Seq(1, 2, 3, 4)//null
  private lazy val default3 = Seq(1, 2, 3, 4)
  private final val default4 = Seq(1, 2, 3, 4)//null
  private val default5 = Seq(1, 2, 3, 4)//null
  private var default6 = Seq(1, 2, 3, 4)//null
```
去掉MainObject1的App特质，同样在MainObject2中调用getData方法结果如下
```
final lazy val 初始化 =>>>>> List(1, 2, 3, 4)
final var 初始化 =>>>>> List(1, 2, 3, 4)
lazy val 初始化 =>>>>> List(1, 2, 3, 4)
final val 初始化 =>>>>> List(1, 2, 3, 4)
val 初始化 =>>>>> List(1, 2, 3, 4)
var 初始化 =>>>>> List(1, 2, 3, 4)
方法调用1 =>>>>>> List(1, 2, 3, 4)
方法调用2 =>>>>>> List(1, 2, 3, 4)
方法调用3 =>>>>>> List(1, 2, 3, 4)
方法调用4 =>>>>>> List(1, 2, 3, 4)
方法调用5 =>>>>>> List(1, 2, 3, 4)
方法调用6 =>>>>>> List(1, 2, 3, 4)
方法调用1 =>>>>>> List(1, 2, 3, 4)
方法调用2 =>>>>>> List(1, 2, 3, 4)
方法调用3 =>>>>>> List(1, 2, 3, 4)
方法调用4 =>>>>>> List(1, 2, 3, 4)
方法调用5 =>>>>>> List(1, 2, 3, 4)
方法调用6 =>>>>>> List(1, 2, 3, 4)
```
所以在平时写object时，运行时一定要去掉所有object中的extends App，我这次遇到这种情况主要是测试完object后忘记删除App导致后续遇到 config读取默认值为null。