技术标签: 面试 1024程序员节 android kotlin
目录
三.Kotlin 中注解 @JvmOverloads 的作用?
四.Kotlin中的MutableList与List有什么区别?
六. kotlin中关键字data的理解?相对于普通的类有哪些特点?
八.kotlin中with、run、apply、let函数的区别?一般用于什么场景?
九.kotlin中Unit的应用以及和Java中void的区别?
十一. Kotlin中的可见性修饰符有哪些?相比于 Java 有什么区别?
十二.你觉得Kotlin与Java混合开发时需要注意哪些问题?
十六.谈谈Kotlin中的Sequence,为什么它处理集合操作更加高效?
十七.请谈谈Kotlin中的Coroutines,它与线程有什么区别?有哪些优点?
十九.Kotlin中的?.然后后面调用方法如果为空的情况下是什么?如果是调用变量是什么情况?
二十.说说 Kotlin中 的 Any 与Java中的 Object 有何异同?
Android面试题总结:Android所有面试题总结https://blog.csdn.net/gongjdde/category_11520676.html?spm=1001.2014.3001.5482
Framework相关内容总结:
https://blog.csdn.net/gongjdde/category_10613658.html
Kotlin相关内容总结:https://blog.csdn.net/gongjdde/category_10998720.html
设计模式相关面试题:https://gonglipeng.blog.csdn.net/article/details/124028915
Android相关面试题:https://gonglipeng.blog.csdn.net/article/details/123905213
Android性能调优相关:https://gonglipeng.blog.csdn.net/article/details/123879928
虚拟机与Framework及高阶技术面试题及跨进程通讯:https://gonglipeng.blog.csdn.net/article/details/123820884
Java多线程及锁相关面试题:https://gonglipeng.blog.csdn.net/article/details/123777633
集合相关面试题:https://gonglipeng.blog.csdn.net/article/details/123639426
网络相关面试题:https://gonglipeng.blog.csdn.net/article/details/122655106
事件分发常见面试题:https://gonglipeng.blog.csdn.net/article/details/122049087
java 基础知识面试题:https://gonglipeng.blog.csdn.net/article/details/122095464
动画相关面试题:https://gonglipeng.blog.csdn.net/article/details/121960860
自定义View相关面试题:https://gonglipeng.blog.csdn.net/article/details/121874617
Handler相关面试题:https://gonglipeng.blog.csdn.net/article/details/121800079
四大组件相关总结:https://gonglipeng.blog.csdn.net/article/details/121738395
参考:Android 的 Kotlin 优先方法 | Android 开发者 | Android Developers
https://blog.csdn.net/gongjdde/category_10998720.html?spm=1001.2014.3001.5482
kotlin是一门编程语言,和java一样都是编译成class文件,然后被虚拟机加载。kotlin是先在android官方优先采用的语言,相比Java,它有以下优势:
kotlin密封类与枚举的区别:区别
密封类总结:
密封类用于表示受限制的类层次结构
Sealed Classes是枚举类的扩展
是否只有一个实例 | 是否是同一类型 | 是否反序列化 | 是否是线程安全 | 是否懒加载 | |
枚举类 | 是 | 是 | 是 | 是 | 否 |
密封类 | 否 | 否 | 否 | 否 | 否 |
Sealed是一个abstract类,它本身不能被实例化,只能用它的子类实例化对象。Sealed的构造方法私有化,禁止在Sealed所定义的文件外使用。
在什么情况下使用枚举或者Sealed?
在Kotlin
中@JvmOverloads
注解的作用就是:在有默认参数值的方法中使用@JvmOverloads
注解,则Kotlin
就会暴露多个重载方法。如果没有加注解@JvmOverloads则只有一个方法,kotlin调用的话如果没有传入的参数用的是默认值。
@JvmOverloads fun f(a: String, b: Int=0, c:String="abc"){
}
// 相当于Java三个方法 不加这个注解就只能当作第三个方法这唯一一种方法
void f(String a)
void f(String a, int b)
// 加不加注解,都会生成这个方法
void f(String a, int b, String c)
List:有序接口,只能读取,不能更改元素;
MutableList:有序接口,可以读写与更改、删除、增加元素。
源码分析MutableList就相当于Java中的ArrayList,List是kotlin自己重写的EmptyList,EmptyList中没有提供add方法remove方法等修改元素操作的方法。
internal object EmptyList : List, Serializable, RandomAccess {
private const val serialVersionUID: Long = -7390468764508069838L
override fun equals(other: Any?): Boolean = other is List<*> && other.isEmpty()
override fun hashCode(): Int = 1
override fun toString(): String = "[]"
override val size: Int get() = 0
override fun isEmpty(): Boolean = true
override fun contains(element: Nothing): Boolean = false
override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty()
override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.")
override fun indexOf(element: Nothing): Int = -1
override fun lastIndexOf(element: Nothing): Int = -1
override fun iterator(): Iterator<Nothing> = EmptyIterator
override fun listIterator(): ListIterator<Nothing> = EmptyIterator
override fun listIterator(index: Int): ListIterator<Nothing> {
if (index != 0) throw IndexOutOfBoundsException("Index: $index")
return EmptyIterator
}
override fun subList(fromIndex: Int, toIndex: Int): List<Nothing> {
if (fromIndex == 0 && toIndex == 0) return this
throw IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex")
}
private fun readResolve(): Any = EmptyList
}
饿汉式
object Singleton
线程安全的懒汉式
class Singleton private constructor() {
companion object {
private var instance: Singleton? = null
get() {
if (field == null) field = Singleton()
return field
}
@Synchronized
fun instance(): Singleton {
return instance!!
}
}
}
双重校验锁式
/**
* 双重校验锁式
* Lazy是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托
* 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果,后续调用 get() 只是返回记录的结果
* Lazy默认的线程模式就是 LazyThreadSafetyMode.SYNCHRONIZED 内部默认双重校验锁
* # Lazy内部实现
* ```
* public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
* when (mode) {
* LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
* LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
* LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
* }
* ```
* ### Lazy接口
* ```
* public interface Lazy<out T> {
* //当前实例化对象,一旦实例化后,该对象不会再改变
* public val value: T
* //返回true表示,已经延迟实例化过了,false 表示,没有被实例化,
* //一旦方法返回true,该方法会一直返回true,且不会再继续实例化
* public fun isInitialized(): Boolean
* }
* ```
* ### SynchronizedLazyImpl
* ```
* private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
* private var initializer: (() -> T)? = initializer
* @Volatile private var _value: Any? = UNINITIALIZED_VALUE
* // final field is required to enable safe publication of constructed instance
* private val lock = lock ?: this
*
* override val value: T
* get() {
* val _v1 = _value
* //判断是否已经初始化过,如果初始化过直接返回,不在调用高级函数内部逻辑
* if (_v1 !== UNINITIALIZED_VALUE) {
* @Suppress("UNCHECKED_CAST")
* return _v1 as T
* }
*
* return synchronized(lock) {
* val _v2 = _value
* if (_v2 !== UNINITIALIZED_VALUE) {
* @Suppress("UNCHECKED_CAST") (_v2 as T)
* }
* else {
* //调用高级函数获取其返回值
* val typedValue = initializer!!()
* //将返回值赋值给_value,用于下次判断时,直接返回高级函数的返回值
* _value = typedValue
* initializer = null
* typedValue
* }
* }
* }
* //省略部分代码
* }
* ```
*/
class Singleton private constructor() {
companion object {
val instance by lazy { Singleton() }
}
}
静态内部类式
class Singleton private constructor() {
companion object {
val instance = SingletonHolder.holder
}
private object SingletonHolder {
val holder = Singleton()
}
}
枚举式
enum class Singleton {
INSTANCE;
}
数据类,相当于MVVM模式下的model类,相对java自动重写了equals()/hashCode()方法、get()方法、set()方法(如果是可写入的)、toString()方法、componentN()方法、copy()方法,注意get/set方法是kotlin中的类都会为属性自动生成的方法,和数据类没关系。
equals/hashCode:equals方法重写使对象的内容一致则返回true,hashCode方法重写使对象的内容一致则hashCode值也一致。
注意:在kotlin中有 == 和 ===,==比较的对象内容,===比较的是对象的引用地址
toString:重写此方法为类和属性值的内容,如:"User(name=John, age=42)"
componentN:编译器为数据类(data class)自动声明componentN()函数,可直接用解构声明,如下:
var girl1: Girl = Girl("嫚嫚", 29, 160, "廊坊")
var (a,b,c,d) = girl1
println("$a,$b,$c,$d")
在kotlin中所谓的解构就是将一个类对象中的参数拆开来,成为一个一个单独的变量,从而来使用这些单独的变量进行操作。
copy: 复制对象使用,当要复制一个对象,只改变一些属性,但其余不变,copy()就是为此而生
属性委托的核心思想是将一个属性(字段)的具体实现委托给另一个类去完成。
应用场景:懒加载技术,通过 by lazy进行懒加载
基本介绍:
使用场景:
区别:
函数 | 是否是扩展函数 | 函数参数(this、it) | 返回值(调用本身、最后一行) |
---|---|---|---|
with | 不是 | this | 最后一行 |
T.run | 是 | this | 最后一行 |
T.let | 是 | it | 最后一行 |
T.also | 是 | it | 调用本身 |
T.apply | 是 | this | 调用本身 |
扩展函数原理:
扩展函数实际上就是一个对应 Java 中的静态函数,这个静态函数参数为接收者类型的对象,然后利用这个对象就可以访问这个类中的成员属性和方法了,并且最后返回一个这个接收者类型对象本身。这样在外部感觉和使用类的成员函数是一样的。
infix可以自定义操作符,比如1 to 2 这种的, 1 add 2,让程序更加语义化
kotlin存在四种可见性修饰符,默认是public。 private、protected、internal、public
1.private、public是和java中的一样的,protected java中同一个包可见,kotlin中不可见。
2.不同的是java中默认是default修饰符(包可见),而kotlin存在internal修饰符(模块内部可见)。
3.kotlin可以直接在文件顶级声明方法、变量等。其中protected不能用来修饰在文件顶级声明的类、方法、变量等。
构造方法默认是public修饰,可以使用可见性修饰符修饰constructor关键字来改变构造方法的可见性。
修饰符 | java | kotlin |
public | 所有类可见 | 所有类可见(默认) |
private | 当前类可见 | 当前类可见 |
protected | 当前类,子类,同一包路径下的类可见 | 当前类,子类可见 |
default | 同一包路径下的类可见(默认) | 无 |
internal | 无 | 同一模块下的类可见 |
kotlin调用java的时候,如果java返回值可能为null 那就必须加上@nullable否则kotlin无法识别,也就不会强制你做非空处理,一旦java返回了null 那么必定会出现null指针异常,加上@nullable注解之后kotlin就能识别到java方法可能会返回null,编译器就能会知道,并且强制你做非null处理,这也就是kotlin的空安全
在kotlin中所谓的解构就是将一个类对象中的参数拆开来,成为一个一个单独的变量,从而来使用这些单独的变量进行操作。
使用方式:
1.常规使用方式:
val (name, age) = person
println(name)
println(age)
2.还可以在for需要中获取Map的key、value值
for ((key, value) in map) {
// 使用该 key、value 做些事情
}
3.如果在解构声明中你不需要某个变量,那么可以用下划线取代其名称:
val (_, status) = getResult()
4.在 lambda 表达式中解构
map.mapValues { entry -> "${entry.value}!" }
map.mapValues { (key, value) -> "$value!" }
关键字 inline 标记函数,该函数就是一个内联函数
作用是可以在编译kotlin文件时直接将内联函数内联掉,这样就是把内联函数执行过程放在调用此内联函数的位置,避免了java中多调用方法的操作,减少性能消耗。
参考:2019-10-21:在Kotlin中,什么是内联函数?有什么作用? · Issue #169 · Moosphan/Android-Daily-Interview · GitHub
kotlin 高阶函数、内联函数_龚礼鹏的博客-程序员宅基地
1.概要简述
kotlin
中构造函数分为主构造
和次级构造
两类constructor
标记次级构造函数,部分情况可省略init
关键词用于初始化代码块,注意与构造函数的执行顺序,类成员的初始化顺序data class
、object/componain object
、sealed class
等构造函数情况与继承问题2.详细说明
主/次 构造函数
kotlin
中任何class
(包括object/data class/sealed class
)都有一个默认的无参构造函数- 如果显式的声明了构造函数,默认的无参构造函数就失效了。
- 主构造函数写在
class
声明处,可以有访问权限修饰符private,public
等,且可以省略constructor
关键字。- 若显式的在
class
内声明了次级构造函数,就需要委托调用主构造函数。- 若在
class
内显式的声明处所有构造函数(也就是没有了所谓的默认主构造),这时候可以不用依次调用主构造函数。例如继承View
实现自定义控件时,三四个构造函数同时显示声明。
init
初始化代码块
kotlin
中若存在主构造函数,其不能有代码块执行,init
起到类似作用,在类初始化时侯执行相关的代码块。
init
代码块优先于次级构造函数中的代码块执行。- 即使在类的继承体系中,各自的
init
也是优先于构造函数执行。- 在主构造函数中,形参加有
var/val
,那么就变成了成员属性的声明。这些属性声明是早于init
代码块的。
特殊类
object/companion object
是对象示例,作为单例类或者伴生对象,没有构造函数。
data class
要求必须有一个含有至少一个成员属性的主构造函数,其余方面和普通类相同。
sealed class
只是声明类似抽象类一般,可以有主构造函数,含参无参以及次级构造等。
kotlin中自定义view与java适配方式:kotlin自定view构造方法写法 - 简书
集合操作低效在哪?
处理集合时性能损耗的最大原因是循环。集合元素迭代的次数越少性能越好。
我们写个例子:
list
.map { it ++ }
.filter { it % 2 == 0 }
.count { it < 3 }
反编译一下,你会发现:Kotlin编译器会创建三个while循环
。
Sequences 减少了循环次数
Sequences
提高性能的秘密在于这三个操作可以共享同一个迭代器(iterator),只需要一次循环即可完成。Sequences
允许 map 转换一个元素后,立马将这个元素传递给 filter操作 ,而不是像集合(lists) 那样,等待所有的元素都循环完成了map操作后,用一个新的集合存储起来,然后又遍历循环从新的集合取出元素完成filter操作。
Sequences 是懒惰的
上面的代码示例,map
、filter
、count
都是属于中间操作,只有等待到一个终端操作,如打印、sum()
、average()
、first()
时才会开始工作,不信?你跑下下面的代码?
val list = listOf(1, 2, 3, 4, 5, 6)
val result = list.asSequence()
.map{ println("--map"); it * 2 }
.filter { println("--filter");it % 3 == 0 }
println("go~")
println(result.average())
扩展:Java8 的 Stream(流) 怎么样呢?
list.asSequence()
.filter { it < 0}
.map { it++ }
.average()
list.stream()
.filter { it < 0}
.map { it++ }
.average()
stream
的处理效率几乎和Sequences
一样高。它们也都是基于惰性求值的原理并且在最后(终端)处理集合。
协程:协程就像非常轻量级的线程。线程是由系统调度的,线程切换或线程阻塞的开销都比较大。而协程依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的,协程是由开发者控制的。所以协程也像用户态的线程,非常轻量级,一个线程中可以创建任意个协程。
协程与线程有什么区别:
Kotlin 协程,不是操作系统级别的概念,无需操作系统支持,线程是操作系统级别的概念,我们开发者通过编程语言(Thread.java)创建的线程,本质还是操作系统内核线程的映射。
Kotlin 协程,是用户态的(userlevel),内核对协程「无感知」;一般情况下,我们说的线程,都是内核线程,线程之间的切换,调度,都由操作系统负责。
Kotlin 协程,是协作式的,由开发者管理,不需要操作系统进行调度和切换,也没有抢占式的消耗,因此它更加「高效」;线程,是抢占式的,它们之间能共享内存资源。
Kotlin 协程,它底层基于状态机实现,多协程之间共用一个实例,资源开销极小,因此它更加「轻量」;线程会消耗操作系统资源。
Kotlin 协程,本质还是运行于线程之上,它通过协程调度器,可以运行到不同的线程上
优点:
a?.let{
//此处的内容就是非空的
}
如果为空不会抛出空指针,而是调用的方法会返回null;调用变量也是会返回null,如下。
fun main() {
var a:A? = null
val c = a?.b
println("Hello World"+c)
}
class A {
var b:String = "xxxx"
}
打印结果:
Hello Worldnull
fun main() {
var a:A? = null
val c = a?.mothed()
println("Hello World"+c)
}
class A {
fun mothed():String{
return "测试测试"
}
}
打印结果;
Hello Worldnull
同:
我们思考下,为什么 Kotlin 设计了一个 Any ?
当我们需要和 Java 互操作的时候,Kotlin 把 Java 方法参数和返回类型中用到的 Object 类型看作 Any,这个 Any 的设计是 Kotlin 兼容 Java 时的一种权衡设计。
所有 Java 引用类型在 Kotlin 中都表现为平台类型。当在 Kotlin 中处理平台类型的值的时候,它既可以被当做可空类型来处理,也可以被当做非空类型来操作。
试想下,如果所有来自 Java 的值都被看成非空,那么就容易写出比较危险的代码。反之,如果 Java 值都强制当做可空,则会导致大量的 null 检查。综合考量,平台类型是一种折中的设计方案。
kotlin有隐式转换,在计算过程中一般都转换成其中的较大类型,因为防止精度丢失。
for,foreach,while,do while,递归,还有集合的高阶方法
一个线程中开启1000个协程也没什么问题,但是如果开启1000个线程则性能消耗无法估量。
协程flow:Kotlin 协程中使用挂起函数可以实现非阻塞地执行任务并将结果返回回来,但是只能返回单个计算结果。但是如果希望有多个计算结果返回回来,则可以使用 Flow。
应用场景:多个数据流执行的情况下。
参考:Kotlin 协程三 —— 数据流 Flow - SharpCJ - 博客园
热数据很迫切,它们尽可能快的生产元素并存储它们。它们创造的元素独立于它们的消费者,它们是集合(List
、Set
)和 channel
冷数据流是惰性的,它们在终端操作上按需处理元素,所有中间函数知识定义应该做什么(通常是用装饰模式),它们通常不存储元素,而是根据需要创建元素,它们的运算次数很少,可以是无限的,它们创建、处理元素的过程通常和消费过程紧挨着。这些元素是 Sequence
、Java Stream
,Flow
和 RxJava 流
(Observable
、Single
等)
协程Flow中的热流是channelFlow,冷流是Flow
fun main() = runBlocking {
val time = measureTimeMillis {
// equeneFlow() //同步 1秒左右
asyncFlow() //异步700多毫秒
}
print("cost $time")
}
//异步的
private suspend fun asyncFlow() {
channelFlow {
for (i in 1..5) {
delay(100)
send(i)
}
}.collect {
delay(100)
println(it)
}
}
//同步的
private suspend fun equeneFlow() {
flow<Int> {
for (i in 1..5) {
delay(100)
emit(i)
}
}.collect {
delay(100)
println(it)
}
}
文章浏览阅读103次。/* 排序 >号 从小到大排序 <从大到小排序 */ list.sort(function(a, b) { return a.date < b.date ? 1 : -1; })如果是简单的list就直接 return a < b ? 1 : -1;即可,如果是list里面套的map,可让list按map里面的指定字段进行排揎。..._sort a
文章浏览阅读375次。<script src="js/jquery-1.8.3.min.js" type="text/javascript"></script> <script type="text/javascript"> function checkName() { var name = document.getElementB..._jsp前端页面将表单是否提交成功作为限制条件
文章浏览阅读1k次。Sequence Numberlzyws7393074532892018-04-25Number Sequenceqq_391789932452017-09-21理解TCP序列号(Sequence Number)和确认号(Acknowledgment Number)hebbely9822017-01-14Number Sequence(规律)l25336363712902017-07-18Numb..._ack num
文章浏览阅读5.9k次。笔记本电脑怎么进CMOS密码巧设置笔记本电脑怎么进CMOS密码巧设置 笔记本电脑为了保护用户的数据安全,往往采用加密的方式,最常见的还是CMOS密码加密技术。为了让你的重要数据更加安全,你可能需要设置不同的密码,这也就要求你记住许多密码。对于笔记本电脑用户来说,真的需要设置一道道密码关卡吗?非也非也! 一、认识与设置笔记本电脑的CMOS密码 笔记本电脑的CMOS密码大致分为超级密码(Supervi..._电脑第一道密码修改
文章浏览阅读2.5k次,点赞2次,收藏5次。迟到的文章,就当库存发出来吧~_jangow01
文章浏览阅读1.7w次,点赞2次,收藏5次。默认情况下RDD的transformation是lazy形式,实际计算只有在ation时才会进行,而且rdd的计算结果默认都是临时的,用过即丢弃,每个action都会触发整个DAG的从头开始计算,因此在迭代计算时都会想到用cache或persist进结果进行缓存。敝人看到很多资料或书籍有的说是persist或cache会触发transformation真正执行计算,有的说是不会!敝人亲自实验了一把..._spark cache和persist不生效
文章浏览阅读637次。树莓派的GPIO操作被抽象为文件读写,下面以一个例子来说明GPIO操作。_树莓派怎么读取gpio口上的信息
文章浏览阅读1.7k次。QNX是一种商用的遵从POSIX规范的类Unix实时操作系统,目标市场主要是面向嵌入式系统。它可能是最成功的微内核操作系统之一。QNX是一种商用的类Unix实时操作系统,遵从POSⅨ规范,目标市场主要是嵌入式系统[1]。QNX成立于1980年,是加拿大一家知名的嵌入式系统开发商。QNX的应用范围极广,包含了:控制保时捷跑车的音乐和媒体功能、核电站和美国陆军无人驾驶Crusher坦克的控制系统[2],还有RIM公司的BlackBerry PlayBook平板电脑。_车机qnx虚拟化软件系统架构
文章浏览阅读1k次,点赞20次,收藏22次。代码功能:信号发生器设计信号发生器由波形选择开关控制波形的输出,分别能输出正弦波、方汉和三角波三种波形,波形的周期为2秒(由40M有源晶振分频控制)。考虑程序的容量,每种波形在一个周期内均取16个取样点,每个样点数据是8位(数值范围:00000000~1111111)要求将D/A变换前的8位二进数据(以十进制方式)输出到数码管动态演示出来。_vhdl正弦波信号发生器
文章浏览阅读629次。Java Concurrency in Practice中对线程安全的定义:当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安全的。显然只有资源竞争时才会导致线程不安全,因此无状态对象永远是线程安全的 。过多的同步会产生死锁的问题,死锁属于程序运行的时_java 线程概述
文章浏览阅读1.2w次,点赞10次,收藏61次。读取表单Sheet2中部分信息。_matlab读取数据
文章浏览阅读1.4w次。最近项目中用到ItemBased Collaborative Filtering,实践过spark mllib中的ALS,但是因为其中涉及到降维操作,大数据量的计算实在不能恭维。所以自己实践实现基于spark的分布式cf,已经做了部分优化。目测运行效率还不错。以下代码package modelimport org.apache.spark.broadcast.Broadcastimp_spark cf