正儿八经上班第一天,刚刚想写的什么的时候,发现同事在项目里面使用了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形式的饿汉式加载。否则使用懒汉式。