}
var name: String
get() {
return name.toUpperCase()
}
set(value) {
name = value
}
var psd: String = “”
private set
get() {
return psd + “@”
}
}
复制代码
16、interface的方法体是否实现是可选的。属性也是抽象的,需要实现类重新赋值。
interface BaseView {
var mNickName: String//抽象的属性
fun walk() {//方法体是否实现是可选的
println(“BaseView,walk!!!”)
}
fun yeal() {}//
}
复制代码
17、密封类
//密封类
sealed class Expr//相当于枚举的扩展
data class Const(val number: Double) : Expr()//这里只有另外一个double的状态入参
data class Sum(val e1: Expr, val e2: Expr) : Expr()//这个密封类里面就有两个Expr的状态入参
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
//因为已经覆盖了所以的情况,就不需要else子句了
}
复制代码
18、枚举
Color.BLACK.name
enumValues().size
enumValues().get(0)
enumValues().plus(Color.WHITE)
enumValues().iterator()
enumValueOf(Color.WHITE.toString())
复制代码
19、list和Comparable
fun <T : Comparable> sort(list: List) {
}
sort(listOf(1, 2, 3)) // OK。Int 是 Comparable 的子类型
// 错误:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型
// sort(listOf(HashMap<Int, String>()))
复制代码
20、object对象
val site = object {
var name = “jack”
var url = “www.tansu.com”
}
复制代码
21、委托类,委托对象(通过关键字 by 建立)
interface MyBase {
fun bark()
// fun bak()
}
class BaseImpl(val x: Int) : MyBase {
fun bak() {}
override fun bark() {
println(“x:$x”)
}
}
// 通过关键字 by 建立 委托类
class Deried(b: MyBase) : MyBase by b
fun test3() {
var baseImpl = BaseImpl(10)
Deried(baseImpl).bark()
baseImpl.bak()
// Deried(baseImpl).bak()//无法访问到非interface定义的方法
}
// 定义包含属性委托的类
class Example {
var p: String by Delegate()
//说明:如果p是var的话,那么委托类需要有getValue和setValue两个方法(因为此时是可变的变量,需要由set方法)
//说明:如果p是val的话,那么委托类只需要有getValue方法即可,另外一个可以不要求(此时为不可变常量,不需要set值进去)
}
// 委托的类
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return “$thisRef, 这里委托了 ${property.name} 属性”//这里name为p
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println(“$thisRef 的 ${property.name} 属性赋值为 $value”)//value为设置进来的值
}
}
fun test4() {
val e = Example()
println(e.p) // 访问该属性,调用 getValue() 函数
e.p = “Runoob” // 调用 setValue() 函数
println(e.p)
// Example@433c675d, 这里委托了 p 属性
// Example@433c675d 的 p 属性赋值为 Runoob
// Example@433c675d, 这里委托了 p 属性
}
复制代码
21、监听类值的变化
class User {
var name: String by Delegates.observable(“初始值”) { prop, old, new ->
println(“旧值:
o
l
d
−
>
新值:
old -> 新值:
old−>新值:new. prop:$prop”)
}
}
val user = User()
user.name = “第一次赋值”
user.name = “第二次赋值”
// 旧值:初始值 -> 新值:第一次赋值
// 旧值:第一次赋值 -> 新值:第二次赋值
复制代码
22、map
//把属性储存在映射中
class MyWebSite(val map: Map<String, Any?>) {
val name: String by map
val url: String by map
}
使用
var myWebSite = MyWebSite(mapOf(“name” to “jack”, “url” to “www.baidu.com”))
myWebSite.name
myWebSite.url
//结果读取出来
复制代码
23、其它
class Foo {
var notNullBar: String by Delegates.notNull()
}
使用
var foo = Foo()
foo.notNullBar = “bar”
println(foo.notNullBar)
kotlin最佳实践
类型的声明与使用
val与var
val->不可变引用,var->可变引用。
我们应该尽可能地使用val关键字来声明所有的kotlin变量 为什么呢?
- 首先一个变量在声明时是不可变的,那就代表你在使用的时候不需要考虑其他地方会对它重新复制和改变(对于对象注意只是引用不可变),直接使用。
- val声明的类型由于必须初始化,它是线程安全的。
- kotlin为了保证类型安全,所有变量声明的地方必须要做初始化,即显示赋一个初值。
空与非空
kotlin
对于可空类型
和非空类型
认为是两个完全不同的类型,比如Int
与Int?
,他们俩就不是相同的类型。利用这个特点和编译时检查,kotlin
基本可以避免空指针异常
。
上面已经说了kotlin的类型声明时必须要有初值
,所以空与非空类型
和val与var
一组合就会变成4种情况,下面我们对一个Person
对象进行声明:
val p:Person = Person()
val p:Person? = null // 这个情况是没有意义的
var p:Person = Person() // 如果这个对象是非空类型,那么初始化的时候必须赋一个非null初值
var p:Person? = null //可以给一个null,也可以给一个对象实例
上面这段代码基本解释了val与var
和空与非空的
关系。
空与非空的使用
kotlin
对空与非空
做了严格的限制,那我们在使用时不都要对可空类型
类型做判断吗?为了避免这个问题,kotlin提供了许多运算符来使开发人员对于可空与非空
编码更加愉快。
- 安全调用运算符 “?.”
比如我们声明了这样一个类型var p:Person? = null
。如果我们直接使用p.name
,kotlin编译器是会报错的无法编译通过,所有我们必须这么做:
if(p != null) p.name
这种代码写多了实在是太没有意义了,所有kotlin
提供了?.
。上面代码我们可以直接这样代替p?.name
。它的实际执行过程是这样的:如果p
为空则不操作,如果p
不为空则调用p.name
。
- Elvis 运算符 “?:”
在kotlin中我们会写这种代码:
val name = if(p != null) p.name else “” //kotlin中的if是一个表达式
不过使用?:
可以更简单实现上面的逻辑 : val name = p?.name ?: ""
。 它的实际执行逻辑是如果p为null,p?.name就会为null
, ?:
会检查前面的结果,如果是null,那么则返回""
。
- 安全转换 “as?”
在kotlin中类型转换关键字为as
。不过类型转换会伴随着转换失败的风险。使用as?
我们可以更优雅的写出转换无风险的代码:
//Person类中的方法
fun equals(o:Any?):Boolean{
val otherPerson = o as? Person ?: return false
…
}
即as?
在转换类型失败时会返回null
- 非空断言 “!!”
如果使用一个可空类型的方法,并且你不想做非空判断,那么你可以这样做: person!!.getAge()
。 不过如果person
为null,这里就会抛出空指针异常
。
其实还是在蛮多case下可以使用它,但是不建议使用, 因为你完全可以使用?、?:来做更优雅的处理, 你也可以使用lateinit来避免编译器的可空提示
val与bylazy、var与lateinit
- bylazy
它只能和val
一块使用。
private val mResultView:View = bylazy{
initResultView()
}
使用bylazy
我们可以对一个变量延迟初始化,即懒加载。它是线程安全的。具体原理是:当我们使用bylazy
声明的变量时,如果这个变量为null,那么就会调用bylazy
代码块来初始化这个变量。
- lateinit
它只能和var一块使用。并且不允许修饰可空类型。那它的使用场景是什么呢? 在有些case下,比如一个构造复杂的对象,我们就是想把变量声明为非空类型并且就是不想给他一个初值(代价太大了),这时候我们就可以使用lateinit
:
lateinit var p : Person //Person的构造函数太复杂了,不想在这里给一个初值
fun refreshUI(p2:Person){
//p = p2
val name = p.name //注意这个地方是可能会抛p为初始化异常的!!!如果你没有初始化
}
由于使用lateinit的时候我们要人工保证这个变量已经被初始化,并且kotlin在你每个使用这个变量的地方都会添加一个非null判断。所以lateinit尽量少用。
when 与 if
if
if
在kotlin
中不只是一个控制结构它也是一个表达式
,即它是有返回结果的,我们可以利用它来代替java
中的三目运算符:
val background = if(isBlcak) R.drawable.black_image else R.drawable.white_image
when
它的使用方法有多种:
- 代替
switch
的功能
when(color){
“red”,“green” ->{ }
“blue”->{ }
}
kotlin
中的when可以用来判断任何对象,它会逐一检查每一个分支,如果满足这个分支的条件就执行。
- 多条件判断
可以使用when
来避免if..elseif..elseif..else
的写法:
val a = 1
val b = 2
when{
a > 0 && b > 0 ->{}
a < 0 && b > 0 ->{}
a < 0 && b < 0 ->{}
else ->{ }
}
- when是带有返回值的表达式
和if
一样,when
也是一个表达式:
val desColor = when(color){
“red”, “gren” -> “red&green”
“blue” -> “blue”
else -> “black” // when作为表达式时必须要有else分支。
}
类
类的构造与主构造函数
- 简单的声明一个 javabean
在kotlin
中我们可以这样简单的定义一个类: class Person(val name:String = "", var age:Int = 0)
这样就定义了一个Person
类,这个类有两个属性:name
和age
。并且他有一个两个参数的构造函数来对这两个属性初始化。可以看出kotlin
将一个类的声明变的十分方便。
- 主构造函数
普通的java
构造函数是有代码块的,即可以做一些逻辑操作,那按照kotlin
上面的方式,我们怎么做构造函数的逻辑操作呢? kotlin
提供了初始化代码块
:
class Person(val name:String = “”, var age:Int = 0){
init{
name = “susion”
age = 13
}
}
init代码块会在主构造函数之后运行,注意不是所有的构造函数。
数据类 data class
更方便的定义一个javabean,我们可以使用数据类:
data class Person(val name:String = “”, val age:Int = 0)
使用data
定义的Person
会默认生成equals
、hashCode
、toString
方法。需要注意的是数据类的属性我们应该尽量定义成val的。这是因为在主构造函数中声明的这些属性都会纳入到equals
和hashCode
方法中。如果某个属性是可变的,
那么这个对象在被加入到容器后就会是一个无效的状态。
object 和 companion object
在kotlin中没有静态方法,也没有静态类。不过kotlin提供了object
与companion object
- object 单例类
使用object
我们可以很轻松的创建一个单例类 :
object LoginStatus{
var isLogin = false
fun login(){
isLogin = true
}
…
}
我们可以这样直接使用LoginStatus.isLogin()
。 那这个单例在kotlin中是怎么实现的呢?我们可以反编译看一下它生成的java代码:
public final class LoginStatus {
private static boolean isLogin;
public static final LoginStatus INSTANCE; // for java调用
… //省略不重要的部分
public final void login() {
isLogin = true;
}
private LoginStatus() {
INSTANCE = (LoginStatus)this;
}
static { //类加载的时候构造实例
new LoginStatus();
}
}
即kotlin object实现的单例是线程安全的。它的对象是在类创建的时候就产生了。
- object的静态方法的使用
上面我们已经知道object
创建单例的原理了。这在某些case
下就很棒,但是某些时候我们不是想要单例,我们只是想要一些静态方法呢?比如我们经常创建的一些工具类(UIUtils、StringUtils)等。我们可以直接使用object
来完成:
public object UIUtils{
…
…
…
…很多方法
}
按照kotlin
单例的设计,我们只要一旦使用这些方法,那么一直有一个单例对象UIUtils存在于内存中。那么这样好吗? 我们是否可以这样写呢 :
public class UIUtils{
…
…
…
…很多方法
}
然后在使用的时候:UIUtils().dp2Px(1)
。这样至少不会有一个对象一直在内存中。我想我们在某些case下可以这样使用我们的工具类。或者你可以使用kotlin的扩展函数
或顶层函数
来定义一些工具方法。所以对于kotlin的object
的使用需要注意。
- companion object
companion object
主要是为了方便我们可以在一个类中创建一些静态方法而存在的,比如:
class Person(val name: String) {
companion object {
fun isMeal(p: Person) = false
}
}
依旧看一下它反编译后的java代码:
public final class Person {
…
public static final Person.Companion Companion = new Person.Companion((DefaultConstructorMarker)null);
…
public static final class Companion {
public final boolean isMeal(@NotNull Person p) {
Intrinsics.checkParameterIsNotNull(p, “p”);
return false;
}
private Companion() {
}
…
}
}
即它也是生成了一个单例类Person.Companion
。不过这个单例类是一个静态类。不允许构造。不过它的实现机制几乎和object
相同。
lambda
kotlin中lambda
的本质就是可以传递给其他函数的一小段代码。kotlin中lambda
使用的最多的就是和集合一块使用。
- lambad与java接口
比如我们经常给View
设置onClickListener
,在kotlin中我们可以很方便的实现这段代码:
userView.setOnClickListener{
} // 如果lambda是函数的最后一个参数,那么是可以放在括号外面的。
即你可以直接传递给它一个lambda
。kotlin在实际编译的时候会把这个lambda编译成一个匿名内部类。 那么所有java
传对象的地方都可以这样使用吗? 当然不是, 只有java参数满足下面条件才可以使用:
这个参数是一个接口,并且这个接口只有一个抽象方法。就可以这样使用,比如Runnable、Callable等。
- with 与 apply
with
个人感觉比较鸡肋,这里就不讲它了。不过apply
是十分实用的。在kotlin
中apply
被实现为一个函数:
public inline fun T.apply(block: T.() -> kotlin.Unit): T { }
即它是一个扩展函数,接收一个lambda,并返回对象本身,并且他是一个内联的函数(下面会讲)
我最常用的一个case是给view设置参数:
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。
4936607)]
[外链图片转存中…(img-F4QHkW7f-1711884936608)]
[外链图片转存中…(img-ui8ftmBf-1711884936608)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-W1UcbqTv-1711884936609)]
最后
由于文章篇幅原因,我只把面试题列了出来,详细的答案,我整理成了一份PDF文档,这份文档还包括了还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 ,帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习。