fun main(args:Array<String>){
println("hello world");
}
fun:函数的声明,固定写法
main:程序的入口
args:接受的参数,数据类型字符串
变量的声明
var name="张三" //变量声明关键词var,语句末尾不需要分号
数据类型
类型 | 范围 |
---|---|
Byte | 整数-128~127 |
Short | 整数-32768~32767 |
Int | 整数-2147483648~2147483647 |
Long | 整数-9223372036854775807~9223372036854775806 |
Float | 小数,小数点可以精确到6位 |
Double | 小数,小数点可以精确到15-16位 |
String | 字符串,用双引号引起来的字符串都可以存 |
类型推断
在声明一个变量时,我们可以不需要显示声明数据类型,kotlin会根据你为变量赋的值动态地推导出其类型
当一个变量被赋予了某个类型的数值之后,不能再赋给他其他类型的数值,否则,会报类型错误
fun main(args:Array<String>){
var a=10 //声明变量a , 将int类型的10 赋值给a, a 将只能接受int型数据
a=15
//a="字符串" //将字符串类型的数据赋值给int型的a ,报错!!
var s="字符串"
}
显示类型声明
格式:var变量名:变量类型=值
var a:Int=10
变量声明的注意事项
var a:Int
常量的声明
关键字:val
格式:val 常量名:常量类型=常量值
获取某种类型数据的最大最小值
fun main(args:Array<String>){
val minByte:Byte=Byte.MIN_VALUE
val maxByte:Byte=Byte.MAX_VALUE
val a:Int=0b0011 //将二进制的0b0011 转换为10进制,并复制给a
}
函数声明基本格式:fun 函数名(参数:参数类型){函数体}
类似于Java中的switch,基本使用格式:
when(变量){
分支A -> 表达式
else -> 表达式
}
带有返回值的When表达式
var result=when(变量){
分支A -> 表达式(要有返回值,最终将值赋给result)
else -> 表达式(要有返回值,最终将值赋给result)
}
声明一个区间数组
var nums1=1..100 //表示我们声明了一个闭区间数组,其中包含的数值为 1-100。 .. 表示闭区间
var nums2=1 util 100 //前闭后开区间,取值 1-99. util 表示前闭后开区间
for基本循环格式
for(变量 in 数组或字符串){
//DO STH
}
带有步进的for循环
for (变量 in 数组 step 步进数量){ //所谓步进,就是递增幅度。默认步进为1
//DO STH
}
数组.reversed() //数组内容反转
数组.count() //获取数组的容量,等价于Java中的数组.length
List
List的基本声明格式:
var list1=listOf(元素1,元素2,元素3) //声明List时主要是通过 listOf()实现
使用for循环同时遍历索引和索引对应的数值
for((index,value) in list.withIndex()){ //重点是 withIndex() 方法,index 接收索引,value 接收对应的值
//DO STH
}
Map(词典)
基本声明格式
var map=TreeMap<键类型,值类型>()
map[key]=value
示例代码
var map=TreeMap<String,String>() //声明 map
map["好"]=good
map["学习"]=study //添加键值对元素
println(map["好"]) //取值并打印
函数的简化
//原函数:
fun sum(a:Int , b:Int):Int{
return a+b
}
//简化后
fun sum(a:Int , b:Int):Int=a+b
使用var声明函数–函数表达式1
kotlin 中除了使用基本的 fun 关键字声明函数外,还可以使用 var 声明。示例如下:
var i={x:Int , y:Int -> x+y} //声明函数i,接收两个Int类型参数 x、y,返回 x+y 的值
i(3,5) //调用使用 var 声明的函数 i
使用var声明函数–函数表达式2
var j:(Int,Int)->Int={x,y -> x+y} //声明函数j,它接收的参数是两个Int, 返回一个Int,对应的表达式是 {x,y->x+y}
j(4,4) //调用函数
具有默认参数值的函数声明
val Pi=3.1415926
fun getRoundArea(PI:Float=Pi , radius:Float):Float{ //为变量PI赋予了默认值 Pi,这样,调用该方法时可以不再传递PI。但,如果我们想传入的值和默认值不一致时还是需要传入的
return PI*radius*radius
}
调用带有默认参数值的函数
var a=getRoundArea(3.14f,5.0f) //因为我们相传入的PI和默认值不一致,所以,需要将3.14f传入
具名参数的使用
所谓具名参数,就是调用某个方法时指明传入的参数是给哪个变量的
var a=getRoundArea(radius=5.0f) //我们需要的PI值与默认值一致,此时不需要再传入PI值。只需要通过 radius=5.0f 声明我们传入了半径值
var a="13"
a.toInt() //字符串转换为Int
var b=13
b.toString() //Int转换为字符串
try{
//可能会出错的代码块
}catch(e:Excepiton){
//出错之后的处理逻辑
}
利用递归实现阶乘函数
fun fact(a:Int):Int{
if(a==1){
return 1
}else{
return a*fact(a-1) //函数内调用函数本身就成为了递归
}
}
BigInteger
在上面的方法中,我们用Int来接收阶乘的值,但如果超出了Int的范围,就会返回0.所以这时候就需要用到BitInteger。BitInteger用来表示一个超大值。
import java.math.BigInteger
fun main(array: Array<String>) {
val num = BigInteger("50") //声明BigInteger常量时传入一个字符串类型的数值
println(fact(num))
}
fun fact(num: BigInteger): BigInteger {
return if (num == BigInteger.ONE) {
BigInteger.ONE
} else {
num * fact(num - BigInteger.ONE)
}
}
什么是尾递归
为什么需要尾递归优化
递归非常耗费内存,因为需要同时保存成千上百个调用记录,很容易发生“栈溢出”错误(stack overflow)。但对于尾递归来说,由于只存在一个调用记录,所以永远不会发生栈溢出错误。
使用尾递归实现累加
fun main(args: Array<String>) {
println(accumulation(5, 1))
}
/**
* tailrec 是尾递归函数的关键字
* 尾递归函数是指,在该函数的最后一步操作中依旧是调用函数本身
* 为了实现尾递归,我们定义了该方法接收两个参数:num 是我们传入的需要计算累加值得的变量,total用来接收最终的返回值
*/
tailrec fun accumulation(num: Int, total: Int): Int {
return if (num == 1) {
total
} else {
accumulation(num - 1, num + total) //此时,该调用的含义是:先计算 total=num+total,然后计算 num=num-1
}
}
//定义一个类,包含两个成员变量 height和width
class Rect(var height:Int,var width:Int)
fun main(args: Array<String>) {
var rect=Rect(5,10) //构建Rect对象,不需要new
println("矩形的宽${rect.width}高${rect.height}") //引用Rect类中成员变量
}
//定义一个类,包含两个成员变量 height和width.并定义一个成员方法
class Rect(var height: Int, var width: Int) {
fun getArea(a: Int, b: Int): Int = a * b
}
fun main(args: Array<String>) {
var rect = Rect(5, 10) //构建Rect对象,不需要new
println("矩形的宽${rect.width}高${rect.height}") //引用Rect类中成员变量
println("矩形的面积是${rect.getArea(rect.width, rect.height)}") //引用Rect类中成员变量
}
class 子类:父类()
父类:
open class Father { //用 open 修饰,允许被继承
var character = "性格内向"
open fun action() { //用open修饰,允许被重写
println("喜欢读书")
}
}
子类:
class Son : Father() { //继承。 Son 继承自 Father
override fun action() { //重写父类方法
//super.action()
println("儿子的性格是$character")
println("儿子不喜欢看书,但是喜欢唱歌")
}
}
抽象类Human:
abstract class Human (var name: String){ //定义抽象类,使用 abstract 修饰。包含成员变量name
abstract fun eat() //定义抽象方法, 使用 abstract 修饰
}
抽象类的子类Man:
class Man(name: String) : Human(name) { //继承自Human抽象类
override fun eat() { //必须重写抽象方法
println("${name}是男人,是家中劳力,所以吃的多")
}
}
调用子类:
fun main(args: Array<String>) {
var man=Man("张三")
man.eat()
}
定义接口IMan
interface IMan { //定义一个男人的接口
fun xiaodidi()
}
Man类实现IMan接口
class Man(name: String) : Human(name) ,IMan{ //男人属于人,所以继承Human;男人有小弟弟,所以实现 IMan接口
override fun xiaodidi() {
println("这是重写IMan接口中的方法——男人有小弟弟")
}
override fun pee() {
println("${name}是男人,是站着尿尿的")
}
override fun eat() {
println("${name}是男人,是家中劳力,所以吃的多")
}
}
场景说明
围裙妈妈只负责做饭,不负责洗碗
小头爸爸洗一次碗可以赚到10元
大头儿子洗一次碗可以赚到1元
小头爸爸承揽了洗碗的活,最终交给大头儿子做,中间赚了9元差价
代码实现–完全委托
定义洗碗的接口
interface IWashBow { //定义一个洗碗接口,包含一个洗碗方法
fun washBow()
}
大头儿子实现接口
class BigHeadSon:IWashBow { //被实现的接口后面不需要加()
override fun washBow() {
println("我是大头儿子,每次洗碗赚1元钱")
}
}
小头爸爸实现接口并委托时间给小头儿子
class SmallHeadFather:IWashBow by BigHeadSon(){ //委托关键字 by;被委托方(即代理方)如果不是单例类,则后面需要跟()
}
fun main(args: Array<String>) {
var father=SmallHeadFather()
father.washBow() //小头爸爸已经将洗碗的操作委托为小头儿子了,所以,此处本质是调用的小头儿子的洗碗操作
}
单例关键字:object
我们在定义一个类时,使用object替换class来修饰这个类,就表示这是一个单例类
单例类作为代理人时,不需要()
场景说明
小头爸爸为了增进父子感情,想和小头儿子一起洗碗
代码实现
小头爸爸重写接口方法,未使用单例时的错误写法
class SmallHeadFather:IWashBow by BigHeadSon(){
override fun washBow() {
println("我是小头爸爸,我把洗碗事件委托给了大头儿子")
BigHeadSon().washBow() //委托方重写了事件之后,需要手动调用代理方的方法。但是,此处又通过()构建了一个小头儿子对象,已经不再是我们初始委托的那个大头儿子了。所以,此处是有问题的。
println("我是小头爸爸,大头儿子洗完碗之后,我赚了9元")
}
}
使用单例后的正确写法
大头儿子单例类:
object BigHeadSon:IWashBow { //单例关键字object,声明为单例类之后会立即在内存中创建单例对象,并一直存在
override fun washBow() {
println("我是大头儿子,每次洗碗赚1元钱")
}
}
小头爸爸委托事件给单例的大头儿子:
class SmallHeadFather:IWashBow by BigHeadSon{ //被委托方(即代理方)是单例类,不需要通过()构建对象
override fun washBow() {
println("我是小头爸爸,虽然我把洗碗事件委托给了小头儿子,但是我要和他一起洗碗")
BigHeadSon.washBow() //委托方重写了事件之后,需要手动调用代理方的方法。由于 BigHeadSon是单例的,所以,这还是我们之前委托的那个儿子
println("我是小头爸爸,我和小头儿子洗完碗之后,我赚了9元")
}
}
外部调用
fun main(args: Array<String>) {
var father=SmallHeadFather()
father.washBow() //小头爸爸已经将洗碗的操作委托为小头儿子了,但因为重写了洗完事件,所以,本子是调用的父亲的洗完事件,父亲的洗完事件中有一部分是自己做的,另一部分是儿子做的
}
枚举示例代码:
enum class Week { //枚举关键字 enum
星期一, 星期二, 星期三, 星期四, 星期五, 星期六, 星期天
}
fun main(args: Array<String>) {
println(Week.星期一)
println("${Week.星期一}在Week中的索引是${Week.星期一.ordinal}")
}
印章类的特点
印章类与枚举的区别
示例代码
场景说明
假设你家有一头公驴、一头母驴、一头公马。那么,
它们可能会生出一头小驴,
也可能会生出一头小骡子。
代码示例
在上述场景中,由于他们能生出的儿子类型时固定的,所以,我们可以使用印章类来标识。
声明印章类
sealed class Son { //使用 sealed 声明 Son 为印章类/密封类
class SmallMule() : Son() //声明小骡子 SmallMule 为 Son的子类。
class SmallDonkey() : Son() //声明小驴子 SmallDonkey 为 Son的子类
fun sayHello(son: Son) {
if (son is SmallMule) { //判断是不是XX的实例的关键字 is
println("小骡子对大家说大家好")
} else if (son is SmallDonkey) {
println("小驴子对大家说大家好")
}
}
}
调用印章类
fun main(args: Array<String>) {
var mule = Son.SmallMule()
var donkey = Son.SmallDonkey()
var list = listOf<Son>(mule, donkey)
for (son in list) {
son.sayHello(son)
}
}