object
单例对象的声明:
代码语言:javascript复制object Model{
var temp = "1"
val temp2 = "2"
const val temp3 = "3"
}
抛出疑问:使用object
修饰的类,是哪种类型的单例模式
这里我们先回顾一下java
六种单例模式
1. 饿汉式
代码语言:javascript复制public class HungryMan {
private HungryMan(){}
private static HungryMan hungryMan = new HungryMan();
public static HungryMan getInstance(){
return hungryMan;
}
}
优点:简单方便,线程安全 缺点:无论是否用到,都会进行实例化,而且在类加载时就会实例化
2. 懒汉式
代码语言:javascript复制public class LazyMan {
private static LazyMan lazyMan = null;
private LazyMan() {
}
public static LazyMan getInstatce() {
if (lazyMan == null) {
lazyMan = new LazyMan();
}
return lazyMan;
}
}
优点:只有在使用时才会生成对象,能够减少内存开销 缺点:线程不安全,只能在单线程中使用,多个线程访问时,会产生多个对象,
3.懒汉式同步锁
代码语言:javascript复制public class LazyMan {
private static volatile LazyMan lazyMan = null;
private LazyMan() {
}
public static LazyMan getInstatce() {
synchronized (LazyMan.class){
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
return lazyMan;
}
}
优点:支持多线程 缺点:每次都会有一个加锁以及释放锁的操作,效率低,可以通过反射破坏单例模式。
4.DCL
双检测锁
代码语言:javascript复制public class LazyMan {
private static volatile LazyMan lazyMan = null;
private LazyMan() {
}
public static LazyMan getInstatce() {
if(lazyMan == null){
synchronized (LazyMan.class){
if (lazyMan == null) {
lazyMan = new LazyMan();
}
}
}
return lazyMan;
}
}
这里引入一下解释一下DCL
双检测锁机制:
DCL
双检测锁机制: 用DCL
双检测锁机制为什么要用valoatile
修饰,因为lazyMan=new LazyMan()
, 并非是一个原子操作。事实上在JVM
中大概做了3件事。
1.给lazyMan
分配内存,
2.调用构造器来初始化成员变量
3.将lazyMan
对象指向分配的内存空间。 但是JVM
的即时编译器中存在指令重排序的优化,也就是说上面的第二步,第三步顺序是不 确定的一旦2,3,顺序乱了,这个是有一个线程调用了方法,结果虽然是非null,但是未初 始化,所以直接报错。
优点:效率高,线程安全 缺点:代码复杂,可以通过反射破坏单例
5.静态内部类
代码语言:javascript复制public class Singleton {
private Singleton() {}
private static class SingletonInstance {//私有静态内部类
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
优点:类的静态属性只有在第一次加载类的时候初始化,所以线程安全 缺点:代码变得复杂,
apk
文件增大
6. 枚举单例
代码语言:javascript复制public enum SingleTon {
SINGLE_TON;
private String field;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
}
优点:线程安全,不用担心反射破话单例模式 缺点:枚举类占用内存多
解析:object 单例类是什么类型的单例
这里我们直接将kotlin
代码转为Java 代码进行查看。
kotlin
代码如下
转为Java
之后
我们可以看到,该Model
类转为Java
代码之后,它是一个饿汉式单例。所以使用object
的类采用的是饿汉式单例。
companion object
伴生对象出现的单例是哪种类型的单例
kotlin
代码如下
class Model{
companion object{
val text = ApiWrapper("11")
}
}
class ApiWrapper (val api : String){
fun s() {
}
}
java
代码如下
public final class Model {
@NotNull
private static final ApiWrapper text = new ApiWrapper("11");
@NotNull
public static final Model.Companion Companion = new Model.Companion((DefaultConstructorMarker)null);
public static final class Companion {
@NotNull
public final ApiWrapper getText() {
return Model.text;
}
private Companion() {
}
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
可以看的出来,如果直接对text
赋值,那么就相当于是一个饿汉式加载
但是如果我们对text
进行by lazy
延迟赋值,那么会是什么样子呢。
public final class Model {
@NotNull
private static final Lazy text$delegate;
@NotNull
public static final Model.Companion Companion = new Model.Companion((DefaultConstructorMarker)null);
static {
text$delegate = LazyKt.lazy((Function0)null.INSTANCE);
}
public static final class Companion {
@NotNull
public final ApiWrapper getText() {
Lazy var1 = Model.text$delegate;
Model.Companion var2 = Model.Companion;
Object var3 = null;
return (ApiWrapper)var1.getValue();
}
private Companion() {
}
public Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
}
}
可以看出,此时变成了懒汉式同步单例
,
至于为什么是同步单例,这里需要大家去看一下LazyKt.lazy()
方法