第9章 文件IO操作、正則表達(dá)式與多線程

第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)主題叠艳。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市捧挺,隨后出現(xiàn)的幾起案子虑绵,更是在濱河造成了極大的恐慌,老刑警劉巖闽烙,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翅睛,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡黑竞,警方通過查閱死者的電腦和手機(jī)捕发,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來很魂,“玉大人扎酷,你說我怎么就攤上這事《舸遥” “怎么了法挨?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵谁榜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我凡纳,道長(zhǎng)窃植,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任荐糜,我火速辦了婚禮巷怜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘暴氏。我一直安慰自己延塑,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布答渔。 她就那樣靜靜地躺著关带,像睡著了一般。 火紅的嫁衣襯著肌膚如雪沼撕。 梳的紋絲不亂的頭發(fā)上豫缨,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天怠肋,我揣著相機(jī)與錄音靠益,去河邊找鬼。 笑死邻耕,一個(gè)胖子當(dāng)著我的面吹牛冲呢,可吹牛的內(nèi)容都是我干的舍败。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼敬拓,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼邻薯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乘凸,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤厕诡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后营勤,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灵嫌,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年葛作,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了寿羞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赂蠢,死狀恐怖绪穆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤玖院,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布菠红,位于F島的核電站,受9級(jí)特大地震影響难菌,放射性物質(zhì)發(fā)生泄漏途乃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一扔傅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧烫饼,春花似錦猎塞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至比藻,卻和暖如春铝量,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背银亲。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來泰國打工慢叨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人务蝠。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓拍谐,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親馏段。 傳聞我的和親對(duì)象是個(gè)殘疾皇子轩拨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容