变量和常量

变量

声明、赋值变量

fun main(args: Array<String>) {
    // 声明变量并初始化值
    var a: Int = 10

    // 声明变量不初始化值
    var b: Int
    // 未初始化的变量不能使用,也没有默认值
    // println(b)
    
    b = 100
    println(b)
}

一次声明多个变量

不支持,最多可以将多个单挑声明拼接到一行

var d: Int = 4; var e: Int = 5; var f: Int = 6

声明只读变量

只读变量仍然是变量,不是常量,他只是只读的,并且只读变量只能赋值一次:

val d = 100

val e : Int
e = 200
// e = 300 // 只读变量只能赋值一次,第二次会报错

匿名变量

kotlin中没有匿名变量

常量

声明并初始化常量

使用const关键字修饰只读变量,就可以声明一个常量,常量在声明时就要初始化值:

const val PI = 3.1415926

fun main(args: Array<String>) {
    // const val f = "fff" // 报错,不能声明在方法中
}

常量的类型要求

Kotlin的常量是编译时常量,在编译时就要确定值,所以:

  1. 他的类型只能为 Char、String、Float、Double、Int、Long、Byte、Short、Boolean

  2. 不能在函数内定义,这时候方法栈还不存在

  3. 编译后的字节码,每个常量就是一个Java public static final 常量

常量生成器

Kotlin 没有内置的常量生成器。使用常量时,通常定义一个常量类,然后引用此类使用某个常量:

fun main(args: Array<String>) {
    println(MyConstants.PI)
}

class MyConstants {
    companion object {
        const val PI = 3.1415926
        const val E = 2.71828
        const val GOLDEN_RATIO = 1.6180339887
    }
}

虽然没有常量生成器,但是可以通过只读变量+函数初始化来作为常量以及常量生成器:

val myConstant = generateConstant()

fun generateConstant(): Double {
    // 这里可以使用任意复杂的逻辑生成常量值
    return 42.0
}

变量和常量的命令规则

  1. 变量和常量的名称应该具有描述性,并且易于理解。

  2. 名称应该以字母或下划线 _ 开头,不能以数字开头。

  3. 名称可以包含字母、数字和下划线 _

  4. 名称应该使用骆驼命名法(camelCase),即单词之间的首字母大写,除了第一个单词。

  5. 名称应该尽可能使用英文单词,避免使用缩写或缩写词。

  6. 名称应该尽量避免使用单个字符作为变量名,除非该变量用途非常明显。

  7. 在 Kotlin 中,使用 val 声明的变量是不可变的常量,应该使用大写字母和下划线来命名。

类型推断

同样的,Kotlin也支持类型推断,在定义变量或者常量时,可以省略类型:

var a = 100 // 自动推断a为Int类型

变量与常量的存储位置

变量和常量的存储位置,取决于作用域以及声明方式:

  1. 对于在函数或局部代码块中声明的变量或常量,它们的存储位置通常在栈内存中。当函数或代码块执行结束时,它们的空间就会被自动回收。

  2. 对于在类中声明的变量或常量,它们的存储位置通常在堆内存中。它们的生命周期与对象的生命周期相同,即在对象销毁时才会被回收。

  3. 如果变量或常量是在顶层声明的,它们的存储位置可能会有所不同:

    1. 在Kotlin/JVM中,顶层变量和常量的存储位置通常是作为该类的静态变量或常量存储在堆内存中。

    2. 而在Kotlin/Native中,顶层变量和常量的存储位置通常是作为该模块的全局变量或常量存储在静态存储区中。

顶层变量是指在Kotlin文件的顶层声明的变量,它们不属于任何类或对象,可以在文件中的任何位置直接使用。不需要代码块包裹,直接生命在文件中的变量就是顶层变量。

变量逃逸

如果一个变量在它的声明作用域之外仍然被引用,那么这个变量就被认为是逃逸的。逃逸变量通常会导致性能问题和内存泄漏,因为它们需要在堆内存中分配空间,而不是在栈内存中分配;也就是说,变量从栈空间逃逸到了堆空间。在许多情况下,避免变量逃逸可以提高程序的性能和减少内存使用。

下面是常见的变量逃逸行为:

  1. 将一个变量传递给另一个函数,而该变量在函数之外声明

    fun foo(list: List<Int>) {
       // ...
    }
    
    val myList = listOf(1, 2, 3)
    foo(myList) // myList 变量逃逸
  2. 将一个变量存储在类的成员变量中,而该变量在方法中初始化

    class MyClass {
       var myVar: String? = null
    
       fun foo() {
          myVar = "Hello, world!" // myVar 变量逃逸
       }
    }
  3. 使用 lambda 表达式或匿名函数,从而使变量逃逸

    fun main() {
       val myList = listOf(1, 2, 3)
    
       myList.forEach { item ->
          println(item) // item 变量逃逸
       }
    }

为了避免变量逃逸,可以采取以下措施:

  1. 尽可能使用不可变变量(使用 val 关键字),这样可以减少变量逃逸的可能性。

  2. 将变量声明为局部变量,而不是成员变量。

  3. 避免将变量传递给其他函数或存储在对象的成员变量中。

  4. 避免在 lambda 表达式或匿名函数中使用变量。如果必须使用变量,可以将其声明为局部变量,并将其作为参数传递给 lambda 表达式。

最后更新于