第9章 文件IO操作溪食、正則表達(dá)式與多線程
我們?cè)凇兜?章 擴(kuò)展函數(shù)與屬性》中已經(jīng)介紹過Kotlin中的類擴(kuò)展的特性囊卜。使用Kotlin的擴(kuò)展函數(shù)功能,我們可以直接為 String 類實(shí)現(xiàn)一個(gè) inc() 函數(shù),這個(gè)函數(shù)把字符串中的每一個(gè)字符值加1
"abc".inc() // bcd
這個(gè)擴(kuò)展函數(shù)實(shí)現(xiàn)如下
fun String.inc(): String {
var result = ""
this.map { result += it + 1 }
return result
}
正是因?yàn)橛辛藦?qiáng)大的擴(kuò)展函數(shù)栅组,我們可以在Java類庫的基礎(chǔ)上擴(kuò)展出大量“看似Java 類中的原生方法” 雀瓢。而實(shí)際上Kotlin的標(biāo)準(zhǔn)庫kotlin-stdlib中大量的API都是通過擴(kuò)展Java的類來實(shí)現(xiàn)的。
本章我們將要介紹的文件IO操作玉掸、正則表達(dá)式與多線程等相關(guān)內(nèi)容都是Kotlin通過擴(kuò)展Java已有的類來實(shí)現(xiàn)的刃麸。首先,我們來介紹文件的讀寫司浪。
9.1 文件 IO 操作
Kotlin IO 操作的 API 在 kotlin.io 包下泊业。Kotlin的原則就是Java已經(jīng)有好用的就直接使用,沒有的或者不好用的啊易,就在原有類的基礎(chǔ)上進(jìn)行功能擴(kuò)展吁伺。例如Kotlin 就給 File 類寫了擴(kuò)展函數(shù)。
Kotlin為 java.io.File 類擴(kuò)展了大量好用的擴(kuò)展函數(shù)认罩,這些擴(kuò)展函數(shù)都在 kotlin/io/FileReadWrite.kt 源代碼文件中箱蝠。我們將在下文中介紹。
同時(shí)垦垂,Kotlin 也針對(duì)InputStream宦搬、OutputStream和 Reader 等都做了簡(jiǎn)單的擴(kuò)展。它們主要在下面的兩個(gè)源文件中:
kotlin/io/IOStreams.kt |
---|
kotlin/io/ReadWrite.kt |
Koltin 的序列化直接采用的 Java 的序列化類的類型別名:
internal typealias Serializable = java.io.Serializable
下面我們來簡(jiǎn)單介紹一下 Kotlin 文件讀寫操作劫拗。Kotlin中常用的文件讀寫 API 如下表所示
函數(shù)簽名 | 功能說明 |
---|---|
File.readText(charset: Charset = Charsets.UTF_8): String | 讀取該文件的所有內(nèi)容作為一個(gè)字符串返回 |
File.readLines(charset: Charset = Charsets.UTF_8): List<String> | 讀取該文件的每一行內(nèi)容间校,存入一個(gè)List<String> 返回 |
File.readBytes(): ByteArray | 讀取文件所有內(nèi)容以ByteArray的方式返回 |
File.writeText(text: String, charset: Charset = Charsets.UTF_8): Unit | 覆蓋寫入text字符串到文件中 |
File.writeBytes(array: ByteArray): Unit | 覆蓋寫入ByteArray字節(jié)流數(shù)組 |
File.appendText(text: String, charset: Charset = Charsets.UTF_8): Unit | 在文件末尾追加寫入text字符串 |
File.appendBytes(array: ByteArray): Unit | 在文件末尾追加寫入ByteArray字節(jié)流數(shù)組 |
9.1.1 讀文件
readText : 獲取文件全部?jī)?nèi)容字符串
我們?nèi)绻?jiǎn)單讀取一個(gè)文件,可以使用readText()方法页慷,它直接返回整個(gè)文件內(nèi)容憔足。代碼示例如下
fun getFileContent(filename: String): String {
val f = File(filename)
return f.readText(Charset.forName("UTF-8"))
}
我們直接使用 File 對(duì)象來調(diào)用 readText 函數(shù)即可獲得該文件的全部?jī)?nèi)容,它返回一個(gè)字符串酒繁。如果指定字符編碼滓彰,可以通過傳入?yún)?shù)Charset來指定,默認(rèn)是UTF-8編碼州袒。
readLines : 獲取文件每行的內(nèi)容
如果我們想要獲得文件每行的內(nèi)容揭绑,可以簡(jiǎn)單通過 split("\n") 來獲得一個(gè)每行內(nèi)容的數(shù)組。我們也可以直接調(diào)用 Kotlin 封裝好的readLines函數(shù)郎哭,獲得文件每行的內(nèi)容他匪。readLines函數(shù)返回一個(gè)持有每行內(nèi)容的字符串 List。
fun getFileLines(filename: String): List<String> {
return File(filename).readLines(Charset.forName("UTF-8"))
}
readBytes:讀取字節(jié)流數(shù)組
我們?nèi)绻M苯硬僮魑募淖止?jié)數(shù)組夸研,可以使用readBytes 函數(shù)
//讀取為bytes數(shù)組
val bytes: ByteArray = f.readBytes()
println(bytes.joinToString(separator = " "))
//與 Java 互操作邦蜜,直接調(diào)用Java 中的 InputStream 和 InputStream類
val reader: Reader = f.reader()
val inputStream: InputStream = f.inputStream()
val bufferedReader: BufferedReader = f.bufferedReader()
}
bufferedReader
獲取該文件的BufferedReader
方法簽名:
fun File.bufferedReader(
charset: Charset = Charsets.UTF_8,
bufferSize: Int = DEFAULT_BUFFER_SIZE
): BufferedReader
9.1.2 寫文件
使用Kotlin擴(kuò)展的函數(shù),寫入文件也變得相當(dāng)簡(jiǎn)單亥至。更讀取文件類似悼沈,我們可以寫入字符串贱迟,也可以寫入字節(jié)流,還可以直接調(diào)用 Java的 Writer 或者 OutputStream 類井辆。寫文件通常分為覆蓋寫(一次性寫入)和追加寫入兩種情況关筒。
writeText: 覆蓋寫文件
我們使用 writeText 函數(shù)直接向一個(gè)文件中寫入字符串 text 的內(nèi)容
fun writeFile(text: String, destFile: String) {
val f = File(destFile)
if (!f.exists()) {
f.createNewFile()
}
f.writeText(text, Charset.defaultCharset())
}
其中,destFile 參數(shù)是目標(biāo)文件名(帶目錄)杯缺。
appendFile: 末尾追加寫文件
使用 appendFile 函數(shù)向一個(gè)文件的末尾追加寫入內(nèi)容 text
fun appendFile(text: String, destFile: String) {
val f = File(destFile)
if (!f.exists()) {
f.createNewFile()
}
f.appendText(text, Charset.defaultCharset())
}
appendBytes
追加字節(jié)數(shù)組到該文件中
方法簽名:
fun File.appendBytes(array: ByteArray)
bufferedWriter
獲取該文件的BufferedWriter
方法簽名:
fun File.bufferedWriter(
charset: Charset = Charsets.UTF_8,
bufferSize: Int = DEFAULT_BUFFER_SIZE
): BufferedWriter
提示: Kotlin 對(duì) File 的擴(kuò)展函數(shù) API 文檔https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/java.io.-file/index.html|
9.1.3 遍歷文件樹
Kotlin 中提供了方便的功能來遍歷文件樹蒸播。
walk 函數(shù): 遍歷文件樹
下面的例子遍歷了指定文件夾下的所有文件。
fun traverseFileTree(filename: String) {
val f = File(filename)
val fileTreeWalk = f.walk()
fileTreeWalk.iterator().forEach { println(it.absolutePath) }
}
測(cè)試代碼:
KFileUtil.traverseFileTree(".")
上面的測(cè)試代碼萍肆,它將輸出當(dāng)前目錄下的所有子目錄及其文件袍榆。
我們還可以遍歷當(dāng)前文件下面所有子目錄文件,存入一個(gè) Iterator<File> 中
fun getFileIterator(filename: String): Iterator<File> {
val f = File(filename)
val fileTreeWalk = f.walk()
return fileTreeWalk.iterator()
}
我們遍歷當(dāng)前文件下面所有子目錄文件塘揣,還可以根據(jù)條件過濾包雀,并把結(jié)果存入一個(gè) Sequence<File> 中
fun getFileSequenceBy(filename: String, p: (File) -> Boolean): Sequence<File> {
val f = File(filename)
return f.walk().filter(p)
}
遍歷文件樹需要調(diào)用擴(kuò)展方法walk()。它會(huì)返回一個(gè)FileTreeWalk對(duì)象亲铡,它有一些方法用于設(shè)置遍歷方向和深度才写,詳情參見FileTreeWalk API 文檔說明。
提示:FileTreeWalk API 文檔鏈接 https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/-file-tree-walk/
遞歸復(fù)制文件
復(fù)制該文件或者遞歸復(fù)制該目錄及其所有子文件到指定路徑奖蔓,如果指定路徑下的文件不存在赞草,會(huì)自動(dòng)創(chuàng)建。
copyRecursively 函數(shù)簽名:
fun File.copyRecursively(
target: File,
overwrite: Boolean = false, // 是否覆蓋吆鹤。true:覆蓋之前先刪除原來的文件
onError: (File, IOException) -> OnErrorAction = { _, exception -> throw exception }
): Boolean
9.2 網(wǎng)絡(luò)IO
Kotlin為java.net.URL增加了兩個(gè)擴(kuò)展方法厨疙,readBytes和readText。我們可以方便的使用這兩個(gè)方法配合正則表達(dá)式實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲的功能疑务。
下面我們簡(jiǎn)單寫幾個(gè)函數(shù)實(shí)例沾凄。
根據(jù) url 獲取該 url 的響應(yīng) HTML函數(shù)
fun getUrlContent(url: String): String {
return URL(url).readText(Charset.defaultCharset())
}
根據(jù) url 獲取該 url 響應(yīng)比特?cái)?shù)組函數(shù)
fun getUrlBytes(url: String): ByteArray {
return URL(url).readBytes()
}
把 url 響應(yīng)字節(jié)數(shù)組寫入文件
fun writeUrlBytesTo(filename: String, url: String) {
val bytes = URL(url).readBytes()
File(filename).writeBytes(bytes)
}
下面這個(gè)例子簡(jiǎn)單的獲取了百度首頁的源代碼。
getUrlContent("https://www.baidu.com")
下面這個(gè)例子根據(jù) url 來獲取一張圖片的比特流知允,然后調(diào)用readBytes()方法讀取到字節(jié)流并寫入文件撒蟀。
writeUrlBytesTo("圖片.jpg", "http://n.sinaimg.cn/default/4_img/uplaod/3933d981/20170622/2fIE-fyhfxph6601959.jpg")
在項(xiàng)目相應(yīng)文件夾下我們可以看到下載好的 “圖片.jpg” 。
9.3 執(zhí)行shell命令
我們使用 Groovy 的文件 IO 操作感覺非常好用温鸽,例如
package com.easy.kotlin
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4)
class ShellExecuteDemoTest {
@Test
def void testShellExecute() {
def p = "ls -R".execute()
def output = p.inputStream.text
println(output)
def fname = "我圖.url"
def f = new File(fname)
def lines = f.readLines()
lines.forEach({
println(it)
})
println(f.text)
}
}
Kotlin 中的文件 IO保屯,網(wǎng)絡(luò) IO 操作跟 Groovy一樣簡(jiǎn)單。
另外嗤朴,從上面的代碼中我們看到使用 Groovy 執(zhí)行終端命令非常簡(jiǎn)單:
def p = "ls -R".execute()
def output = p.inputStream.text
在 Kotlin 中,目前還沒有對(duì) String 類和 Process 擴(kuò)展這樣的函數(shù)虫溜。其實(shí)擴(kuò)展這樣的函數(shù)非常簡(jiǎn)單雹姊。我們完全可以自己擴(kuò)展。
首先衡楞,我們來擴(kuò)展 String 的 execute() 函數(shù)吱雏。
fun String.execute(): Process {
val runtime = Runtime.getRuntime()
return runtime.exec(this)
}
然后敦姻,我們來給 Process 類擴(kuò)展一個(gè) text函數(shù)。
fun Process.text(): String {
var output = ""
// 輸出 Shell 執(zhí)行的結(jié)果
val inputStream = this.inputStream
val isr = InputStreamReader(inputStream)
val reader = BufferedReader(isr)
var line: String? = ""
while (line != null) {
line = reader.readLine()
output += line + "\n"
}
return output
}
完成了上面兩個(gè)簡(jiǎn)單的擴(kuò)展函數(shù)之后歧杏,我們就可以在下面的測(cè)試代碼中镰惦,可以像 Groovy 一樣執(zhí)行終端命令了:
val p = "ls -al".execute()
val exitCode = p.waitFor()
val text = p.text()
println(exitCode)
println(text)
實(shí)際上,通過之前的很多實(shí)例的學(xué)習(xí)犬绒,我們可以看出 Kotlin 的擴(kuò)展函數(shù)相當(dāng)實(shí)用旺入。Kotlin 語言本身API 也大量使用了擴(kuò)展功能。
9.4 正則表達(dá)式
我們?cè)?Kotlin 中除了仍然可以使用 Java中的 Pattern凯力,Matcher 等類之外茵瘾,Kotlin 還提供了一個(gè)正則表達(dá)式類 kotlin/text/regex/Regex.kt ,我們通過 Regex 的構(gòu)造函數(shù)來創(chuàng)建一個(gè)正則表達(dá)式咐鹤。
9.4.1 構(gòu)造 Regex 表達(dá)式
使用Regex構(gòu)造函數(shù)
val r1 = Regex("[a-z]+")
val r2 = Regex("[a-z]+", RegexOption.IGNORE_CASE)
其中的匹配選項(xiàng) RegexOption 是直接使用的 Java 類 Pattern中的正則匹配選項(xiàng)拗秘。
使用 String 的 toRegex 擴(kuò)展函數(shù)
val r3 = "[A-Z]+".toRegex()
9.4.2 Regex 函數(shù)
Regex 里面提供了豐富的簡(jiǎn)單而實(shí)用的函數(shù),如下表所示
函數(shù)名稱 | 功能說明 |
---|---|
matches(input: CharSequence): Boolean | 輸入字符串全部匹配 |
containsMatchIn(input: CharSequence): Boolean | 輸入字符串至少有一個(gè)匹配 |
matchEntire(input: CharSequence): MatchResult? | 輸入字符串全部匹配祈惶,返回一個(gè)匹配結(jié)果對(duì)象 |
replace(input: CharSequence, replacement: String): String | 把輸入字符串中匹配的部分替換成replacement的內(nèi)容 |
replace(input: CharSequence, transform: (MatchResult) -> CharSequence): String | 把輸入字符串中匹配到的值雕旨,用函數(shù) transform映射之后的新值替換 |
find(input: CharSequence, startIndex: Int = 0): MatchResult? | 返回輸入字符串中第一個(gè)匹配的值 |
findAll(input: CharSequence, startIndex: Int = 0): Sequence<MatchResult> | 返回輸入字符串中所有匹配的值MatchResult的序列 |
下面我們分別就上面的函數(shù)給出簡(jiǎn)單實(shí)例。
matches
輸入字符串全部匹配正則表達(dá)式返回 true , 否則返回 false捧请。
>>> val r1 = Regex("[a-z]+")
>>> r1.matches("ABCzxc")
false
>>> val r2 = Regex("[a-z]+", RegexOption.IGNORE_CASE)
>>> r2.matches("ABCzxc")
true
>>> val r3 = "[A-Z]+".toRegex()
>>> r3.matches("GGMM")
true
containsMatchIn
輸入字符串中至少有一個(gè)匹配就返回true凡涩,沒有一個(gè)匹配就返回false。
>>> val re = Regex("[0-9]+")
>>> re.containsMatchIn("012Abc")
true
>>> re.containsMatchIn("Abc")
false
matchEntire
輸入字符串全部匹配正則表達(dá)式返回 一個(gè)MatcherMatchResult對(duì)象血久,否則返回 null突照。
>>> val re = Regex("[0-9]+")
>>> re.matchEntire("1234567890")
kotlin.text.MatcherMatchResult@34d713a2
>>> re.matchEntire("1234567890!")
null
我們可以訪問MatcherMatchResult的value熟悉來獲得匹配的值。
>>> re.matchEntire("1234567890")?.value
1234567890
由于 matchEntire 函數(shù)的返回是MatchResult? 可空對(duì)象氧吐,所以這里我們使用了安全調(diào)用符號(hào) ?.
讹蘑。
replace(input: CharSequence, replacement: String): String
把輸入字符串中匹配的部分替換成replacement的內(nèi)容。
>>> val re = Regex("[0-9]+")
>>> re.replace("12345XYZ","abcd")
abcdXYZ
我們可以看到筑舅,"12345XYZ"中12345
是匹配正則表達(dá)式 [0-9]+
的內(nèi)容座慰,它被替換成了 abcd
。
replace
函數(shù)
replace 函數(shù)簽名如下
replace(input: CharSequence, transform: (MatchResult) -> CharSequence): String
它的功能是把輸入字符串中匹配到的值翠拣,用函數(shù) transform映射之后的新值替換版仔。
>>> val re = Regex("[0-9]+")
>>> re.replace("9XYZ8", { (it.value.toInt() * it.value.toInt()).toString() })
81XYZ64
我們可以看到,9XYZ8
中數(shù)字9和8是匹配正則表達(dá)式[0-9]+
的內(nèi)容误墓,它們分別被transform函數(shù)映射 (it.value.toInt() * it.value.toInt()).toString()
的新值 81 和 64 替換蛮粮。
find
函數(shù)
返回輸入字符串中第一個(gè)匹配的MatcherMatchResult對(duì)象。
>>> val re = Regex("[0-9]+")
>>> re.find("123XYZ987abcd7777")
kotlin.text.MatcherMatchResult@4d4436d0
>>> re.find("123XYZ987abcd7777")?.value
123
findAll
返回輸入字符串中所有匹配的值的MatchResult的序列谜慌。
>>> val re = Regex("[0-9]+")
>>> re.findAll("123XYZ987abcd7777")
kotlin.sequences.GeneratorSequence@f245bdd
我們可以通過 forEach 循環(huán)遍歷所以匹配的值
>>> re.findAll("123XYZ987abcd7777").forEach{println(it.value)}
123
987
7777
9.4.3 使用 Java 的正則表達(dá)式類
除了上面 Kotlin 提供的函數(shù)之外然想,我們?cè)?Kotlin 中仍然可以使用 Java 的正則表達(dá)式的 API。
val re = Regex("[0-9]+")
val p = re.toPattern()
val m = p.matcher("888ABC999")
while (m.find()) {
val d = m.group()
println(d)
}
上面的代碼運(yùn)行輸出:
888
999
9.5 多線程編程
Kotlin中沒有synchronized欣范、volatile關(guān)鍵字变泄。Kotlin的Any類似于Java的Object令哟,但是沒有wait(),notify()和notifyAll() 方法妨蛹。
那么并發(fā)如何在Kotlin中工作呢屏富?放心,Kotlin 既然是站在 Java 的肩膀上蛙卤,當(dāng)然少不了對(duì)多線程編程的支持——Kotlin通過封裝 Java 中的線程類狠半,簡(jiǎn)化了我們的編碼。同時(shí)我們也可以使用一些特定的注解表窘, 直接使用 Java 中的同步關(guān)鍵字等典予。下面我們簡(jiǎn)單介紹一下使用Kotlin 進(jìn)行多線程編程的相關(guān)內(nèi)容。
9.5.1 創(chuàng)建線程
我們?cè)?Java中通常有兩種方法在Java中創(chuàng)建線程:
- 擴(kuò)展Thread類
- 或者實(shí)例化它并通過構(gòu)造函數(shù)傳遞一個(gè)Runnable
因?yàn)槲覀兛梢院苋菀椎卦贙otlin中使用Java類乐严,這兩個(gè)方式都可以使用瘤袖。
使用對(duì)象表達(dá)式創(chuàng)建
object : Thread() {
override fun run() {
Thread.sleep(3000)
println("A 使用 Thread 對(duì)象表達(dá)式: ${Thread.currentThread()}")
}
}.start()
此代碼使用Kotlin的對(duì)象表達(dá)式創(chuàng)建一個(gè)匿名類并覆蓋run()方法。
使用 Lambda 表達(dá)式
下面是如何將一個(gè)Runnable傳遞給一個(gè)新創(chuàng)建的Thread實(shí)例:
Thread({
Thread.sleep(2000)
println("B 使用 Lambda 表達(dá)式: ${Thread.currentThread()}")
}).start()
我們?cè)谶@里看不到Runnable昂验,在Kotlin中可以很方便的直接使用上面的Lambda表達(dá)式來表達(dá)捂敌。
還有更簡(jiǎn)單的方法嗎? 且看下文解說既琴。
使用 Kotlin 封裝的 thread 函數(shù)
例如占婉,我們寫了下面一段線程的代碼
val t = Thread({
Thread.sleep(2000)
println("C 使用 Lambda 表達(dá)式:${Thread.currentThread()}")
})
t.isDaemon = false
t.name = "CThread"
t.priority = 3
t.start()
后面的四行可以說是樣板化的代碼。在 Kotlin 中把這樣的操作封裝簡(jiǎn)化了甫恩。
thread(start = true, isDaemon = false, name = "DThread", priority = 3) {
Thread.sleep(1000)
println("D 使用 Kotlin 封裝的函數(shù) thread(): ${Thread.currentThread()}")
}
這樣的代碼顯得更加精簡(jiǎn)整潔了逆济。事實(shí)上,thread()函數(shù)就是對(duì)我們編程實(shí)踐中經(jīng)常用到的樣板化的代碼進(jìn)行了抽象封裝磺箕,它的實(shí)現(xiàn)如下:
public fun thread(start: Boolean = true, isDaemon: Boolean = false, contextClassLoader: ClassLoader? = null, name: String? = null, priority: Int = -1, block: () -> Unit): Thread {
val thread = object : Thread() {
public override fun run() {
block()
}
}
if (isDaemon)
thread.isDaemon = true
if (priority > 0)
thread.priority = priority
if (name != null)
thread.name = name
if (contextClassLoader != null)
thread.contextClassLoader = contextClassLoader
if (start)
thread.start()
return thread
}
這只是一個(gè)非常方便的包裝函數(shù)奖慌,簡(jiǎn)單實(shí)用。從上面的例子我們可以看出松靡,Kotlin 通過擴(kuò)展 Java 的線程 API简僧,簡(jiǎn)化了樣板代碼。
9.5.2 同步方法和塊
synchronized不是Kotlin中的關(guān)鍵字雕欺,它替換為@Synchronized 注解岛马。 Kotlin中的同步方法的聲明將如下所示:
@Synchronized fun appendFile(text: String, destFile: String) {
val f = File(destFile)
if (!f.exists()) {
f.createNewFile()
}
f.appendText(text, Charset.defaultCharset())
}
@Synchronized 注解與 Java中的 synchronized 具有相同的效果:它會(huì)將JVM方法標(biāo)記為同步。 對(duì)于同步塊屠列,我們使用synchronized() 函數(shù)啦逆,它使用鎖作為參數(shù):
fun appendFileSync(text: String, destFile: String) {
val f = File(destFile)
if (!f.exists()) {
f.createNewFile()
}
synchronized(this){
f.appendText(text, Charset.defaultCharset())
}
}
跟 Java 基本一樣。
9.5.3 可變字段
同樣的笛洛,Kotlin沒有 volatile 關(guān)鍵字夏志,但是有@Volatile注解。
@Volatile private var running = false
fun start() {
running = true
thread(start = true) {
while (running) {
println("Still running: ${Thread.currentThread()}")
}
}
}
fun stop() {
running = false
println("Stopped: ${Thread.currentThread()}")
}
@Volatile會(huì)將JVM備份字段標(biāo)記為volatile撞蜂。
當(dāng)然盲镶,在 Kotlin 中我們有更好用的協(xié)程并發(fā)庫。在代碼工程實(shí)踐中蝌诡,我們可以根據(jù)實(shí)際情況自由選擇溉贿。
本章小結(jié)
Kotlin 是一門工程實(shí)踐性很強(qiáng)的語言,從本章介紹的文件IO浦旱、正則表達(dá)式以及多線程等內(nèi)容中宇色,我們可以領(lǐng)會(huì)到 Kotlin 的基本原則:充分使用已有的 Java 生態(tài)庫,在此基礎(chǔ)之上進(jìn)行更加簡(jiǎn)單實(shí)用的擴(kuò)展颁湖,大大提升程序員們的生產(chǎn)力宣蠕。從中我們也體會(huì)到了Kotlin 編程中的極簡(jiǎn)理念——不斷地抽象、封裝甥捺、擴(kuò)展抢蚀,使之更加簡(jiǎn)單實(shí)用。
本章示例代碼:https://github.com/EasyKotlin/chapter15_file_io
另外镰禾,筆者綜合了本章的內(nèi)容皿曲,使用 SpringBoot + Kotlin 寫了一個(gè)簡(jiǎn)單的圖片爬蟲 Web 應(yīng)用,感興趣的讀者可參考源碼:https://github.com/EasyKotlin/chatper15_net_io_img_crawler
Kotlin 開發(fā)者社區(qū)
國內(nèi)第一Kotlin 開發(fā)者社區(qū)公眾號(hào)吴侦,主要分享屋休、交流 Kotlin 編程語言、Spring Boot备韧、Android劫樟、React.js/Node.js、函數(shù)式編程织堂、編程思想等相關(guān)主題叠艳。