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种变量/常量
//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 并执行
很显然是可以打印出值,完整代码如下
/**
* @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)
}
完整代码
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方法
代码如下
/**
* 测试 初始化顺序
*
* @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。
文档信息
- 本文作者:梦境迷离
- 本文链接:https://blog.dreamylost.cn/scala/Scala-%E5%8F%98%E9%87%8F%E5%88%9D%E5%A7%8B%E5%8C%96%E9%A1%BA%E5%BA%8F.html
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)