? ? ? ? 最近希望系統(tǒng)性的學習下Kotlin相關的知識點,畢竟谷歌已經(jīng)把kotlin作為安卓的首選開發(fā)語音,所以抽空完整的看完了<Kotlin實踐>,并通過總結書中的核心知識點,希望能一步步的鞏固書中知識,這里的內容總結會以問答的方式逐步了解書中的重要知識點,我們知道安卓原來是基于Java作為開發(fā)語音,現(xiàn)在切換為Kotlin開發(fā)語音,如果我們想了解Kotlin語言的新特性,站在一個更高的角度看,我們把自己當成Android Studio的編譯器、解析器的開發(fā)者,需要實現(xiàn)一個kotlin項目的編譯器和解析器去解析相關項目代碼,我們應該如何實現(xiàn)相關編譯器與解析器,實現(xiàn)原理如何?
? ? ? ?下面基于<Kotlin實踐>書籍的核心內容,我會通過問答的方式匯總出書中的主要內容,并適當?shù)母鶕?jù)自己的理解做相關知識點擴展,全面擁抱AI,問題答案來自騰訊元寶和上面書籍中等來源。
下面對kotlin相關的知識點,我這邊根據(jù)自己的理解做了分類:
一:Kotlin產(chǎn)生背景、介紹、與java項目遷移相關
1.1、谷歌推出Kotlin的核心原因有哪些?
1.2、Kotlin開發(fā)語言相比Java開發(fā)語言,有什么新特性和優(yōu)勢?
1.3、谷歌對Kotlin開發(fā)語言的支持具體有哪些方面?
1.4、kotlin中,提出的函數(shù)式編程思想,有什么特點和優(yōu)勢嗎?
1.5、在Android項目開發(fā)過程中,在java與Kotlin的項目中,兩種語言的交互過程中,有哪些需要注意的點或者需要了解的知識點?
二:基礎語法解析
2.1、kotlin中,頂層函數(shù)與頂層屬性的作用是什么與使用場景有哪些?提供具體的案例實現(xiàn)?
2.2、kotlin中,頂層函數(shù)與伴生對象函數(shù)的區(qū)別是什么?
2.3、kotlin中,Any、Unit、Nothing的作用分別是什么?
2.4、kotlin中,如何實現(xiàn)對象的序列化與反序列化的?提供一個具體案例demo與分析?
2.5、kotlin中,反射相關涉及到的類有哪些?提供一個反射相關的案例demo?
2.6、kotlin中的可見性修飾符與java的可見性修飾符有什么區(qū)別?
2.7、kotlin中,主構造器、次構造器等相關的知識點有哪些?
2.8、kotlin中,什么是類委托?類委托的作用是什么?提供具體案例?
2.9、kotlin中,什么是屬性委托?屬性委托的作用是什么?提供具體案例?
2.10、kotlin中,var、val、const的區(qū)別是什么?
2.11、kotlin中,lateInit與by?lazy的區(qū)別是什么?
2.12、kotin中,?、?.、?:、??!、::相關操作符的作用是什么?具體使用案例?
2.13、kotlin中,如何使用get約定函數(shù)初始化變量時?好處是什么?
2.14、kotlin中,Lambda表達式與帶參數(shù)的Lambda表達式的區(qū)別?相關的使用場景有哪些?
2.15、kotlin中,控制流相關關鍵字:if、for、when對比java,有什么新特性?
2.16、kotlin中,帶標簽的break、continue的功能實現(xiàn)?提供具體案例分析?
2.17、kotlin中,受檢異常與非受檢異常的區(qū)別?kotlin與java相關實現(xiàn)有什么區(qū)別嗎?
2.18、kotlin中,什么是伴生對象?伴生對象什么特性嗎?
2.19、kotlin中,標簽@的使用場景有哪些?提供具體案例分析?
2.20、kotlin中,run與runBlocking的區(qū)別?
2.21、kotlin中,系統(tǒng)的作用域函數(shù)有哪些?他們的作用?
三:泛型與集合相關
3.1、kotlin中,關于泛型的相關知識點有哪些?協(xié)變(out)、逆變(in)的概念?提供具體的案例?
3.2、kotlin中,星號投影的概念?使用場景?具體案例分析?
3.3、kotlin中,什么是類型擦除?為什么要做類型擦除?
3.4、kotlin中,什么場景下需要防止類型擦除?如何防止類型擦除?
3.5、kotlin中,通過inline與reified如何防止類型擦除?具體案例分析?
3.6、kotlin中,集合相關的設計實現(xiàn)與java的集合相關設計實現(xiàn)上有什么區(qū)別?有什么新特性?請一一舉例?
3.7、kotlin中,集合與序列的區(qū)別是什么?具體案例分析?
四:kotlin多種類定義與解析
4.1、kotlin中,相比java,類的定義與實現(xiàn),有什么新特性?提供具體案例?
4.2、kotlin中,相比java,抽象類的定義與實現(xiàn),有什么新特性?提供具體案例?
4.3、kotlin中,相比java,接口的定義與實現(xiàn),有什么新特性?提供具體案例?
4.4、kotlin中,相比java,枚舉類的定義與實現(xiàn),有什么新特性?提供具體案例?
4.5、kotlin中,相比java,注解類的定義與實現(xiàn),有什么新特性?提供具體案例?
4.6、kotlin中,數(shù)據(jù)類data有什么特點與特性?提供具體案例?
4.7、kotlin中,封閉類sealed有什么特點與特性?提供具體案例?
4.8、kotlin中,對象類object有什么特點與特性?提供具體案例?
五:kotlin多種函數(shù)的定義與解析
5.1、kotlin中,關于參數(shù)列表,默認參數(shù)、命名參數(shù)、可變參數(shù)、函數(shù)參數(shù)的作用與案例分析?
5.2、kotlin中,什么高階函數(shù)?高階函數(shù)的作用是什么?提供高階函數(shù)的案例demo?
5.3、kotlin中,什么擴展函數(shù)?擴展函數(shù)的作用是什么?提供擴展函數(shù)的案例demo?
5.4、kotlin中,什么內聯(lián)函數(shù)?內聯(lián)函數(shù)的作用是什么?提供內聯(lián)函數(shù)的案例demo?
5.5、kotlin中,什么中輟函數(shù)?中輟函數(shù)的作用是什么?提供中輟函數(shù)的案例demo?
5.6、kotlin中,什么局部函數(shù)?局部函數(shù)的作用是什么?提供局部函數(shù)的案例demo?
5.7、kotlin中,什么掛起函數(shù)?掛起函數(shù)的作用是什么?提供掛起函數(shù)的案例demo?
5.8、kotlin中,什么運算符重載函數(shù)?運算符重載函數(shù)的作用是什么?提供運算符重載函數(shù)的案例demo?
5.9、kotlin中,什么場景下要防止函數(shù)內聯(lián)?具體案例分析?
六:并發(fā)、異步、多線程同步相關
6.1、kotlin中,如何實現(xiàn)多線程安全的?提供相關案例?
6.2、kotlin中,協(xié)程與線程的區(qū)別有哪些?匯總一下協(xié)程的相關知識點?并提供詳細案例分析?
6.3、kotlin中,協(xié)程作用域一共有多少種?分別有什么區(qū)別?提供具體案例?
6.4、kotlin中,為什么沒有多線程安全的集合出現(xiàn)?
6.5、kotlin中,協(xié)程提供的并發(fā)控制方式有哪些?
6.6、kotlin中,詳解介紹一下異步數(shù)據(jù)流Flow的概念產(chǎn)生的原因等?
6.7、Flow和其他Kotlin協(xié)程特性如何結合使用?
6.8、kotlin中,F(xiàn)low對象的創(chuàng)建方式?提供具體案例分析?
6.9、Flow與RxJava中的Observable有何相似之處和不同點?
6.10、kotlin中,flow的設計思想與rxjava的設計思想有哪些相同點?與不同點?
七:Kotlin語法中常見設計模式實現(xiàn)
7.1、kotlin中,如何多種方式實現(xiàn)單例模式?
7.2、kotlin中,有哪些語法結構就可以方便實現(xiàn)特定的設計模式?
八:其他相關
8.1、kotlin DSL是什么?相關使用場景有哪些?
一:Kotlin產(chǎn)生背景、介紹、與java項目遷移相關
1.1、谷歌推出Kotlin的核心原因有哪些?
谷歌推出Kotlin的核心原因可從技術革新、生態(tài)戰(zhàn)略、法律規(guī)避和市場競爭四個維度深入解析,其背后是對開發(fā)者效率、平臺競爭力和長期技術布局的綜合考量:
一、技術革新:解決Java痛點,重構開發(fā)體驗
空安全與語法簡化:語法簡化使代碼量減少40%-60%,顯著提升開發(fā)效率。
函數(shù)式編程與協(xié)程:Kotlin支持Lambda表達式、擴展函數(shù)和數(shù)據(jù)類等現(xiàn)代特性,其協(xié)程(Coroutines)更是革命性地簡化了異步編程。??
與Java的無縫互操作:Kotlin可直接調用Java代碼,反之亦然。
二、生態(tài)戰(zhàn)略:構建全棧開發(fā)閉環(huán)
移動端與云服務的協(xié)同:谷歌在GCP(Google?Cloud?Platform)中大力推廣Kotlin,提供Kotlin示例項目覆蓋云函數(shù)、Firestore、Pub/Sub等服務,這種整合使開發(fā)者能用同一語言開發(fā)移動端和后端,提升全棧開發(fā)效率。
跨平臺開發(fā)布局:Kotlin?Multiplatform(KMP)允許一套代碼編譯為Android、iOS、Web和桌面應用,例如,共享業(yè)務邏輯:這一特性幫助開發(fā)者減少70%的重復代碼,與谷歌的跨平臺戰(zhàn)略(如Flutter)形成互補。
開源與社區(qū)驅動:Kotlin由JetBrains開發(fā)并開源,谷歌通過Kotlin基金會(2017年成立)與JetBrains合作,推動語言迭代。
三、法律規(guī)避:擺脫Oracle的Java專利枷鎖
Java?API版權訴訟
OpenJDK與Kotlin的雙重保障
四、市場競爭:對標Swift與React?Native
與Swift爭奪開發(fā)者
應對React?Native等跨平臺方案:React?Native的JavaScript生態(tài)對Android原生開發(fā)構成威脅,Kotlin通過Kotlin/JS(編譯為JavaScript)和Kotlin?Multiplatform,允許開發(fā)者用同一語言編寫跨平臺代碼,同時保持原生性能,谷歌希望通過Kotlin統(tǒng)一移動端、云端和AI開發(fā),形成技術閉環(huán)。
總結:谷歌推動Kotlin的本質是構建一個高效、安全、開源、跨平臺且法律風險可控的技術生態(tài),其核心價值體現(xiàn)如下。
開發(fā)者效率:語法簡化與工具鏈優(yōu)化提升生產(chǎn)力。
平臺競爭力:通過Kotlin-first策略鞏固Android生態(tài)。
法律安全:規(guī)避Java專利風險,確保長期技術自主性。
技術前瞻:協(xié)程、跨平臺等特性引領開發(fā)范式變革。
生態(tài)整合:與GCP、Jetpack?Compose等產(chǎn)品深度協(xié)同。
這一戰(zhàn)略不僅重塑了Android開發(fā)格局,更使Kotlin成為谷歌技術布局中的“通用語言”,為未來的云原生、AI和跨平臺開發(fā)奠定基礎。
1.2、Kotlin開發(fā)語言相比Java開發(fā)語言,有什么新特性和優(yōu)勢?
一:語法簡潔性 空安全機制:在Java里,空指針異常是常見問題,開發(fā)者需要進行大量的空值檢查,而Kotlin通過類型系統(tǒng)區(qū)分可空和非空類型,從編譯層面減少了空指針異常的出現(xiàn)。
數(shù)據(jù)類:Kotlin的數(shù)據(jù)類能自動生成equals()、hashCode()、toString()等方法。
擴展函數(shù):Kotlin允許為現(xiàn)有的類添加新的函數(shù),無需繼承或修改原始類。
二:函數(shù)式編程支持
Lambda表達式:Kotlin支持簡潔的Lambda表達式,能更方便地進行函數(shù)式編程。
高階函數(shù):Kotlin允許函數(shù)作為參數(shù)傳遞或返回值返回,這在Java中實現(xiàn)起來較為復雜。
三:協(xié)程異步編程
Kotlin的協(xié)程提供了一種更簡潔、高效的異步編程方式,能夠避免回調地獄,使異步代碼看起來像同步代碼。
四:互操作性
Kotlin和Java具有良好的互操作性,可在Kotlin代碼中直接調用Java代碼,反之亦然。
五:智能類型轉換
Kotlin的智能類型轉換功能,能在條件判斷后自動進行類型轉換,無需手動強制類型轉換。
六:密封類
密封類用于限制類的繼承結構,在Kotlin中,密封類的子類必須在同一文件中定義,這在進行模式匹配時非常有用,能確保所有可能的情況都被處理。
綜上所述,Kotlin憑借簡潔的語法、對函數(shù)式編程的支持、高效的異步編程方式等特性,在現(xiàn)代軟件開發(fā)中具有顯著優(yōu)勢。
1.3、谷歌對Kotlin開發(fā)語言的支持具體有哪些方面?
谷歌對Kotlin編程語言的支持是全面且深入的,涵蓋戰(zhàn)略定位、工具鏈、生態(tài)整合、社區(qū)建設等多個層面,旨在推動kotlin成為現(xiàn)代開發(fā)的核心語言之一,以下是具體支持方面的詳細說明:
一、戰(zhàn)略定位與官方背書
1、Android開發(fā)首選語言
2、內部項目全面遷移
二、工具鏈與開發(fā)環(huán)境支持
1、Android studio深度集成
2、Gradle構建系統(tǒng)支持
三、生態(tài)系統(tǒng)整合
1、Jetpack組件優(yōu)先適配
2、Google Cloud支持
3、Flutter與跨平臺開發(fā)
四:文檔、教程與培訓
1、官方文檔與指南
2、培訓與認知
五:社區(qū)與開發(fā)者生態(tài)建設
1、I/O大會與技術布道
2、贊助與開源項目
3、開發(fā)者社區(qū)活動
六:長期維護與版本兼容
1、Android系統(tǒng)級適配
2、與Java的互操作性保障
1.4、kotlin中,提出函數(shù)式編程思想有什么特點和優(yōu)勢?
Kotlin中的函數(shù)式編程思想融合了函數(shù)式編程范式的特性,具有以下特點和優(yōu)勢:
1、一等公民的函數(shù)
在Kotlin里,函數(shù)是一等公民,這意味著函數(shù)可以作為參數(shù)傳遞給其他函數(shù)、作為返回值返回,還能賦值給變量。
案例:
//?定義一個函數(shù),將另一個函數(shù)作為參數(shù)
fun?operateOnNumbers(a:?Int,?b:?Int,?operation:?(Int,?Int)?->?Int):?Int?{
????return?operation(a,?b)
}
//?定義一個加法函數(shù)
fun?add(a:?Int,?b:?Int):?Int?{
????return?a?+?b
}
fun?main()?{
????val?result?=?operateOnNumbers(3,?5,?::add)
????println(result)?
}
2、不可變數(shù)據(jù)
函數(shù)式編程強調使用不可變數(shù)據(jù),Kotlin也支持這一特性,使用val關鍵字聲明的變量是不可變的,這有助于減少副作用,提高代碼的可維護性和線程安全性。
3、純函數(shù)
純函數(shù)是指沒有副作用的函數(shù),即函數(shù)的輸出只依賴于輸入,并且不會修改外部狀態(tài)。
4、高階函數(shù)
高階函數(shù)是指可以接收函數(shù)作為參數(shù)或返回函數(shù)的函數(shù),Kotlin提供了豐富的高階函數(shù),如map、filter、reduce等,這些函數(shù)可以方便地對集合進行操作。
5、Lambda表達式
Kotlin支持Lambda表達式,它是一種簡潔的函數(shù)定義方式,Lambda表達式可以作為參數(shù)傳遞給高階函數(shù),使代碼更加簡潔。
優(yōu)勢:
1、代碼簡潔易讀
函數(shù)式編程的特性,如高階函數(shù)和Lambda表達式,使得代碼更加簡潔,減少了樣板代碼,例如,使用map和filter函數(shù)可以在一行代碼中完成復雜的集合操作,提高了代碼的可讀性。
2、可維護性高
由于函數(shù)式編程強調不可變數(shù)據(jù)和純函數(shù),代碼的副作用減少,使得代碼更易于理解和維護,純函數(shù)的輸出只依賴于輸入,不會受到外部狀態(tài)的影響,因此更容易進行單元測試和調試。
3、并行處理能力強
不可變數(shù)據(jù)和純函數(shù)的特性使得函數(shù)式編程的代碼更容易進行并行處理,因為純函數(shù)不會修改外部狀態(tài),多個函數(shù)可以并行執(zhí)行而不會產(chǎn)生競態(tài)條件,提高了程序的性能。
4、易于組合和復用
函數(shù)式編程中的函數(shù)可以像積木一樣進行組合,形成更復雜的功能,高階函數(shù)可以將不同的函數(shù)組合在一起,實現(xiàn)代碼的復用,例如,可以將多個map和filter函數(shù)組合起來,對集合進行多次轉換和過濾。
5、提高代碼的抽象層次
函數(shù)式編程鼓勵使用抽象的概念,如高階函數(shù)和泛型,提高了代碼的抽象層次,這使得代碼更加通用,能夠處理不同類型的數(shù)據(jù),減少了代碼的重復。
綜上所述,Kotlin中的函數(shù)式編程思想為開發(fā)者提供了一種簡潔、可維護、高性能的編程方式,有助于提高開發(fā)效率和代碼質量。
1.5、在Android項目開發(fā)過程中,在java與Kotlin的項目中,兩種語言的交互過程中,有哪些需要注意的點或者需要了解的知識點?
在Android項目中進行Java與Kotlin的交互時,有不少需要關注的知識點和注意事項:
一:基礎語法差異
空安全:Kotlin有嚴格的空安全機制,而Java沒有,在Java調用Kotlin代碼時,要注意Kotlin的可空類型,在Java代碼里調用時,需要進行空值檢查。
數(shù)據(jù)類:Kotlin的數(shù)據(jù)類會自動生成equals()、hashCode()、toString()等方法,而Java需要手動實現(xiàn),在Java中調用Kotlin數(shù)據(jù)類時,可直接使用這些自動生成的方法。
二:函數(shù)調用
默認參數(shù)與命名參數(shù):Kotlin支持默認參數(shù)和命名參數(shù),Java則不支持,若要在Java中調用帶有默認參數(shù)的Kotlin函數(shù),需要手動指定所有參數(shù)。
擴展函數(shù):Kotlin的擴展函數(shù)在Java中沒有直接對應的概念,在Java中調用Kotlin的擴展函數(shù)時,需要使用靜態(tài)方法調用的方式。
三:異常處理
受檢異常與非受檢異常:Java有受檢異常和非受檢異常的區(qū)別,而Kotlin沒有受檢異常,在Kotlin調用Java代碼時,需要處理Java方法可能拋出的受檢異常。
四:集合類型
集合的可變性:Kotlin的集合類型分為可變集合和不可變集合,而Java沒有明確區(qū)分,在交互時,要注意集合的可變性。
五:線程與協(xié)程
異步編程方式:Java傳統(tǒng)上使用線程、Future、CompletableFuture等進行異步編程,而Kotlin有協(xié)程,在交互時,要注意兩種異步編程方式的轉換,如果在Kotlin中調用Java的異步方法,需要手動處理回調,如果在Java中調用Kotlin的協(xié)程方法,需要理解協(xié)程的生命周期和調度機制。
六:類和接口的定義
抽象類和接口的差異:Kotlin的接口可以有默認實現(xiàn),而Java在JDK 8之前接口不能有默認實現(xiàn),在交互時,要注意接口方法的實現(xiàn)情況。
二:基礎語法解析
2.1、kotlin中,頂層函數(shù)與頂層屬性的作用是什么與使用場景有哪些?提供具體的案例實現(xiàn)?
在Kotlin里,頂層函數(shù)與頂層屬性能夠直接定義于文件中,而無需依賴類。
頂層函數(shù)
簡化代碼結構:無需將函數(shù)封裝在類中,能讓代碼更簡潔,減少不必要的類定義。
提高代碼復用性:可在不同類中方便地調用頂層函數(shù),增強代碼的復用能力。
使用場景
工具函數(shù):像字符串處理、日期轉換等通用工具函數(shù)。
輔助函數(shù):在多個類里都可能用到的輔助邏輯,例如日志記錄、數(shù)據(jù)驗證等。
案例實現(xiàn):
下面是一個包含字符串處理工具函數(shù)的Kotlin文件StringUtils.kt:
//?去除字符串前后的空格
fun?String.trimWhitespace():?String?{
????return?this.trim()
}
//?檢查字符串是否為回文
fun?String.isPalindrome():?Boolean?{
????return?this?==?this.reversed()
}
你可以在其他Kotlin文件中使用這些頂層函數(shù):
fun?main()?{
????val?str?=?"??Hello?World??"
????val?trimmedStr?=?str.trimWhitespace()
????println("Trimmed?string:?$trimmedStr")
????val?palindromeStr?=?"radar"
????val?isPalindrome?=?palindromeStr.isPalindrome()
????println("Is?'$palindromeStr'?a?palindrome??$isPalindrome")
}
頂層屬性
全局狀態(tài)管理:能夠在整個項目中共享某個屬性的值。
配置參數(shù)管理:用于存儲一些全局的配置信息,如API地址、默認設置等。
使用場景
全局常量:像應用的版本號、默認的超時時間等常量。
全局變量:在某些情況下,需要在不同類中共享一個可變的狀態(tài)。
案例實現(xiàn)
以下是一個包含全局常量和全局變量的Kotlin文件AppConfig.kt:
//?全局常量
const?val?APP_VERSION?=?"1.0.0"
//?全局變量
var?apiBaseUrl:?String?=?"https://example.com/api"
//?初始化配置的函數(shù)
fun?initConfig(newApiBaseUrl:?String)?{
????apiBaseUrl?=?newApiBaseUrl
}
你可以在其他Kotlin文件中使用這些頂層屬性:
fun?main()?{
????println("App?version:?$APP_VERSION")
????println("Current?API?base?URL:?$apiBaseUrl")
????//?初始化配置
????initConfig("https://new-example.com/api")
????println("New?API?base?URL:?$apiBaseUrl")
}
綜上所述,頂層函數(shù)和頂層屬性能夠提升代碼的簡潔性與復用性,適合用于實現(xiàn)通用工具、管理全局狀態(tài)等場景。?
2.2、kotlin中,頂層函數(shù)與伴生對象函數(shù)的區(qū)別是什么?
一:定義位置
頂層函數(shù):頂層函數(shù)直接定義在Kotlin文件中,不隸屬于任何類,它可以獨立存在于文件的頂層,與類處于平級關系。
伴生對象函數(shù):伴生對象函數(shù)定義在類的伴生對象里,伴生對象是類內部的一個特殊對象,每個類只能有一個伴生對象。
二:作用域
頂層函數(shù):頂層函數(shù)的作用域是整個包,只要在同一個包或者通過導入的方式,就可以在其他文件中調用該頂層函數(shù)。
伴生對象函數(shù):伴生對象函數(shù)的作用域局限于定義它的類,它與類緊密相關,通常用于實現(xiàn)與該類相關的靜態(tài)方法,在使用伴生對象函數(shù)時,需要通過類名來調用。
三:使用場景
頂層函數(shù):適合用于實現(xiàn)通用的工具函數(shù),這些函數(shù)不依賴于特定類的狀態(tài),可在多個類中復用。
伴生對象函數(shù):常用于實現(xiàn)工廠方法、單例模式等與類創(chuàng)建和初始化相關的邏輯,它可以訪問類的私有構造函數(shù),方便創(chuàng)建類的實例。
四:訪問權限
頂層函數(shù):頂層函數(shù)的訪問權限默認是public,可以在包內和包外被訪問,如果需要限制訪問范圍,可以使用internal、private等修飾符。
伴生對象函數(shù):伴生對象函數(shù)可以使用不同的訪問修飾符,如private、protected、internal等,使用private修飾時,只能在類內部訪問,使用protected修飾時,只有該類及其子類可以訪問,使用internal修飾時,只能在同一個模塊內訪問。
五:與類的關系
頂層函數(shù):頂層函數(shù)與類沒有直接的關聯(lián),它是獨立于類存在的,即使類被刪除或修改,只要不影響頂層函數(shù)的調用,頂層函數(shù)仍然可以正常使用。
伴生對象函數(shù):伴生對象函數(shù)與類緊密相關,它可以訪問類的靜態(tài)成員和私有成員。伴生對象函數(shù)通常用于實現(xiàn)與類相關的邏輯,是類的一部分。
2.3、kotlin中,Any、Unit、Nothing的作用分別是什么?
Any:
Any是Kotlin中所有非空類型的根類型,類似于Java里的Object類,這意味著Kotlin中的任何非空類都直接或間接地繼承自Any,不過,Any只包含三個基本方法:equals()、hashCode()?和?toString(),不像Java 的Object類有更多的方法(如?wait()、notify()?等)。
使用場景
通用類型參數(shù):當你需要編寫一個可以處理任意類型對象的函數(shù)或類時,可使用Any作為類型參數(shù)。
類型檢查與轉換:在進行類型檢查和轉換時,Any可作為基礎類型。
Unit:
Unit相當于Java中的void,它是一種只有一個值的類型,這個值就是Unit本身,在Kotlin里,當一個函數(shù)沒有返回值時,默認返回類型就是Unit,不過在函數(shù)定義時通??梢允÷圆粚?。
使用場景
無返回值函數(shù):當函數(shù)不需要返回任何有意義的數(shù)據(jù)時,可將返回類型指定為Unit或省略返回類型。
Lambda表達式:在Lambda表達式中,如果不需要返回值,可使用Unit。
Nothing:
Nothing是一種沒有任何值的類型,它表示該類型永遠不會有實例,通常用于那些永遠不會正常返回的函數(shù),比如會拋出異?;蛘哌M入無限循環(huán)的函數(shù)。
使用場景
異常拋出函數(shù):當函數(shù)總是拋出異常時,可將返回類型指定為Nothing。
無限循環(huán)函數(shù):對于那些會進入無限循環(huán)且不會返回的函數(shù),也可使用Nothing作為返回類型。
綜上所述,Any?是所有非空類型的基類型,Unit用于表示無返回值,Nothing表示函數(shù)不會正常返回。
2.4、kotlin中,如何實現(xiàn)對象的序列化與反序列化的?提供一個具體案例demo與分析?
使用kotlinx.serialization庫
kotlinx.serialization是Kotlin官方提供的序列化庫,它支持多種格式(如JSON、Protobuf等),并且可以通過注解來控制序列化和反序列化的行為。
示例代碼:
//?使用?@Serializable注解
@Serializable
data?class?Person(val?name:?String,?val?age:?Int)
fun?main()?{
????//?創(chuàng)建一個?Person?對象
????val?person?=?Person("John",?30)
????//?序列化對象為?JSON?字符串
????val?jsonString?=?Json.encodeToString(person)
????println("Serialized?JSON:?$jsonString")
????//?從?JSON?字符串反序列化對象
????val?deserializedPerson?=?Json.decodeFromString<Person>(jsonString)
????println("Deserialized?object:?$deserializedPerson")
}
代碼分析
類定義:Person?類使用?@Serializable?注解標記,表明該類可以被序列化和反序列化。
序列化過程:使用?Json.encodeToString?方法將?Person?對象轉換為?JSON?字符串。
反序列化過程:使用?Json.decodeFromString?方法從JSON字符串中恢復Person對象。
kotlinx.serialization:是Kotlin官方提供的序列化庫,支持多種格式,具有更好的靈活性和跨語言能力,適合現(xiàn)代的開發(fā)場景。
2.5、kotlin中,反射相關涉及到的類有哪些?提供一個反射相關的案例demo?
在Kotlin里,反射機制能讓程序在運行時獲取類、方法、屬性等信息,并且可以動態(tài)調用方法、訪問屬性。
反射相關的類:
KClass:表示Kotlin類,類似于Java中的Class類,可通過對象的::class或者類名的::class來獲取KClass實例。
KFunction:代表Kotlin函數(shù),能通過KClass獲取類中的函數(shù)信息,還能動態(tài)調用函數(shù)。
KProperty:表示Kotlin屬性,可用于獲取類中屬性的信息,也能動態(tài)訪問屬性。
案例:
class?Person(val?name:?String,?var?age:?Int)?{
????fun?sayHello()?{
????????println("Hello,?my?name?is?$name?and?I'm?$age?years?old.")
????}
????fun?addAge(years:?Int):?Int?{
????????age?+=?years
????????return?age
????}
}
fun?main()?{
????val?person?=?Person("John",?30)
????val?kClass?=?person::class
????println("Class?name:?${kClass.simpleName}")
????//?獲取并調用sayHello方法
????val?sayHelloFunction?=?kClass.functions.find?{?it.name?==?"sayHello"?}
????sayHelloFunction?.call(person)
????//?獲取并調用?addAge?方法
????val?addAgeFunction?=?kClass.functions.find?{?it.name?==?"addAge"?}
????val?newAge?=?addAgeFunction?.call(person,?5)
????println("New?age:?$newAge")
????//?獲取并訪問name屬性
????val?nameProperty?=?kClass.memberProperties.find?{?it.name?==?"name"?}
????val?name?=?nameProperty?.get(person)
????println("Name:?$name")
????//?獲取并修改?age?屬性
????val?ageProperty?=?kClass.memberProperties.find?{?it.name?==?"age"?}?as??kotlin.reflect.KMutableProperty<Int>
????ageProperty?.set(person,?35)
}????
代碼分析:
獲取KClass實例:借助person::class得到Person類的KClass實例,從而獲取類的相關信息。
調用方法:利用kClass.functions.find找到指定名稱的方法,再通過call方法調用該方法。
訪問屬性:使用kClass.memberProperties.find找到指定名稱的屬性,使用get方法獲取屬性的值,對于可變屬性,還能使用set方法修改屬性的值。
通過這個案例,你可以了解到如何在Kotlin中使用反射機制來動態(tài)獲取類的信息、調用方法以及訪問屬性。
2.6、kotlin中的可見性修飾符與java的可見性修飾符有什么區(qū)別?
修飾符種類:
Java:Java有四種可見性修飾符,分別是public、protected、private和默認(沒有顯式修飾符)。
public:元素可以被任何類訪問。
protected:元素可以被同一個包內的類以及不同包中的子類訪問。
private:元素只能在定義它的類內部被訪問。
默認(無修飾符):元素可以被同一個包內的類訪問。
Kotlin:Kotlin有五種可見性修飾符,分別是public、protected、private、internal和默認(沒有顯式修飾符)。
public:與Java中的public類似,元素可以被任何類訪問。
protected:在類內部,與Java中的protected類似,元素可以被同一個類及其子類訪問,但在Kotlin中,protected成員不能在包級訪問。
private:元素只能在定義它的類、文件內部(對于頂層聲明)被訪問。
internal:元素可以在同一個模塊內被訪問,模塊可以是一個Gradle項目、Maven項目等。
默認(無修飾符):默認是public。
2.7、kotlin中,主構造器、次構造器等相關的知識點有哪些?
一:主構造器
主構造器是類頭的一部分,位于類名之后,使用constructor關鍵字定義,不過該關鍵字在多數(shù)情況下可省略,主構造器的參數(shù)可使用val或var修飾,這樣就能直接將其聲明為類的屬性。
初始化塊:主構造器不能包含任何代碼,若要進行初始化操作,可使用初始化塊(init?塊),初始化塊會在主構造器執(zhí)行時被調用。
二:主構造器參數(shù)的默認值
主構造器的參數(shù)可以設置默認值,這樣在創(chuàng)建類的實例時,如果不提供該參數(shù)的值,就會使用默認值。
三:次構造器
次構造器使用constructor關鍵字定義,位于類體內部,每個類可以有多個次構造器,次構造器必須直接或間接地調用主構造器。
次構造器必須通過?this()?關鍵字調用同一個類的其他構造器(可以是主構造器或其他次構造器),以確保主構造器被調用。
四:主構造器和次構造器的使用場景
主構造器:適用于類的基本屬性初始化,特別是當這些屬性在創(chuàng)建對象時就需要確定值的情況,它能讓類的定義更加簡潔,避免在類體中重復聲明屬性。
次構造器:當需要提供多種創(chuàng)建對象的方式時,可使用次構造器,例如,有些情況下只需要部分屬性的值,或者需要根據(jù)不同的參數(shù)組合來創(chuàng)建對象。
注意事項
若類有主構造器,類體中的屬性初始化語句和初始化塊會在主構造器調用之后執(zhí)行。
若類沒有主構造器,次構造器不需要調用其他構造器。
案例:
class?Person(private val?name:?String?=?"Unknown",?var?age:?Int?=?0)?{
????//?第一個次構造器
????constructor(name:?String)?:?this(name,?0)?{
????????println("Creating?person?with?name?only:?$name")
????}
????//?第二個次構造器
????constructor()?:?this("Unknown")?{
????????println("Creating?person?with?default?values")
????}
}
2.8、kotlin中,什么是類委托?類委托的作用是什么?提供具體案例?
一:類委托的概念
在Kotlin里,類委托是一種實現(xiàn)代碼復用和組合的機制,它遵循委托模式,允許一個類將其部分或全部的功能委托給另一個對象來完成,而不是通過繼承的方式,Kotlin提供了簡潔的語法來實現(xiàn)類委托,使用by關鍵字就能把接口的實現(xiàn)委托給另一個對象。
二:類委托的作用
代碼復用:借助委托模式,能夠把公共的功能封裝到一個委托類中,多個類可以共享這個委托類的功能,避免代碼重復。
降低耦合度:類委托可以降低類之間的耦合度,一個類可以通過委托給不同的對象來實現(xiàn)不同的功能,而不需要繼承一個龐大的基類。
增強靈活性:在運行時能夠動態(tài)地改變委托對象,從而改變類的行為。
三:具體案例
假設我們要實現(xiàn)一個Set接口,并且希望在添加元素時記錄日志,可以使用類委托來實現(xiàn)這個功能。
//?定義一個記錄日志的Set類,委托給HashSet實現(xiàn)Set接口
class?LoggingSet<T>(private?val?innerSet:?MutableSet<T>?=?HashSet())?:?MutableSet<T>?by?innerSet?{
????override?fun?add(element:?T):?Boolean?{
????????println("Adding?element:?$element")
????????return?innerSet.add(element)
????}
????override?fun?addAll(elements:?Collection<T>):?Boolean?{
????????println("Adding?all?elements:?$elements")
????????return?innerSet.addAll(elements)
????}
}
fun?main()?{
????val?loggingSet?=?LoggingSet<Int>()
????loggingSet.add(1)
????loggingSet.addAll(listOf(2,?3,?4))
????println(loggingSet.size)?
}????
代碼解釋:
委托的實現(xiàn):LoggingSet類實現(xiàn)了MutableSet<T>接口,使用by?innerSet把MutableSet接口的實現(xiàn)委托給innerSet對象(這里是HashSet),這意味著LoggingSet類會自動獲得HashSet實現(xiàn)的MutableSet接口的所有方法。
功能擴展:LoggingSet類重寫了add和addAll方法,在添加元素之前記錄日志,然后調用委托對象innerSet的相應方法來完成實際的添加操作。
使用委托類:在main函數(shù)中,創(chuàng)建了一個LoggingSet實例,調用add和addAll方法時會記錄日志,同時可以使用MutableSet接口的其他方法,如size。
通過類委托,LoggingSet類在復用HashSet功能的基礎上,添加了日志記錄的功能,同時保持了代碼的簡潔和可維護性。
2.9、kotlin中,什么是屬性委托?屬性委托的作用是什么?提供具體案例?
一:屬性委托的概念
在Kotlin里,屬性委托是一種機制,它允許將屬性的?get()?和?set()?方法的實現(xiàn)委托給另一個對象,借助by關鍵字,可把屬性的訪問邏輯委托給一個委托對象,從而避免在每個屬性中重復編寫相同的訪問邏輯。
二:屬性委托的作用
代碼復用:能把屬性的訪問邏輯封裝到委托對象中,多個屬性可以復用這個委托對象,減少代碼重復。
簡化代碼:讓屬性的定義更簡潔,將復雜的訪問邏輯隱藏在委托對象里,使類的定義更清晰。
提高可維護性:當屬性的訪問邏輯需要修改時,只需修改委托對象的實現(xiàn),而不用在每個使用該邏輯的屬性處修改。
具體案例
1、延遲初始化屬性
使用lazy()?函數(shù)實現(xiàn)屬性的延遲初始化,這是Kotlin標準庫提供的一種屬性委托方式。
????//?使用?lazy()?函數(shù)進行屬性委托,實現(xiàn)延遲初始化
????val?lazyValue:?String?by?lazy?{
????????println("Initializing?lazyValue")
????????"Lazy?Initialized?Value"
????}
2、可觀察屬性
使用Delegates.observable()?實現(xiàn)可觀察屬性,當屬性值發(fā)生變化時會觸發(fā)相應的回調。
//?使用?Delegates.observable()?進行屬性委托,實現(xiàn)可觀察屬性
????var?name:?String?by?Delegates.observable("Initial?Name")?{
????????property,?oldValue,?newValue?->
????????println("Property?${property.name}?changed?from?$oldValue?to?$newValue")
????}
代碼解釋:
Delegates.observable()?函數(shù)接收一個初始值和一個回調函數(shù)。
當name屬性的值發(fā)生變化時,會調用回調函數(shù),打印屬性名、舊值和新值。
3、把屬性存儲在映射中
可以將屬性的值存儲在一個映射中,使用映射作為委托對象。
class?User(val?map:?MutableMap<String,?Any?>)?{
????//?將屬性委托給映射
????val?name:?String?by?map
????val?age:?Int?by?map
}
fun?main()?{
????val?userMap?=?mutableMapOf(
????????"name"?to?"John",
????????"age"?to?30
????)
????val?user?=?User(userMap)
????println(user.name)?
????println(user.age)?
}????
代碼解釋:
User類的name和age屬性的訪問邏輯被委托給map對象。
訪問屬性時,會從map中獲取相應的值。
通過這些案例可以看出,屬性委托能讓代碼更加簡潔、靈活,提高代碼的可維護性和復用性。
2.10、kotlin中,var、val、const的區(qū)別是什么?
var:用于聲明可變變量,適用于需要動態(tài)更新值的場景。
val:用于聲明只讀變量,保證變量初始化后值不會被修改,支持延遲初始化。
const:用于聲明編譯時常量,要求在編譯時確定值,只能在頂層或object類中使用,可提高程序性能。
2.11、kotlin中,lateInit與by lazy的區(qū)別是什么?
lateinit:
適用變量類型:lateinit只能用于var修飾的非空類型變量,不能用于val修飾的變量,因為val變量一旦初始化就不能再修改,同時,該變量不能是基本數(shù)據(jù)類型(如Int、Double等),因為基本數(shù)據(jù)類型在Kotlin中沒有對應的可空包裝類型,必須在聲明時初始化。
初始化時機:lateinit變量在聲明時并未初始化,而是在后續(xù)的代碼中手動進行初始化,在初始化之前,如果訪問該變量,會拋出UninitializedPropertyAccessException異常。
線程安全:lateinit本身不具備線程安全機制,如果多個線程同時嘗試初始化或訪問lateinit變量,可能會引發(fā)競態(tài)條件,導致程序出現(xiàn)不可預期的結果,因此,在多線程環(huán)境下使用lateinit變量時,需要手動進行同步控制。
使用場景:常用于在類的構造函數(shù)執(zhí)行完成后,再對變量進行初始化的情況,例如在依賴注入框架中,對象的依賴項可能在構造之后才被注入,或者在Android開發(fā)里,一些視圖控件在onCreate方法調用之后才會被初始化。
by?lazy:
適用變量類型:by?lazy主要用于val修飾的變量,因為它實現(xiàn)的是延遲初始化的只讀屬性,不過,也可以用于var變量,但需要自定義委托,一般較少這樣使用。
初始化時機:by?lazy變量在第一次被訪問時才會進行初始化,一旦初始化完成,后續(xù)訪問該變量將直接返回已初始化的值,不會再次執(zhí)行初始化代碼。
使用場景:適用于某些計算開銷較大或者依賴其他對象初始化的屬性,希望在第一次訪問該屬性時才進行初始化,以此提高程序的性能。
線程安全:by?lazy默認是線程安全的,lazy()?函數(shù)有三種不同的模式,默認模式(LazyThreadSafetyMode.SYNCHRONIZED)會確保在多線程環(huán)境下,變量的初始化代碼只被執(zhí)行一次,避免了競態(tài)條件,如果不需要線程安全,可以使用LazyThreadSafetyMode.PUBLICATION或LazyThreadSafetyMode.NONE模式。
2.12、kotin中,?、?.、?:、??!、::相關操作符的作用是什么?具體使用案例?
?:可空類型聲明
??用于聲明一個變量或參數(shù)可以為null,在Kotlin里,默認情況下變量是不可為null的,若要允許變量為?null,就需要在類型后面加上??。
?.:安全調用操作符
?.?用于安全地調用對象的方法或訪問對象的屬性,當對象為null時,不會拋出NullPointerException,而是直接返回null。
?::Elvis操作符
?:?用于在對象為null時提供一個默認值,若左邊的表達式為null,則返回右邊的表達式的值。
!!:非空斷言操作符
!!?用于斷言一個可空類型的變量不為null若該變量為null,則會拋出NullPointerException。
:::引用操作符
::?用于引用類、屬性、方法等,可以將類、屬性或方法作為對象傳遞,常用于函數(shù)式編程、反射等場景。
綜上所述,這些操作符在Kotlin中能幫助你更安全、便捷地處理null值,以及進行函數(shù)式編程和反射操作。
2.13、kotlin中,如何使用get約定函數(shù)初始化變量時?好處是什么?
在Kotlin里,你可以通過定義get約定函數(shù)(即自定義getter方法)來初始化變量。
優(yōu)點一:延遲初始化
使用get約定函數(shù)可以實現(xiàn)屬性的延遲初始化,也就是說,屬性的值不會在對象創(chuàng)建時就計算,而是在第一次訪問該屬性時才進行計算,這對于一些計算開銷較大或者依賴其他對象初始化的屬性非常有用,可以提高程序的性能。
案例:
class?MyClass?{
????val?heavyObject:?HeavyObject
????????get()?{
????????????println("Getting?heavyObject...")
????????????return?HeavyObject()
????????}
}
在上述代碼中,HeavyObject?的初始化是在第一次訪問myClass.heavyObject時才進行的,避免了在MyClass對象創(chuàng)建時就進行不必要的初始化。
優(yōu)點二:動態(tài)計算屬性值
get約定函數(shù)可以根據(jù)對象的其他屬性或狀態(tài)動態(tài)計算屬性的值,這樣可以確保屬性的值始終是最新的,并且避免了手動更新屬性值的麻煩。
案例:
class?Rectangle(val?width:?Int,?val?height:?Int)?{
????val?area:?Int
????????get()?=?width?*?height
}
在這個例子中,area屬性的值是根據(jù)width和height動態(tài)計算的,當width或height發(fā)生變化時,area的值會自動更新。
優(yōu)點三:封裝和控制訪問
通過自定義get方法,可以對屬性的訪問進行封裝和控制,可以在get方法中添加額外的邏輯,如權限檢查、數(shù)據(jù)驗證等,從而提高代碼的安全性和可維護性。
案例:
class?User(private?val?isAdmin:?Boolean)?{
????val?secretData:?String
????????get()?{
????????????if?(isAdmin)?{
????????????????return?"This?is?secret?data."
????????????}?else?{
????????????????return?"You?don't?have?access?to?secret?data."
????????????}
????????}
}
在這個例子中,只有管理員用戶(isAdmin?為?true)才能訪問secretData,通過get方法實現(xiàn)了對屬性訪問的控制。
綜上所述,使用get約定函數(shù)初始化變量可以實現(xiàn)延遲初始化、動態(tài)計算屬性值以及封裝和控制訪問等功能,提高代碼的性能、可維護性和安全性。
2.14、kotlin中,Lambda表達式與帶參數(shù)的Lambda表達式的區(qū)別?相關的使用場景有哪些?
Lambda表達式:
Lambda表達式是Kotlin中一種簡潔表示匿名函數(shù)的方式,它沒有名稱,通常用于需要傳遞一個簡短函數(shù)作為參數(shù)的場景,其基本語法結構為?{?參數(shù)列表?->?函數(shù)體?},如果參數(shù)列表為空,可以省略?->。
帶參數(shù)的Lambda表達式:
帶參數(shù)的Lambda表達式是Lambda表達式的一種具體形式,它明確指定了參數(shù)列表,參數(shù)列表位于?{?和?->?之間,函數(shù)體在?->?之后,通過參數(shù)列表,Lambda表達式可以接收外部傳遞的數(shù)據(jù)并進行處理。
案例:
fun?json(block:?JsonBuilder.()?->?Unit):?String?{
????val?builder?=?JsonBuilder()
????builder.block()
????return?builder.toString()
}
????val?person?=?Person("Alice",?25)
????val?jsonString?=?json?{
????????"name"?to?person.name
????????"age"?to?person.age
????}
????println(jsonString)
2.15、kotlin中,控制流相關關鍵字:if、for、when對比java,有什么新特性?
Kotlin中的if新特性:
作為表達式:Kotlin中的if不僅可以作為語句,還能作為表達式返回值,這讓代碼更加簡潔。
val?a?=?10
val?b?=?20
val?max?=?if?(a?>?b)?a?else?b
省略大括號:如果if`或else分支只有一條語句,可以省略大括號。
val?x?=?5
if?(x?>?3)?println("x?is?greater?than?3")
Kotlin中的for新特性:
區(qū)間遍歷:Kotlin?支持使用區(qū)間進行for循環(huán),提供了更簡潔的語法。
//?遍歷1到5的區(qū)間
for?(i?in?1..5)?{
????println(i)
}
//?倒序遍歷
for?(i?in?5?downTo?1)?{
????println(i)
}
//?帶步長的遍歷
for?(i?in?1..10?step?2)?{
????println(i)
}
遍歷集合時獲取索引:在遍歷集合時,可以方便地同時獲取元素和其索引。
val?list?=?listOf("apple",?"banana",?"cherry")
for?((index,?value)?in?list.withIndex())?{
????println("Index:?$index,?Value:?$value")
}
Kotlin中的when新特性:
強大的匹配能力:when可以匹配任意類型的表達式,不僅僅局限于switch支持的類型,還可以進行范圍匹配、類型匹配等。
val?num?=?2
when?(num)?{
????1?->?println("One")
????2?->?println("Two")
????in?3..10?->?println("Between?3?and?10")
????else?->?println("Other")
}
//?類型匹配
val?obj:?Any?=?"Hello"
when?(obj)?{
????is?String?->?println("It's?a?string:?$obj")
????is?Int?->?println("It's?an?integer:?$obj")
????else?->?println("Other?type")
}
作為表達式:when可以作為表達式返回值,使代碼更加簡潔。
val?num?=?2
val?result?=?when?(num)?{
????1?->?"One"
????2?->?"Two"
????else?->?"Other"
}
println(result)
綜上所述,Kotlin在控制流關鍵字上引入了許多新特性,使代碼更加簡潔、靈活和強大。?
2.16、kotlin中,帶標簽的break、continue的功能實現(xiàn)?提供具體案例分析?
在Kotlin里,break和continue關鍵字的功能和Java類似,用于控制循環(huán)的執(zhí)行流程,不過,Kotlin還引入了標簽的概念,讓break和continue能在嵌套循環(huán)或者Lambda表達式中更靈活地使用。
????outer@?for?(i?in?1..3)?{
????????for?(j?in?1..3)?{
????????????if?(i?==?2?&&?j?==?2)?{
????????????????break@outer
????????????}
????????????println("i?=?$i,?j?=?$j")
????????}
????}
????println("Outer?loop?ended.")
????outer@?for?(i?in?1..3)?{
????????for?(j?in?1..3)?{
????????????if?(i?==?2?&&?j?==?2)?{
????????????????continue@outer
????????????}
????????????println("i?=?$i,?j?=?$j")
????????}
????}
????println("Outer?loop?ended.")
Kotlin的break和continue關鍵字以及帶標簽的使用方式,為控制循環(huán)流程提供了強大而靈活的手段。
2.17、kotlin中,受檢異常與非受檢異常的區(qū)別?kotlin與java相關實現(xiàn)有什么區(qū)別嗎?
在Java和Kotlin等編程語言中,異常是程序運行時出現(xiàn)的錯誤情況,異常主要分為受檢異常(Checked?Exception)和非受檢異常(Unchecked?Exception)。
受檢異常(Checked?Exception)
定義:受檢異常通常代表程序外部的一些不可預測情況,如文件不存在、網(wǎng)絡連接失敗等,在Java中,這些異常是Exception類(不包括RuntimeException及其子類)的子類。
處理要求:編譯器會強制要求開發(fā)者對受檢異常進行處理,要么使用try-catch語句捕獲并處理,要么在方法簽名中使用throws關鍵字聲明拋出該異常。
示例場景:在進行文件操作時,F(xiàn)ileNotFoundException就是一個受檢異常,如果不處理該異常,代碼將無法通過編譯。
非受檢異常(Unchecked?Exception)
定義:非受檢異常通常是由程序的邏輯錯誤引起的,如空指針引用、數(shù)組越界等,在Java中,非受檢異常是RuntimeException及其子類,以及Error及其子類。
處理要求:編譯器不會強制要求開發(fā)者處理非受檢異常,開發(fā)者可以選擇處理,也可以不處理。
示例場景:當嘗試訪問一個空對象的方法時,會拋出NullPointerException,這是一個非受檢異常。
無受檢異常機制:Kotlin取消了受檢異常機制,所有異常都是非受檢異常,這意味著編譯器不會強制要求開發(fā)者處理異常。
fun?main()?{
????val?file?=?File("nonexistent.txt")
????try?{
????????val?fis?=?FileInputStream(file)
????}?catch?(e:?java.io.FileNotFoundException)?{
????????println("File?not?found:?${e.message}")
????}
}
在這個示例中,雖然FileInputStream構造函數(shù)可能會拋出FileNotFoundException,但Kotlin編譯器不會強制要求使用try-catch語句處理該異常,開發(fā)者可以選擇是否處理異常。
代碼更簡潔:由于取消了受檢異常機制,Kotlin代碼不需要在方法簽名中聲明拋出異常,使代碼更加簡潔,例如,在Java中,如果一個方法可能拋出受檢異常,需要在方法簽名中使用throws關鍵字聲明,而在Kotlin中,不需要在方法簽名中聲明異常:
fun?readFile()?{
????val?file?=?File("nonexistent.txt")
????val?fis?=?FileInputStream(file)
}
2.18、kotlin中,什么是伴生對象?伴生對象什么特性嗎?
伴生對象(Companion?Object)
與類關聯(lián):伴生對象是類內部的一個特殊對象,每個類只能有一個伴生對象,它與類緊密關聯(lián),可通過類名直接訪問其成員,類似于Java中的靜態(tài)成員。
可訪問類的私有成員:伴生對象可以訪問類的私有構造函數(shù)和私有成員。
可實現(xiàn)接口:伴生對象可以實現(xiàn)接口。
案例:
class?Person?private?constructor(val?name:?String,?val?age:?Int)?{
????companion?object?Factory?{
????????fun?createPerson(name:?String,?age:?Int):?Person?{
????????????return?Person(name,?age)
????????}
????}
}
2.19、kotlin中,標簽@的使用場景有哪些?提供具體案例分析?
在Kotlin里,標簽(@)有著多種使用場景,它能讓代碼的控制流更加清晰和靈活,還能用于指定接收者類型等。
一:在循環(huán)中使用標簽控制break和continue
在嵌套循環(huán)里,默認的break和continue語句僅對當前所在的循環(huán)起作用,若要控制外層循環(huán),就可以使用標簽。
fun?main()?{
????outer@?for?(i?in?1..3)?{
????????for?(j?in?1..3)?{
????????????if?(i?==?2?&&?j?==?2)?{
????????????????//?跳出外層循環(huán)
????????????????break@outer
????????????}
????????????println("i?=?$i,?j?=?$j")
????????}
????}
????println("Outer?loop?ended.")
????outer2@?for?(i?in?1..3)?{
????????for?(j?in?1..3)?{
????????????if?(i?==?2?&&?j?==?2)?{
????????????????//?跳過外層循環(huán)的本次迭代
????????????????continue@outer2
????????????}
????????????println("i?=?$i,?j?=?$j")
????????}
????}
????println("Another?outer?loop?ended.")
}
在這個例子中,使用outer和outer2標簽分別標記了外層的for循環(huán),當i等于2且j等于2時,break@outer語句會直接終止外層循環(huán),continue@outer2語句會跳過外層循環(huán)中剩余的代碼,直接進入外層循環(huán)的下一次迭代。
二:在Lambda表達式中使用標簽控制返回
在Lambda表達式里,默認的return語句會從包含該Lambda表達式的函數(shù)中返回,若要從Lambda表達式中返回,可以使用標簽。
fun?main()?{
????val?numbers?=?listOf(1,?2,?3,?4,?5)
????numbers.forEach?lit@{?num?->
????????if?(num?==?3)?{
????????????//?從?Lambda?表達式中返回
????????????return@lit
????????}
????????println(num)
????}
????println("forEach?loop?ended.")
}
在這個例子中,使用lit標簽標記了forEach函數(shù)中的Lambda表達式,當num等于3時,return@lit語句會從Lambda表達式中返回,繼續(xù)執(zhí)行forEach函數(shù)的下一次迭代。
三:指定接收者類型
在擴展函數(shù)或者Lambda表達式中,可以使用標簽來指定接收者類型,從而訪問特定接收者的成員。
class?Outer?{
????class?Inner?{
????????fun?printMessage()?{
????????????println("This?is?an?inner?class.")
????????}
????}
}
fun?Outer.Inner.printWithOuter()?{
????this@Inner.printMessage()
????//?這里可以使用標簽指定?Outer?類型的接收者
????//?假設?Outer?類有其他方法或屬性可以在這里訪問
}
fun?main()?{
????val?outer?=?Outer()
????val?inner?=?outer.Inner()
????inner.printWithOuter()
}
在這個例子中,printWithOuter是Outer.Inner的擴展函數(shù),在函數(shù)內部使用this@Inner明確指定接收者為Inner類的實例,從而調用Inner類的printMessage方法。
四:類的構造函數(shù)委托
在類的構造函數(shù)中,使用標簽可以明確指定委托的構造函數(shù)。
class?MyClass?constructor(a:?Int)?{
????constructor(a:?Int,?b:?Int)?:?this@MyClass(a)?{
????????//?構造函數(shù)委托
????}
}
在這個例子中,第二個構造函數(shù)使用this@MyClass(a)明確委托調用第一個構造函數(shù)。
通過這些案例可以看出,標簽在Kotlin中是一個非常有用的特性,它能讓代碼的控制流更加靈活,提高代碼的可讀性和可維護性。?
2.20、kotlin中,run與runBlocking的區(qū)別?
run函數(shù):
所屬類別:run是一個作用域函數(shù),屬于Kotlin標準庫提供的作用域函數(shù)家族,和let、also、apply等函數(shù)類似。
用途:主要用于在一個代碼塊內執(zhí)行一系列操作,可對對象進行配置或者計算某個值,它既可以作為擴展函數(shù)調用,也可以作為普通函數(shù)調用。
runBlocking函數(shù):
所屬類別:runBlocking是Kotlin協(xié)程庫中的函數(shù),用于創(chuàng)建一個新的協(xié)程作用域,并阻塞當前線程,直到協(xié)程作用域內的所有協(xié)程執(zhí)行完畢。
用途:通常在測試代碼或者主函數(shù)中使用,用于啟動和等待協(xié)程的執(zhí)行,不適合在生產(chǎn)環(huán)境的異步代碼中使用,因為它會阻塞線程。
語法與使用方式
run函數(shù)
作為擴展函數(shù):
????val?person?=?Person("Alice",?25)
????val?result?=?person.run?{
????????age?+=?1
????????"Name:?$name,?Age:?$age"
????}
}
在這個例子中,run作為person對象的擴展函數(shù)調用,在run代碼塊中可以直接訪問person對象的屬性和方法,run函數(shù)的返回值是代碼塊的最后一個表達式的值。
作為普通函數(shù):
fun?main()?{
????val?result?=?run?{
????????val?a?=?10
????????val?b?=?20
????????a?+?b
????}
????println(result)
}
這里run作為普通函數(shù)調用,用于執(zhí)行一個代碼塊并返回代碼塊的最后一個表達式的值。
runBlocking函數(shù)
????runBlocking?{
????????launch?{
????????????delay(1000)
????????????println("Coroutine?finished")
????????}
????????println("Waiting?for?coroutine...")
????}
????println("Main?function?finished")
在這個例子中,runBlocking創(chuàng)建了一個新的協(xié)程作用域,在這個作用域內啟動了一個協(xié)程,runBlocking會阻塞當前線程,直到協(xié)程作用域內的所有協(xié)程執(zhí)行完畢,然后才會繼續(xù)執(zhí)行runBlocking之后的代碼。
3、線程阻塞情況
run函數(shù)
run函數(shù)不會阻塞線程,它只是在當前線程中執(zhí)行代碼塊,代碼塊執(zhí)行完畢后,程序會繼續(xù)執(zhí)行run函數(shù)之后的代碼。
runBlocking函數(shù)
runBlocking函數(shù)會阻塞當前線程,直到協(xié)程作用域內的所有協(xié)程執(zhí)行完畢,這意味著在runBlocking內部的協(xié)程執(zhí)行期間,調用runBlocking的線程會被掛起,不能執(zhí)行其他任務。
4、適用場景
run函數(shù)
1、對對象進行配置或者初始化,例如對一個復雜對象的多個屬性進行賦值。
2、執(zhí)行一段需要返回值的代碼塊,避免創(chuàng)建額外的局部函數(shù)。
runBlocking函數(shù)
1、編寫測試代碼時,用于啟動和等待協(xié)程的執(zhí)行,確保測試代碼在協(xié)程執(zhí)行完畢后再繼續(xù)執(zhí)行。
2、在主函數(shù)中啟動協(xié)程,因為主函數(shù)是同步執(zhí)行的,需要阻塞線程等待協(xié)程執(zhí)行完畢,但在生產(chǎn)環(huán)境的異步代碼中應避免使用,因為它會阻塞線程,影響程序的性能和響應性。?
2.21、kotlin中,?系統(tǒng)的作用域函數(shù)有哪些?他們的作用?
Kotlin提供了五個作用域函數(shù),分別是let、run、with、apply和also,這些函數(shù)的主要作用是在特定的作用域內操作對象,并且在語法和返回值上各有特點,下面為你詳細介紹每個作用域函數(shù)及其作用。
一:let函數(shù)
let函數(shù)主要用于對一個對象進行一系列操作,并可以方便地處理可能為null的對象,它會將調用對象作為參數(shù)傳遞給Lambda表達式,并返回Lambda表達式的結果。
函數(shù)實現(xiàn):
fun?<T,?R>?T.let(block:?(T)?->?R):?R
案例:
val?str:?String??=?"Hello"
val?result?=?str?.let?{
????it.uppercase()
}
println(result)?//?輸出:?HELLO
在這個例子中,let函數(shù)接收一個Lambda表達式,將str作為參數(shù)傳遞給Lambda表達式,并返回it.uppercase()的結果。
二:run函數(shù)
run函數(shù)既可以像let函數(shù)一樣對對象進行操作,也可以用于執(zhí)行一段代碼塊并返回其結果,當作為對象的擴展函數(shù)調用時,它會將對象作為Lambda表達式的接收者,通過this訪問對象的屬性和方法,當作為普通函數(shù)調用時,它可以執(zhí)行一段獨立的代碼塊。
作為擴展函數(shù):
fun?<T,?R>?T.run(block:?T.()?->?R):?R
作為普通函數(shù):
fun?<R>?run(block:?()?->?R):?R
作為擴展函數(shù)案例:
val?user?=?User("Alice",?25)
val?result?=?user.run?{
????age++
????name.uppercase()
}
println(result)?//?輸出:?ALICE
作為普通函數(shù)案例:
val?sum?=?run?{
????val?a?=?10
????val?b?=?20
????a?+?b
}
println(sum)?//?輸出:?30
在第一個例子中,run函數(shù)將user對象作為Lambda表達式的接收者,通過this訪問user的屬性和方法,在第二個例子中,run函數(shù)執(zhí)行了一段獨立的代碼塊,并返回代碼塊的結果。
三:with函數(shù)
with函數(shù)是一個非擴展函數(shù),它接收一個對象和一個Lambda表達式,將對象作為Lambda表達式的接收者,通過this訪問對象的屬性和方法,并返回Lambda表達式的結果,它通常用于對一個對象進行一系列操作。
函數(shù)實現(xiàn):
fun?<T,?R>?with(receiver:?T,?block:?T.()?->?R):?R
案例:
val?user?=?User("Bob",?30)
val?result?=?with(user)?{
????age++
????name.uppercase()
}
println(result)?//?輸出:?BOB
在這個例子中,with函數(shù)將user對象作為Lambda表達式的接收者,通過this訪問user的屬性和方法,并返回name.uppercase()的結果。
四:apply函數(shù)
apply函數(shù)會將調用對象作為Lambda表達式的接收者,通過this訪問對象的屬性和方法,并返回調用對象本身,它常用于對象的初始化配置。
函數(shù)實現(xiàn):
fun?<T>?T.apply(block:?T.()?->?Unit):?T
案例:
val?user?=?User("Charlie",?35).apply?{
????age++
????name?=?"Charlie?Updated"
}
println(user.name)?//?輸出:?Charlie?Updated
println(user.age)?//?輸出:?36
在這個例子中,apply函數(shù)對user對象進行了屬性的修改,并返回修改后的user對象。
五:also函數(shù)
also函數(shù)會將調用對象作為參數(shù)傳遞給Lambda表達式,并返回調用對象本身,它常用于在對象上執(zhí)行一些額外的操作,如日志記錄、調試等。
函數(shù)實現(xiàn):
fun?<T>?T.also(block:?(T)?->?Unit):?T
案例:
val?str?=?"Hello".also?{
????println("The?string?is:?$it")
}
println(str)?//?輸出:?Hello
在這個例子中,also函數(shù)將str作為參數(shù)傳遞給Lambda表達式,在Lambda表達式中打印了str的值,并返回str對象本身。
綜上所述,這五個作用域函數(shù)在不同的場景下各有優(yōu)勢,你可以根據(jù)具體需求選擇合適的作用域函數(shù)來簡化代碼。?