1. 程式人生 > >Kotlin中一些知識點學習

Kotlin中一些知識點學習

1. 協程

fun main(args: Array<String>) {
    launch(CommonPool) {
        delay(1000L) 
        println("World!") 
    }
    println("Hello,")
    Thread.sleep(2000L)
}

/* 
執行結果: ("Hello,"會立即被列印, 1000毫秒之後, "World!"會被列印)
Hello, 
World!
*/

解釋一下delay方法:
在協程裡delay方法作用等同於執行緒裡的sleep, 都是休息一段時間, 但不同的是delay不會阻塞當前執行緒, 而像是設定了一個鬧鐘, 在鬧鐘未響之前, 執行該協程的執行緒可以被安排做了別的事情, 當鬧鐘響起時, 協程就會恢復執行.

協程啟動後還可以取消:
launch方法有一個返回值, 型別是Job, Job有一個cancel方法, 呼叫cancel方法可以取消協程, 看一個數羊的例子:

fun main(args: Array<String>) {
    val job = launch(CommonPool) {
        var i = 1
        while(true) {
            println("$i little sheep")
            ++i
            delay(500L)  // 每半秒數一隻, 一秒可以輸兩隻
        }
    }

    Thread
.sleep(1000L) // 在主執行緒睡眠期間, 協程裡已經數了兩隻羊 job.cancel() // 協程才數了兩隻羊, 就被取消了 Thread.sleep(1000L) println("main process finished.") }

執行結果是,如果不呼叫cancel, 可以數到4只羊:

1 little sheep
2 little sheep
main process finished.

注意還有一個方法:job.join() // 持續等待,直到子協程執行完成

1.1 理解suspend方法

suspend方法的語法很簡單, 只是比普通方法只是多了個suspend

關鍵字:

suspend fun foo(): ReturnType {
    // ...
}

suspend方法只能在協程裡面呼叫, 不能在協程外面呼叫.
suspend方法本質上, 與普通方法有較大的區別, suspend方法的本質是非同步返回(注意: 不是非同步回撥).
現在, 我們先來看一個非同步回撥的例子:

fun main(...) {
  requestDataAsync {
    println("data is $it")
  }
  Thead.sleep(10000L)  // 這個sleep只是為了保活程序
}

fun requestDataAsync(callback: (String)->Unit) {
    Thread() {
        // do something need lots of times.
        callback(data)
    }.start()
}

邏輯很簡單, 就是通過非同步的方法拉一個數據, 然後使用這個資料, 按照以往的程式設計方式, 若要接受非同步回來的資料, 唯有使用callback.
但是假如使用協程, 可以不使用callback, 而是直接把這個資料”return”回來, 呼叫者不使用callback接受資料, 而是像呼叫同步方法一樣接受返回值. 如果上述功能改用協程, 將會是:

fun main(...) {
    launch(Unconfined) {  // 請重點關注協程裡是如何獲取非同步資料的
        val data = requestDataAsync()  // 非同步回來的資料, 像同步一樣return了
        println("data is $it")
    }

    Thead.sleep(10000L) // 請不要關注這個sleep
}

suspend fun requestDataAsync() { // 請注意方法前多了一個suspend關鍵字
    return async(CommonPool) { // 先不要管這個async方法, 後面解釋
        // do something need lots of times.
        // ...
        data  // return data, lambda裡的return要省略
    }.await()
}

這裡, 我們首先將requestDataAsync轉成了一個suspend方法, 其原型的變化是:
1.在前加了個suspend關鍵字.
2.去除了原來的callback引數.

這是怎麼做到的呢?
當程式執行到requestDataAsync內部時, 通過async啟動了另外一個新的子協程去拉取資料, 啟動這個新的子協程後, 當前的父協程就掛起了, 此時requestDataAsync還沒有返回.子協程一直在後臺跑, 過了一段時間, 子協程把資料拉回來之後, 會恢復它的父協程, 父協程繼續執行, requestDataAsync就把資料返回了.
為了加深理解, 我們來對比一下另一個例子: 不使用協程, 將非同步方法也可以轉成同步的方法(在單元測試裡, 我們經常這麼做):

fun main(...) {
    val data = async2Sync()  // 資料是同步返回了, 但是執行緒也阻塞了
    println("data is $it")
    // Thead.sleep(10000L)  // 這一句在這裡毫無意義了, 註釋掉
}

private var data = ""
private fun async2Sync(): String {
    val obj = Object() // 隨便建立一個物件當成鎖使用
    requestDataAsync { data ->
        this.data = data  // 暫存data
        synchronized(locker) {
            obj.notifyAll() // 通知所有的等待者
        }
    }
    obj.wait() // 阻塞等待
    return this.data
}

fun requestDataAsync(callback: (String)->Unit) {
    // ...普通的非同步方法
}

注意對比上一個協程的例子, 這樣做表面上跟它是一樣的, 但是這裡main方法會阻塞的等待async2Sync()方法完成. 同樣是等待, 協程就不會阻塞當前執行緒, 而是自己主動放棄執行權, 相當於遣散當前執行緒, 讓它去幹別的事情去.
為了更好的理解這個”遣散”的含義, 我們再來看一個例子:

fun main(args: Array<String>) {
    // 1. 程式開始
    println("${Thread.currentThread().name}: 1");  

    // 2. 啟動一個協程, 並立即啟動
    launch(Unconfined) { // Unconfined意思是在當前執行緒(主執行緒)執行協程
        // 3. 本協程在主執行緒上直接開始執行了第一步
        println("${Thread.currentThread().name}: 2");  

        /* 4. 本協程的第二步呼叫了一個suspend方法, 呼叫之後, 
         * 本協程就放棄執行權, 遣散執行我的執行緒(主執行緒)請幹別的去.
         * 
         * delay被呼叫的時候, 在內部建立了一個計時器, 並設了個callback.
         * 1秒後計時器到期, 就會呼叫剛設定的callback.
         * 在callback裡面, 會呼叫系統的介面來恢復協程. 
         * 協程在計時器執行緒上恢復執行了. (不是主執行緒, 跟Unconfined有關)
         */
        delay(1000L)  // 過1秒後, 計時器執行緒會resume協程

        // 7. 計時器執行緒恢復了協程, 
        println("${Thread.currentThread().name}: 4")
    }

    // 5. 剛那個的協程不要我(主執行緒)幹活了, 所以我繼續之前的執行
    println("${Thread.currentThread().name}: 3");

    // 6. 我(主執行緒)睡2秒鐘
    Thread.sleep(2000L)

    // 8. 我(主執行緒)睡完後繼續執行
    println("${Thread.currentThread().name}: 5");
}

執行結果:

main: 1
main: 2
main: 3
kotlinx.coroutines.ScheduledExecutor: 4
main: 5

上述程式碼的註釋詳細的列出了程式執行流程, 看完之後, 應該就能明白 “遣散” 和 “放棄執行權” 的含義了.
Unconfined的含義是不給協程指定執行的執行緒, 逮到誰就使用誰, 啟動它的執行緒直接執行它, 但被掛起後, 會由恢復它的執行緒繼續執行, 如果一個協程會被掛起多次, 那麼每次被恢復後, 都可能被不同執行緒繼續執行.

現在再來回顧剛剛那句: suspend方法的本質就是非同步返回.含義就是將其拆成 “非同步” + “返回”:
首先, 資料不是同步回來的(同步指的是立即返回), 而是非同步回來的.
其次, 接受資料不需要通過callback, 而是直接接收返回值.
呼叫suspend方法的詳細流程是:
在協程裡, 如果呼叫了一個suspend方法, 協程就會掛起, 釋放自己的執行權, 但在協程掛起之前, suspend方法內部一般會啟動了另一個執行緒或協程, 我們暫且稱之為”分支執行流”吧, 它的目的是運算得到一個數據.當suspend方法裡的*分支執行流”完成後, 就會呼叫系統API重新恢復協程的執行, 同時會資料返回給協程(如果有的話).
為什麼不能再協程外面呼叫suspend方法?
suspend方法只能在協程裡面呼叫, 原因是隻有在協程裡, 才能遣散當前執行緒, 在協程外面, 不允許遣散, 反過來思考, 假如在協程外面也能遣散執行緒, 會怎麼樣, 寫一個反例:

fun main(args: Array<String>) {
    requestDataSuspend(); 
    doSomethingNormal();
}
suspend fun requestDataSuspend() { 
    // ... 
}
fun doSomethingNormal() {
    // ...
}

requestDataSuspend是suspend方法, doSomethingNormal是正常方法, doSomethingNormal必須等到requestDataSuspend執行完才會開始, 如果main方法失去了並行的能力, 所有地方都失去了並行的能力, 這肯定不是我們要的, 所以需要約定只能在協程裡才可以遣散執行緒, 放棄執行權, 於是suspend方法只能在協程裡面呼叫.

協程建立後, 並不總是立即執行, 要分是怎麼建立的協程, 通過launch方法的第二個引數是一個列舉型別CoroutineStart, 如果不填, 預設值是DEFAULT, 那麼協程建立後立即啟動, 如果傳入LAZY, 建立後就不會立即啟動, 直到呼叫Job的start方法才會啟動.

在協程裡, 所有接受callback的方法, 都可以轉成不需要callback的suspend方法,上面的requestDataSuspend方法就是一個這樣的例子, 我們回過頭來再看一眼:

suspend fun requestDataSuspend() {
    return async(CommonPool) {
        // do something need lots of times.
        // ...
        data  // return data
    }.await()
}

其內部通過呼叫了async和await方法來實現(關於async和await我們後面再介紹), 這樣雖然實現功能沒問題, 但並不最合適的方式, 上面那樣做只是為了追求最簡短的實現, 合理的實現應該是呼叫suspendCoroutine方法, 大概是這樣:

suspend fun requestDataSuspend() {
    suspendCoroutine { cont ->
        // ... 細節暫時省略
    }
}
// 可簡寫成:
suspend fun requestDataSuspend() = suspendCoroutine { cont ->
    // ...
}

在完整實現之前, 需要先理解suspendCoroutine方法, 它是Kotlin標準庫裡的一個方法, 原型如下:

suspend fun <T> suspendCoroutine(block: (Continuation<T>) -> Unit): T

現在來完善一下剛剛的例子:

suspend fun requestDataSuspend() = suspendCoroutine { cont ->
    requestDataFromServer { data -> // 普通方法還是通過callback接受資料
        if (data != null) {
            cont.resume(data)
        } else {
            cont.resumeWithException(MyException())
        }
    }
}

/** 普通的非同步回撥方法 */
fun requestDataFromServer(callback: (String)->Unit) {
    // ... get data from server, it will call back when finished.
}

suspendCoroutine有個特點:

suspendCoroutine { cont ->
    // 如果本lambda裡返回前, cont的resume和resumeWithException都沒有呼叫
    // 那麼當前執行流就會掛起, 並且掛起的時機是在suspendCoroutine之前
    // 就是在suspendCoroutine內部return之前就掛起了

    // 如果本lambda裡返回前, 呼叫了cont的resume或resumeWithException
    // 那麼當前執行流不會掛起, suspendCoroutine直接返回了, 
    // 若呼叫的是resume, suspendCoroutine就會像普通方法一樣返回一個值
    // 若呼叫的是resumeWithException, suspendCoroutine會丟擲一個異常
    // 外面可以通過try-catch來捕獲這個異常
}

回過頭來看一下, 剛剛的實現有呼叫resume方法嗎, 我們把它摺疊一下:

suspend fun requestDataSuspend() = suspendCoroutine { cont ->
    requestDataFromServer { ... }
}

清晰了吧, 沒有呼叫, 所以suspendCoroutine還沒有返回之前就掛起了, 但是掛起之前lambda執行完了, lambda裡呼叫了requestDataFromServer, requestDataFromServer裡啟動了真正做事的流程(非同步執行的), 而suspendCoroutine則在掛起等待.
等到requestDataFromServer完成工作, 就會呼叫傳入的callback, 而這個callback裡呼叫了cont.resume(data), 外層的協程就恢復了, 隨後suspendCoroutine就會返回, 返回值就是data.

1.2 async/await模式:

我們前面多次使用了launch方法, 它的作用是建立協程並立即啟動, 但是有一個問題, 就是通過launch方法建立的協程都沒辦法攜帶返回值. async之前也出現過, 但一直沒有詳細介紹.
async方法作用和launch方法基本一樣, 建立一個協程並立即啟動, 但是async建立的協程可以攜帶返回值.
launch方法的返回值型別是Job, async方法的返回值型別是Deferred, 是Job的子類, Deferred裡有個await方法, 呼叫它可得到協程的返回值.
async/await是一種常用的模式, async的含義是啟動一個非同步操作, await的含義是等待這個非同步操作結果.
是誰要等它啊, 在傳統的不使用協程的程式碼裡, 是執行緒在等(執行緒不幹別的事, 就在那裡傻等). 在協程裡不是執行緒在等, 而且是執行流在等, 當前的流程掛起(底下的執行緒會被遣散去幹別的事), 等到有了運算結果, 流程才繼續執行.
所以我們又可以順便得出一個結論: 在協程裡執行流是線性的, 其中的步驟無論是同步的還是非同步的, 後面的步驟都會等前面的步驟完成.
我們可以通過async起多個任務, 他們會同時執行, 我們之前使用的async姿勢不是很正常, 下面看一下使用async正常的姿勢:

fun main(...) {
    launch(Unconfined) {
        // 任務1會立即啟動, 並且會在別的執行緒上並行執行
        val deferred1 = async { requestDataAsync1() }

        // 上一個步驟只是啟動了任務1, 並不會掛起當前協程
        // 所以任務2也會立即啟動, 也會在別的執行緒上並行執行
        val deferred2 = async { requestDataAsync2() }

        // 先等待任務1結束(等了約1000ms), 
        // 然後等待任務2, 由於它和任務1幾乎同時啟動的, 所以也很快完成了
        println("data1=$deferred2.await(), data2=$deferred2.await()")
    }

    Thead.sleep(10000L) // 繼續無視這個sleep
}

suspend fun requestDataAsync1(): String {
    delay(1000L)
    return "data1"    
}
suspend fun requestDataAsync2(): String {
    delay(1000L)
    return "data2"    
}

執行結果很簡單, 不用說了, 但是協程總耗時是多少呢, 約1000ms, 不是2000ms, 因為兩個任務是並行執行的.
有一個問題: 假如任務2先於任務1完成, 結果是怎樣的呢?
答案是: 任務2的結果會先儲存在deferred2裡, 當呼叫deferred2.await()時, 會立即返回, 不會引起協程掛起, 因為deferred2已經準備好了.
所以, suspend方法並不總是引起協程掛起, 只有其內部的資料未準備好時才會.
需要注意的是: await是suspend方法, 但async不是, 所以它才可以在協程外面呼叫, async只是啟動了協程, async本身不會引起協程掛起, 傳給async的lambda(也就是協程體)才可能引起協程掛起.

2.函式

2.1預設引數

函式引數可以有預設值,當省略相應的引數時使用預設值。與其他語言相比,這可以減少過載數量。

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) { …… }

覆蓋方法總是使用與基型別方法相同的預設引數值。 當覆蓋一個帶有預設引數值的方法時,必須從簽名中省略預設引數值:

open class A {
    open fun foo(i: Int = 10) { …… }
}

class B : A() {
    override fun foo(i: Int) { …… }  // 不能有預設值
}

如果一個預設引數在一個無預設值的引數之前,那麼該預設值只能通過使用命名引數呼叫該函式來使用:

fun foo(bar: Int = 0, baz: Int) { /* …… */ }

foo(baz = 1) // 使用預設值 bar = 0

不過如果最後一個 lambda 表示式引數從括號外傳給函式函式呼叫,那麼允許預設引數不傳值:

fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { /* …… */ }

foo(1) { println("hello") } // 使用預設值 baz = 1 
foo { println("hello") }    // 使用兩個預設值 bar = 0 與 baz = 1

2.2 命名引數

可以在呼叫函式時使用命名的函式引數。當一個函式有大量的引數或預設引數時這會非常方便。
給定以下函式

fun reformat(str: String,
             normalizeCase: Boolean = true,
             upperCaseFirstLetter: Boolean = true,
             divideByCamelHumps: Boolean = false,
             wordSeparator: Char = ' ') {
……
}

我們可以使用預設引數來呼叫它:

reformat(str)

然而,當使用非預設引數呼叫它時,該呼叫看起來就像:

reformat(str, true, true, false, '_')

使用命名引數我們可以使程式碼更具有可讀性:

reformat(str,
    normalizeCase = true,
    upperCaseFirstLetter = true,
    divideByCamelHumps = false,
    wordSeparator = '_'
)

並且如果我們不需要所有的引數:

reformat(str, wordSeparator = '_')

當一個函式呼叫混用位置引數與命名引數時,所有位置引數都要放在第一個命名引數之前。例如,允許呼叫 f(1, y = 2) 但不允許 f(x = 1, 2)。
可以通過使用星號操作符將可變數量引數(vararg) 以命名形式傳入:

fun foo(vararg strings: String) { /* …… */ }

foo(strings = *arrayOf("a", "b", "c"))
foo(strings = "a") // 對於單個值不需要星號

3.其他知識點

3.1 componentX (多宣告)

val f1 = Forecast(Date(), 27.5f, "Shinny")
val (date, temperature, details) = f1
//=======================
// 上面的多宣告會被編譯成下面的程式碼
val date = f1.component1()
val temperature = f1.component2()
val details = f1.copmponent3()
// 對映物件的每一個屬性到一個變數中,這就是 多宣告。
// object class 預設具有該屬性。但普通 class 想要具有這種屬性,需要這樣做:
class person(val name: String, val age: Int) {
    operator fun component1(): String {
        return name
    }
    operator fun component2(): Int {
        return age
    }
}

val 必須有: 用來儲存在 component1 和 component2 中返回建構函式傳進來的引數的。
operator 暫時還不明真相,IDE 提示的。 操作符過載,函式名為操作符名(即系統預設的關鍵詞,此處為 component1,component2).當使用該操作時,自己重寫的操作會覆蓋系統預設的操作。

// 常見用法:該特性功能強大,可以極大的簡化程式碼量。 如 map 中的擴充套件函式實現,允許在迭代時使用 key value
for ((key, value) in map) {
    Log.d("map","key:$key, value:$value")
}

3.2 inline (行內函數)

行內函數與普通的函式有點不同。一個行內函數會在編譯的時候被替換掉,而不是真正的方法呼叫。這在譯寫情況下可以減少記憶體分配和執行時開銷。例如,有一函式只接收一個函式作為它的引數。如果是普通函式,內部會建立一個含有那個函式的物件。而行內函數會把我們呼叫這個函式的地方替換掉,所以它不需要為此生成一個內部的物件。

// 例一、建立程式碼塊只提供 Lollipop 或更高版本來執行
inline fun supportsLollipop(code: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        code()
    }
}
// usage
supportsLollipop {
    window.setStatusBarColor(Color.BLACK)
}

3.3 Application 單例化和屬性的 Delegated (by)

class App : Application() {
      companion object {
          private var instance: Application? = null
          fun  instance() = instance!!
      }
      override fun onCreate() {
          super.onCreate()
          instance = this
      }
  }

我們可能需要一個屬性具有一些相同的行為,使用 lazy 或 observable 可以被很有趣的實現重用,而不是一次又一次的去宣告那些相同的程式碼。kotlin 提供了一個委託屬性到一個類的方法。這就是委託屬性。

class Delegate<T> : ReadWriteProperty<Any?, T> {
      fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return ...
      }
      fun setValue(thisRef: Any?,property: KProperty<*>, value: T) {...}  
      // 如果該屬性是不可修改(val), 就會只有一個 getValue 函式
  }

3.4 Not Null

場景1:需要在某些地方初始化該屬性,但不能在建構函式中確定,或不能在建構函式中做任何事。
場景2:在 Activity fragment service receivers…中,一個非抽象的屬性在建構函式執行之前需要被賦值。

解決方案1:使用可 null 型別並且賦值為 null,直到真正去賦值。但是,在使用時就需要不停的進行 not null 判斷。
解決方案2:使用 notnull 委託。含有一個可 null 的變數並會在設定該屬性時分配一個真實的值。如果該值在被獲取之前沒有被分配,它就會丟擲一個異常。

class App : Application() {
  companion object {
    var instance: App by Delegates.notnull()
  }
   override fun onCreate() {
      super.onCreate()
      instance = this
    }
}

3.5 從 Map 中對映值

另一種委託方式,屬性的值會從一個map中獲取 value,屬性的名字對應這個map 中的 key。

import kotlin.properties.getValue
class Configuration(map: Map<String,Any?>) {
  val width: Int by map
  val height: Int by map
  val dp: Int by map
  val deviceName: String by map
}
// usage
conf = Configuration(mapof(
  "width" to 1080,
  "height" to 720,
  "dp" to 240,
   "deviceName" to "myDecive"
)) 

3.6 custom delegate

自定義委託需要實現 ReadOonlyProperty / ReadWriteProperty 兩個類,具體取決於被委託的物件是 val 還是 var。

// step1
private class NotNullSingleValueVar<T>() : ReadWriteProperty<Any?, T> {
  private var value: T? = null
  override fun getValue(thisRef: Any?, property: KProperty<*>): T {
      return value ?: throw IllegalStateException("${desc.name not initialized}")
  }

  override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
      this.value = if (this.value == null) value else throw IllegalStateException("${desc.name} already initialized")
      }
}
// step2: usage
object DelegatesExt {
  fun notNullSingleValue<T>(): ReadWriteProperty<Any?, T> = NotNullSingleValueVar()
}

3.7 重新實現 Application 單例

class App : Application() {
    companion object {
        var instance: App by Delegates.notNull()
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}  
// 此時可以在 app 的任何地方修改這個值,因為**如果使用 Delegates.notNull(), 
//屬性必須是 var 的。可以使用剛剛建立的委託,只能修改該值一次
companion object {
    var instance: App by DeleagesExt.notNullSingleValue()

4.使用協程別導錯包

import org.jetbrains.anko.coroutines.experimental.Ref
import org.jetbrains.anko.coroutines.experimental.asReference
implementation "org.jetbrains.anko:anko-coroutines:$anko_version"
fun loadAndShowData() {
// Ref<T> uses the WeakReference under the hood
val ref: Ref<MainActivity> = this.asReference()

async(CommonPool) {
val data = getData()

// Use ref() instead of [email protected]
launch(UI) {
 ref().asyncOverlay()
 }
}
}


fun getData(): Data { ... } 
fun showData(data: Data) { ... } 
async(UI) {
   val data: Deferred<Data> = bg {
      // Runs in background
      getData() 
}
 // This code is executed on the UI thread 
showData(data.await()) 
}

相關推薦

Kotlin一些知識點學習

1. 協程 fun main(args: Array<String>) { launch(CommonPool) { delay(1000L) println("World!") }

C語言一些知識點

  1.學程式設計:找程式碼,讀程式碼,改程式碼,寫程式碼;少看書。但少看不是不看,看一本書要限制在一天內。任務是通讀書中主要的概念,明白大致是幹什麼用的,形成一個大概的框架;然後一週內幹掉書中全部程式碼,程式碼不理解再瀏覽書中相關章節,注意是瀏覽。或者刷部落格。程式碼實在刷不懂,可以先刷後面,待

spring源碼學習知識點

學習 pos pri proto 創建 except 完成 bean 容器 一、循環依賴 循環依賴就是循環引用,就是兩個或多個bean之間互相持有對方。 1.構造器循環依賴 表示通過構造器註入造成的循環依賴,此依賴是無法解決的,只能拋出BeanCurrentlyInCrea

整理c#學習知識點

alt 其他 相同 bool 操作數 分享 知識 轉義符 else 首先作為一個小白,在短時間內學會C#是有一定難度的,我就最近學習的內容,梳理了一下知識點,日後回過頭來多看看多想想,我想這對自己的學習幫助很大,知識點如下: 1.轉義符 在代碼中書寫一個斜杠(\),用於改變

es一些知識點記錄

一個 url 5-0 num mage 每次 強制 查詢 策略 1. forcemerge接口 強制段合並,設置為1時,是期望最終只有1個索引段。但實際情況是,合並的結果是段的總數會減少,但仍大於1,可以多次執行強制合並的命令。 設置的的目標值越小。合並消耗

python一些小的知識點

asc byte 知識 error nbsp 中一 文件 users cte 1:只有數字,字符串有小數據池 小數據池:在一定範圍內,即使分別給兩兩個變量賦相等的值,它們的id地址還會是相同的 數字範圍: -5 ~ 256 字符串:1:不能有特殊字符     2:str*

es6 一些常見的新知識點

es6 bsp keys 使用 字符串 key 函數類 map www 1,箭頭函數,基本語法:()=>{}; 2,cookie的應用 3,頁面重定向 4,void關鍵字 5,生成器function* () {},yield關鍵字,.next()方法,不能用箭頭函數來

Python一些基礎知識點的匯總:零基礎也能看懂的Python基礎

裏的 方法 format 官網 str 啟動 基礎知識 mea 輸出字符串 ①首先得去官網下載個Python,目前最新版本我不太清除。下載鏈接http://www.python.org/downloads/。下載完成後直接啟動python shell 開始你的py

機器學習一些方向以及方法的大體解釋

子空間學習:subspace learning (1) 子空間學習是指通過投影實現高維特徵向低維空間的對映,是一種經典的降維思想。 例如人臉影象,如果每幅影象提取出來的特徵是1000維,則每幅影象對應著1000維空間中的一個點。維數太高給計算帶來很多問題,且很多人認為真實有效的人臉影象特徵並

關於python學習知識點

字串操作                                           &nb

學習JSP相關知識點對應的小專案

檔案上傳下載:     1、網盤精靈(使用FileUpload技術實現檔案上傳下載,能顯示出資源所有檔案,通過樹的遍歷實現) 過濾器:     1、實現不同請求方式的編碼問題統一(增強getParam

html一些知識點(input的size和width的區別,HTML字元實體,複選框的checked,等等知識點)

html中的一些知識點(input的size和width的區別,HTML字元實體,複選框中的checked,等等知識點) 直接看下面的程式碼和截圖 <%@page import="org.apache.jasper.tagplugins.jstl.core.ForEach"%>

Java ConcurrentHashMap的一些知識點

1.ConcurrentHashMap ConcurrentHashMap在JDK1.8以後不採用分段鎖的機制,取消了segments欄位,直接採用transient,volatile,HashEnt

快速學習Kotlin(九)Kotlin的特殊符號

反引號(鍵盤左上角和波浪線在一起的符號) `` 在Kotlin中可以用反引號解決關鍵字衝突的問題,可以強行將一個不合法的字元變為合法。 fun `123`(){ println("this is message") } fun main(args: Array&

開發過程遇到的一些知識點

這些知識點都是開發過程中遇到的並且瞭解的,也許不是太詳細,記錄下來留作筆記,感興趣的朋友可以再深入的查詢下。 一:SLF4J的應用(整合了log4j) SLF4J全稱是Simple Logging Facade For Jave,即簡單的日誌門面。SLF4J並不是具體的日

java SE學習過程知識點小結(一)(很多內容過於基礎,希望能幫助到學習路上的同學)————歡迎老手批評指正

①、把boolean測試放在括號內:例如while(x==4){}  //當然看過很多部落格,裡面有工作經驗的工作者說以後公司可能習慣性寫(4==x) ②、所有java程式都定義在類中(也是區別於C++的主要特徵) ③、物件本身已知到的事物稱為例項變數,它代表物件的狀態,物件可執行的動作稱為方法。 ④、類可以

python學習過程細節知識點

1.print語句中的end方法:設定print語句結束語句的字元        print輸出語句預設換行,即pring語句結束語句的預設字元是換行字元        prirnt('this is shu chu yu ju ',end='')#設定該print輸出語

JS讓新手倍感震驚、違反直覺、出乎意料、的一些知識點彙總記錄

本文記錄在自己學習js過程中,違反直覺,出乎意料,倍感震驚的知識點。當然,不瞭解這個知識點,很容易出錯,因為畢竟違反直覺,出乎意料,倍感震驚嘛! 1. 兩個內容一樣的陣列竟然不相等? 1 var a = [1,2,3]; 2 var b = [1,2,3]; 3 consol

jQuery一些必須要知道的知識點總結--20個(上)

1. jQuery 庫中的 $() 是什麼? $()函式是JQuery函式的別稱,就是一種標誌,符合其思想:write less,do more。第一次接觸可能有些不適應,是jQuery程式碼佶屈聱牙,晦澀難懂。我們需要適應一段時間,過一段時間,就會愛上其簡潔,方便,飛

【筆記】C++一些易混淆的知識點

Note 文章的知識點基本來自於《C++ Primer》,其中的觀點可以看作是對原文的翻譯 Public,Protected,Private三種繼承方式 派生類在繼承列表中通過在基類名字前加上許可權標籤來確定繼承方式。 class Derived