我的kotlin学习笔记(一)——对象

正儿八经上班第一天,刚刚想写的什么的时候,发现同事在项目里面使用了kotlin。真不知道是该高兴奈还是该高兴奈,总有人推着你前进。那我也开始吧。半年前看过一点,现在忘得差不多了,写的比较乱,都是边开发边学习。
————————-jjj的kotlin学习笔记————————-
1、对象

1.1、对象表达式

先回顾下java的匿名内部类:匿名内部类就是没有名字内部类,因为没有名字只能使用 一次。

// 匿名内部类:
new 类名或接口名(){
    重写方法;
}; 

// 常见实例
textView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        ...
    }
});

那kotlin中如何表达匿名内部类?
这时候对象表达式就登场了:

// 对象表达式:
object: 类名或接口名(){
    重写方法
};
// 实例:
textView.setOnClickListener(object : View.OnClickListener{
    override fun onClick(v: View?) {
        ...
    }
})

如果父类有构造函数,则必须传递相应的构造函数;
如果有多个父类,则使用逗号分隔,写在冒号后面。

var a = object : A(this), B {
    override fun testA() {
        ...
    }

    override fun testB() {
        ...
    }
}

abstract class A(ctx: Context) {
    abstract fun testA()
}
interface B {
    fun testB()
}

如果只需要一个对象,没有父类的情况。

val o = object {
    var x = 1
    var y = 2
}
var result = o.x + o.y

值得注意的是,匿名对象可以用作本地(同一方法里)和私有(private)作用域中。具体原因看链接文档

// 私有函数,其返回类型是匿名对象类型
private fun foo1() = object {
     val x: String = "x"
     val y: String = "y"
}

fun bar(){
    // 本地属性
    val foo2 = object {
        val x: String = "x"
        val y: String = "y"
    }

    val x1 = foo1().x
    val x2 = foo2.y
}
1.2、对象声明和伴生对象

由于kotlin移除了static的概念,便引入对象声明。
对象声明由object关键字+名称表示,对象声明不能用在赋值语句的右边,使用场景:

// 1.静态变量
object Constant {
    /**
     * baseUrl
     */
    val REQUEST_BASE_URL = "http://gank.io/api/"
    val ALL = "all"
    val ANDROID = "Android"
    ...
}

// 2、单例模式 
object SimpleSington {
    fun test() {}
}

到这里,先回顾下什么是单例

单例是种设计模式;使用时,单例的对象必须保证只有一个实例存在,不允许自由构造对象。因此单例的构造函数不对外开放,通常为private。

单例模式的分类:

// 1、饿汉式:
public class Singleton{  
    private static Singleton instance = new Singleton();  
    private Singleton(){}  
    public static Singleton newInstance(){  
        return instance;  
    }  
}

// 2、懒汉式:
public class Singleton{  
    private static Singleton instance = null;  
    private Singleton(){}  
    public static Singleton newInstance(){  
        if(null == instance){  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}  
// 懒汉式:加同步锁解决线程同步问题
public class Singleton{  
    private static Singleton instance = null;  
    private Singleton(){}  
    public static synchronized Singleton newInstance(){  
        if(null == instance){  
            instance = new Singleton();  
        }  
        return instance;  
    }  
}  

// 3、双重校验锁:
public class Singleton {  
    private static Singleton instance = null;  
    private Singleton(){}  
    public static Singleton getInstance() {  
        if (instance == null) {  
            synchronized (Singleton.class) {  
                if (instance == null) {//2  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}  

// 双重校验锁:防止指令重排优化
public class Singleton {  
    private static volatile Singleton instance = null;  
    private Singleton(){}  
    public static Singleton getInstance() {  
        if (instance == null) {  
            synchronized (Singleton.class) {  
                if (instance == null) {  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
}  

// 4、静态内部类
public class Singleton{  
    private static class SingletonHolder{  
        public static Singleton instance = new Singleton();  
    }  
    private Singleton(){}  
    public static Singleton newInstance(){  
        return SingletonHolder.instance;  
    }  
}  

// 5、枚举
public enum Singleton{  
    instance;  
    public void whateverMethod(){}      
}  

下面依次分析下各模式:
饿汉式:最简单的实现方式,在类加载的时候就会对实例进行创建,实例在整个程序周期都存在,这也是它的缺点:该类只要加载单例就会被创建,即使没用到,所以容易形成了内存浪费。优点是:只在类加载的时候创建一次实例,不会存在多个线程创建多个实例的情况,避免多线程同步的问题。

懒汉式:解决了饿汉式的类加载后单例创建的问题,懒汉式的单例只在需要的时候才创建,如果单例已经创建,再次调用接口不会重新创建新的对象,而是返回之前的创建对象。但是懒汉模式没有考虑线程安全问题,多个线程可能并发调用它的getInstance()方法,导致创建多个实例,这时候可以加上同步锁。

双重校验锁:懒汉式中使用synchronized修饰的同步方法比一般方法要慢很多,如果多次调用单例方法将存在性能问题。双重校验锁调整了synchronized修饰的位置,并在同步代码块外多了一层instance为空的判断。因此,大部分情况下,调用getInstance()都不会执行到同步代码块,从而提高了程序性能。但也不是万无一失的,设立涉及到java中的指令重排优化,最终结果到时候双重校验锁失效,不过jdk1.5版本后新增的volatile关键字可以解决这个尴尬问题。

总之双重校验锁既实现了延迟加载,又解决了线程并发问题,同时还解决了执行效率的问题。

静态内部类:这种加载方式利用了类加载机智保证只创建一个instanc实例。与饿汉模式一样,不存在多线程并发的问题。不一样的是,静态内部类是在内部类里面创建对象实例的,只要应用中不使用内部类,JVM就不会去加载这个单例类,也不会去创建单例对象,从而实现了懒汉式的延迟加载。

枚举:上面的四种单例模式都有两个共同的缺点:
(1)需要额外的工作来实现序列化
(2)可以使用反射强行调用私有构造函数
枚举很好的解决了这两个问题,使用枚举除了线程安全和防止反射调用构造器之外,还提供了自动序列化机制,防止序列化的时候创建新的对象。奇怪的是在实际工作中,很少有人用枚举。

————————————分割线———————————
kotlin声明单例模式,请参考原文

// 常见写法
object SimpleSington {
    fun test() {}
}

// 在Kotlin里调用
SimpleSington.test()

// 在java里调用
SimpleSington.INSTANCE.test();

// 上面的写法相类似于java中的:
public final class SimpleSington {
   public static final SimpleSington INSTANCE;

   private SimpleSington() {
      INSTANCE = (SimpleSington)this;
   }

   static {
      new SimpleSington();
   }
}

你可能会发现,原来object类型的单例模式,本质上就是饿汉式加载,即在类加载的时候创建单例。

kotlin的懒汉式加载
先了解下kotlin中伴生对象:类内部的对象声明可以用companion关键字标记,

// 1、伴生对象实例
class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}
// 调用方式
val instance = MyClass.create()

// 2、可省略伴生对象的名称
class MyClass {
    companion object {
    }
}
// 调用方式
val x = MyClass.Companion

// 懒汉式加载方式
class LazySingleton private constructor(){
    companion object {
        val instance: LazySingleton by lazy { LazySingleton() }
    }
}

懒汉式加载特征:
1. 显示声明构造方法为private;
2. companion object用来在class内部声明一个对象;
3. LazySingleton 的实例instance通过lazy来实现懒汉式加载
4. lazy默认情况下线程是安全的,可以避免多个线程同事访问生成多个实例的问题

对于实例初始化花费时间较少,并且内存占用较低的话,应该使用object形式的饿汉式加载。否则使用懒汉式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值