最近,看到一个scala里关于lazy懒加载的关键字。测试了一遍它的使用和功能。lazy修饰变量就是为了延迟初始化。
使用lazy关键字修饰变量后,只有在使用该变量时,才会调用其实例化方法。
1.不使用lazy关键字。
object lazy_study {
def init():String={
println(1+1)
return "lazy study"
}
def main(agrs:Array[String]): Unit = {
val name = init()
println("测试lazy")
println(name)
println(name)
println(name)
}
}
结果:
可以看到,在声明时,立即进行实例化,调用了`init()“实例化方法
2.使用lazy关键字。
object lazy_study {
def init():String={
println(1+1)
return "lazy study"
}
def main(agrs:Array[String]): Unit = {
lazy val name = init()
println("测试lazy")
println(name)
println(name)
println(name)
}
}
结果:
可以看到,在声明时,并没有进行实例化这个val变量,并且只有在调用惰性变量时,才会去实例化这个变量。并且无论缩少次调用,实例化方法只会执行一次。
意思是:只有在第一次使用该变量时,变量对应的表达式才会被计算。之后再调用时,会通过一个叫flag变量(做一次位与操作)判断是否已经初始化过,保证只有在第一次被访问时才会调用该方法。scala里的lazy变量,背后的实现是个典型的双重检测锁(DCL)模式。
对于这样一个表达式: lazy val t:T = expr 无论expr是什么东西,字面量也好,方法调用也好。Scala的编译器都会把这个expr包在一个方法中,并且生成一个flag来决定只在t第一次被访问时才调用该方法。
对于使用了lazy的变量来说,之后对它的调用,每次使用前都会以线程安全的方式检查是否已经初始化,会有额外的开销,并没有那么高效。
因此,我们需要考虑惰性值的使用情况。只有当保护式带来的额外开销小于初始化带来的开销时,或者将某些值惰性化能简化系统初始化过程并确保执行顺序满足条件时,我们才应该使用惰性值。这种特性对于特别耗时的计算操作特别有用。
因此,lazy的使用要具体情况具体分析,并不一定都是有用的。
两个使用场景可以参考这篇文章。