1、面向对象
/**
* 任何复杂的数据类型都是由基本数据类型组成的
* 面向对象最重要的特点:用基本的数据类型描述复杂的事物
*
* 1.定义对象用class关键字
* 2.对象一般都有静态属性和动态行为,但不是必须具备
*/
class Rect {}
class Boy {
//静态属性
var name: String = "张三"
var age: Int = 20
}
class Girl {
//动态行为
fun greeting() {
println("你好!")
}
}
class People {
//静态属性
var name: String = "王五"
var age: Int = 20
//动态行为
fun greeting() {
println("你好!")
}
}
fun main() {
val rect = Rect()
val boy = Boy()
val girl = Girl()
val people = People()
println(people.name)
println(people.age)
people.greeting()
}
2、属性的get和set
/**
* 1、通过反编译成java代码,发现kotlin的字段都是私有的,并且会自动生成get和set方法
*/
class Person0 {
var name = "张三"
var age = 18
}
/**
* 2、修改get和set的可见性
*/
class Person1 {
var name = "张三"
private set//把name的set方法私有化,即name只能访问不能修改
var age = 18
val sex = true
}
/**
* 3、自定义get和set访问器
* 注:这里要注意 field 字段的使用,直接赋值或者获取值其实是调用的set和get方法,因而会进入无限递归调用
*/
class Person2 {
var name = "张三"
var age = 18
set(value) {
if (value in 0..150) {
//age = value //这里直接赋值其实是调用的set方法,所以会进入无限递归调用自己
field = value
} else {
field = 0
}
//field = if (value in 0..150) value else 0 //if表达式
}
}
fun main() {
val person0 = Person0()
println("name=${person0.name} age=${person0.age}") //name=张三 age=18
val person1 = Person1()
//person1.name = "李四" //编译报错,name的set方法私有化了,故不能修改其值
//person1.sex = false //sex为不可变变量,故不能修改,但是可以获取
val sex = person1.sex
val person2 = Person2()
person2.age = 200
println("age=${person2.age}") //age=0,赋值超过了0-100,故设置为0
}
3、构造函数
/**
* 1、主构造函数
* (1)定义方式:直接跟在类名后面
* (2)实现代码:主要构造函数的实现是在 init 关键字包含的代码块中
*/
class News0(title: String, content: String) {
var title = ""
var content = ""
//主构造函数的实现部分
init {
this.title = title
this.content = content
}
}
/**
* 2、主构造函数参数使用 var 和 val
* (1)构造函数参数使用 var 或者 val 则会自动生成相应的成员变量
* (2)使用 val 时只能在构造函数里面赋值,后面不能再修改了
*/
class News1(var title: String, var content: String)
class News2(var title: String, val content: String)
/**
* 3、次构造函数
* (1)次构造函数使用 constructor 在类里面声明
* (2)次构造函数后面可以使用 this 关键字调用其他次构造函数或主构造函数
*/
class News3(var title: String, val content: String) {
var author = ""
constructor(title: String, content: String, author: String) : this(title, content) {
this.author = author
}
}
/**
* 4、构造函数执行顺序:先执行主构函数的 init,再执行次构函数
*/
class News4(title: String, content: String) {
var title = ""
var content = ""
var author = ""
var date = ""
//构造函数的实现部分
init {
this.title = title
this.content = content
println("init执行!")
}
constructor(title: String, content: String, author: String) : this(title, content) {
this.author = author
println("次构造函数执行!")
}
constructor(title: String, content: String, author: String, date: String) : this(
title,
content,
author
) {
this.date = date
println("次构造函数2执行!")
}
}
fun main() {
val news0 = News0("王者荣耀", "S18赛季更新。")
println("news0 [title=${news0.title}, content=${news0.content}]")
//news0 [title=王者荣耀, content=S18赛季更新]
val news1 = News1("QQ飞车", "11城来赛一场啊!")
println("news1 [title=${news1.title}, content=${news1.content}]")
//news1 [title=QQ飞车, content=11城来赛一场啊!]
val news2 = News2("QQ飞车", "11城来赛一场啊!")
news2.title = "泡泡卡丁车" //编译通过,var变量可以修改
//news2.content = "中国龙赛场战一场!" //编译失败,val变量不能修改
val news3 = News3("欢乐斗地主", "来血战到底啊!", "腾讯")
println("news3 [title=${news3.title}, content=${news3.content}, author=${news3.author}]")
//news3 [title=欢乐斗地主, content=来血战到底啊!, author=腾讯]
val news4 = News4("皮皮麻将", "刮风、下雨、碰碰碰!", "腾讯")
println("news4 [title=${news4.title}, content=${news4.content}, author=${news4.author}]")
//init执行!
//次构造函数执行!
//news4 [title=皮皮麻将, content=刮风、下雨、碰碰碰!, author=腾讯]
val news5 = News4("皮皮麻将", "刮风、下雨、碰碰碰!", "腾讯", "2020-12-20")
println("news5 [title=${news5.title}, content=${news5.content}, author=${news5.author}, date=${news5.date}]")
//init执行!
//次构造函数执行!
//次构造函数2执行!
//news5 [title=皮皮麻将, content=刮风、下雨、碰碰碰!, author=腾讯]
}
4、封装
/**
* 所谓封装就是隐藏内部实现的细节,暴露一些安全的接口给外部;
* 与 Java 一样 kotlin 也是通过 private 关键字来隐藏内部的实现细节
*/
class WashMachine(val brand: String, val capacity: Float) {
// 这里的是否关门就是封装,人们不能直接来修改这个属性
// 只能通过洗衣机提供的 openDoor() 和 closeDoor() 来控制
private var isOpenDoor: Boolean = false
fun openDoor() {
isOpenDoor = true
println("洗衣机门打开了")
}
fun closeDoor() {
isOpenDoor = false
println("洗衣机门关闭了")
}
fun startWash() {
if (isOpenDoor){
println("警告:没有关门,不能开始洗衣服!")
} else {
println("放水 -> 开始洗衣服 -> 脱水烘干 -> 衣服洗好了")
}
}
}
fun main() {
val washMachine = WashMachine("海尔", 12.0f)
washMachine.openDoor() //洗衣机门打开了
println("放衣服和臭袜子,倒入洗衣液")
washMachine.startWash() //警告:没有关门,不能开始洗衣服!
washMachine.closeDoor() //洗衣机门关闭了
washMachine.startWash() //放水 -> 开始洗衣服 -> 脱水烘干 -> 衣服洗好了
}
5、继承
/**
* 1、类的继承
* (1)kotlin 的 class 默认都是 final 类型的,不能被继承
* (2)只有被 open 关键字修饰的类才可以被继承(关键字 open 的作用与 final 相反)
* (3)kotlin 的 class 只能单继承
*/
open class Parent0
class Child0 : Parent0()
/**
* 2、初始化父类的构造函数
* (1)子类有主构造函数,则直接在主构造函数后面初始化父类的构造函数
* (2)子类没有主构造函数,则需要使用 super 关键字或者调用其他的次构造函数来初始化父类的构造函数
*/
open class Parent1 {
constructor()
constructor(name: String)
constructor(name: String, age: Int) : this(name)
}
class Child1_0 : Parent1() //class未定义构造函数时会有一个默认的构造函数,此处为Child1_0()
class Child1_1(name: String) : Parent1(name)
class Child1_2(name: String, age: Int) : Parent1(name, age)
class Child1_3 : Parent1 {
constructor() : super()
constructor(name: String) : super(name)
constructor(name: String, age: Int) : super(name, age)
constructor(name: String, age: Int, sex: Boolean) : this(name, age)
}
/**
* 3、方法的重写
* (1)kotlin 的 class 中的方法默认也是 final 类型,不能被重写
* (2)只有被 open 关键字修饰的方法才能被重写
* (3)重写父类中 open 修饰的方法,需在子类的方法前面加 override 关键字
* (4)若在父类中方法 override 前再加上 final 后,子类中的该方法是不能被重写的
*/
open class Parent2 {
open fun todo() {
println("Parent2的todo方法。")
}
}
open class Child2_0 : Parent2() {
final override fun todo() {
super.todo()
println("Child20的todo方法。")
}
}
class Child2_1 : Child2_0() {
//编译报错,final 方法不能被重写
/*override fun todo() {
super.todo()
}*/
}
/**
* 4、属性的重写
* (1)只有被 open 关键字修饰的属性才能被重写
* (2)var 属性只能被 var 属性重写
* (3)val 既可以被 var 属性重写,也可以被val属性重写
* (4)本质上,val 相当于 get 方法,var 相当于 get 和 set 方法
*/
open class Parent3{
open var name: String = "Parent3"
open val nickName: String = "Parent3_nick"
open val age: Int = 0
var sex:Boolean = false
}
class Child3_0:Parent3(){
override var name: String = "Child3_0"
//override val name: String = "" //编译失败,var 不能重写成 val
override val nickName: String = "Child3_0_nick"
override var age: Int = 10
//override var sex: Boolean = true //编译失败,非 open 属性不能重写
}
6、抽象类
/**
* 1、抽象类的定义和特点
* (1)抽象类反映的是事物的本质
* (2)使用 abstract 表示抽象类、抽象属性和抽象方法
* (3)抽象类不能被实例化,即不能创建抽象类的对象
* (4)抽象类可以有普通的方法和属性
* (5)抽象类、抽象属性和抽象方法默认都是 open 的
*/
abstract class Human {
abstract var color: String //抽象属性
abstract var language: String //抽象属性
open val sleep: Boolean = true //普通属性
abstract fun eat() //抽象方法
fun walk() { //普通方法
println("人类都是用双脚走路!")
}
}
/**
* 2、抽象类的继承
* (1)抽象类只能单继承
* (2)抽象类也可以继承抽象类
* (3)继承一个抽象类必须实现其所有的抽象方法和属性,否则子类也必须声明为抽象类
*/
class ZhHuman : Human() {
override var color: String = "黄皮肤"
override var language: String = "中文"
override fun eat() {
println("中国人用筷子吃饭!")
}
}
abstract class UsHuman : Human() {
//美国人的肤色很多,这里也不能确定,所以不实现 color,将类声明为抽象类
//override var color: String = "白皮肤"
override var language: String = "英语"
override fun eat() {
println("美国人用刀叉吃饭!")
}
}
fun main() {
val zhHuman = ZhHuman()
println(zhHuman.color) //黄皮肤
println(zhHuman.language) //中文
zhHuman.eat() //中国人用筷子吃饭!
zhHuman.walk() //人类都是用双脚走路!
//val usHuman = UsHuman() //编译错误,抽象类不能实例化
}
7、接口
/**
* 1、接口的定义和特点
* (1)接口反映的是事物的能力
* (2)使用 interface 来定义接口
* (3)接口不能实例化,即不能创建接口对象
* (4)kotlin 接口里面的字段不能实现(java 里面可以实现)
* (5)kotlin 接口里面的方法可以实现(java 里面不能实现)
*/
//骑自行车
interface RideBike {
fun ride()
}
//开车
interface DriveCar {
var license: String //驾照号码
fun drive() {
println("挂挡 -> 踩油门 -> 走你")
}
}
/**
* 2、接口的实现
* (1)接口也可以继承接口,添加新的能力
* (2)一个类可以实现多个接口,用逗号隔开
* (3)实现接口必须实现接口定义的所有未实现的方法和属性
*/
//开船
interface Sail : DriveCar {
fun course() //航向
}
class XiaoMing : RideBike, Sail {
//小明的驾照号码是123456789
override var license: String = "123456789"
override fun ride() {
println("小明学会了骑自行车!")
}
override fun course() {
println("一路向北!")
}
}
fun main() {
val xiaoMing = XiaoMing()
xiaoMing.ride() //小明学会了骑自行车!
xiaoMing.drive() //挂挡 -> 踩油门 -> 走你
xiaoMing.course() //一路向北!
println(xiaoMing.license) //123456789
}
8、多态
/**
* 1.多态
* (1)多态即同一类对象对同一种功能具有不同的表现形式
* (2)方法重写:指向父类的对象调用此方法,其实执行的是子类重写的方法
* (2)方法重载:由于父类没有此方法,故指向父类的对象不能调用此方法
*/
open class Animal {
open fun call() {
println("动物叫声")
}
}
class Cat : Animal() {
override fun call() {
println("猫喵喵叫")
}
}
class Duck : Animal() {
fun call(isBig: Boolean) {
println("鸭子呱呱叫")
}
}
fun main() {
val animal1: Animal = Cat()
val animal2: Animal = Duck()
animal1.call() //执行的是子类重写的方法,输出:猫喵喵叫
animal2.call() //子类没有重写,执行的是父类的方法,输出:动物叫声
//animal2.call(true) //编译失败,Animal没有这个方法,故不能调用
}
9、智能类型转换
/**
* 1、is 关键字:用来判断某个对象是否是某个具体的类型
* 2、as 关键字:用来将某个对象强转成某个类型,如果类型转换错误会抛出 ClassCastException
* 3、智能类型转换:用is判断之后就不需要再用 as 显示转换,编译器会自动转换
*/
open class Canine
class ShepHerdDog : Canine() {
fun herdShep() {
println("牧羊犬放羊")
}
}
class RuralDog : Canine() {
fun watchDoor() {
println("中华田园犬看家")
}
}
fun main() {
val canine1: Canine = ShepHerdDog()
val canine2: Canine = RuralDog()
if(canine1 is ShepHerdDog){
//is判断了,就不需要再用as显示转换
//val shepHerdDog = canine1 as ShepHerdDog
canine1.herdShep() //牧羊犬放羊
}
if(canine2 is RuralDog){
canine2.watchDoor() //中华田园犬看家
}
//如果类型转换错误会抛出ClassCastException
val shepHerdDog = canine1 as ShepHerdDog
shepHerdDog.herdShep() //牧羊犬放羊
val ruralDog = canine2 as RuralDog
ruralDog.watchDoor() //中华田园犬看家
}
10、嵌套类
/**
* 1、嵌套类
* (1)嵌套类是静态类
* (2)嵌套类和外部类没有关系,不能访问外部类的成员
* (3)嵌套类可以直接通过构造函数创建对象
*/
class OutClass {
var content = "嵌套类"
class NestedClass {
fun sayHello() {
//println("hello $content") //编译失败
println("Hello 嵌套类")
}
}
}
/**
* 2、内部类
* (1)内部类用关键字 inner 声明
* (2)内部类是普通类
* (3)可以把它看成是外部类的一个成员,它可以访问外部类的成员
* (4)内部类的对象必须要通过外部类的对象来创建
* (5)内部类访问外部类的同名成员需要使用 this@OutClass2 的形式调用
*/
class OutClass2 {
var content = "外部类"
inner class InnerClass {
var content = "内部类"
fun sayHello1() {
println("Hello $content")
}
fun sayHello2() {
println("Hello ${this@OutClass2.content}")
}
}
}
fun main() {
val nestedClass = OutClass.NestedClass()
nestedClass.sayHello() //Hello 嵌套类
val innerClass = OutClass2().InnerClass()
innerClass.sayHello1() //Hello 内部类
innerClass.sayHello2() //Hello 外部类
}