![](https://img.haomeiwen.com/i11218161/6858ef687927d78e.jpg)
目录
1. 说一下kotlin的优缺点。kotlin具有哪些优势?
2. NULL检查机制
3.kotlin的lateinit和by lazy的区别?
4.写一个java单例模式
5.说一下Kotlin的伴生对象
6.kotlin与Java互相调用有什么问题
7.构造函数有哪几种
8.类:DataClass
9.类的修饰符
10.swithch和When
11. kotlin的异常和java的异常
kotlin的注解和java的注解
1.说一下kotlin的优缺点。kotlin具有哪些优势?
优点:
![](https://img.haomeiwen.com/i11218161/2abe722ce380865e.jpg)
1).Kotlin语言简练,而且Kotlin里面可以没有空指针问题,在Android中使用Java就会存在很多令人头大的空指针问题。
2). 协程的应用,可以减少嵌套,代码写的优雅
- .简洁,代码量可以少30%,在一些语法上面,比如:viewbing--kotlin的和jetpack里面的 databinding
4).高阶函数,可以传入把方法作为参数传递
kotlin相对于Java有什么优势?函数式编程的优势?函数式和面向对象比较
Java和Kotlin的一些区别,Kotlin的缺点?
缺点:
1). 编译比较慢,自动为属性生成很多的get/set方法
2). Java自动转换成kotlin带来的问题
3). 会增加方法数量 过多的方法数量会导致编译速度变慢 .产生了很多java,中间类,导致包体积变大
基础语法讲解:
Kotlin不具备的特点:
静态成员
通配符类
非私有域
非检查型异常
原始类型
三元运算符a?b:c
![](https://img.haomeiwen.com/i11218161/b8e61f91e823aec1.jpg)
2. NULL检查机制
空安全:kotlin没有默认值,所以必须给它初始化
Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理
2.1 定义变量是否为空!
//类型后面加?表示可为空
var age: String? = "23" // 可空类型
var age: String = "23" // 不可空类型
2.2 如果一个函数的参数是不可空类型,那么不能将可空类型作为参数进行传递,
// 定义一个函数,它接受一个可空的String类型参数
fun printMessage(message: String?) {
// 使用`?.`操作符来安全地调用方法,如果message为空则不执行任何操作
message?.let {
println(it)
}
}
fun main() {
val nullableMessage: String? = "Hello, World!" // 可空类型
val nonNullMessage: String = "This message is guaranteed to be non-null" // 非空类型
// 调用函数并传入一个可空类型的参数
printMessage(nullableMessage)
// 调用函数并传入一个非空类型的参数
printMessage(nonNullMessage)
}
2.3 空安全调用
使用 ?. 操作符可以在变量为 null 时避免调用方法或访问属性,从而避免 NullPointerException。
val ages1 = age?.toInt()
等价于java的
Integer ages1;
if (age != null) {
ages1 = Integer.valueOf(age);
} else {
ages1 = null;
}
2.4 非空断言
//抛出空指针异常
val ages = age!!.toInt()
这个要少用, 否则会导致空安全没啥用
非空断言, 导致的编译不通过, 比如
解决办法可用let函数解决,比如
另外一种办法: (重要,非常常用)
//age为空返回-1
val ages2 = age?.toInt() ?: -1
?,如果是空不会报错
-1操作
nextDirective(result?.result,preDirectiveResult)
let的用法,不为空才往下执行.替代了if(result==null)
result?.let {
saveNextDirectiveConfig()
var index = nextDirectiveIndexMap.get(result)
SLogUtil.d(TAG, "nextDirective index: $index")
var directive = nextDirectiveList?.get(index!!)
directive?.preDirectiveResult=preDirectiveResult
directive?.comsume()
}
2.5 空类型转换
使用 as
关键字进行安全转换
//不做处理返回 null
val ages1 = age?.toInt()
空安全总结:
1).变量默认都是不为空的,除非用?编译的时候出查找出是否为空
2).!!说明一定不为NULL,一般都不要用!!
3).使用?,不为NULL,才使用。 可以为空值:通过加一个?号。通过看编译生成的java代码,可以看到已判空了
4).lateinit:保证后面初始化,这样lde不会检查报错
3. kotlin的lateinit和by lazy的区别? kotlin lazy使用,lazy viewmodel**
lateinit的使用:
在没有初始化前, 使用时会报错的! 一般搭配isInitialized和let使用, 比如:
原始的写法:
private lateinit var fragment: Fragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("MainActivity", "onCreate")
if (savedInstanceState != null) {
fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
}
if (fragment == null) {
fragment = SpaceFragment()
}
改进后的版本:
private lateinit var fragment: Fragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.d("MainActivity", "onCreate")
if (savedInstanceState != null) {
supportFragmentManager.findFragmentById(R.id.fragment_container1)?.let {
fragment = it // 需要用let
}
}
if (!this::fragment.isInitialized) {
fragment = SpaceFragment()
}
lateinit的原理其实是:
自动生成get和set方法
get方法会判断变量是否为空,如果为空则抛出未初始化异常,如果不为空则返回变量
set方法普通的var 变量一样
从原理分析Kotlin的延迟初始化: lateinit var和by lazy
lazy{} 只能用在val类型。by lazy可以使用于类属性或者局部变量。(后面自动初始化)
lazy有3种模式!
lateinit 只能用在var类型,能用来修饰类属性,不能用来修饰局部变量,并且只能用来修饰对象,不能用来修饰基本类型.lateinit不能用在可空的属性上
(延时初始化,最后还是要程序员初始化!)
val name: String by lazy { "sherlbon" }
lateinit var adapter: MyAdapter
定义常量与变量
类型推断:val和var
val= value, 表示值
var=variable 变量
可变变量定义:var 关键字
var <标识符> : <类型> = <初始化值>
不可变变量定义:val 关键字,只能赋值一次的变量(类似Java中final修饰的变量)
val <标识符> : <类型> = <初始化值>
延时初始化原理:by lazy本身是一种属性委托。属性委托的关键字是by。delegate
通过看kotlin编译后的代码:
生成一个该属性的附加属性:namedelegate;
当该属性被调用,即其getter方法被调用时返回name
就是延时执行set和get方法
4.写一个java单例模式(DCL),kotlin中如何实现单例?(object)object的原理是什么?线程安全吗?
kt的伴生对象是饿汉模式还是懒汉模式?
第一种:object 饿汉式 安全: 原理: 反编译后会发现,object实际是一个基于static的饿汉式的单例
第二种:lazy 懒汉式 更加模式 layz支持传LazyThreadSafetyMode,有三种可选,默认的是SYNCHRONIZED,也就是线程安全,用了cas安全, 扩展函数,委托的用法
第三种:伴生变量: 懒汉式 安全
单列的写法:Kotlin下的5种单例模式
![](https://img.haomeiwen.com/i11218161/42a0e5abcb24ab34.jpg)
伴生变量和单列的区别:
单例有很多种, 伴生变量是实现的一种!
委托: by (), 属性委托,
在Kotlin中,委托是一种设计模式,可以让一个类委托另一个类处理某些工作。这种设计模式可以让你在不修改现有类的情况下,给类添加新的行为。
首先委托的概念就是把一个对象的职责委托给另外一个对象,在kotlin中有属性的委托和类的委托。属性的委托比如by lazy,
他的作用是使用到的时候才加载简化了判空代码也节省了性能。
类的委托通常是一个接口委托一个对象interface by Class。目的是对一个类的解耦方便以后相同功能的代码复用。例子就不举例了,就是但凡开发中想到有些代码是可以复用的时候可以考虑能不能写成一个接口去交给委托类去实现。
一、接口/类委托
二、属性委托
三、延迟委托
四、观察者委托
五、Map委托
常用的是属性委托: 需要重写setter和getter方法,
private val textStr by TextDelegate()
class TextDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "我是赋值给与的文本"
}
}
private var textStr by TextDelegate()
class TextDelegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "我是赋值给与的文本"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
YYLogUtils.w("设置的值为:$value")
}
}
YYLogUtils.w("textStr:$textStr")
textStr = "abc123"
Lifecycle 通过委托机制实现其功能。具体来说,组件可以将自己的生命周期状态委托给 LifecycleOwner 对象,LifecycleOwner 对象则负责管理这些组件的生命周期。
问题: Kotlin中的object 与companion object的区别?
根据最终生成的代码, 分析得出!
共同点: 都是static final类 ,都是private构造器 , 方法都是final
不同点, object 算简单饿汉式单例,生成了实例
companion object 懒汉式, 匿名 生成java类名为 Companion ,可能也是一个类中只能有一个companion object的原因
5.说一下Kotlin的伴生对象(关键字companion使用要说出来),
java 中定义了一个静态的变量,例如 final static int A = 10 在Kotlin 中 怎样 实现
类名为默认的Companion, 也可以自定义!
静态static调用
companion object {
private const val AI_COACH_VIDEO_INFO = "ai_coach_video_info"
private const val AI_COACH_CONFIG_INFO = "ai_coach_config_info"
private const val AI_COACH_HIGH_RECORD = "ai_coach_high_record"
@JvmStatic
fun startActivity(context: Context, videoInfo: VideoInfos, configInfo: AICoachConfig.Infos, highRecord: Int) {
val intent = Intent(context, AiCoachTrainPullUpGuideActivity::class.java)
intent.putExtra(AI_COACH_VIDEO_INFO, videoInfo)
intent.putExtra(AI_COACH_CONFIG_INFO, configInfo)
intent.putExtra(AI_COACH_HIGH_RECORD, highRecord)
context.startActivity(intent)
}
}
静态方法调用:通过添加注解@JVMsstatic
Kotlin的成员变量无法被Java子类使用的问题
解决方法
abstract class Test{
@JvmField
protected var TAG: String
}
6.kotlin与Java互相调用有什么问题?Kotlin 兼容 Java 遇到的最大的 “坑”
1).首先,Kotlin本身就不存在空指针问题,如果你在Kotlin中去调用Java某个类的方法的时候,这个方法最好使用注解的方式来避免返回空指针。
2).Java自动转换成kotlin是个非常好的特性,但是也会带来问题。Javadoc原来的结构会被破坏掉,静态field和方法会转换成companion object中的普通声明,如果有Java代码调用这个类,就会出现点问题,除非你后面手动给这个转换后的companion object 添加上@JvmField和@JvmStatic。这是个不小的隐患,需要特别注意下
如果是调用静态方法,必须用类名调用。不能用单列。但是java可以!!
@JvmField 注解。
3).final这个东西。kotlin默认final。不让继承。要申请。kotlin的构造函数。
java如何转kotlin代码? kotlin如何转java代码?
把MainActivity转换成Kotlin代码
Kotlin plugin包含了一个有趣的特性,它能把Java代码转成Kotlin代码。正如任何自动化那样,结果不会很完美,但是在你第一天能够使用Kotlin语言开始编写代码之前,它还是提供了很多的帮助。
所以我们在MainActivity.java类中使用它。打开文件,然后选择Code -> Convert Java File to Kotlin File。对比它们的不同之处,可以让你更熟悉这门语言。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
7.构造函数有哪几种
构造函数:init
主构造器和次构造函数
init {
reportList=ArrayList()
}
init方法的调用时机, init{}的原理: 他会在构造函数值之前初始化
构造函数的写法:
constructor(view: View) : super(view) {
name = view.findViewById(R.id.item_title_popup_tv) as TextView
}
constructor(context: Context, list: ArrayList<String>) {
this.context = context
this.list = list
notifyDataSetChanged()
}
Kotlin中,一个类中可以有一个主构造函数和多个次构造函数。主构造函数就是声明在类头的构造函数,次构造函数必须委托主构造函数,文字太枯燥,举几个例子就一目了然了。
//这是没有省略所有关键字的类声明,当然如果没有可见性的修饰符(默认是public),可以省略看见性修饰符和constructor
class Player public constructor(val name :String){
//如果想在构造函数中定一些变量或者创建对象,可以直接在类体中定义,也可以在init{...}代码块中去定义
val id = "111"
init{
println("$id 是在初始化的代码块中")
}
fun play(){
println("$name start paly")
}
// 调用player("222").play()输出下面字符串:
// 111 是在初始化的代码块中
// 222 start paly
}
// 主构造函数和次构造函数并存
//初始化块中的代码实际上会成为主构造函数的一部分。委托给主构造函数会作为次构造函数的第一条语句,因此所有初始化块中的代码都会在次构造函数体之前执行。即使该类没有主构造函数,这种委托仍会隐式发生,并且仍会执行初始化块
class Player(val name: String) {
val id = "111"
init {
println("$id 是在初始化的代码块中")
}
constructor(gender: String, name: String) : this("123") {
println("我是 $gender 性")
}
fun play() {
println("$name start paly")
}
}
Kotlin构造函数小结:
可以有一个主构造函数和多个次构造函数
可以只有主构造函数和次构造函数
主、次构造函数同时存在的时候,次构造函数必须直接或者间接的委托到住构造函数
没有声明主构构造函数,会自动生成一个无参数的主构造函数,这点与java一样。
在主构造函数中你可以设置默认的值。
主构造函数和次级构造函数的区别:
data class ItemTransferTask(
var type: Int,
var icon: Int,
var name: String,
var fileCount: Int,
var fileSize: String?
) {
constructor(
type: Int,
icon: Int,
name: String,
size: Float,
statu: Int,
speed: Float,
progress: Float,
date: Long,
mineType: Int,
fileCount: Int,
fileSize: String?
) : this(type, icon, name, fileCount, fileSize)
}
8.数据类DataClass
kotlin里面, 可以不需要有class类!
构造方法:
class VideoController(videoView: ScalableVideoViewAsyn) {
先用Java写一个数据类,方便我们后面做对比
public class User {
private String id;
private String name;
private int gender;
private String avatar;
private int age;
public User(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
public String getAvatar() {
return avatar;
}
public void setAvatar(String avatar) {
this.avatar = avatar;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
我们再来看看Kotlin如果实现一个对象类
data class User(val id: String, var name: String = "", var gender: Int = 0, var avatar: String = "", var age: Int = 0)
dataclass里面的hashcode会变化, 更加变量和属性的不一样, 构造函数生成的hashcode也会不一样
抽象类
抽象是面向对象编程的特征之一,类本身,或类中的部分成员,都可以声明为abstract的。抽象成员在类中不存在具体的实现。
注意:无需对抽象类或抽象成员标注open注解。
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
嵌套类
class Outer { // 外部类
private val bar: Int = 1
class Nested { // 嵌套类
fun foo() = 2
}
}
fun main(args: Array<String>) {
val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
println(demo) // == 2
}
内部类
内部类使用 inner 关键字来表示。
内部类会带有一个对外部类的对象的引用,所以内部类可以访问外部类成员属性和成员函数。
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
为了消除歧义,要访问来自外部作用域的 this,我们使用this@label,其中 @label 是一个 代指 this 来源的标签。
匿名内部类
使用对象表达式来创建匿名内部类:
class Test {
var v = "成员属性"
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定义接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array<String>) {
var test = Test()
/**
* 采用对象表达式来创建接口对象,即匿名内部类的实例。
*/
test.setInterFace(object : TestInterFace {
override fun test() {
println("对象表达式创建匿名内部类的实例")
}
})
}
9. 类的修饰符
类的修饰符包括 classModifier 和accessModifier:
classModifier: 类属性修饰符,标示类本身特性。
abstract // 抽象类
final // 类不可继承,默认属性
enum // 枚举类
open // 类可继承,类默认是final的
annotation // 注解类
accessModifier: 访问权限修饰符
private // 仅在同一个文件中可见
protected // 同一个文件中或子类可见
public // 所有调用的地方都可见
internal // 同一个模块中可见
类名:没有public修饰,继承的话用:
class AiCoachTrainPullUpGuideActivity : ActivitySportBase() {
类,默认是final不能继承,继承要用open
Kotlin的可见修饰符与Java类似,但是默认的可见性不一样,如果省略修饰符:Java默认包私有,Kotlin默认声明是publi
kotlin访问修饰符: 比如protected, 规则, 和java不同,
10.swithch和When
when和switch的区别: fallthrough
when的原理:
通过反编译, when 有2种可能, if 和 switch();
override fun setStatus(status: Int) {
mTrainStatus = status
when (status) {
TrainStatus.STATUS_PREPARE -> {
setStatusPrepare()
}
TrainStatus.STATUS_ACTION_INTRO -> {
setStatusActionIntro()
}
TrainStatus.STATUS_CHECK_GRAVITY -> {
mTimeRecorder.cancelTask()
playTipVoice(AICoachTrainVoice.PLAVE_PHONE)
}
TrainStatus.STATUS_RECOGNIZE_POSE -> {
mTimeRecorder.cancelTask()
playTipVoice(AICoachTrainVoice.ADJUST_BODY)
mSportPoseRecognize?.startRecognizeCheck()
}
TrainStatus.STATUS_TRAINING -> {
mTimeRecorder.cancelTask()
mTipVoice.stop()
}
}
mView.setTrainingStatus(status)
}
多个点击事件
用when代替switch
override fun onClick(v: View?) {
when (v?.id) {
R.id.login ->
if (checkContent(true)) {
dialog = SweetAlertDialog(this, SweetAlertDialog.PROGRESS_TYPE)
.setTitleText("正在登录...")
dialog?.setCancelable(false)
dialog?.show()
loginPresenter?.login(username.text.toString(), password.text.toString())
}
R.id.register ->
if (checkContent(false)) {
dialog = SweetAlertDialog(this, SweetAlertDialog.PROGRESS_TYPE)
.setTitleText("正在注册...")
dialog?.setCancelable(false)
dialog?.show()
loginPresenter?.register(username.text.toString(), password.text.toString(), email.text.toString())
}
}
}
这里我们将共同的验证逻辑放进了嵌套函数 validate 中,并且 login 函数之外的其他地方无法访问这个嵌套函数。
网友评论