基础类型
在Java中有基本类型和引用类型之分,但是在Kotlin中一切都是对象
// Java
short
int
long
char
float
double
Short
Integer
// ...
// Kotlin
Double
Float
Long
Int
Short
Char
Boolean
类型转换
在Kotlin中为了兼容Java,Kotlin中的Double、Int等基本类型在编译后会转化为Java的基本数据类型double、int等,但是Kotlin的Double?、Int?等可空对象将会转化为Java的引用类型Double、Integer。
// Kotlin必须显示转换 1. 可读性强 2. 一切都是透明的可控制的
fun main(){
val intValue = 666
val longValue : Long = intValue.toLong()
// Char类型
val c = 'A'
val code: Int = c.toInt()
println(code) // 65
}
/** 反编译后的代码
public final class Simple08Kt {
public static final void main() {
int intValue = 666;
long longValue = (long)intValue;
char c = 65;
System.out.println(c);
}
// $FF: synthetic method
public static void main(String[] var0) {
main();
}
}
*/
类和构造函数
在学习类和构造函数时我们可以将Kotlin和Java对比起来学习这样学习效率会更高,Java中一般一个文件定义一个类,类名与文件名一致,如果同一个文件中有多个类,那么只能有一个类时public。我们都知道Java是一门面向对象语言,在面向对象设计中需要考虑一个基本问题:“如何把变动的事物与保持不变的事物区分开来”。为了解决这一问题,Java提供了访问权限修饰词,为程序员指明被修饰关键词,哪些是可以访问的哪些是不能访问的。访问权限控制的等级,从最大权限到最小权限一次为:public
、protected
、默认(没有关键词)、private
public | protected | 默认 | private | |
---|---|---|---|---|
当前类 | √ | √ | √ | √ |
子类 | √ | √ | √ | × |
同一个包下 | √ | × | √ | × |
外部包或外部模块 | √ | × | × | × |
总结:被public
修饰的关键词任意位置都可以访问;被protected修饰的关键词只有当前类、⼦类、同⼀个包中可以访问,外部包或者外部模块⽆法访问;没有修饰词修饰的关键词,只有当前类、⼦类、同⼀个包中可以访问,外部包或者外部模块⽆法访问;被private修饰的关键词,当前类可以访问其他位置都⽆法访问。
对象的创建
Animal a1 = new Animal()
当我们用上面Java语句创建一个对象时会发生什么。
首先Jvm
虚拟机遇到new指令时,会在常量池中定位到一个类的符号引用,并且检查这个符号引用所代表的类是否被加载,如果没有被加载就需要进行类加载过程,类加载器将当前这个类的字节码文件加载到内存中。
接下来虚拟机为该新生对象分配内存,虚拟机会将分配的内存空间都初始化为零值(除了对象头)。我们都知道如果类的某个成员是基本数据类型,即使没有进行初始化,Java也会确保它获得一个默认值(不适用于局部变量,即并非某个类的字段),便是虚拟机这时候进行初始化的。
然后,虚拟机对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的GC
分代年龄等信息。这些信息存放在对象的对象头(Object Header)之中。
最后创建的对象的地址会放在操作数栈中 的某个地址中,在当前线程栈中的⽅法栈帧的局部变量区域申请内存空间给a1
,从操作数栈中弹出顶部操作数的地址赋值给a1
。
构造函数
为什么要有构造函数?初始化和清理是涉及安全的两个问题,C++引入了构造器的概念,这是一个在创建对象时被自动调用的特殊方法。Java中也采用的构造器,使用构造器能确保初始化每个对象,此外Java还提供了"垃圾回收器",对于不在使用的内存资源,垃圾回收器能自动将其释放。
class Person {
Person(){
System.out.print("Person ");
}
}
public class Sample01 {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Person();
}
}
}
/** Output:
* Person Person Person Person Person Person Person Person Person Person
*/
上面是一个带有构造器的简单类,在Java中,构造器的名称必须与类名完全相同,没有参数的构造函数被称为默认构造函数,如果在定义类的时候没有写构造函数,编译器在编译的时候会自动生成无参构造函数。当一个构造参数无法满足需求时,还可以重载多个不同参数构造函数。
// 1. 主构造函数
//创建⼀个没有主体的类
class Person
//显示指定构造⽅法
class Person2 constructor()
//主构造函数需要传递⼀个字符串对象 constructor不能省略
class Person3 constructor(var name:String){
init {
println("First initializer block that prints ${name}")
}
}
//有修饰符限定构造函数权限时 constructor不能省略
class Person4 private constructor()
//如果有⽗类 必须直接调⽤⽗类的构造函数
open class Father // open 关键词,kotlin中的类默认是private修饰的无法被继承,如需继承则添加open关键字在类前面
class Person5 : Father()
//如果⽗类有参数 ⼦类必须提供相应有参的构造函数
open class Father2 constructor(name: String)
class Person6 constructor(name: String) : Father2(name)
// 2. 此构造函数
// 注意:a.次构造函数必须直接调⽤主构造函数
// b.如果有⽗类,必须调⽤⽗类的构造函数
//次构造函数必须继承主构造函数
class Person7(name: String) {
constructor(name: String, age: Int):this(name)
}
open class Father3 constructor(name: String)
class Person8 constructor(name: String) : Father3(name){
constructor(name: String, age: Int):this(name)
}
上面是Kotlin中带有构造函数的实例,Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。主构造函数可以用关键字constructor显示的指定,也可以跟在类名(与可选类型参数)后。主构造函数不能包含任何的代码。初始化的代码可以放到以 init{} 关键字作为前缀的初始化块(initializer blocks)中。
如果类有一个主构造函数,每个次构造函数需要委托给主构造函数,可以直接委托或者通过别的次构造函数间接委托。委托到同一个类的另一个构造函数用 this() 关键字即可。
实践应用
如何巧妙使⽤private来私有化构造⽅法,给外部提供⼀个单例对象(整个程序运⾏中这个类只有⼀个对象)
- 首先私有化构造函数,外部⽆法创建这个类的实例
- 提供⼀个静态的成员属性,这个静态变量只会初始化⼀次
- 给外部提供⼀个静态⽅法,外部可以通过这个方法获取该对象,但是需要注意:我们这⾥只是一个简单的单例没考虑内存消耗和线程安全
- 最后外部获取这个对象
class Person9 private constructor(){
companion object{
private val defaultInstance = Person9()
fun getInstance():Person9{
return defaultInstance
}
}
}