第 1 章 scala的概述1
1.1 學習sdala的原因 1
1.2 Scala語言誕生小故事 1
1.3 Scala 和 Java 以及 jvm 的關系分析圖 2
1.4 Scala語言的特點 3
1.5 Windows下搭建Scala開發環境 4
1.6 Linux下搭建Scala開發環境 5
1.7 Scala開發工具的介紹 8
1.7.1 idea工具的介紹 8
1.7.2 Scala插件安裝 8
1.8 scala的開發的快速入門 10
1.8.1 IDE工具Idea 來開發 “hello,world” 10
1.8.2 Scala程序反編譯-說明scala程序的執行流程 15
1.8.3 使用java寫了一段模擬的代碼 17
1.8.4 課堂小練習 18
1.8.5 Scala執行流程分析 18
1.8.6 Scala程序開發注意事項(重點) 19
1.9 Scala語言轉義字符 19
1.10 Scala語言輸出的三種方式 21
1.10.1 基本介紹 21
1.10.2 應用案例 21
1.11 Scala源碼的查看的關聯 22
1.12 注釋(comment) 23
1.12.1 介紹: 23
1.12.2 Scala中的注釋類型 23
1.12.3 文檔注釋的案例 24
1.13 規范的代碼風格 25
1.13.1 正確的注釋和注釋風格: 25
1.13.2 規范代碼的幾點要求 25
1.14 Scala官方編程指南 25
第 2 章 變量28
2.1 變量的介紹 28
2.1.1 概念 28
2.1.2 變量使用的基本步驟 28
2.2 Scala變量使用案例入門 28
2.3 Scala變量使用說明 29
2.3.1 變量聲明基本語法 29
2.3.2 注意事項 29
2.4 程序中 +號的使用 32
2.5 數據類型 33
2.5.1 scala數據類型的介紹 33
2.6 scala數據類型體系一覽圖 34
2.6.1 一覽圖 34
2.6.2 對scala數據類型體系總結 35
2.7 整數類型 36
2.7.1 基本介紹 36
2.7.2 整型的類型 36
2.7.3 應用案例 36
2.7.4 整型的使用細節 37
2.8 浮點類型 39
2.8.1 基本介紹 39
2.8.2 浮點型的分類 39
2.8.3 浮點型使用細節 39
2.9 字符類型(Char) 41
2.9.1 基本介紹 41
2.9.2 案例演示: 41
2.9.3 字符類型使用細節 41
2.9.4 字符類型本質探討 42
2.10 布爾類型:Boolean 43
2.10.1 基本介紹 43
2.11 Unit類型、Null類型和Nothing類型 43
2.11.1 基本說明 43
2.11.2 使用細節和注意事項 44
2.12 值類型轉換 45
2.12.1 值類型隱式轉換 45
2.12.2 高級隱式轉換和隱式函數 48
2.12.3 強制類型轉換 48
2.13 值類型轉換-課堂練習題 49
2.14 值類型和String類型的轉換 50
2.14.1 介紹 50
2.14.2 基本類型轉String類型 50
2.14.3 String類型轉基本數據類型 50
2.14.4 注意事項 51
2.15 標識符的命名規范 51
2.15.1 標識符概念 51
2.15.2 標識符的命名規則(記住) 51
2.15.3 標識符舉例說明 52
2.15.4 標識符命名注意事項 53
2.15.5 關鍵字介紹 53
第 3 章 運算符54
3.1 運算符介紹 54
3.2 算術運算符一覽 54
3.2.1 案例演示 54
3.2.2 細節說明 55
3.2.3 課堂練習 56
3.3 關系運算符(比較運算符) 57
3.3.1 一覽圖: 57
3.3.2 案例演示 58
3.3.3 細節說明 58
3.4 邏輯運算符 58
3.4.1 一覽圖 58
3.5 賦值運算符 59
3.5.1 位運算符 60
3.6 運算符的特別說明 61
3.7 運算符優先級 62
3.8 鍵盤輸入語句 63
3.8.1 介紹 63
3.8.2 步驟 : 63
3.8.3 案例演示: 64
第 4 章 流程控制67
4.1 分支控制if-else 67
4.1.1 分支控制if-else介紹 67
4.1.2 單分支 67
4.1.3 單分支對應流程圖 68
4.1.4 雙分支 68
4.1.5 多分支 70
4.1.6 分支控制if-else 注意事項 73
4.2 嵌套分支 75
4.2.1 基本介紹 75
4.2.2 基本語法 75
4.2.3 應用案例1 75
4.2.4 多分支的案例應用案例 76
4.3 switch分支結構 77
4.4 for循環控制 77
4.4.1 基本介紹 77
4.4.2 范圍數據循環方式1 77
4.4.3 范圍數據循環方式2 78
4.4.4 循環守衛 79
4.4.5 引入變量 80
4.4.6 嵌套循環 81
4.4.7 循環返回值 82
4.4.8 使用花括號{}代替小括號() 82
4.4.9 注意事項和細節說明 84
4.4.10 for循環練習題(學員先做) 85
4.5 while循環控制 86
4.5.1 基本語法 86
4.5.2 while循環應用實例 86
4.5.3 注意事項和細節說明 87
4.6 do..while循環控制 87
4.6.1 注意事項和細節說明 88
4.6.2 課堂練習題【學員先做】 88
4.7 多重循環控制 89
4.7.1 介紹: 89
4.7.2 應用實例: 89
4.8 while循環的中斷 92
4.8.1 基本說明 92
4.8.2 應用案例 92
4.8.3 課堂練習題: 94
第 5 章 函數式編程基礎96
5.1 函數式編程內容及授課順序說明 96
5.2 函數式編程授課順序即oop和函數式編程關系 96
5.2.1 幾個概念的說明 97
5.3 函數式編程介紹 98
5.4 為什么需要函數 98
5.5 函數介紹 98
5.5.1 基本語法 99
5.5.2 函數的快速入門 99
5.6 函數-調用機制 99
5.6.1 函數-調用過程 100
5.7 函數-遞歸調用 101
5.7.1 基本介紹 101
5.7.2 遞歸調用快速入門 101
5.7.3 函數遞歸需要遵守的重要原則: 102
5.7.4 遞歸課堂練習題 102
5.8 函數注意事項和細節討論 103
5.9 函數練習題 108
5.10 過程 109
5.10.1 基本介紹 109
5.10.2 案例說明: 109
5.11 惰性函數 110
5.11.1 看一個應用場景 110
5.11.2 畫圖說明[大數據推薦系統] 110
5.11.3 Java實現懶加載的代碼 110
5.11.4 介紹 111
5.11.5 案例演示 111
5.11.6 注意事項和細節 112
5.12 異常 112
5.12.1 介紹 113
5.12.2 Java異常處理回顧 113
5.12.3 Java異常處理的注意點. 113
5.12.4 Scala異常處理舉例 114
5.12.5 scala異常的小結和注意事項 114
5.13 函數的課堂練習題 115
第 6 章 面向對象編程基礎部分117
6.1 類與對象 117
6.1.1 看一個養貓貓問題 117
6.1.2 快速入門-面向對象的方式解決養貓問題 117
6.1.3 如何定義類 118
6.1.4 屬性 119
6.1.5 屬性的高級部分 121
6.1.6 如何創建對象 122
6.1.7 類和對象的內存分配機制 124
6.1.8 方法 125
6.1.9 課堂練習題 126
6.2 類與對象應用實例 128
6.2.1 小狗案例 128
6.3 構造器 130
6.3.1 看一個需求 130
6.3.2 回顧-Java構造器基本語法 131
6.3.3 回顧-Java構造器的特點 131
6.3.4 Scala構造器的介紹 132
6.3.5 Scala構造器的基本語法 132
6.3.6 快速入門 133
6.3.7 Scala構造器注意事項和細節 134
6.4 屬性高級 139
6.4.1 構造器參數 139
6.4.2 Bean屬性 141
6.5 對象創建的流程分析 143
6.5.1 看一個案例 143
6.5.2 流程分析(面試題) 143
第 7 章 面向對象編程(中級)145
7.1 包 145
7.1.1 看一個應用場景 145
7.1.2 回顧-Java包的三大作用 145
7.1.3 回顧-Java打包命令 145
7.1.4 打包的本質分析 146
7.1.5 快速入門 146
7.1.6 回顧-Java如何引入包 146
7.1.7 回顧-Java包的特點 147
7.1.8 Scala包的基本介紹 147
7.1.9 Scala包快速入門 147
7.1.10 Scala包的特點概述 147
7.1.11 Scala包的命名 149
7.1.12 Scala會自動引入的常用包 149
7.1.13 Scala包注意事項和使用細節 150
7.1.14 包對象 153
7.1.15 包對象的應用案例 154
7.1.16 包對象的注意事項 154
7.2 包的可見性 155
7.2.1 Scala中包的可見性介紹: 155
7.2.2 Scala中包的可見性和訪問修飾符的使用 155
7.3 包的引入 157
7.3.1 Scala引入包的細節和注意事項 157
7.4 面向對象編程方法-抽象 159
7.4.1 如何理解抽象 159
7.5 面向對象編程三大特征 162
7.5.1 基本介紹 162
7.5.2 封裝介紹 162
7.5.3 封裝的理解和好處 163
7.5.4 如何體現封裝 163
7.5.5 封裝的實現步驟 163
7.5.6 快速入門案例 164
7.5.7 封裝的課堂練習題 165
7.5.8 Scala封裝的注意事項和細節 165
7.6 面向對象編程-繼承 166
7.6.1 Java繼承的簡單回顧 166
7.6.2 繼承基本介紹和示意圖 166
7.6.3 Scala繼承的基本語法 167
7.6.4 Scala繼承快速入門 167
7.6.5 Scala繼承給編程帶來的便利 169
7.6.6 子類繼承了什么,怎么繼承了? 169
7.6.7 重寫方法 171
7.6.8 Scala中類型檢查和轉換 172
7.6.9 Scala中超類的構造 177
7.6.10 覆寫字段 182
7.6.11 抽象類 187
7.6.12 Scala抽象類使用的注意事項和細節討論 189
7.6.13 匿名子類 190
7.6.14 課后練習題 192
第 8 章 面向對象編程-高級194
8.1 靜態屬性和方法 194
8.1.1 靜態屬性-提出問題 194
8.1.2 回顧下Java的靜態概念 194
8.1.3 Scala中靜態的概念-伴生對象 194
8.1.4 伴生對象的快速入門 195
8.1.5 伴生對象的小結 196
8.1.6 伴生對象解決小孩游戲問題 198
8.1.7 apply方法的補充 199
8.2 單例對象 200
8.2.1 什么是單例對象 200
8.2.2 回顧-Java單例對象 201
8.2.3 Scala中單例對象 203
8.2.4 單例模式的面試題 203
8.3 接口 204
8.3.1 回顧Java接口 204
8.3.2 Scala接口的介紹 205
8.4 特質(trait) 205
8.4.1 trait 的聲明 205
8.4.2 Scala中trait 的使用 206
8.4.3 特質的快速入門案例 206
8.4.4 特質trait 的再說明 209
8.4.5 帶有特質的對象,動態混入 211
8.4.6 scala創建對象的四種形式 214
8.4.7 疊加特質 214
8.4.8 在特質中重寫抽象方法 217
8.4.9 當作富接口使用的特質 220
8.4.10 特質中的具體字段 221
8.4.11 特質構造順序 221
8.4.12 擴展類的特質 222
8.4.13 自身類型 224
8.5 嵌套類 225
8.5.1 基本介紹 225
8.5.2 類型投影 229
8.6 數組的基本使用 230
第 9 章 隱式轉換和隱式參數232
9.1 隱式轉換 232
9.1.1 提出問題 232
9.1.2 隱式函數基本介紹 232
9.1.3 隱式函數快速入門 232
9.1.4 隱式函數的底層工作原理 233
9.1.5 隱式轉換的注意事項和細節 233
9.2 隱式轉換豐富類庫功能 234
9.2.1 基本介紹 234
9.2.2 分析解決方案 234
9.2.3 快速入門案例 234
9.3 隱式值 235
9.3.1 基本介紹 235
9.3.2 應用案例 235
9.3.3 課堂測試題 236
9.3.4 課堂練習 236
9.4 隱式類 237
9.4.1 基本介紹 237
9.4.2 隱式類使用有如下幾個特點: 238
9.4.3 應用案例 238
9.5 隱式的轉換時機 239
9.6 隱式解析機制 239
9.7 隱式轉換的前提 239
第 10 章 數據結構上-集合241
10.1 數據結構的特點 241
10.1.1 scala集合基本介紹 241
10.1.2 可變集合和不可變集合舉例 241
10.2 不可變集合繼承層次一覽 242
10.3 Scala可變集合繼承關系一覽圖 244
10.4 數組-定長數組(聲明泛型) 245
10.4.1 第一種方式定義數組 245
10.4.2 第二種方式定義數組 246
第 1 章 scala的概述
1.1 學習sdala的原因
1.2 Scala語言誕生小故事
1.3 Scala 和 Java 以及 jvm 的關系分析圖
1.4 Scala語言的特點
Scala是一門以java虛擬機(JVM)為運行環境並將面向對象和函數式編程的最佳特性結合在一起
的靜態類型編程語言。
1) Scala 是一門多范式 (multi-paradigm) 的編程語言,Scala支持面向對象和函數式編程
2) Scala源代碼(.scala)會被編譯成Java字節碼(.class),然后運行於JVM之上,並可以調用現有的Java類庫,實現兩種語言的無縫對接。[案例演示]
3) scala 單作為一門語言來看, 非常的簡潔高效 (三元運算, ++ , --)
4) Scala 在設計時,馬丁·奧德斯基 是參考了Java的設計思想,可以說Scala是源於java,同時馬丁·奧德斯基 也加入了自己的思想,將函數式編程語言的特點融合到JAVA中, 因此,對於學習過Java的同學,只要在學習Scala的過程中,搞清楚Scala 和 java相同點和不同點,就可以快速的掌握Scala這門語言
5) 快速有效掌握Scala的三點建議 [1. 學習scala的特有的語法 2. 區別 scala和Java 3. 如何規范使用scala]
1.5 Windows下搭建Scala開發環境
具體的步驟
1) 首先把jdk1.8 安裝
2) 下載對應的scala安裝文件 scala-2.11.8.zip
3) 解壓 我這里解壓到 d:/program
4) 配置scala 的環境變量
5) 測試一下, 輸入scala的指令看看效果
6) OK!
1.6 Linux下搭建Scala開發環境
在實際開發中,我們的項目是部署到linux,因此,我們需要在Linux下搭建scala的環境。
具體的步驟如下:
1) 下載對應的scala的安裝軟件.scala-2.11.8.tgz
2) 通過遠程登錄工具,將安裝軟件上傳到對應的linux系統(xshell5 xftp5)
3) mkdir /usr/local/scala 創建目錄
4) tar -xvzf scala-2.11.8.tgz && mv scala-2.11.8 /usr/local/scala/ 將安裝文件解壓,並且移動到 /usr/local/scala
5) 配置環境變量 vim /etc/profile
在該文件中配置 scala的bin目錄 /usr/local/scala/scala-2.11.8/bin
6) 測試一把
- Scala的REPL
1) 介紹
上面打開的scala命令行窗口,我們稱之為REPL,是指:Read->Evaluation->Print->Loop,也稱之為交互式解釋器。
2) 說明
在命令行窗口中輸入scala指令代碼時,解釋器會讀取指令代碼(R)並計算對應的值(E),然后將結果打印出來(P),接着循環等待用戶輸入指令(L)。從技術上講,這里其實並不是一個解釋器,而是指令代碼被快速的編譯成Java字節碼並被JVM加載執行。最終將執行結果輸出到命令行中
3) 示意圖
1.7 Scala開發工具的介紹
1.7.1 idea工具的介紹
1.7.2 Scala插件安裝
- 看老師的步驟
1) scala-intellij-bin-2017.2.6.zip
2) 建議該插件文件放到scala的安裝目錄
3) 將插件安裝到idea
4) 先找到安裝插件位置 file->setting...
5) 點擊ok->apply -> 重啟idea 即可
6) ok
1.8 scala的開發的快速入門
1.8.1 IDE工具Idea 來開發 “hello,world”
- 步驟1:file->new project -> 選擇 maven
- 步驟2:
- 步驟3:
- 步驟4:默認下,maven不支持scala的開發,需要引入scala框架.
右鍵項目點擊-> add framework support... ,在下圖選擇scala
注意:如果是第一次引入框架,Use libary 看不到,需要配置, 配置就是選擇你的scala安裝目錄,然后工具就會自動識別,就會顯示user libary .
- 步驟5: 創建項目的源文件目錄
說明:右鍵main目錄->創建一個diretory -> 寫個名字(比如 scala)-> 右鍵scala 目錄->mark directory -> 選擇 source root 即可
- 步驟6: 開發一個Hello.scala的程序
- 創建包com.atguigu.chapter01 開發的程序
運行后,就可以看到輸出
1.8.2 Scala程序反編譯-說明scala程序的執行流程
| //package com.atguigu.chapter01
//下面我們說明一下scala程序的一執行流程 //分析 //1. object 在底層會生成兩個類 Hello , Hello$ //2. Hello 中有個main函數,調用 Hello$ 類的一個靜態對象 MODULES$ /* public final class Hello { public static void main(String[] paramArrayOfString) { Hello$.MODULE$.main(paramArrayOfString); } } */ //3. Hello$.MODULE$. 對象時靜態的,通過該對象調用Hello$的main函數 /* public void main(String[] args) { Predef..MODULE$.println("hello,scala"); } */ //4. 可以理解我們在main中寫的代碼在放在Hello$ 的main, 在底層執行scala編譯器做了一個包裝
object Hello { def main(args: Array[String]): Unit = { println("hello,scala") } }
|
1.8.3 使用java寫了一段模擬的代碼
| package com.atguigu.chapter01;
public class Hello2 { public static void main(String[] args) { Hello2$.MODULE$.main(args); } }
final class Hello2$ { public static final Hello2$ MODULE$;
static { MODULE$ = new Hello2$(); }
public void main(String[] args) { System.out.println("hello,scala"); } //private Hello$() { MODULE$ = this; }
}
|
1.8.4 課堂小練習
1.8.5 Scala執行流程分析
1.8.6 Scala程序開發注意事項(重點)
1) Scala源文件以 “.scala" 為擴展名。
2) Scala程序的執行入口是main()函數。
3) Scala語言嚴格區分大小寫。
4) Scala方法由一條條語句構成,每個語句后不需要分號(Scala語言會在每行后自動加分號),這也體現出Scala的簡潔性。
5) 如果在同一行有多條語句,除了最后一條語句不需要分號,其它語句需要分號。
1.9 Scala語言轉義字符
scala的轉義字符。
- 應用案例
輸出的結果:
C:\Users\Administrator\Desktop\SGG 韓順平 scala 核心編程\day01\筆記
k"kk
你好
hello
u
- 課后練習
1.10 Scala語言輸出的三種方式
1.10.1 基本介紹
1) 字符串通過+號連接(類似java)。
2) printf用法 (類似C語言)字符串通過 % 傳值。
3) 字符串通過$引用(類似PHP)。
1.10.2 應用案例
| package com.atguigu.chapter01.printdemo
object TestPrint { def main(args: Array[String]): Unit = { //使用+ var name : String = "tom" var sal : Double = 1.2 println("hello" + sal + name )
//使用格式化的方式printf printf("name=%s sal=%f\n", name, sal) //使用$引用的方式,輸出變量,類似php println(s"第三種方式 name=$name sal = $sal") } }
|
1.11 Scala源碼的查看的關聯
在使用scala過程中,為了搞清楚scala底層的機制,需要查看源碼,下面看看如果關聯和查看Scala的源碼包
1) 查看源碼, 選擇要查看的方法或者類, 輸入 ctrl + b
當我們沒關聯源碼時,看到如下圖像:
2) 關聯源碼,看老師演示
步驟1:將我們的源碼包拷貝到 scala/lib 文件夾下. scala-sources-2.12.4
步驟2: 關聯即可,選中這個文件夾,進行關聯, 最后,可以看到源碼
1.12 注釋(comment)
1.12.1 介紹:
用於注解說明解釋程序的文字就是注釋,注釋提高了代碼的閱讀性;
注釋是一個程序員必須要具有的良好編程習慣。將自己的思想通過注釋先整理出來,再用代碼去體現。
1.12.2 Scala中的注釋類型
1) 單行注釋
2) 多行注釋
3) 文檔注釋
1.12.3 文檔注釋的案例
package com.atguigu.chapter01.comment2
object TestDemo {
/**
* @deprecated xxx
* @param args
*/
def main(args: Array[String]): Unit = {
//print("0k")
//ctrl +D ctrl +y
/*
println("ok1")
println("ok1")
println("ok1")
println("ok1")
println("ok1")
*/
println("ok")
}
}
- 使用scaladoc 指令生成了相應文檔.
1.13 規范的代碼風格
1.13.1 正確的注釋和注釋風格:
帶看Scala源碼
1.13.2 規范代碼的幾點要求
1) 正確的縮進和空白
2) 使用一次tab操作,實現縮進,默認整體向右邊移動,時候用shift+tab整體向左移
3) 或者使用 ctrl + alt + L 來進行格式化 [演示]
4) 運算符兩邊習慣性各加一個空格。比如:2 + 4 * 5。
5) 一行最長不超過80個字符,超過的請使用換行展示,盡量保持格式優雅
1.14 Scala官方編程指南
還可以根據需要檢索自己要查看的類或者函數。
第 2 章 變量
2.1 變量的介紹
2.1.1 概念
變量相當於內存中一個數據存儲空間的表示,你可以把變量看做是一個房間的門
牌號,通過門牌號我們可以找到房間,而通過變量名可以訪問到變量(值)。
2.1.2 變量使用的基本步驟
1) 聲明/定義變量 (scala要求變量聲明時初始化)
2) 使用
2.2 Scala變量使用案例入門
變量在內存中的布局
2.3 Scala變量使用說明
2.3.1 變量聲明基本語法
var | val 變量名 [: 變量類型] = 變量值
說明:在scala中聲明一個變量時,可以不指定類型,編譯器根據值確定
2.3.2 注意事項
1) 聲明變量時,類型可以省略(編譯器自動推導,即類型推導)
2) 類型確定后,就不能修改,說明Scala 是強數據類型語言.
案例說明:
3) 在聲明/定義一個變量時,可以使用var 或者 val 來修飾, var 修飾的變量可改變,val 修飾的變量不可改 [案例].
4) val修飾的變量在編譯后,等同於加上final通過反編譯看下底層代碼
對應的底層的.class是
5) var 修飾的對象引用可以改變,val 修飾的則不可改變,但對象的狀態(值)卻是可以改變的。(比如: 自定義對象、數組、集合等等) [分析val好處]
6) 變量聲明時,需要初始值
2.4 程序中 +號的使用
1) 當左右兩邊都是數值型時,則做加法運算
2) 當左右兩邊有一方為字符串,則做拼接運
3) 案例演示:
2.5 數據類型
2.5.1 scala數據類型的介紹
1) Scala 與 Java有着相同的數據類型,在Scala中數據類型都是對象,也就是說scala沒有java中的原生類型
2) Scala數據類型分為兩大類 AnyVal(值類型) 和 AnyRef(引用類型), 注意:不管是AnyVal還是AnyRef 都是對象。[案例演示 Int , Char]
3) 相對於java的類型系統,scala要復雜些!也正是這復雜多變的類型系統才讓面向對象編程和函數式編程完美的融合在了一起
2.6 scala數據類型體系一覽圖
2.6.1 一覽圖
2.6.2 對scala數據類型體系總結
1) scala中一切數據都是對象,都是Any的子類。
2) scala中數據類型分為兩大類 值類型(AnyVal) , 引用類型(AnyRef),不管是值類型還是引用類型都是對象。
3) scala數據類型仍然遵守低精度的值類型向高精度的值類型,自動轉換(隱式轉換)
4) scala中有兩個特殊的類型 Null , 它只有一個實例就是null, 在scala中,也叫bottom class
5) scala中有個特殊的類型 Nothing, 它是用於在一個函數沒有正常返回值使用 , 因為這樣我們可以把拋出的返回值,返回給任何的變量或者函數.
2.7 整數類型
2.7.1 基本介紹
Scala的整數類型就是用於存放整數值的,比如 12 , 30, 3456等等
2.7.2 整型的類型
| 數據類型 |
描述 |
| Byte [1] |
8位有符號補碼整數。數值區間為 -128 到 127 |
| Short [2] |
16位有符號補碼整數。數值區間為 -32768 到 32767 |
| Int [4] |
32位有符號補碼整數。數值區間為 -2147483648 到 2147483647 |
| Long [8] |
64位有符號補碼整數。數值區間為 -9223372036854775808 到 9223372036854775807 = 2的(64-1)次方-1 |
2.7.3 應用案例
2.7.4 整型的使用細節
1) Scala各整數類型有固定的表數范圍和字段長度,不受具體OS的影響,以保證Scala程序的可移植性。
2) Scala的整型 常量/字面量 默認為 Int 型,聲明Long型 常量/字面量 須后加‘l’’或‘L’ [反編譯看]
反編譯后的字節碼文件
3) Scala程序中變量常聲明為Int型,除非不足以表示大數,才使用Long
2.8 浮點類型
2.8.1 基本介紹
Scala的浮點類型可以表示一個小數,比如 123.4f,7.8 ,0.12等等
2.8.2 浮點型的分類
| Float [4] |
32 位, IEEE 754標准的單精度浮點數 |
| Double [8] |
64 位 IEEE 754標准的雙精度浮點數 |
2.8.3 浮點型使用細節
- 應用案例
| package com.atguigu.chapter01.floattest
object FloatDemo { def main(args: Array[String]): Unit = {
var n1 = 5.12E2 //5.12 * 10的2次方 println("n1=" + n1) var n2 = 5.12e-2 // 5.12 / 10的2次方 println("n2=" + n2)
//建議,在開發中需要高精度小數時,請選擇Double var n3 = 2.2345678912f // var n4 = 2.2345678912 println("n3=" + n3) println("n4=" + n4)
//運行的結果 /* n1=512.0 n2=0.0512 n3=2.2345679 n4=2.2345678912 */
} }
|
2.9 字符類型(Char)
2.9.1 基本介紹
字符類型可以表示單個字符,字符類型是Char, 16位無符號Unicode字符(2個字節), 區間值為 U+0000 到 U+FFFF
2.9.2 案例演示:
2.9.3 字符類型使用細節
1) 字符常量是用單引號(‘ ’)括起來的單個字符。例如:var c1 = 'a‘ var c2 = '中‘ var c3 = '9'
2) Scala 也允許使用轉義字符‘\’來將其后的字符轉變為特殊字符型常量。例如:var c3 = ‘\n’ // '\n'表示換行符
3)可以直接給Char賦一個整數,然后輸出時,會按照對應的unicode 字符輸出 ['\u0061' 97]
4) Char類型是可以進行運算的,相當於一個整數,因為它都對應有Unicode碼.
2.9.4 字符類型本質探討
1) 字符型 存儲到 計算機中,需要將字符對應的碼值(整數)找出來
存儲:字符——>碼值——>二進制——>存儲
讀取:二進制——>碼值——> 字符——>讀取
2) 字符和碼值的對應關系是通過字符編碼表決定的(是規定好), 這一點和Java一樣。
2.10 布爾類型:Boolean
2.10.1 基本介紹
1) 布爾類型也叫Boolean類型,Booolean類型數據只允許取值true和false
2) boolean類型占1個字節。
3) boolean 類型適於邏輯運算,一般用於程序流程控制[后面詳解]:
- if條件控制語句;
- while循環控制語句;
- do-while循環控制語句;
- for循環控制語句
2.11 Unit類型、Null類型和Nothing類型
2.11.1 基本說明
| Unit |
表示無值,和其他語言中void等同。用作不返回任何結果的方法的結果類型。Unit只有一個實例值,寫成()。 |
| Null |
null , Null 類型只有一個實例值 null |
| Nothing |
Nothing類型在Scala的類層級的最低端;它是任何其他類型的子類型。 當一個函數,我們確定沒有正常的返回值,可以用Nothing 來指定返回類型,這樣有一個好處,就是我們可以把返回的值(異常)賦給其它的函數或者變量(兼容性) |
2.11.2 使用細節和注意事項
1) Null類只有一個實例對象,null,類似於Java中的null引用。null可以賦值給任意引用類型(AnyRef),但是不能賦值給值類型(AnyVal: 比如 Int, Float, Char, Boolean, Long, Double, Byte, Short)
2) Unit類型用來標識過程,也就是沒有明確返回值的函數。
由此可見,Unit類似於Java里的void。Unit只有一個實例,
(),這個實例也沒有實質的意義
3) Nothing,可以作為沒有正常返回值的方法的返回類型,非常直觀的告訴你這個方法不會正常返回,而且由於Nothing是其他任意類型的子類,他還能跟要求返回值的方法兼容。
2.12 值類型轉換
2.12.1 值類型隱式轉換
- 介紹
當Scala程序在進行賦值或者運算時,精度小的類型自動轉換為精度大的數據類型,這個就是自動類型轉換(隱式轉換)。
- 數據類型按精度(容量)大小排序為
- 案例演示
演示一下基本數據類型轉換的基本情況。
- 注意事項和細節說明
自動類型轉換細節說明
1) 有多種類型的數據混合運算時,系統首先自動將所有數據轉換成容量最大的那種數據類型,然后再進行計算。
2) 當我們把精度(容量)大 的數據類型賦值給精度(容量)小 的數據類型時,就會報錯,反之就會進行自動類型轉換。
3) (byte, short) 和 char之間不會相互自動轉換。
4) byte,short,char 他們三者可以計算,在計算時首先轉換為int類型。
5) 自動提升原則: 表達式結果的類型自動提升為 操作數中最大的類型
| package com.atguigu.chapter02.transtest
object TransferDemo { def main(args: Array[String]): Unit = {
var n = 1 + 2.0 println(n) // n 就是Double
//當我們把精度(容量)大 的數據類型賦值給精度(容量)小 的數據類型時,就會報錯 var n2 : Long = 1L //var n3 : Int = n2 //錯誤,原因不能把高精度的數據直接賦值和低精度。
//(byte, short) 和 char之間不會相互自動轉換 var n4 : Byte = 1 //var c1 : Char = n4 錯
//byte,short,char 他們三者可以計算,在計算時首先轉換為int類型
var n5 : Byte = 1 var c2 : Char = 1 // var n : Short = n5 + c2 //當n5 + c2 結果類型就是int // var n6 : Short = 10 + 90 //錯誤 var n7 : Short = 100 //√
} }
|
2.12.2 高級隱式轉換和隱式函數
- scala還提供了非常強大的隱式轉換機制(隱式函數,隱式類等等),我們放在高級部分專門用一個章節來講解
2.12.3 強制類型轉換
- 介紹
自動類型轉換的逆過程,將容量大的數據類型轉換為容量小的數據類型。使用時要加上強制轉函數,但可能造成精度降低或溢出,格外要注意。
- 案例演示
java : int num = (int)2.5
scala : var num : Int = 2.7.toInt
- 強制類型轉換細節說明
1) 當進行數據的 從 大——>小,就需要使用到強制轉換
2) 強轉符號只針對於最近的操作數有效,往往會使用小括號提升優先級
3) Char類型可以保存 Int的常量值,但不能保存Int的變量值,需要強轉
4) Byte和Short類型在進行運算時,當做Int類型處理。
2.13 值類型轉換-課堂練習題
2.14 值類型和String類型的轉換
2.14.1 介紹
在程序開發中,我們經常需要將基本數據類型轉成String 類型。或者將String類型轉成基本數據類型。
2.14.2 基本類型轉String類型
語法: 將基本類型的值+"" 即可
案例演示
2.14.3 String類型轉基本數據類型
案例說明:
2.14.4 注意事項
1) 在將String 類型轉成 基本數據類型時,要確保String類型能夠轉成有效的數據,比如 我們可以把 "123" , 轉成一個整數,但是不能把 "hello" 轉成一個整數
2) 思考就是要把 "12.5" 轉成 Int ? // 錯誤,在轉換中,不會使用截取
2.15 標識符的命名規范
2.15.1 標識符概念
Scala 對各種變量、方法、函數等命名時使用的字符序列稱為標識符
凡是自己可以起名字的地方都叫標識符
2.15.2 標識符的命名規則(記住)
1) Scala中的標識符聲明,基本和Java是一致的,但是細節上會有所變化。
2) 首字符為字母,后續字符任意字母和數字,美元符號,可后接下划線_
3) 數字不可以開頭。
4) 首字符為操作符(比如+ - * / ),后續字符也需跟操作符 ,至少一個(反編譯)
5) 操作符(比如+-*/)不能在標識符中間和最后.
6) 用反引號`....`包括的任意字符串,即使是關鍵字(39個)也可以 [true]
案例:
2.15.3 標識符舉例說明
hello // ok
hello12 // ok
1hello // error
h-b // error
x h // error
h_4 // ok
_ab // ok
Int // ok , 因為在scala Int 是預定義的字符
Float // ok
_ // error ,因為在scala _ 有很多其他的作用。
Abc // ok
+*- // ok
+a // error
2.15.4 標識符命名注意事項
1) 包名:盡量采取有意義的包名,簡短,有意義
2) 變量名、函數名 、方法名 采用駝峰法。
2.15.5 關鍵字介紹
Scala有39個關鍵字:
package, import, class, object, trait, extends, with, type, forSome
private, protected, abstract, sealed, final, implicit, lazy, override
try, catch, finally, throw
if, else, match, case, do, while, for, return, yield
def, val, var
this, super
new
true, false, null
第 3 章 運算符
3.1 運算符介紹
運算符是一種特殊的符號,用以表示數據的運算、賦值和比較等。
1) 算術運算符
2) 賦值運算符
3) 比較運算符(關系運算符)
4) 邏輯運算符
5) 位運算符
3.2 算術運算符一覽
3.2.1 案例演示
案例演示算術運算符的使用(Operator.scala)。
+, - , * , / , % 重點講解 /、%
+、-、* 是一個道理,完全可以類推。
算數運算符的運算規則和Java一樣
案例:
3.2.2 細節說明
1) 對於除號“/”,它的整數除和小數除是有區別的:整數之間做除法時,只保留整數部分而舍棄小數部分。 例如:var x : Int = 10/3 ,結果是 3
2) 當對一個數取模時,可以等價 a%b=a-a/b*b , 這樣我們可以看到 取模的一個本質運算(和java 的取模規則一樣)。
3) 注意:Scala中沒有++、--操作符,需要通過+=、-=來實現同樣的效果
4) 案例
3.2.3 課堂練習
假如還有97天放假,問:xx個星期零xx天
定義一個變量保存華氏溫度,華氏溫度轉換攝氏溫度的公式為:5/9*(華氏溫度-100),請求出華氏溫度對應的攝氏溫度。[測試:232.5]
3.3 關系運算符(比較運算符)
3.3.1 一覽圖:
3.3.2 案例演示
3.3.3 細節說明
1) 關系運算符的結果都是Boolean型,也就是要么是true,要么是false。
2) 關系運算符組成的表達式,我們稱為關系表達式。 a > b
3) 比較運算符“==”不能誤寫成“=”
4) 使用陷阱: 如果兩個浮點數進行比較,應當保證數據類型一致.
3.4 邏輯運算符
3.4.1 一覽圖
3.5 賦值運算符
3.5.1 位運算符
3.6 運算符的特別說明
Scala不支持三目運算符 , 在Scala 中使用 if – else 的方式實現。
課堂練習
1) 案例1:求兩個數的最大值
2) 案例2:求三個數的最大值
3.7 運算符優先級
3.8 鍵盤輸入語句
3.8.1 介紹
在編程中,需要接收用戶輸入的數據,就可以使用鍵盤輸入語句來獲取。InputDemo.scala
3.8.2 步驟 :
導入該類的所在包
創建該類對象(聲明變量)
調用里面的功能
3.8.3 案例演示:
要求:可以從控制台接收用戶信息,【姓名,年齡,薪水】。
1) 回顧Java的實現
2) Scala的實現
使用java模擬一下為什么可以通過object的名字,直接調用其方法.
第 4 章 流程控制
4.1 分支控制if-else
4.1.1 分支控制if-else介紹
讓程序有選擇的的執行,分支控制有三種:
單分支
雙分支
多分支
4.1.2 單分支
- 基本語法
if (條件表達式) {
執行代碼塊
}
說明:當條件表達式為ture 時,就會執行 { } 的代碼。
- 案例說明
請大家看個案例[IfDemo.scala]:
編寫一個程序,可以輸入人的年齡,如果該同志的年齡大於18歲,則輸出 “age > 18”
4.1.3 單分支對應流程圖
4.1.4 雙分支
- 基本語法
if (條件表達式) {
執行代碼塊1
} else {
執行代碼塊2
}
- 案例演示
請大家看個案例[IfDemo2.scala]:
編寫一個程序,可以輸入人的年齡,如果該同志的年齡大於18歲,則輸出 “age >18”。否則 ,輸出 “ age <= 18 ”, 自己寫。
- 雙分支的流程圖
- 應用案例:
練習題:
4.1.5 多分支
- 基本語法
if (條件表達式1) {
執行代碼塊1
}
else if (條件表達式2) {
執行代碼塊2
}
……
else {
執行代碼塊n
}
- 多分支的流程圖
- 案例演示
請大家看個案例[IfDemo3.scala]:
岳小鵬參加scala考試,他和父親岳不群達成承諾:
如果:
成績為100分時,獎勵一輛BMW;
成績為(80,99]時,獎勵一台iphone7plus;
當成績為[60,80]時,獎勵一個 iPad;
其它時,什么獎勵也沒有。
說明: 成績在程序中指定!
代碼如下:
- 案例演示2
求ax2+bx+c=0方程的根。a,b,c分別為函數的參數,如果:b2-4ac>0,則有兩個解;
b2-4ac=0,則有一個解;b2-4ac<0,則無解; [a=3 b=100 c=6]
提示1:x1=(-b+sqrt(b2-4ac))/2a
X2=(-b-sqrt(b2-4ac))/2a
提示2:sqrt(num) 在 scala 包中(默認引入的) 的math 的包對象有很多方法直接可用.
//分析1. 多分支 2. 2. b2-4ac 計算出保存到一個變量 3. 引入math.sqrt
//走代碼。
4.1.6 分支控制if-else 注意事項
1) 如果大括號{}內的邏輯代碼只有一行,大括號可以省略, 這點和java 的規定一樣。
2) Scala中任意表達式都是有返回值的,也就意味着if else表達式其實是有返回結果的,具體返回結果的值取決於滿足條件的代碼體的最后一行內容.[案例演示]
3) Scala中是沒有三元運算符,因為可以這樣簡寫
4) 案例演示
4.2 嵌套分支
4.2.1 基本介紹
在一個分支結構中又完整的嵌套了另一個完整的分支結構,里面的分支的結構稱為內層分支外面的分支結構稱為外層分支。嵌套分支不要超過3層
4.2.2 基本語法
if(){
if(){
}else{
}
}
4.2.3 應用案例1
參加百米運動會,如果用時8秒以內進入決賽,否則提示淘汰。並且根據性別提示進入男子組或女子組。【可以讓學員先練習下5min】, 輸入成績和性別,進行判斷。1分鍾思考思路
double second; char gender;
4.2.4 多分支的案例應用案例
4.3 switch分支結構
在scala中沒有switch,而是使用模式匹配來處理。
模式匹配涉及到的知識點較為綜合,因此我們放在后面講解
4.4 for循環控制
4.4.1 基本介紹
Scala 也為for 循環這一常見的控制結構提供了非常多的特性,這些for 循環的特性被稱為for 推導式(for comprehension)或for 表達式(for expression)
4.4.2 范圍數據循環方式1
- 基本案例
for(i <- 1 to 3){
print(i + " ")
}
println()
- 說明
1) i 表示循環的變量, <- 規定好 to 規定
2) i 將會從 1-3 循環, 前后閉合
3) 輸出10句 "hello,SGG!"
4.4.3 范圍數據循環方式2
- 基本案例
for(i <- 1 until 3) {
print(i + " ")
}
println()
說明:
1) 這種方式和前面的區別在於 i 是從1 到 3-1
2) 即使前閉合后開的范圍
3) 輸出10句 "hello,SGG!"
for ( int i = 0; i < arr.lenght(); i++) {
}
4.4.4 循環守衛
- 基本案例
for(i <- 1 to 3 if i != 2) {
print(i + " ")
}
println()
- 基本案例說明
1) 循環守衛,即循環保護式(也稱條件判斷式,守衛)。保護式為true則進入循環體內部,為false則跳過,類似於continue
2) 上面的代碼等價
for (i <- 1 to 3){
if (i != 2) {
print(i + "")
}
}
- 案例
4.4.5 引入變量
- 基本案例
for(i <- 1 to 3; j = 4 - i) {
print(j + " ")
}
- 對基本案例說明
1) 沒有關鍵字,所以范圍后一定要加;來隔斷邏輯
2) 上面的代碼等價
4.4.6 嵌套循環
- 基本案例
for(i <- 1 to 3; j <- 1 to 3) {
println(" i =" + i + " j = " + j)
}
- 對基本案例說明
1) 沒有關鍵字,所以范圍后一定要加;來隔斷邏輯
2) 上面的代碼等價
3) 案例
4.4.7 循環返回值
- 基本案例
val res = for(i <- 1 to 10) yield i
println(res)
- 對基本案例說明
將遍歷過程中處理的結果返回到一個新Vector集合中,使用yield關鍵字
Vector(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)
4.4.8 使用花括號{}代替小括號()
- 基本案例
for(i <- 1 to 3; j = i * 2) {
println(" i= " + i + " j= " + j)
}
- 可以寫成
for{
i <- 1 to 3
j = i * 2} {
println(" i= " + i + " j= " + j)
}
- 對基本案例說明
1) {}和()對於for表達式來說都可以
2) for 推導式有一個不成文的約定:當for 推導式僅包含單一表達式時使用圓括號,當其包含多個表達式時使用大括號
3) 當使用{} 來換行寫表達式時,分號就不用寫了
4) 案例
4.4.9 注意事項和細節說明
1) scala 的for循環形式和java是較大差異,這點請同學們注意,但是基本的原理還是一樣的。
2) scala 的for循環是表達式,是有返回值的。java的for循環不是表達式,沒有返回值.
3) scala 的for循環的步長如何控制! [for(i <- Range(1,3,2)]
4.4.10 for循環練習題(學員先做)
1) 打印1~100之間所有是9的倍數的整數的個數及總和.
2) 完成下面的表達式輸出
4.5 while循環控制
4.5.1 基本語法
循環變量初始化
while (循環條件) {
循環體(語句)
循環變量迭代
}
4.5.2 while循環應用實例
畫出流程圖
輸出10句"你好,SGG"
4.5.3 注意事項和細節說明
1) 循環條件是返回一個布爾值的表達式
2) while循環是先判斷再執行語句
3) 與If語句不同,While語句本身沒有值,即整個While語句的結果是Unit類型的()
4) 因為while中沒有返回值,所以當要用該語句來計算並返回結果時,就不可避免的使用變量 ,而變量需要聲明在while循環的外部,那么就等同於循環的內部對外部的變量造成了影響, 也就違背了函數式編程的重要思想(純函數思想),所以不推薦使用,而是推薦使用for循環。
4.6 do..while循環控制
4.6.1 注意事項和細節說明
1) 循環條件是返回一個布爾值的表達式
2) do..while循環是先執行,再判斷
3) 和while 一樣,因為do…while中沒有返回值,所以當要用該語句來計算並返回結果時,就不可避免的使用變量 ,而變量需要聲明在do...while循環的外部,那么就等同於循環的內部對外部的變量造成了影響, 也就違背了函數式編程的重要思想(純函數思想),所以不推薦使用,而是推薦使用for循環
4.6.2 課堂練習題【學員先做】
1) 計算1—100的和 【課后】
2) 統計1——200之間能被5整除但不能被3整除的個數 【課堂】
4.7 多重循環控制
4.7.1 介紹:
1) 將一個循環放在另一個循環體內,就形成了嵌套循環。其中,for ,while ,do…while均可以作為外層循環和內層循環。【建議一般使用兩層,最多不要超過3層】
2) 實質上,嵌套循環就是把內層循環當成外層循環的循環體。當只有內層循環的循環條件為false時,才會完全跳出內層循環,才可結束外層的當次循環,開始下一次的循環。
3) 設外層循環次數為m次,內層為n次, 則內層循環體實際上需要執行m*n=mn次。
4.7.2 應用實例:
1) 統計三個班成績情況,每個班有5名同學,求出各個班的平均分和所有班級的平均分[學生的成績從鍵盤輸入]。
| package com.atguigu.chapter03.opertest.exer import scala.io._ object Mulforexam { def main(args: Array[String]): Unit = { /* 統計三個班成績情況,每個班有5名同學, 求出各個班的平均分和所有班級的平均分[學生的成績從鍵盤輸入]。 分析: 1. 班級個數 classNum, 學生個數 sutNum, 總分 totalFen, 每個班級的總分 sum 2. 使用雙層循環 */ val classNum = 3 val sutNum = 5 var totalFen = 0.0 var fen = 0.0 var sum = 0.0 for (i <- 1 to classNum) { sum = 0.0 //每個班級的總分 for (j <- 1 to sutNum) { printf("請輸入第%d個班的第%d學生的成績\n", i, j) fen = StdIn.readDouble() sum += fen } //sum 就是i班級總分 totalFen += sum printf("第%d個班級的平均分是%.2f \n", i, (sum / sutNum))
} printf("所有學生的平均分是%.2f \n", totalFen / (sutNum * classNum))
}
}
|
2) 統計三個班及格人數,每個班有5名同學。
在第一個案例的基礎上調整即可:
3) 打印出九九乘法表
4.8 while循環的中斷
4.8.1 基本說明
Scala內置控制結構特地去掉了break和continue,是為了更好的適應函數式編程,推薦使用函數式的風格解決break和contine的功能,而不是一個關鍵字。
4.8.2 應用案例
對代碼的總結
1) 在break() 函數 代碼是
def break(): Nothing = { throw breakException }
2) breakable(op: =>Unit) 函數, 是一個高階函數,可以接受另外一個函數或者一段並執行,並處理如果捕獲到breakException, 就 throw ex
def breakable(op: => Unit) {
try {
op
} catch {
case ex: BreakControl =>
if (ex ne breakException) throw ex
}
}
3) 從這里看到,我們的while循環就是傳入的代碼,可以被breakable執行。
4) scala中有這個風格,如果我們傳入的是參數【代碼】,是多行的,則我們的可以使用{} 替代()
5) 在scala如果要使用continue的邏輯,則使用if-else 來處理。
6) 在for循環中,一樣可以使用breakable( break() )
4.8.3 課堂練習題:
1) 100以內的數求和,求出 當和 第一次大於20的當前數【for】
2) 實現登錄驗證,有三次機會,如果用戶名為”張無忌” ,密碼”888”提示登錄成功,否則提示還有幾次機會,請使用for 循環完成
第 5 章 函數式編程基礎
5.1 函數式編程內容及授課順序說明
5.2 函數式編程授課順序即oop和函數式編程關系
5.2.1 幾個概念的說明
在學習Scala中將方法、函數、函數式編程和面向對象編程明確一下:
1) 在scala中,方法和函數幾乎可以等同(比如他們的定義、使用、運行機制都一樣的),只是函數的使用方式更加的靈活多樣。
2) 函數式編程是從編程方式(范式)的角度來談的,可以這樣理解函數式編程把函數當做一等公民,充分利用函數 支持的函數的多種使用方式。
比如:
在Scala當中,函數是一等公民,像變量一樣,既可以作為函數的參數使用,也可以將函數賦值給一個變量. ,函數的創建不用依賴於類或者對象,而在Java當中,函數的創建則要依賴於類、抽象類或者接口.
3) 面向對象編程是以對象為基礎的編程方式。
4) 在scala中函數式編程和面向對象編程融合在一起了。
5.3 函數式編程介紹
- "函數式編程"是一種"編程范式"(programming paradigm)。
- 它屬於"結構化編程"的一種,主要思想是把運算過程盡量寫成一系列嵌套的函數調用。
- 函數式編程中,將函數也當做數據類型,因此可以接受函數當作輸入(參數)和輸出(返回值)。
- 函數式編程中,最重要的就是函數。
5.4 為什么需要函數
請大家完成這樣一個需求:
輸入兩個數,再輸入一個運算符(+,-,*,/),得到結果.。
先使用傳統的方式來解決,看看有什么問題沒有?
1) 代碼冗余
2) 不利於代碼的維護
5.5 函數介紹
為完成某一功能的程序指令(語句)的集合,稱為函數
5.5.1 基本語法
5.5.2 函數的快速入門
5.6 函數-調用機制
5.6.1 函數-調用過程
- 為了讓大家更好的理解函數調用過程, 看1個案例,並畫出示意圖,這個很重要
- 計算兩個數的和,並返回 getSum
5.7 函數-遞歸調用
5.7.1 基本介紹
一個函數在函數體內又調用了本身,我們稱為遞歸調用
5.7.2 遞歸調用快速入門
5.7.3 函數遞歸需要遵守的重要原則:
1) 執行一個函數時,就創建一個新的受保護的獨立空間(新函數棧)
2) 函數的局部變量是獨立的,不會相互影響
3) 遞歸必須向退出遞歸的條件逼近,否則就是無限遞歸,死龜了:)
4) 當一個函數執行完畢,或者遇到return,就會返回,遵守誰調用,就將結果返回給誰,同時當函數執行完畢或者返回時,該函數棧本身也會被系統銷毀
5.7.4 遞歸課堂練習題
- 題1:斐波那契數 [學員練習]
請使用遞歸的方式,求出斐波那契數1,1,2,3,5,8,13...
給你一個整數n,求出它的斐波那契數是多少?
- 題2:求函數值 [演示]
已知 f(1)=3; f(n) = 2*f(n-1)+1;
請使用遞歸的思想編程,求出 f(n)的值?
- 題3:猴子吃桃子問題
有一堆桃子,猴子第一天吃了其中的一半,並再多吃了一個!以后每天猴子都吃其中的一半,然后再多吃一個。當到第十天時,想再吃時(還沒吃),發現只有1個桃子了。問題:最初共多少個桃子?
5.8 函數注意事項和細節討論
1) 函數的形參列表可以是多個,函數沒有形參,調用時 可以不帶()
2) 形參列表和返回值列表的數據類型可以是值類型和引用類型。【案例演示】
3) Scala中的函數可以根據函數體最后一行代碼自行推斷函數返回值類型。那么在這種情況下,return關鍵字可以省略 [前面我們講快速入門說過]
4) 因為Scala可以自行推斷,所以在省略return關鍵字的場合,返回值類型也可以省略
5) 如果函數明確使用return關鍵字,那么函數返回就不能使用自行推斷了,這時要明確寫成 : 返回類型 = ,當然如果你什么都不寫,即使有return 返回值為()
6) 如果函數明確聲明無返回值(聲明Unit),那么函數體中即使使用return關鍵字也不會有返回值
7) 如果明確函數無返回值或不確定返回值類型,那么返回值類型可以省略(或聲明為Any)
8) Scala語法中任何的語法結構都可以嵌套其他語法結構,即:函數中可以再聲明/定義函數,類中可以再聲明類 ,方法中可以再聲明/定義方法
9) Scala函數的形參,在聲明參數時,直接賦初始值(默認值),這時調用函數時,如果沒有指定實參,則會使用默認值。如果指定了實參,則實參會覆蓋默認值
10) 如果函數存在多個參數,每一個參數都可以設定默認值,那么這個時候,傳遞的參數到底是覆蓋默認值,還是賦值給沒有默認值的參數,就不確定了(默認按照聲明順序[從左到右])。在這種情況下,可以采用帶名參數 [案例演示+練習]
11) scala 函數的形參默認是val的,因此不能在函數中進行修改.
12) 遞歸函數未執行之前是無法推斷出來結果類型,在使用時必須有明確的返回值類型
13) Scala函數支持可變參數
//支持0到多個參數
def sum(args :Int*) : Int = {
}
//支持1到多個參數
def sum(n1: Int, args: Int*) : Int = {
}
說明:
1) args 是集合, 通過 for循環 可以訪問到各個值。
2) 案例演示: 編寫一個函數sum ,可以求出 1到多個int的和
5.9 函數練習題
5.10 過程
5.10.1 基本介紹
將函數的返回類型為Unit的函數稱之為過程(procedure),如果明確函數沒有返回值,那么等號可以省略
5.10.2 案例說明:
5.11 惰性函數
5.11.1 看一個應用場景
惰性計算(盡可能延遲表達式求值)是許多函數式編程語言的特性。惰性集合在需要時提供其元素,無需預先計算它們,這帶來了一些好處。首先,您可以將耗時的計算推遲到絕對需要的時候。其次,您可以創造無限個集合,只要它們繼續收到請求,就會繼續提供元素。函數的惰性使用讓您能夠得到更高效的代碼。Java 並沒有為惰性提供原生支持,Scala提供了。
5.11.2 畫圖說明[大數據推薦系統]
5.11.3 Java實現懶加載的代碼
5.11.4 介紹
當函數返回值被聲明為lazy時,函數的執行將被推遲,直到我們首次對此取值,該函數才會執行。這種函數我們稱之為惰性函數,在Java的某些框架代碼中稱之為懶加載(延遲加載)。
5.11.5 案例演示
看老師演示+反編譯
5.11.6 注意事項和細節
1) lazy 不能修飾 var 類型的變量
2) 不但是 在調用函數時,加了 lazy ,會導致函數的執行被推遲,我們在聲明一個變量時,如果給聲明了 lazy ,那么變量值得分配也會推遲。 比如 lazy val i = 10
5.12 異常
5.12.1 介紹
1) Scala提供try和catch塊來處理異常。try塊用於包含可能出錯的代碼。catch塊用於處理try塊中發生的異常。可以根據需要在程序中有任意數量的try...catch塊。
2) 語法處理上和Java類似,但是又不盡相同
5.12.2 Java異常處理回顧
5.12.3 Java異常處理的注意點.
1) java語言按照try—catch—finally的方式來處理異常
2) 不管有沒有異常捕獲,都會執行finally, 因此通常可以在finally代碼塊中釋放資源
3) 可以有多個catch,分別捕獲對應的異常,這時需要把范圍小的異常類寫在前面,把范圍大的異常類寫在后面,否則編譯錯誤。會提示 "Exception 'java.lang.xxxxxx' has already been caught"【案例演示】
5.12.4 Scala異常處理舉例
5.12.5 scala異常的小結和注意事項
1) 我們將可疑代碼封裝在try塊中。 在try塊之后使用了一個catch處理程序來捕獲異常。如果發生任何異常,catch處理程序將處理它,程序將不會異常終止。
2) Scala的異常的工作機制和Java一樣,但是Scala沒有“checked(編譯期)”異常,即Scala沒有編譯異常這個概念,異常都是在運行的時候捕獲處理。
3) 用throw關鍵字,拋出一個異常對象。所有異常都是Throwable的子類型。throw表達式是有類型的,就是Nothing,因為Nothing是所有類型的子類型,所以throw表達式可以用在需要類型的地方
4) 在Scala里,借用了模式匹配的思想來做異常的匹配,因此,在catch的代碼里,是一系列case子句來匹配異常。【前面案例可以看出這個特點, 模式匹配我們后面詳解】,當匹配上后 => 有多條語句可以換行寫,類似 java 的 switch case x: 代碼塊..
5) 異常捕捉的機制與其他語言中一樣,如果有異常發生,catch子句是按次序捕捉的。因此,在catch子句中,越具體的異常越要靠前,越普遍的異常越靠后,如果把越普遍的異常寫在前,把具體的異常寫在后,在scala中也不會報錯,但這樣是非常不好的編程風格。
6) finally子句用於執行不管是正常處理還是有異常發生時都需要執行的步驟,一般用於對象的清理工作,這點和Java一樣。
7) Scala提供了throws關鍵字來聲明異常。可以使用方法定義聲明異常。 它向調用者函數提供了此方法可能引發此異常的信息。 它有助於調用函數處理並將該代碼包含在try-catch塊中,以避免程序異常終止。在scala中,可以使用throws注釋來聲明異常
5.13 函數的課堂練習題
編寫一個函數,從終端輸入一個整數(1—9),打印出對應的乘法表
第 6 章 面向對象編程基礎部分
6.1 類與對象
6.1.1 看一個養貓貓問題
張老太養了兩只貓貓:一只名字叫小白,今年3歲,白色。還有一只叫小花,今年100歲,花色。請編寫一個程序,當用戶輸入小貓的名字時,就顯示該貓的名字,年齡,顏色。如果用戶輸入的小貓名錯誤,則顯示 張老太沒有這只貓貓。
貓有三種數據(屬性) è 使用變量不好用
需要的數據類型
1.存儲不同數據類型數據。
2.可以對這些數據進行操作
3.類
6.1.2 快速入門-面向對象的方式解決養貓問題
6.1.3 如何定義類
- 基本語法
[修飾符] class 類名 {
類體
}
- 定義類的注意事項
1) scala語法中,類並不聲明為public,所有這些類都具有公有可見性(即默認就是public),[修飾符在后面再詳解].
2) 一個Scala源文件可以包含多個類.
6.1.4 屬性
- 基本介紹
- 案例演示:
屬性是類的一個組成部分,一般是值數據類型,也可是引用類型。比如我們前面定義貓類 的 age 就是屬性
- 屬性的細節和注意事項
案例代碼:
| package com.atguigu.chapter06.Demo
object Test01 { def main(args: Array[String]): Unit = { //創建一個Cat對象 var cat : Cat = new Cat() var cat2 = new Cat() // 這里的.name 並不是直接訪問屬性,而是通過一個public方法訪問.(講解屬性時,再剖析) cat2.name = "tom貓" //public void name_$eq(String x$1) { this.name = x$1; } cat2.age = 3 cat2.color = "黑色" println("cat2.name=" + cat2.name)// public String name() var tiger = new Tiger() cat2.boss = tiger println("cat2.boss.name=" + cat2.boss.name) //cat2.boss().name() } } class Cat { //如果這里是class 類 ,在底層只有一個類Cat //三個屬性 var name: String = "" // 在底層,會生成類似java的 setter [xx_$eq] getter [xxxx()] var age: Int = 0 var color: String = "" var boss: Tiger = _ // _ 表示給屬性一個默認值
}
class Tiger{ // 對應一個 Tiger.class var name = "華南虎~" }
|
6.1.5 屬性的高級部分
6.1.6 如何創建對象
- 基本語法
val | var 對象名 [:類型] = new 類型()
- 說明
1) 如果我們不希望改變對象的引用(即:內存地址), 應該聲明為val 性質的,否則聲明為var, scala設計者推薦使用val ,因為一般來說,在程序中,我們只是改變對象屬性的值,而不是改變對象的引用
2) scala在聲明對象變量時,可以根據創建對象的類型自動推斷,所以類型聲明可以省略,但當類型和后面new 對象類型有繼承關系即多態時,就必須寫了
代碼如下:
| package com.atguigu.chapter06.ClassAndObject
object Demo01 { def main(args: Array[String]): Unit = { //在開發中,我們會從數據庫中獲取對個對象(封裝) //我們通常不會去改變e對象本身 e = null //通常是對e對象進行查看,或者修改 val e = new Employee() e.sal += 1.0 //e = null 在開發中不多,因此我們創建對象時,一般來說是val println("e.sal=" + e.sal)
/* scala在聲明對象變量時,可以根據創建對象的類型自動推斷,所以類型聲明可以省略, 但當類型和后面new 對象類型有繼承關系即多態時,就必須寫了 */ val e2: A = new Employee() println(e2) // e2 } }
class A { } //Employee類 class Employee extends A{ var id: Int = 10 var name: String = "tom" var sal: Double = 10000.0
override def toString: String = { return "id=" + id + "name=" + name + "sal=" + sal } }
|
6.1.7 類和對象的內存分配機制
- 看一個思考題
我們定義一個Person類(包括 名字,年齡) ,創建一個對象(ObjectDemo.scala) - 寫了一段代碼,並畫出了示意圖:
- 代碼對應的內存布局示意圖
6.1.8 方法
- 基本說明
Scala中的方法其實就是函數,只不過一般將對象中的函數稱之為方法。聲明規則請參考函數式編程中的函數聲明。
- 基本語法,和函數一樣
def 方法名(參數列表) [:返回值類型] = {
方法體
}
- 方法案例演示
給Cat類添加cal方法,可以計算兩個數的和
6.1.9 課堂練習題
1) 編寫類(MethodExec),編程一個方法,方法不需要參數,在方法中打印一個
10*8 的矩形,在main方法中調用該方法。
2) 修改上一個程序,編寫一個方法中,方法不需要參數,計算該矩形的面積,並將其作為方法返回值。在main方法中調用該方法,接收返回的面積值並打印(結果保留小數點2位)。
第一題和第二題的代碼:
課后的練習
6.2 類與對象應用實例
6.2.1 小狗案例
1) 編寫一個Dog類,包含name(String)、age(int)、weight(double)屬性
2) 類中聲明一個say方法,返回String類型,方法返回信息中包含所有屬性值。
3) 在另一個TestDog Object中的main方法中,創建Dog對象,並訪問say方法和所有屬性,將調用結果打印輸出
4) 代碼:
6.3 構造器
6.3.1 看一個需求
我們來看一個需求:前面我們在創建Person的對象時,是先把一個對象創建好后,再給他的年齡和姓名屬性賦值,如果現在我要求,在創建人類的對象時,就直接指定這個對象的年齡和姓名,該怎么做? 這時就可以使用構造方法/構造器。
6.3.2 回顧-Java構造器基本語法
[修飾符] 方法名(參數列表){
構造方法體
}
6.3.3 回顧-Java構造器的特點
1) 在Java中一個類可以定義多個不同的構造方法,構造方法重載
2) 如果程序員沒有定義構造方法,系統會自動給類生成一個默認無參構造方法(也叫默認構造器),比如 Person (){}
3) 一旦定義了自己的構造方法,默認的構造方法就覆蓋了,就不能再使用默認的無參構造方法,除非顯示的定義一下,即: Person(){};
4) 案例
6.3.4 Scala構造器的介紹
和Java一樣,Scala構造對象也需要調用構造方法,並且可以有任意多個構造方法。
Scala類的構造器包括: 主構造器 和 輔助構造器
6.3.5 Scala構造器的基本語法
class 類名(形參列表) { // 主構造器
// 類體
def this(形參列表) { // 輔助構造器
}
def this(形參列表) { //輔助構造器可以有多個...
}
}
。
6.3.6 快速入門
創建Person對象的同時初始化對象的age屬性值和name屬性值
6.3.7 Scala構造器注意事項和細節
1) Scala構造器作用是完成對新對象的初始化,構造器沒有返回值。
2) 主構造器的聲明直接放置於類名之后 [反編譯]
3) 主構造器會執行類定義中的所有語句,這里可以體會到Scala的函數式編程和面向對象編程融合在一起,即:構造器也是方法(函數),傳遞參數和使用方法和前面的函數部分內容沒有區別【案例演示+反編譯]
對應的反編譯的文件
4) 如果主構造器無參數,小括號可省略,構建對象時調用的構造方法的小括號也可以省略
5) 輔助構造器名稱為this(這個和Java是不一樣的),多個輔助構造器通過不同參數列表進行區分, 在底層就是java的構造器重載.
在對應的反編譯的.class 對應class Person2
6) 如果想讓主構造器變成私有的,可以在()之前加上private,這樣用戶只能通過輔助構造器來構造對象了.
說明:因為Person3的主構造器是私有,因此就需要使用輔助構造器來創建對象。
7) 輔助構造器的聲明不能和主構造器的聲明一致,會發生錯誤
6.4 屬性高級
前面我們講過屬性了,這里我們再對屬性的內容做一個加強.
6.4.1 構造器參數
1) Scala類的主構造器函數的形參未用任何修飾符修飾,那么這個參數是局部變量。
2) 如果參數使用val關鍵字聲明,那么Scala會將參數作為類的私有的只讀屬性使用 【案例+反編譯】
3) 如果參數使用var關鍵字聲明,那么那么Scala會將參數作為類的成員屬性使用,並會提供屬性對應的xxx()[類似getter]/xxx_$eq()[類似setter]方法,即這時的成員屬性是私有的,但是可讀寫。【案例+反編譯】
4) 代碼
對應的反編譯的.class文件
6.4.2 Bean屬性
- 基本說明
JavaBeans規范定義了Java的屬性是像getXxx()和setXxx()的方法。許多Java工具(框架)都依賴這個命名習慣。為了Java的互操作性。將Scala字段加@BeanProperty時,這樣會自動生成規范的 setXxx/getXxx 方法。這時可以使用 對象.setXxx() 和 對象.getXxx() 來調用屬性。
- 注意
給某個屬性加入@BeanPropetry注解后,會生成getXXX和setXXX的方法
並且對原來底層自動生成類似xxx(),xxx_$eq()方法,沒有沖突,二者可以共存。
- 案例演示
反編譯的.class文件
6.5 對象創建的流程分析
6.5.1 看一個案例
class Person {
var age: Int = 90
var name: String = _
def this(n: String, a: Int) {
this()
this.name = n
this.age = a
}
}
var p : Person = new Person("小倩",20)
6.5.2 流程分析(面試題)
1) 加載類信息(屬性信息,方法信息)
2) 在堆中,給對象開辟空間
3) 調用主構造器對屬性進行初始化
4) 使用輔助構造器對屬性進行初始化
5) 把對象空間的地址,返回給p引用
第 7 章 面向對象編程(中級)
7.1 包
7.1.1 看一個應用場景
現在有兩個程序員共同開發一個項目,程序員xiaoming希望定義一個類取名 Dog ,程序員xiaoqiang也想定義一個類也叫 Dog。兩個程序員為此還吵了起來,怎么辦?
7.1.2 回顧-Java包的三大作用
1) 區分相同名字的類
2) 當類很多時,可以很好的管理類
3) 控制訪問范圍
7.1.3 回顧-Java打包命令
打包基本語法
package com.atguigu;
7.1.4 打包的本質分析
實際上就是創建不同的文件夾來保存類文件,畫出示意圖。
7.1.5 快速入門
使用打包技術來解決上面的問題,不同包下Dog類
7.1.6 回顧-Java如何引入包
語法: import 包;
比如 import java.awt.*;
我們引入一個包的主要目的是要使用該包下的類
比如 import java.util.Scanner; 就只是引入一個類Scanner。
7.1.7 回顧-Java包的特點
java中包名和源碼所在的系統文件目錄結構要一致,並
且編譯后的字節碼文件路徑也和包名保持一致。[如圖]
7.1.8 Scala包的基本介紹
和Java一樣,Scala中管理項目可以使用包,但Scala中的包的功能更加強大,使用也相對復雜些,下面我們學習Scala包的使用和注意事項。
7.1.9 Scala包快速入門
使用打包技術來解決上面的問題,不同包下Dog類
7.1.10 Scala包的特點概述
- 基本語法
package 包名
- Scala包的三大作用(和Java一樣)
1) 區分相同名字的類
2) 當類很多時,可以很好的管理類
3) 控制訪問范圍
- Scala中包名和源碼所在的系統文件目錄結構要可以不一致,但是編譯后的字節碼文件路徑和包名會保持一致(這個工作由編譯器完成)。[案例演示]
- 案例
7.1.11 Scala包的命名
- 命名規則:
只能包含數字、字母、下划線、小圓點.,但不能用數字開頭, 也不要使用關鍵字。
demo.class.exec1 //對嗎 錯誤,因為 class 關鍵字
demo.12a // 對嗎,錯誤
- 命名規范:
一般是小寫字母+小圓點一般是
com.公司名.項目名.業務模塊名
比如:com.atguigu.oa.model com.atguigu.oa.controller
com.sina.edu.user
com.sohu.bank.order
7.1.12 Scala會自動引入的常用包
7.1.13 Scala包注意事項和使用細節
1) scala進行package 打包時,可以有如下形式
2) 包也可以像嵌套類那樣嵌套使用(包中有包), 這個在前面的第三種打包方式已經講過了,在使用第三種方式時的好處是:程序員可以在同一個文件中,將類(class / object)、trait 創建在不同的包中,這樣就非常靈活了。[案例+反編譯]
3) 作用域原則:可以直接向上訪問。即: Scala中子包中直接訪問父包中的內容, 大括號體現作用域。(提示:Java中子包使用父包的類,需要import)。在子包和父包 類重名時,默認采用就近原則,如果希望指定使用某個類,則帶上包名即可
| package com.atguigu { class A01 { //這時,編譯器,就會在com.atguigu包下生成A01.class
} class Cat{ //這時,編譯器,就會在com.atguigu包下生成Cat.class
} package scala{ class A01 { ////這時,編譯器,就會在com.atguigu.sacla包下生成A01.class
} class Cat {////這時,編譯器,就會在com.atguigu.sacla包下生成Cat.class
} object Test01{ //這時,編譯器,就會在com.atguigu.sacla包下生成Test01.class Test01$.class def main(args: Array[String]): Unit = { println("ok~~")
//創建一個Cat對象 //說明 //1. Scala中子包中直接訪問父包中的內容 //2. 在子包和父包 類重名時,默認采用就近原則 //3. 如果希望指定使用某個類,則帶上包全名即可 val cat = new Cat() println("cat=" + cat) //使用的是com.atguigu.scala.Cat[使用就近原則] val cat2 = new com.atguigu.Cat()//使用的是com.atguigu.Cat[指定原則] println("cat2" + cat2)
} } } } |
4) 父包要訪問子包的內容時,需要import對應的類等
5) 可以在同一個.scala文件中,聲明多個並列的package(建議嵌套的pakage不要超過3層)
6) 包名可以相對也可以絕對,比如,訪問BeanProperty的絕對路徑是:
_root_. scala.beans.BeanProperty ,在一般情況下:我們使用相對路徑來引入包,只有當包名沖突時,使用絕對路徑來處理
7.1.14 包對象
基本介紹:包可以包含類、對象和特質trait,但不能包含函數或變量的定義。這是Java虛擬機的局限。為了彌補這一點不足,scala提供了包對象的概念來解決這個問題
7.1.15 包對象的應用案例
7.1.16 包對象的注意事項
7.2 包的可見性
7.2.1 Scala中包的可見性介紹:
在Java中,訪問權限分為: public,private,protected和默認。在Scala中,你可以通過類似的修飾符達到同樣的效果。但是使用上有區別。
7.2.2 Scala中包的可見性和訪問修飾符的使用
1) 當屬性訪問權限為默認時,從底層看屬性是private的,但是因為提供了xxx_$eq()[類似setter]/xxx()[類似getter] 方法,因此從使用效果看是任何地方都可以訪問)
2) 當方法訪問權限為默認時,默認為public訪問權限
3) private為私有權限,只在類的內部和伴生對象中可用 【案例演示】
4) protected為受保護權限,scala中受保護權限比Java中更嚴格,只能子類訪問,同包無法訪問
5) 在scala中沒有public關鍵字,即不能用public顯式的修飾屬性和方法。【案演】
6) 包訪問權限(表示屬性有了限制。同時增加了包的訪問權限),這點和Java不一樣,體現出Scala包使用的靈活性。
7.3 包的引入
7.3.1 Scala引入包的細節和注意事項
1) 在Scala中,import語句可以出現在任何地方,並不僅限於文件頂部,import語句的作用一直延伸到包含該語句的塊末尾。這種語法的好處是:在需要時在引入包,縮小import 包的作用范圍,提高效率。
2) Java中如果想要導入包中所有的類,可以通過通配符*,Scala中采用下 _ [案例演示]
3) 如果不想要某個包中全部的類,而是其中的幾個類,可以采用選取器(大括號)
4) 如果引入的多個包中含有相同的類,那么可以將不需要的類進行重命名進行區分,這個就是重命名
5) 如果某個沖突的類根本就不會用到,那么這個類可以直接隱藏掉
import java.util.{ HashMap=>_, _} //
7.4 面向對象編程方法-抽象
7.4.1 如何理解抽象
我們在前面去定義一個類時候,實際上就是把一類事物的共有的屬性和行為提取出來,形成一個物理模型(模板)。這種研究問題的方法稱為抽象。[見后面ppt]
- 代碼如下
| package com.atguigu.chapter06.abstractt
object AccountDemo01 { def main(args: Array[String]): Unit = { //測試一下我們抽象的Account是否ok val myAccount = new Account("gh111111",200.0,123456) //查詢 myAccount.query(123456) //取款 myAccount.withdraw(123456, 201) myAccount.query(123456)
} }
/* 不管是哪種賬號, 是誰的賬號 屬性: 賬號,余額,密碼 方法 存款(depostie) 取款(withdraw) 查詢(query)
*/ class Account(InAccountNO:String,money:Double,inPwd:Int) { val accountNo:String = InAccountNO var banlance:Double = money var pwd:Int = inPwd
//查詢 def query(pwd:Int): Unit = { if (pwd != this.pwd) { println("密碼錯誤") return } println(this.accountNo + " 余額是:" + this.banlance) }
//withdraw取款 def withdraw(pwd:Int, money:Int): Unit = { //判斷 if (pwd != this.pwd) { println("密碼錯誤") return } if (money > this.banlance) { println("余額不足") return } this.banlance -= money println("取款成功" + this.accountNo + " 取出來 " + money + " 余額有:" + this.banlance) }
}
|
7.5 面向對象編程三大特征
7.5.1 基本介紹
面向對象編程有三大特征:封裝、繼承和多態。下面我們一一為同學們進行詳細的講解。
7.5.2 封裝介紹
封裝(encapsulation)就是把抽象出的數據和對數據的操作封裝在一起,數據被保護在內部,程序的其它部分只有通過被授權的操作(成員方法),才能對數據進行操作
7.5.3 封裝的理解和好處
1) 隱藏實現細節
2) 提可以對數據進行驗證,保證安全合理
7.5.4 如何體現封裝
1) 對類中的屬性進行封裝
2) 通過成員方法,包實現封裝
7.5.5 封裝的實現步驟
1) 將屬性進行私有化
2) 提供一個公共的set方法,用於對屬性判斷並賦值
def setXxx(參數名 : 類型) : Unit = {
//加入數據驗證的業務邏輯
屬性 = 參數名
}
3) 提供一個公共的get方法,用於獲取屬性的值
def getXxx() [: 返回類型] = {
return 屬性
}
7.5.6 快速入門案例
- 看一個案例
那么在Scala中如何實現這種類似的控制呢?
請大家看一個小程序(TestEncap.scala),不能隨便查看人的年齡,工資等隱私,並對輸入的年齡進行合理的驗證[要求1-120之間]。
7.5.7 封裝的課堂練習題
7.5.8 Scala封裝的注意事項和細節
7.6 面向對象編程-繼承
7.6.1 Java繼承的簡單回顧
class 子類名 extends 父類名 { 類體 }
子類繼承父類的屬性和方法
7.6.2 繼承基本介紹和示意圖
繼承可以解決代碼復用,讓我們的編程更加靠近人類思維.當多個類存在相同的屬性(變量)和方法時,可以從這些類中抽象出父類(比如Student),在父類中定義這些相同的屬性和方法,所有的子類不需要重新定義這些屬性和方法,只需要通過extends語句來聲明繼承父類即可。
和Java一樣,Scala也支持類的單繼承。
畫出繼承的示意圖
7.6.3 Scala繼承的基本語法
class 子類名 extends 父類名 { 類體 }
7.6.4 Scala繼承快速入門
編寫一個Student 繼承 Person的案例,體驗一下Scala繼承的特點
| package com.atguigu.chapter06.extend
object Extends01 { def main(args: Array[String]): Unit = { //測試 val stu1 = new Student stu1.name = "tom" stu1.age = 20 stu1.showInfo() stu1.studying()
} }
class Person { //屬性name 和 age,默認 var name : String = _ var age : Int = _ //方法 def showInfo(): Unit = { println("學生信息如下:") println("名字:" + this.name) } }
//extends關鍵字,繼承了Person class Student extends Person { //將Student特有方法寫在Student類 def studying(): Unit = { println(this.name + "學習 scala中....") } } |
7.6.5 Scala繼承給編程帶來的便利
1) 代碼的復用性提高了
2) 代碼的擴展性和維護性提高了【面試官問?】
7.6.6 子類繼承了什么,怎么繼承了?
子類繼承了所有的屬性,只是私有的屬性不能直接訪問,需要通過公共的方法去訪問【debug代碼驗證可以看到】
代碼:
對應的反編譯的.class 文件
7.6.7 重寫方法
說明: scala明確規定,重寫一個非抽象方法需要用override修飾符,調用超類的方法使用super關鍵字 【案例演示+反編譯】
- 案例演示
說明
1) 當上面的代碼被反編譯后,我們發現override 關鍵字也去掉,即和java是一樣.
2) super在反編譯后,和java也一樣。
7.6.8 Scala中類型檢查和轉換
- 基本介紹
1) 要測試某個對象是否屬於某個給定的類,可以用isInstanceOf方法。用asInstanceOf方法將引用轉換為子類的引用。classOf獲取對象的類名。
2) classOf[String]就如同Java的 String.class 。
3) obj.isInstanceOf[T]就如同Java的obj instanceof T 判斷obj是不是T類型。
4) obj.asInstanceOf[T]就如同Java的(T)obj 將obj強轉成T類型。
- 案例演示
- 最佳實踐
- 最佳實踐的代碼
| package com.atguigu.chapter06.extend
object TypeConvertExer { def main(args: Array[String]): Unit = { //創建對象 val stu = new Stu val worker = new Worker test(stu) //stuid=.. test(worker)//workerId=
} def test(p: Person04): Unit = { //如果傳入是Student實例,則調用sayOk //如果傳入是Worker實例,則調用sayHello //進行p.asInstanceOf[T]轉換時,要求,p 的引用本身就是指向T類型的引用 if (p.isInstanceOf[Stu]) { //轉換 p.asInstanceOf[Stu].sayOk() } else if (p.isInstanceOf[Worker]) { p.asInstanceOf[Worker].sayHello() } else { println("轉換錯誤") } } }
class Person04 { var name : String = "tom" def printName() { println("Person printName() " + name) } }
class Stu extends Person04 { var stuId:Int = 100 def sayOk(): Unit = { println("stuid=" + this.stuId) } }
class Worker extends Person04 { var workerId:Int = 200 def sayHello(): Unit = { println("workerId=" + this.workerId) } }
|
7.6.9 Scala中超類的構造
- 回顧-Java中超類的構造
說明:
從代碼可以看出:在Java中,創建子類
對象時,子類的構造器總是去調用一個
父類的構造器(顯式或者隱式調用)
代碼:
| package com.atguigu.chapter06.extend;
public class SuperConstruct { public static void main(String[] args) { //測試 //下面代碼執行的效果是: // A() // B() // A(String name)jack // B(String name)jack B02 b = new B02(); B02 b2 = new B02("jack"); } } //父類 A02 class A02 { //構造器無參. public A02() { System.out.println("A()"); } //構造器,有參 public A02(String name) { System.out.println("A(String name)" + name); } } //子類B02,繼承A02 class B02 extends A02{
public B02() { //這里會隱式調用super(); 就是無參的父類構造器A02() super(); System.out.println("B()"); } public B02(String name) { super(name); System.out.println("B(String name)" + name); } }
|
- Scala超類的構造說明
1) 類有一個主構器和任意數量的輔助構造器,而每個輔助構造器都必須先調用主構造器(也可以是間接調用.),這點在前面我們說過了。
上面的代碼說明
(1) 先去執行extends Person05() 的Person05()
(2) 在執行class Emp05 類中的 主構造器 Emp05()
(3) 再執行 Emp05的輔助構造器 this(name:String)
2) 只有主構造器可以調用父類的構造器。輔助構造器不能直接調用父類的構造器。在Scala的構造器中,你不能調用super(params).
- 應用實例
要求:編寫程序,創建一個學生對象。體會Scala中超類的構造流程
| package com.atguigu.chapter06.extend
object ScalaSuperConstructExer1 { def main(args: Array[String]): Unit = { //創建一個學生對象 //分析下面的代碼是如何運行的 //1.Person06( pname : String ) name= jack //2.Student06( studentname : String ) sno = 20
val stu = new Student06("jack") stu.printName() //Student printName() name jack sno 20 //Person printName() jack } }
class Person06( pname : String ) { var name : String = pname def printName() { println("Person printName() " + name) } def this() { this("xx") } } class Emp06(empname : String) extends Person06(empname) { var empno : Int = 10 //這里需要顯示的使用override override def printName() { println("Emp printName() " + name) super.printName() } }
class Student06( studentname : String ) extends Person06() { //xxx var sno : Int = 20 override def printName() { println("Student printName() " + name + "sno=" + sno) super.printName() } } |
- 對上面的案例小結
class Student06( studentname : String ) extends Person06()
1) 當一個子類繼承了一個父類后,是上面語法.
2) extends Person06() 表示 當我們構建一個子類對象,會調用父類的構造器 Person06(),要求我們的父類,必須有一個對應的構造器,該構造器可以是主構造器,也可以是輔助構造器.
3) 父類的所有輔助構造器,仍然需要調用父類的主構造器
7.6.10 覆寫字段
- 基本介紹
在Scala中,子類改寫父類的字段,我們成為覆寫/重寫字段。覆寫字段需使用 override修飾。
- 回顧:在Java中只有方法的重寫,沒有屬性/字段的重寫,准確的講,是隱藏字段代替了重寫。參考:Java中為什么字段不能被重寫.doc [字段隱藏案例]。
- 回顧了java的動態綁定機制:
| package com.atguigu.chapter06.extend;
public class DaynDemo { public static void main(String[] args) { AA a = new BB(); //提出java的動態綁定機制 //1. 當調用對象方法的時候,該方法會和該對象的內存地址綁定 //2. 當調用對象屬性時,沒有動態綁定機制,哪里聲明,那里使用 //說明: //1. 如果沒有注銷BB的sum, a.sum() = 40 //2. 如果注銷BB的sum, a.sum() = 30 //3. 如果沒有注銷BB的sum1, a.sum1() = 30 //4. 如果注銷BB的sum1, a.sum1() = 20 System.out.println("a.sum=" + a.sum()); //40 =》 30 System.out.println("a.sum1=" + a.sum1());//30 =》 20 } }
class AA { public int i = 10; public int sum() { return getI() + 10; } public int sum1() { return i + 10; } public int getI() { return i; } }
class BB extends AA { public int i = 20; // public int sum() { // return i + 20; // } public int getI() { return i; } // public int sum1() { // return i + 10; // } } |
- 對代碼的說明:
//提出java的動態綁定機制
//1. 當調用對象方法的時候,該方法會和該對象的內存地址綁定
//2. 當調用對象屬性時,沒有動態綁定機制,哪里聲明,那里使用
- Scala覆寫字段快速入門
我們看一個關於覆寫字段的案例。【案例演示+分析+反編譯】
- 覆寫字段的注意事項和細節
1) def只能重寫另一個def(即:方法只能重寫另一個方法)
2) val只能重寫另一個val 或 重寫不帶參數的def [案例+分析]
為什么編譯器不支持 val 去重寫 var 屬性,原因是會造成數據設置和或者不一致.
b.age = 10 // b.age 調用的是父類的 public age_$eq()
println(b.age) //b.age 調用的子類的public age()
子類的val 屬性,可以去重寫 父類不帶參數的def。
3) var只能重寫另一個抽象的var屬性
案例演示:
- 抽象屬性:聲明未初始化的變量就是抽象的屬性,抽象屬性在抽象類
- var重寫抽象的var屬性小結
1) 一個屬性沒有初始化,那么這個屬性就是抽象屬
2) 抽象屬性在編譯成字節碼文件時,屬性並不會聲明,但是會自動生成抽象方法,所以類必須聲明為抽象類
3) 如果是覆寫一個父類的抽象屬性,那么override 關鍵字可省略 [原因:父類的抽象屬性,生成的是抽象方法,因此就不涉及到方法重寫的概念,因此override可省略]
7.6.11 抽象類
- 基本介紹
在Scala中,通過abstract關鍵字標記不能被實例化的類。方法不用標記abstract,只要省掉方法體即可。抽象類可以擁有抽象字段,抽象字段就是沒有初始值的字段
- 快速入門案例
我們看看如何把Animal做成抽象類, 包含一個抽象的方法cry()
- 抽象類基本語法
7.6.12 Scala抽象類使用的注意事項和細節討論
1) 抽象類不能被實例
2) 抽象類不一定要包含abstract方法。也就是說,抽象類可以沒有abstract方法
3) 一旦類包含了抽象方法或者抽象屬性,則這個類必須聲明為abstract
4) 抽象方法不能有主體,不允許使用abstract修飾。
5) 如果一個類繼承了抽象類,則它必須實現抽象類的所有抽象方法和抽象屬性,除非它自己也聲明為abstract類。【案例演示+反編譯】
6) 抽象方法和抽象屬性不能使用private、final 來修飾,因為這些關鍵字都是和重寫/實現相違背的。
7) 子類重寫抽象方法不需要override,寫上也不會錯.
7.6.13 匿名子類
- 基本介紹
Java一樣,可以通過包含帶有定義或重寫的代碼塊的方式創建一個匿名的子類
- 回顧-java匿名子類使用
- scala的匿名子類的創建
7.6.14 課后練習題
第 8 章
面向對象編程-高級
8.1 靜態屬性和方法
8.1.1 靜態屬性-提出問題
提出問題的主要目的就是讓大家思考解決之道,從而引出我要講的知識點.
說:有一群小孩在玩堆雪人,不時有新的小孩加入,請問如何知道現在共有多少人在玩?請使用面向對象的思想,編寫程序解決
基本介紹
8.1.2 回顧下Java的靜態概念
public static 返回值類型 方法名(參數列表) {方法體}
說明: Java中靜態方法並不是通過對象調用的,而是通過類對象調用的,所以靜態操作並不是面向對象的。
8.1.3 Scala中靜態的概念-伴生對象
說明:Scala語言是完全面向對象(萬物皆對象)的語言,所以並沒有靜態的操作(即在Scala中沒有靜態的概念)。但是為了能夠和Java語言交互(因為Java中有靜態概念),就產生了一種特殊的對象來模擬類對象,我們稱之為類的伴生對象。這個類的所有靜態內容都可以放置在它的伴生對象中聲明和調用
8.1.4 伴生對象的快速入門
- 基本的案例代碼
代碼的說明
1) println(ScalaPerson.sex) //這里底層是 ScalaPerson$.Module$.sex()
2) 特點
class ScalaPerson { //生成 ScalaPerson類,會有對 object ScalaPerson 的屬性或方法操作
var name : String = _
}
8.1.5 伴生對象的小結
8.1.6 伴生對象解決小孩游戲問題
如果,設計一個var total Int表示總人數,我們在創建一個小孩時,就把total加1,並且 total是所有對象共享的就ok了!,我們使用伴生對象來解決
畫一個小圖給大家理解。
| package com.atguigu.chapter08.objectt
object ChildGame { def main(args: Array[String]): Unit = { val child1 = new Child("白骨精") val child2 = new Child("蜘蛛精") val child3 = new Child("犀牛精") Child.joinGame(child1) //Child$.Module$ 這個是一個靜態變量 Child.joinGame(child2) Child.joinGame(child3) //顯示人數 Child.showNum() } }
class Child(cName: String) { var name: String = cName }
object Child { //靜態的屬性 var totalNum: Int = 0 //加入游戲的方法 def joinGame(c: Child): Unit = { println(c.name + " 加入到游戲中...") totalNum += 1 } //顯示當前有多少小孩的方法 def showNum(): Unit = { printf("當前有%d個小孩完游戲\n", totalNum) } }
|
8.1.7 apply方法的補充
8.2 單例對象
8.2.1 什么是單例對象
單例對象是指:使用單例設計模式保證在整個的軟件系統中,某個類只能存在一個對象實例
8.2.2 回顧-Java單例對象
在Java中,創建單例對象分為餓漢式和懶漢式,我們看看是如何實現的
步驟如下:
1) 構造器私有化
2) 類的內部創建對象
3) 向外暴露一個靜態的公共方法
4) 代碼實現
| package com.atguigu.chapter08.single;
public class JavaSingle { public static void main(String[] args) { //餓漢式 Single s1 = Single.getInstance();//得到一個Single對象 Single s2 = Single.getInstance();//得到一個Single對象 System.out.println(s1 == s2); // true
//懶漢式
//對上面兩種方式的優化 System.out.println("----------------------------------"); Singleton ss1 = Singleton.getInstance(); Singleton ss2 = Singleton.getInstance(); System.out.println("ss1.hashcode" + ss1.hashCode()); System.out.println("ss2.hashcode" + ss2.hashCode());
} }
//缺點是 創建了,但是用,有資源浪費 //優點:線程安全。 class Single{// 餓漢式 private Single(){} //私有化構造器 private static Single s = new Single(); public static Single getInstance(){ return s; } }
//優點:在使用的時候,才去創建,因此沒有資源的浪費。 //缺點:有線程安全問題 class Single2{// 懶漢式 private Single2(){} //私有化構造器 private static Single2 s = null; public static Single2 getInstance(){ if (s == null) { s = new Single2(); } return s; } }
//對上面的兩種方法進行優化,使用靜態內部類來優化 class Singleton { private Singleton() {} //私有化 // //靜態內部類 1.在使用到時,才會加載 2.在加載時,不會中斷(線程安全) private static class SingletonInstance{ private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { //對外提供的public靜態方法 return SingletonInstance.INSTANCE; } } |
8.2.3 Scala中單例對象
8.2.4 單例模式的面試題
8.3 接口
8.3.1 回顧Java接口
- 聲明接口
interface 接口名
- 實現接口
class 實現類類名 implements 接口名1,接口2
1) 在Java中, 一個類可以實現多個接口。
2) 在Java中,接口之間支持多繼承
3) 接口中屬性都是常量
4) 接口中的方法都是抽象的
8.3.2 Scala接口的介紹
從面向對象來看,接口並不屬於面向對象的范疇,Scala是純面向對象的語言,在Scala中,沒有接口。
Scala語言中,采用特質trait(特征)來代替接口的概念,也就是說,多個類具有相同的特征(特征)時,就可以將這個特質(特征)獨立出來,采用關鍵字trait聲明。
- 如何理解特質?圖
8.4 特質(trait)
8.4.1 trait 的聲明
trait 特質名 {
trait體
}
1) trait 命名 一般首字母大寫.
Cloneable , Serializable
object T1 extends Serializable {
}
Serializable: 就是scala的一個特質。
8.4.2 Scala中trait 的使用
說明
1) 類和特質關系,使用的 繼承的關系。因為scala的特質,有傳統interface 特點,同時又抽象類特點。
2) 當一個類去繼承特質時,第一個連接詞是extends ,后面是with
3) 如果一個類在繼承特質和父類時,應當把父類寫在extends后。
8.4.3 特質的快速入門案例
- 快速入門的要求
- 具體代碼
| package com.atguigu.chapter08.traitpart
object TraitDemo01 { def main(args: Array[String]): Unit = { println("ok~") val c = new C c.getConnect("root", "123456") var f = new F f.getConnect("admin", "11111") } }
//增加獲取數據庫連接功能(實現可以不同) //(trait)
//編寫一個trait ,寫上連接數據庫的方法的規范 trait Trait01 { //抽象方法 def getConnect(user:String, pwd:String): Unit }
//1. 先把繼承關系寫出
class A {} class B extends A{} class C extends A with Trait01{ override def getConnect(user: String, pwd: String): Unit = { println("連接mysql數據庫...") } }
class D {} class E extends D {} class F extends D with Trait01{ override def getConnect(user: String, pwd: String): Unit = { println("連接oracle數據庫..") } }
|
- 代碼的說明
1) 如果我們創建了一個trait [Trait01] , 該trait只有抽象的方法,那么在底層就只會生成一個interface , 文件Trait01.class
2) 繼承了trait的類,並行實現trait的抽象方法(這點和java一樣)
8.4.4 特質trait 的再說明
1) Scala提供了特質(trait),特質可以同時擁有抽象方法和具體方法,一個類可以實現/繼承多個特質。【案例演示+反編譯】
2) 特質中沒有實現的方法就是抽象方法。類通過extends繼承特質,通過with可以繼承多個特質
3) 所有的java接口都可以當做Scala特質使用
8.4.5 帶有特質的對象,動態混入
1) 除了可以在類聲明時繼承特質以外,還可以在構建對象時混入特質,擴展目標類的功能【反編譯看動態混入本質】
代碼:
2) 此種方式也可以應用於對抽象類功能進行擴展
3) 動態混入是Scala特有的方式(java沒有動態混入),可在不修改類聲明/定義的情況下,擴展類的功能,非常的靈活,耦合性低 。
4) 動態混入可以在不影響原有的繼承關系的基礎上,給指定的類擴展功能
5) 思考:如果抽象類中有沒有實現的方法,如何動態混入特質?
8.4.6 scala創建對象的四種形式
8.4.7 疊加特質
- 基本介紹
1) 構建對象的同時如果混入多個特質,稱之為疊加特質,
2) 那么特質聲明順序從左到右,方法執行順序從右到左。
- 疊加特質應用案例
1) 目的:分析疊加特質時,對象的構建順序,和執行方法的順序
2) 代碼
| package com.atguigu.chapter08.traitpart
//疊加特質案例 object TraitDemo05 { def main(args: Array[String]): Unit = { val mySQL4 = new MySQL4 with File4 with DB4 //請思考 mySQL4 的構建順序 //1. 當構建一個混入對象時,構建順序和 聲明的順序一致(從左到右),機制和類的繼承一致.
//(1) Operate4.. //(2) Data4 //(3) DB4 //(4) File4
//看看當我們執行混入對象的方法時,它的執行順序如何 //1. 執行方法時,是從右到左執行. println("mySQL4.insert(100) 執行順序.......") mySQL4.insert(100) //(1) 向文件 向數據庫 //(2) 向數據庫 向文件 //(3) 插入數據 = 100 插入數據 = 100
println("----------------------------------------") //val mySQL4_ = new MySQL4 with File4 with DB4 //(1) Operate4.. //(2) Data4 //(3) File4 //(4) DB4
} } class MySQL4 {} trait Operate4 { println("Operate4...") def insert(id : Int) } trait Data4 extends Operate4 { println("Data4") override def insert(id : Int): Unit = { println("插入數據 = " + id) } } trait DB4 extends Data4 { println("DB4") override def insert(id : Int): Unit = { println("向數據庫") super.insert(id) } } trait File4 extends Data4 { println("File4") override def insert(id : Int): Unit = { println("向文件") //Scala中特質中如果調用super,並不是表示調用父特質的方法, // 而是向前面(左邊)繼續查找特質,如果找不到,才會去父特質查找 super.insert(id) }} |
- 對上面代碼的小結
1) 當構建一個混入對象時,構建順序和 聲明的順序一致(從左到右),機制和類的繼承一致
2) 執行方法時,是從右到左執行(按特質)
3) Scala中特質的方法中如果調用super,並不是表示調用父特質的方法,而是向前面(左邊)繼續查找特質,如果找不到,才會去父特質查找
- 疊加特質注意事項和細節
1) 特質聲明順序從左到右。
2) Scala在執行疊加對象的方法時,會首先從后面的特質(從右向左)開始執行
3) Scala中特質中如果調用super,並不是表示調用父特質的方法,而是向前面(左邊)繼續查找特質,如果找不到,才會去父特質查找
4) 如果想要調用具體特質的方法,可以指定:super[特質].xxx(…).其中的泛型必須是該特質的直接超類類型
8.4.8 在特質中重寫抽象方法
- 提出問題
- 解決問題
- 重寫抽象方法時需要考慮混入特質的順序問題和完整性問題
| package com.atguigu.chapter08.traitpart.traitabstractoverride02
object TraitAbstractOverride { def main(args: Array[String]): Unit = { //當我們重新的特質的抽象方法(還調用super...) //考慮動態混入的順序問題 var mysql2 = new MySQL5 with DB5 mysql2.insert(100)
// 錯誤 var mysql3 = new MySQL5 with File5
// var mysql4 = new MySQL5 with File5 with DB5 // mysql4.insert(200)
//這里混入時,就考慮的混入的順序和完整性. var mysql4 = new MySQL5 with DB5 with File5 mysql4.insert(200) } }
trait Operate5 { def insert(id: Int) //抽象方法 }
trait File5 extends Operate5 { //使用的是abstract override abstract override def insert(id: Int): Unit = { println("將數據保存到文件中..") super.insert(id) } }
trait DB5 extends Operate5 { def insert(id: Int): Unit = { println("將數據保存到數據庫中..") } } class MySQL5 {} |
8.4.9 當作富接口使用的特質
8.4.10 特質中的具體字段
8.4.11 特質構造順序
8.4.12 擴展類的特質
- 特質可以繼承自類,以用來拓展該類的一些功能
- 所有混入該特質的類,會自動成為那個特質所繼承的超類的子類
- 如果混入該特質的類,已經繼承了另一個類(A類),則要求A類是特質超類的子類,否則就會出現了多繼承現象,發生錯誤
8.4.13 自身類型
- 說明
自身類型:主要是為了解決特質的循環依賴問題,同時可以確保特質在不擴展某個類的情況下,依然可以做到限制混入該特質的 類的類型。
- 應用案例
舉例說明自身類型特質,以及如何使用自身類型特質
8.5 嵌套類
8.5.1 基本介紹
在Scala中,你幾乎可以在任何語法結構中內嵌任何語法結構。如在類中可以再定義一個類,這樣的類是嵌套類,其他語法結構也是一樣。
嵌套類類似於Java中的內部類。
| package com.atguigu.chapter08.innerclass
object InnerClass01 { def main(args: Array[String]): Unit = { //創建成員內部類對象 //1. 先創建ScalaOuterClasss實例 val outer01 = new ScalaOuterClass val outer02 = new ScalaOuterClass //2. 創建ScalaInnerClasss實例 //說明 //1. 在scala中 成員內部類的類型默認是和外部對象關聯 //2. 即 outer01.ScalaInnerClass類型 val inner01 = new outer01.ScalaInnerClass var inner02 = new outer02.ScalaInnerClass println(inner01 + " " + inner02)
//創建靜態內部類實例 val staticInner = new ScalaOuterClass.ScalaStaticInnerClass() println(staticInner)
} } class ScalaOuterClass { //外部類 class ScalaInnerClass { //成員內部類 } }
object ScalaOuterClass { //伴生對象 class ScalaStaticInnerClass { //靜態內部類 } }
|
請編寫程序,在內部類中訪問外部類的屬性。
- 方式1
內部類如果想要訪問外部類的屬性,可以通過外部類對象訪問。
即:訪問方式:外部類名.this.屬性名
代碼:
| package com.atguigu.chapter08.innerclass
object InnerClass02 { def main(args: Array[String]): Unit = { //創建成員內部類對象 //1. 先創建ScalaOuterClasss實例 val outer01 = new ScalaOuterClass val outer02 = new ScalaOuterClass //2. 創建ScalaInnerClasss實例 //說明 //1. 在scala中 成員內部類的類型默認是和外部對象關聯 //2. 即 outer01.ScalaInnerClass類型 val inner01 = new outer01.ScalaInnerClass var inner02 = new outer02.ScalaInnerClass //println(inner01 + " " + inner02) inner01.info() //scott 1.2 }
}
class ScalaOuterClass { var name: String = "scott" private var sal: Double = 1.2
class ScalaInnerClass { //成員內部類 def info() = { // 訪問方式:外部類名.this.屬性名 // 怎么理解 ScalaOuterClass.this 就相當於是 ScalaOuterClass 這個外部類的一個實例, // 然后通過 ScalaOuterClass.this 實例對象去訪問 name 屬性 // 只是這種寫法比較特別,學習java的同學可能更容易理解 ScalaOuterClass.class 的寫法. println("name = " + ScalaOuterClass.this.name + " age =" + ScalaOuterClass.this.sal) } } }
|
請編寫程序,在內部類中訪問外部內的屬性。
- 方式2
內部類如果想要訪問外部類的屬性,也可以通過外部類別名訪問(推薦)。
即:訪問方式:外部類名別名.屬性名
8.5.2 類型投影
- 先看一段代碼,引出類型投影
- 使用類型投影解決
解決方式-使用類型投影
類型投影是指:在方法聲明上,如果使用 外部類#內部類 的方式,表示忽略內部類的對象關系,等同於Java中內部類的語法操作,我們將這種方式稱之為 類型投影(即:忽略對象的創建方式,只考慮類型)【案例演示】
8.6 數組的基本使用
| package com.atguigu.chapter08.innerclass
object ArrayTest { def main(args: Array[String]): Unit = { //scala的數組的基本時候用 //不可變數組 //使用方式1 //創建 val arr01 = Array(10,20,30) //創建數組時,直接賦值 //遍歷 for (item <- arr01) { println(item) } //修改 arr01(0) = 90 for (item <- arr01) { println(item) }
//方式2 val arr02 = new Array[Int](10) //默認為0 println("--------arr02--------") for (item <- arr02) { println(item) } arr02(9) = 90 for (item <- arr02) { println(item) }
} }
|
第 9 章 隱式轉換和隱式參數
9.1 隱式轉換
9.1.1 提出問題
9.1.2 隱式函數基本介紹
隱式轉換函數是以implicit關鍵字聲明的帶有單個參數的函數。這種函數將會自動應用,將值從一種類型轉換為另一種類型
9.1.3 隱式函數快速入門
9.1.4 隱式函數的底層工作原理
9.1.5 隱式轉換的注意事項和細節
1) 隱式轉換函數的函數名可以是任意的,隱式轉換與函數名稱無關,只與函數簽名(函數參數類型和返回值類型)有關。
2) 隱式函數可以有多個(即:隱式函數列表),但是需要保證在當前環境下,只有一個隱式函數能被識別
9.2 隱式轉換豐富類庫功能
9.2.1 基本介紹
如果需要為一個類增加一個方法,可以通過隱式轉換來實現。(動態增加功能)比如想為MySQL類增加一個delete方法
9.2.2 分析解決方案
在當前程序中,如果想要給MySQL類增加功能是非常簡單的,但是在實際項目中,如果想要增加新的功能就會需要改變源代碼,這是很難接受的。而且違背了軟件開發的OCP開發原則 【open close princeple】
在這種情況下,可以通過隱式轉換函數給類動態添加功能。
9.2.3 快速入門案例
9.3 隱式值
9.3.1 基本介紹
隱式值也叫隱式變量,將某個形參變量標記為implicit,所以編譯器會在方法省略隱式參數的情況下去搜索作用域內的隱式值作為缺省參數
9.3.2 應用案例
9.3.3 課堂測試題
9.3.4 課堂練習
| package com.atguigu.chapter09.implicitpart
object ImplicitExer02 {
def main(args: Array[String]): Unit = { // 隱式變量(值) implicit val name: String = "Scala" implicit val name1: String = "World"
def hello(implicit content: String = "jack"): Unit = { println("Hello " + content) } //調用hello hello } } |
- 對上面代碼的小結
1) 隱式值的優先級高於默認值
2) 當匹配到多個隱式值就會報錯(匹配時,是安裝類型匹配)
9.4 隱式類
9.4.1 基本介紹
在scala2.10后提供了隱式類,可以使用implicit聲明類,隱式類的非常強大,同樣可以擴展類的功能,比前面使用隱式轉換豐富類庫功能更加的方便,在集合中隱式類會發揮重要的作用。
9.4.2 隱式類使用有如下幾個特點:
1) 其所帶的構造參數有且只能有一個
2) 隱式類必須被定義在“類”或“伴生對象”或“包對象”里,即隱式類不能是 頂級的(top-level objects)。
3) 隱式類不能是case class(case class在后續介紹)
4) 作用域內不能有與之相同名稱的標示符
9.4.3 應用案例
9.5 隱式的轉換時機
1) 當方法中的參數的類型與目標類型不一致時
2) 當對象調用所在類中不存在的方法或成員時,編譯器會自動將對象進行隱式轉換(根據類型)
9.6 隱式解析機制
9.7 隱式轉換的前提
在進行隱式轉換時,需要遵守兩個基本的前提:
1) 不能存在二義性
2) 隱式操作不能嵌套 // [舉例:]如:隱式轉換函數
第 10 章
數據結構上-集合
10.1 數據結構的特點
10.1.1 scala集合基本介紹
1) Scala同時支持不可變集合和可變集合,不可變集合可以安全的並發訪問
2) 兩個主要的包:
不可變集合:scala.collection.immutable
可變集合: scala.collection.mutable
3) Scala默認采用不可變集合,對於幾乎所有的集合類,Scala都同時提供了可變和不可變的版本
10.1.2 可變集合和不可變集合舉例
1) 不可變集合:scala不可變集合,就是這個集合本身不能變[內存地址不能變](類似java的數組,是不可以動態增長的)
2) 可變集合:可變集合(ArrayList , 是可以動態增長的) 就是這個集合的本身是可以變的[內存地址可變],成為一個新的集合.
3) 以java舉例說明
| package com.atguigu.chapter10.collection;
import java.util.ArrayList; public class JavaCollection { public static void main(String[] args) {
//不可變集合類似java的數組 int[] nums = new int[3]; nums[2] = 11; //? //nums[3] = 90; //?
// String[] names = {"bj", "sh"}; // System.out.println(nums + " " + names); // // //可變集合舉例 ArrayList al = new ArrayList<String>(); al.add("zs"); al.add("zs2"); System.out.println(al + " " + al.hashCode()); //地址 al.add("zs3"); System.out.println(al + " " + al.hashCode()); //地址 } } |
10.2 不可變集合繼承層次一覽
- 對上面圖的一個小結
1.Set、Map是Java中也有的集合
2.Seq是Java沒有的,我們發現List歸屬到Seq了,因此這里的List就和java不是同
一個概念了
3.我們前面的for循環有一個 1 to 3 ,就是IndexedSeq 下的Vector
4.String也是屬於IndexeSeq
5.我們發現經典的數據結構比如Queue 和 Stack被歸屬到LinerSeq
6.大家注意Scala中的Map體系有一個SortedMap,說明Scala的Map可以支持
排序
7.IndexSeq 和 LinearSeq 的區別[IndexSeq是通過索引來查找和定位,
因此速度快,比如String就是一個索引集合,通過索引即可定位]
[LineaSeq 是線型的,即有頭尾的概念,這種數據結構一般是通過遍歷來查找,
它的價值在於應用到一些具體的應用場景
10.3 Scala可變集合繼承關系一覽圖
10.4 數組-定長數組(聲明泛型)
10.4.1 第一種方式定義數組
這里的數組等同於Java中的數組,中括號的類型就是數組的類型
val arr1 = new Array[Int](10)
//賦值,集合元素采用小括號訪問
arr1(1) = 7
- 案例演示
| package com.atguigu.chapter10.arraypart
object ArrayDemo01 { def main(args: Array[String]): Unit = { //說明 //1. new 是關鍵字,[Int] 是指定可以存放的數據類型 //2. 如果希望存放任意數據類型,則指定Any //3. (4) ,表示數組的大小,確定后就不可以變化 //4. 在底層 Array 對應的代碼是 /* int[] nums = new int[3]; nums[2] = 11; */ val arr01 = new Array[Int](4) println(arr01.length) // 4 arr01(3) = 10 //修改某個元素的值 //遍歷數組 for (i <- arr01) { println(i) } } } |
|
|
10.4.2 第二種方式定義數組
- 說明
在定義數組時,直接賦值
//使用apply方法創建數組對象
val arr1 = Array(1, 2)
- 舉例說明
| //第二種創建數組的方式apply var arr02 = Array(1, 3, "xxx") for (i <- arr02) { println(i) } |
10.5 數組-變長數組(聲明泛型)
10.5.1 基本使用和應用案例
| package com.atguigu.chapter10.arraypart
import scala.collection.mutable.ArrayBuffer
object ArrayBuffer01 { def main(args: Array[String]): Unit = { //說明 //1. ArrayBuffer 需要引入 scala.collection.mutable.ArrayBuffer //2. ArrayBuffer[Any](3, 2, 5) 的 (3, 2, 5) 初始元素有三個 //3. ArrayBuffer是有序的集合!!!! //4. 增加元素使用的是 append方法(),支持可變參數。 val arr01 = ArrayBuffer[Any](3, 2, 5) //遍歷數組 for (i <- arr01) { println(i) } println(arr01.length) // 3 //println("arr01.hash=" + arr01.hashCode())
//增加了元素,使用的方法是 //def append(elems: A*) { appendAll(elems) } arr01.append(90.0,13) //println("arr01.hash=" + arr01.hashCode())
arr01(1) = 89 //修改第2個元素的值 println("--------------------------") for (i <- arr01) { println(i) } println(arr01.length) // 5 } }
|
10.5.2 定長數組與變長數組的轉換
arr1.toBuffer //定長數組轉可變數組
arr2.toArray //可變數組轉定長數組
- 說明:
1) arr2.toArray 返回結果才是一個定長數組, arr2本身沒有變化
2) arr1.toBuffer返回結果才是一個可變數組, arr1本身沒有變化
- 案例演示
| package com.atguigu.chapter10.arraypart
import scala.collection.mutable.ArrayBuffer
object Array22ArrayBuffer { def main(args: Array[String]): Unit = { //創建一個空的可變數組 val arr2 = ArrayBuffer[Int]() // 追加值 arr2.append(1, 2, 3) println(arr2) // 1,2,3
// ArrayBuffer ==> Array //說明 //1. arr2.toArray 返回的結果是一個新的定長數組集合 //2. arr2它沒有變化 val newArr = arr2.toArray
println(newArr) // // Array ===> ArrayBuffer //說明 //1. newArr.toBuffer 返回一個變長數組 newArr2 //2. newArr 沒有任何變化,依然是定長數組 val newArr2 = newArr.toBuffer newArr2.append(123) println(newArr2)
} }
|
10.6 數組-多維數組
10.6.1 多維數組的定義和使用
- 定義
val arr = Array.ofDim[Double](3,4)
//說明:二維數組中有三個一維數組,每個一維數組中有四個元素
- 案例演示
| package com.atguigu.chapter10.arraypart
object DimArray { def main(args: Array[String]): Unit = { //創建一個二維數組[3,4] //說明 //1. 創建了一個 二維數組, 有三個元素,每個元素是,含有4個元素一維數組() //2. 在底層仍然是java的方式處理 // int[][] arr = (int[][])Array..MODULE$.ofDim(3, 4, ClassTag..MODULE$.Int()); // arr[1][2] = 88; val arr = Array.ofDim[Int](3, 4) arr(1)(2) = 88 //遍歷二維數組 for (i <- arr) { //i 就是一維數組 for (j <- i) { print(j + " ") } println() }
} }
|
10.6.2 關於特質的接受參數(多態的)補充
| package com.atguigu.chapter10.arraypart
object TraitUse { def main(args: Array[String]): Unit = { //測試: val a01 = new A B.test(a01) // b ok.. } }
trait MyTrait01 {} //特質 //A類繼承MyTrait01 class A extends MyTrait01 {}
object B { //test方法,可以接受一個繼承MyTrait01的類的實例(這點和java一樣,體現接口多態) def test(m: MyTrait01): Unit = { println("b ok..") } }
|
10.7 數組-Scala數組與Java數組的互轉
10.7.1 Scala數組轉Java數組(List)
在項目開發中,有時我們需要將Scala數組轉成Java數組,看下面案例:
| package com.atguigu.chapter10.arraypart
import scala.collection.mutable.ArrayBuffer
object ArraytoArrayList { def main(args: Array[String]): Unit = {
//創建了ArrayBuffer val arr = ArrayBuffer("1", "2", "3")
//下面的import 引入了我們需要的隱式函數【這里就是隱式函數的應用】 //implicit def bufferAsJavaList[A](b : scala.collection.mutable.Buffer[A]) : java.util.List[A] import scala.collection.JavaConversions.bufferAsJavaList
//這里使用了我們的隱式函數bufferAsJavaList完成兩個ArrayBuffer -> List 轉換 //返回的就是 List<String> val javaArr = new ProcessBuilder(arr)
//返回的是 List<String> val arrList = javaArr.command() println(arrList) //輸出 [1, 2, 3] } }
|
10.7.2 Java數組(List)轉Scala數組
在項目開發中,有時我們需要將Java數組轉成Scala數組,看下面案例
- 案例如下:
| //=============================================================================
//Java數組(List)轉Scala數組
import scala.collection.JavaConversions.asScalaBuffer import scala.collection.mutable println("---------------------Java數組(List)轉Scala數組--------------------------") // java.util.List ==> Buffer val scalaArr: mutable.Buffer[String] = arrList println(scalaArr) |
10.8 元組Tuple-元組的基本使用
10.8.1 基本介紹
元組也是可以理解為一個容器,可以存放各種相同或不同類型的數據。
說的簡單點,就是將多個無關的數據封裝為一個整體,稱為元組
注意:元組中最大只能有22個元素
10.8.2 元組的創建
| package com.atguigu.chapter10.tuplepart
object TupleDemo01 { def main(args: Array[String]): Unit = { //說明 //1. 創建了一個元組 tuple1 類似 Tuple4 //2. 元組最多有 22個元素, Tuple1 ---->Tuple22 //3. 獲取元組的元素的方法 tuple._1 表示取出第一個元素,依次類推 //4. 下面的代碼的底層 Tuple4 tuple1 val tuple1 = (1, 2, 3, "hello") // 底層 Tuple4 tuple1
println(tuple1) println(tuple1._1 + " " + tuple1._2)
} } |
10.9 元組Tuple-元組數據的訪問
10.9.1 基本介紹
訪問元組中的數據,可以采用順序號(_順序號),也可以通過索引(productElement)訪問。
10.9.2 應用案例
| /* override def productElement(n: Int) = n match { case 0 => _1 case 1 => _2 case 2 => _3 case 3 => _4 case _ => throw new IndexOutOfBoundsException(n.toString()) } */ println(tuple1.productElement(3)) //等價 tuple1._4,即第四個元素. |
10.10 元組Tuple-元組數據的遍歷
使用元組的迭代器進行遍歷.
| println("----------------遍歷元組---------------------") //遍歷元組 for (item <- tuple1.productIterator) { println("item=" + item) } |
10.11 列表 List-創建List
10.11.1 基本介紹
Scala中的List 和Java List 不一樣,在Java中List是一個接口,真正存放數據是ArrayList,而Scala的List可以直接存放數據,就是一個object,默認情況下Scala的List是不可變的。
val List = scala.collection.immutable.List
object List extends SeqFactory[List]
10.11.2 創建List的應用案例
| package com.atguigu.chapter10.listpart
object ListCreate { def main(args: Array[String]): Unit = {
//為什么可以直接使用List ,和 Nil? //原因是 在 package object scala 有聲明 /* val List = scala.collection.immutable.List val Nil = scala.collection.immutable.Nil */
val list1 = List(1,2,3) println("list1=" + list1) //空list的創建 val list2 = Nil println("list2=" + list2) //List()
} }
|
10.11.3 列表 List-訪問List元素
| //訪問list的元素 //list(1) 表示的下標 是從0開始的. val e = list1(1) println("e=" + e) //2 |
10.11.4 列表 List-元素的追加
- 基本說明
向列表中增加元素, 會返回新的列表/集合對象。注意:Scala中List元素的追加形式非常獨特,和Java不一樣。
- 方式1-在列表最后追加數據
| var list1 = List(1, 2, 3, "abc") // :+運算符表示在列表的最后增加數據4 // 說明 :+ 是追加符號 //1. : 這邊是集合, + 這邊是元素. val list2 = list1 :+ 4 println(list1) //list1沒有變化 println(list2) //新的列表結果是 [1, 2, 3, "abc", 4] |
- 方式2-在列表的最前面追加數據
| println("-------------------------------------") // +:運算符表示在列表的最前面增加數據40 // 說明 :+ 是追加符號 val list3 = 40 +: list1 println("list1=" + list1) //list1沒有變化 (1, 2, 3, "abc") println("list3=" + list3) //List(40, 1, 2, 3, "abc") |
10.11.5 方式3-在列表的最后增加數據
- 說明:
1) 符號::表示向集合中 新建集合添加元素。
2) 運算時,集合對象一定要放置在最右邊,
3) 運算規則,從右向左。
4) ::: 運算符是將集合中的每一個元素加入到空集合中去[要求::: 左右兩邊都是集合]
- 應用案例
| package com.atguigu.chapter10.listpart
object ListAdd3 { def main(args: Array[String]): Unit = {
println("---------------案例演示------------------") //list1是一個列表 val list1 = List(1, 2, 3, "abc")
//步驟如下 //1. () 空集合 //2. ((1, 2, 3, "abc")) //3 (6, (1, 2, 3, "abc")) //4 (5,6, (1, 2, 3, "abc")) //5 (4,5,6, (1, 2, 3, "abc")) val list5 = 4 :: 5 :: 6 :: list1 :: Nil println("list5=" + list5)// 4,5,6, (1, 2, 3, "abc") //
//步驟 //1 (1, 2, 3, "abc") //2 (6, 1, 2, 3, "abc") //3 (5, 6, 1, 2, 3, "abc") //4 (4, 5, 6, 1, 2, 3, "abc") val list6 = 4 :: 5 :: 6 :: list1
println("list6=" + list6)
//步驟 //1. () //2. (1, 2, 3, "abc") //3. (6,1, 2, 3, "abc") //4. (5,6,1, 2, 3, "abc") //5. (4,5,6,1, 2, 3, "abc") val list7 = 4 :: 5 :: 6 :: list1 ::: Nil //4 :: 5 :: 6 :: list1 println("list7=" + list7) //案例1 + 說明
println("-----------課堂練習。。。----------------")
val list11 = List(1, 2, 3, "abc") val list55 = 4 :: 5 :: 6 :: list11 println(list55) //(4, 5, 6, 1, 2, 3, abc)
//錯誤! ::: 【6 ::: list12】 6是集合 // val list12 = List(1, 2, 3, "abc") // val list52 = 4 :: 5 :: 6 ::: list12 ::: Nil // println(list5)
val list1_ = List(1, 2, 3, "abc") val list5_ = 4 :: 5 :: list1_ ::: list1_ ::: Nil println("list5_=" + list5_) // (4,5,1, 2, 3, "abc",1, 2, 3, "abc")
} }
|
10.12 隊列 Queue-基本介紹
10.12.1 基本介紹
10.12.2 隊列的說明
1) 隊列是一個有序列表,在底層可以用數組或是鏈表來實現。
2) 其輸入和輸出要遵循先入先出的原則。即:先存入隊列的數據,要先取出。后存入的要后取出
3) 在Scala中,由設計者直接給我們提供隊列類型使用
10.12.3 應用案例
- 創建隊列
- 隊列中添加數據
| // 想q1中增加元素 20 // 說明 // 1. += 函數 def +=(elem: A): this.type = { appendElem(elem); this } // 2. q1 += 20 底層 q1.$plus$eq(20) // 3. 隊列中,可以有重復數據 q1 += 20 //q1.$plus$eq(20) q1 += 20 //q1.$plus$eq(20) println(q1)
//說明 //1. 將List(2,4,6) 取出,然后加入到q1 q1 ++= List(2,4,6) println(q1) // (20,20,2,4,6) |
10.13 隊列 Queue-刪除隊列元素
- 說明
- 案例
| //刪除隊列的元素 //1. 刪除的是隊列的第一個元素 //2. 隊列本身發生變化 q1.dequeue() println("q1=" + q1) |
10.14 隊列 Queue-給隊列添加元素
- 案例
| //使用方法添加元素 q1.enqueue(10,90,11) println("q1=" + q1) // (20,20,2,4,6,10,90,11) |
10.15 隊列 Queue-返回隊列的元素
10.15.1 返回隊列的第一個元素
| //返回隊列的頭
val q2 = new mutable.Queue[Int] q2 ++= List(1,2,3) //說明 //執行 q2.head ,返回隊列頭元素,但是對隊列本身沒有影響 println("q2.head=" + q2.head) // 1 println("q2=" + q2) |
10.15.2 返回隊列最后一個元素
println(q1.last)
10.15.3 返回隊列的尾部
即:返回除了第一個以外剩余的元素, 可以級聯使用,這個在遞歸時使用較多。
| //返回隊列尾部數據 // 說明 //1. q2.tail 返回的是一個隊列 [從q2的第一個元素后的所有元素] //2. q2本身沒有變化 //3. tail可以級聯適應 val q3 = q2.tail println("q3=" + q3) //(2,3) |
10.16 關於上邊界和下邊界的知識點
10.17 映射 Map-基本介紹
10.17.1 Java中的Map回顧
HashMap 是一個散列表,它存儲的內容是鍵值對(key-value)映射,Java中的HashMap是無序的。
- 案例演示:
| public class JavaMap { public static void main(String[] args) { HashMap<String,Integer> hm = new HashMap(); hm.put("no1", 100); hm.put("no2", 200); hm.put("no3", 300); hm.put("no4", 400);
System.out.println(hm); System.out.println(hm.get("no2"));
} }
|
10.18 Scala中的Map介紹
Scala中的Map 和Java類似,也是一個散列表,它存儲的內容也是鍵值對(key-value)映射,Scala中不可變的Map是有序的,可變的Map是無序的。
10.19 映射 Map-構建Map
10.19.1 方式1-構造不可變映射
Scala中的不可變Map是有序,構建Map中的元素底層是Tuple2類型
- 案例演示
| package com.atguigu.chapter10.mappart
object ScalaMap { def main(args: Array[String]): Unit = {
val map = Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30) println("map=" + map) //1. 取值, 知道key取值 println(map("Bob")) // 20 //2. 遍歷map for (v <- map) { println("v=" + v) println("v.key=" + v._1 + " v.value=" + v._2) } } }
|
- 對上面代碼的小結
1.從輸出的結果看到,輸出順序和聲明順序一致
2.構建Map集合中,集合中的元素其實是Tuple2類型
3.默認情況下(即沒有引入其它包的情況下),Map是不可變map
4.為什么說Map中的元素是Tuple2 類型 [反編譯或看對應的apply]
10.19.2 方式2-構造可變映射
案例:
| //構建可變的map //說明 //1. 可變的需要帶包 //2. 可以是無序 val map2 = mutable.Map("Alice" -> 10, "Bob" -> 20, "Kotlin" -> 30) println("map2=" + map2) |
10.19.3 方式3-創建空的映射
val map3 = new scala.collection.mutable.HashMap[String, Int]
println(map3)
10.19.4 方式4-對偶元組
即創建包含鍵值對的二元組, 和第一種方式等價,只是形式上不同而已。
對偶元組 就是只含有兩個數據的元組。
| //以元組的方式構建map對象 val map3 = mutable.Map(("Alice" , 10), ("Bob" , 20), ("Kotlin" , 30)) println("map3=" + map3) |
10.20 映射 Map-取值
10.20.1 方式1-使用map(key)
val value1 = map1("Alice")
println(value1)
說明:
1) 如果key存在,則返回對應的值
2) 如果key不存在,則拋出異常
3) 在Java中,如果key不存在則返回null
10.20.2 方式2-使用contains方法檢查是否存在key
- 基本說明
// 返回Boolean
// 1.如果key存在,則返回true
// 2.如果key不存在,則返回false
map4.contains("B")
- 案例
| //方式2,先判斷是否有key,然后做相應的處理 if (map3.contains("A")) { println("存在" + map3("A")) } else { println("key存在") } |
10.20.3 方式3-使用map.get(key).get取值
通過 映射.get(鍵) 這樣的調用返回一個Option對象,要么是Some,要么是None
- 案例演示
| /方式3: 是個get的方式來取值 var map4 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) ) println(map4.get("A")) //Some val some1 = map4.get("A") for (item<-some1) { println("item=" + item) } println(map4.get("A").get) //得到Some在取出 println("uuukey=" + map4.get("uuu"))// None |
- 案例說明
1) map.get方法會將數據進行包裝
2) 如果 map.get(key) key存在返回some,如果key不存在,則返回None
3) 如果map.get(key).get key存在,返回key對應的值,否則,拋出異常 java.util.NoSuchElementException: None.get
10.20.4 方式4-使用map4.getOrElse()取值
- 說明
getOrElse 方法 : def getOrElse[V1 >: V](key: K, default: => V1)
說明:
1) 如果key存在,返回key對應的值。
2) 如果key不存在,返回默認值。在java中底層有很多類似的操作
- 案例
| //方式4:使用getOrElse() val map5 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) ) println(map5.getOrElse("A1","默認")) //1 |
10.20.5 選擇使用取值的方式建議
1) 如果你確定key存在,則直接使用map(), 或者 get()即可
2) 如果不確定key是否存在,但是只是想得到一個值,則使用getOrElse即可
3) 如果不確定key是否存在,而且是當key不存在時,需要對應的業務邏輯則時候用constrait判斷..
10.21 映射 Map-對map修改、添加和刪除
10.21.1 更新map的元素
案例:
| //更新map的元素 val map6 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) ) //如果key存在,則修改 //如果key不存在,則添加 map6("A") = 20 println("map6=" + map6) |
- 對代碼的說明
1) map 是可變的,才能修改,否則報錯
2) 如果key存在:則修改對應的值,key不存在,等價於添加一個key-val
10.21.2 添加map元素
- 方式1-增加單個元素
案例
| //第1種添加,添加單對 key-val //說明 //1. 如果key不存在,則添加 //2. 如果key存在,則修改 val map7 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) ) map7 += ( "D" -> 4 ) map7 += ( "B" -> 50 ) println("map7=" + map7) |
- 添加多個元素
| //也可以添加多個key-val map7 += ("UU"->1, "FF"->3) |
10.21.3 刪除map元素
- 案例
| //刪除元素的操作 //說明 //1. 可以刪除多個key //2. 如果key存在,則刪除,如果key不存在,就不會拋出異常 val map8 = mutable.Map( ("A", 1), ("B", "北京"), ("C", 3) ) map8 -= ("A", "BB") println("map8=" + map8) |
- 代碼說明
1) "A","B" 就是要刪除的key, 可以寫多個.
2) 如果key存在,就刪除,如果key不存在,也不會報錯.
10.22 映射 Map-對map遍歷
- 案例演示
| //遍歷map val map9 = mutable.Map(("A", 1), ("B", "北京"), ("C", 3)) //方式1 for ((k, v) <- map9) { print("k=" + k + "v=" + v + "\t") } //方式2,只遍歷key for (k <- map9.keys) { print("k=" + k + "\t") } //方式3,只遍歷values for (v <- map9.values) { print("v=" + v + "\t") } //方式4,取出每個元素(Tuple2) for (item <- map9) { println("item=" + item) // println("k=" + item._1 + "v=" + item._2) }
|
10.23 集 Set-基本介紹
集是不重復元素的結合。集不保留順序,默認是以哈希集實現
10.23.1 Java中Set的回顧
java中,HashSet是實現Set<E>接口的一個實體類,數據是以哈希表的形式存放的,里面的不能包含重復數據。Set接口是一種一個不包含重復元素的 collection,HashSet中的數據也是沒有順序的
具體的代碼
| package com.atguigu.chapter10.setpart; import java.util.*; public class JavaSet { public static void main(String[] args) { //說明 //1. 元素不能重復 //2. 沒有順序 HashSet hs = new HashSet<String>(); hs.add("jack"); hs.add("tom"); hs.add("jack"); hs.add("jack2"); System.out.println(hs);
} } |
10.23.2 Scala中Set的說明
默認情況下,Scala 使用的是不可變集合,如果你想使用可變集合,需要引用 scala.collection.mutable.Set 包
10.24 集 Set-創建
10.24.1 可變set 和不可變set的創建
| object ScalaSet { def main(args: Array[String]): Unit = {
//1. 創建一個Set // //默認是不可變集, 無序 val set01 = Set(1,2,4,"abc", 20, 13.4) println("set01=" + set01) //默認是可變集, 無序 val set02 = mutable.Set(1,2,4,"abc", 20, 67.8) println("set02=" + set02)
} } |
10.24.2 可變集合的元素添加
| //可變Set的添加 //1. 如果添加的對象已經存在,則不會重復添加,也不會報錯 set02 += 11 set02 += 11 set02 += 11 //這種方式,就添加后,返回一個新的Set //set02.+(90) println("set02=" + set02) // 不可變的Set 不能執行 += , //set01 += 80
|
- 代碼小結
1) 1. 如果添加的對象已經存在,則不會重復添加,也不會報錯
2) set02.+(90)返回新的集合
10.24.3 可變集合的元素刪除
val set02 = mutable.Set(1,2,4,"abc")
set02 -= 2
set02.remove("abc")
println(set02)
說明:如果刪除的對象不存在,則不生效,也不會報錯
10.24.4 集Set的遍歷
val set02 = mutable.Set(1, 2, 4, "abc")
for(x <- set02) {
println(x)
}
10.25 集 Set-更多操作
第 11 章 數據結構下-集合操作
11.1 看一個實際需求
1) 要求:請將list(3,5,7) 中的所有元素都 * 2 ,將其結果放到一個新的集合中返回,即返回一個新的list(6,10,14), 請編寫程序實現.
2) 使用傳統的方法解決
11.2 集合元素的映射-map映射操作
11.2.1 講解了高階函數的基本使用
| package com.atguigu.chapter11.mapoper
//高階函數基本使用
object HighOrderDef { def main(args: Array[String]): Unit = {
val res = test(sum, 2.0) println("res=" + res) // 4.0
//調用高階函數test2 test2(myPrint)
}
//test函數的說明 //1.test函數可以接受一個函數,test函數就是高階函數 //2.f: Double => Double : f就是形參名,表示函數 ,Double:傳入的函數可以形參(Double) // => 一個規定 Double 接受的函數的返回值. //3. n1: Double :普通的形參 def test(f: Double => Double, n1: Double) = { f(n1) //執行 }
def sum(d1: Double): Double = { d1 * 2 }
//再寫一個高階函數的使用 def test2(f: () => Unit): Unit = { f() }
def myPrint(): Unit = { println("hello,world!") } } |
11.2.2 使用map映射函數來解決
| package com.atguigu.chapter11.mapoper
object MapOperDemo { def main(args: Array[String]): Unit = {
//使用傳統的方式 //1. 優點 簡單,好理解 //2. 缺點: 不夠簡潔,高效,沒有使用函數式編程 // val list1 = List(3, 5, 7) // var list2 = List[Int]() // for (item <- list1) {//遍歷 // list2 = list2 :+ item * 2 // } // println(list2)
//使用map映射函數解決 val list1 = List(3, 5, 7) //對list1.map(mul) 解釋 //1. 將 list1集合元素遍歷,傳給mul,進行運算 2 * n //2. 將運算的結果放入到一個新的集合中,並返回 val list2 = list1.map(mul) println("list2=" + list2) // (6, 10, 14)) }
//寫成函數 def mul(n: Int): Int = { 2 * n } }
|
11.2.3 課堂練習
請將 val names = List("Alice", "Bob", "Nick") 中的所有單詞,全部轉成字母大寫,返回到一下新的List集合中.
代碼:
| package com.atguigu.chapter11.mapoper
object MapExercise01 { def main(args: Array[String]): Unit = { val names = List("Alice", "Bob", "Nick") def upper(s:String): String = { s.toUpperCase } val names2 = names.map(upper) println("names=" + names2) // } }
|
11.2.4 flatmap映射:flat即壓扁,壓平,扁平化映射
- 基本介紹
flatmap:flat即壓扁,壓平,扁平化,效果就是將集合中的每個元素的子元素映射到某個函數並返回新的集合。
- 看一個案例:
| object FlatMap { def main(args: Array[String]): Unit = {
val names = List("Alice", "Bob", "Nick") def upper( s : String ) : String = { s. toUpperCase } val names2 = names.flatMap(upper) println("names2=" + names2) //List(A, L, I, C, E, B, O, B, N, I, C, K) } } |
- 對代碼的簡單分析
//注意:每個字符串也是char集合
println(names.flatMap(upper)) //map 遍歷 【 "Alice", "Bob", "Nick" 】
//flatMap (A,l,i,c,e, ....) ==upper===>(A,L,I)
11.3 集合元素的過濾-filter
filter:將符合要求的數據(篩選)放置到新的集合中
- 應用案例:
將 val names = List("Alice", "Bob", "Nick") 集合中首字母為'A'的篩選到新的集合。
| package com.atguigu.chapter11.mapoper
object FilterMap { def main(args: Array[String]): Unit = { val names = List("Alice", "Bob", "Nick") //普通函數 //1. 如果s是以"A" 開頭的,我就返回true, 否則返回false def startA(s:String): Boolean = { s.startsWith("A") }
//2. 使用filter來對集合進行過濾操作 // 當我們的names的每個元素,會調用startA進行判斷,如果為true,就放入到新集合 val names2 = names.filter(startA) println("names2=" + names2) // List("Alice")
} }
|
11.4 化簡
11.4.1 看一個需求:
val list = List(1, 20, 30, 4 ,5) , 求出list的和.
11.4.2 化簡:
- 化簡:將二元函數引用於集合中的函數,。
- 上面的問題當然可以使用遍歷list方法來解決,這里我們使用scala的化簡方式來完成。[案例演示+代碼說明]
- 代碼實現:
| package com.atguigu.chapter11.mapoper
object reduce { def main(args: Array[String]): Unit = { val list = List(1, 20, 30, 4, 5) //sum 返回兩個數的和 def sum(n1: Int, n2: Int): Int = { n1 + n2 } //分析執行流程.
//1, 20, 30, 4, 5 //1. (1,20) 30, 4,5 //2 (21 30), 4,5 //3. (51 , 4), 5 //4. 55 , 5 //5. 60
val res = list.reduceLeft(sum) println("res=" + res) // 60
//(1, (20, (30, (4, 5)))) val res2 = list.reduceRight(sum) println("res2=" + res2) // 60
} }
|
11.4.3 reduceLefft(_ - _)這個函數的執行邏輯如圖
reduceRight() 和 上面一樣,只是從右邊開始進行簡化.
11.4.4 課堂練習題
| package com.atguigu.chapter11.mapoper
object ReduceExe { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4 ,5) def minus( num1 : Int, num2 : Int ): Int = { num1 - num2 } println("r1=" + list.reduceLeft(minus)) // 輸出? -13 println("r2=" + list.reduceRight(minus)) //輸出? 3 println("r3=" + list.reduce(minus)) //輸出? 3 從左化簡
} }
|
- 使用化簡的方法求出 List(3,4,2,7,5) 最小的值
| package com.atguigu.chapter11.mapoper
object ReduceExe02 { def main(args: Array[String]): Unit = { /* 使用化簡的方法求出 List(3,4,2,7,5) 最小的值
*/ def minVal(n1:Int,n2:Int): Int = { if (n1 > n2) n2 else n1 } val list1 = List(3,4,2,7,5) println("最小值=" + list1.reduceLeft(minVal))
} }
|
11.5 折疊
11.5.1 基本介紹
fold函數將上一步返回的值作為函數的第一個參數繼續傳遞參與運算,直到list中的所有元素被遍歷。
- 可以把reduceLeft看做簡化版的foldLeft。
如何理解:
def reduceLeft[B >: A](@deprecatedName('f) op: (B, A) => B): B =
if (isEmpty) throw new UnsupportedOperationException("empty.reduceLeft")
else tail.foldLeft[B](head)(op)
大家可以看到. reduceLeft就是調用的foldLeft[B](head),並且是默認從計划的head元素開始操作的。 - 相關函數:fold,foldLeft,foldRight,可以參考reduce的相關方法理解
11.5.2 應用案例
看下面代碼看看輸出什么,並分析原因.
| package com.atguigu.chapter11.mapoper
object FoldDemo01 { def main(args: Array[String]): Unit = { val list = List(1, 2, 3, 4)
def minus(num1: Int, num2: Int): Int = { num1 - num2 } //執行流程分析(((5, 1), 2), 3, 4) println(list.foldLeft(5)(minus)) //-5 //執行流程分析:(1, 2, 3, 4) (3) println(list.foldRight(5)(minus)) //3
} }
|
11.5.3 foldLeft和foldRight 縮寫方法分別是:/:和:\
| package com.atguigu.chapter11.mapoper
object FoldDemo02 { def main(args: Array[String]): Unit = {
val list4 = List(1, 9, 2, 8)
def minus(num1: Int, num2: Int): Int = { num1 - num2 } // /: 等價 foldLeft var i6 = (0 /: list4) (minus) // (-20) (1,9,2,8) println(i6) // 輸出?
// :\ 等價 foldRight i6 = (list4 :\ 10 )(minus) // (1,9,2,8) (-4) println(i6) // 輸出? // -4
} }
|
11.6 掃描
11.6.1 基本介紹
掃描,即對某個集合的所有元素做fold操作,但是會把產生的所有中間結果放置於一個集合中保存
11.6.2 案例演示
| package com.atguigu.chapter11.mapoper
object ScanDemo { def main(args: Array[String]): Unit = { def minus( num1 : Int, num2 : Int ) : Int = { num1 - num2 } //5 (1,2,3,4,5) =>(5,4,2,-1,-5,-10) val i8 = (1 to 5).scanLeft(5)(minus) //Vector(5, 4, 2, -1, -5, -10) println(i8) def add( num1 : Int, num2 : Int ) : Int = { num1 + num2 } //5 (1,2,3,4,5) =>(5,6,8, 11,15,20) val i9 = (1 to 5).scanLeft(5)(add) //Vector(5, 6, 8, 11, 15, 20) println(i9)
} }
|
11.6.3 課堂練習
請寫出下面的運行結果
def test(n1:Int ,n2 :Int): Int = {
n1 * n2
}
val i10 = (1 to 3).scanLeft(3)(test)
println("i10=" + i10) // (3,3,6,18)
11.7 課堂練習1
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
將sentence 中各個字符,通過foldLeft存放到 一個ArrayBuffer中
目的:理解flodLeft的用法.
| package com.atguigu.chapter11.mapoper
import scala.collection.mutable.ArrayBuffer
object Exercise03 { def main(args: Array[String]): Unit = { val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
def putArry( arr : ArrayBuffer[Char], c : Char ): ArrayBuffer[Char] = { arr.append(c) arr } //創建val arr = ArrayBuffer[Char]() val arr = ArrayBuffer[Char]() println(sentence.foldLeft(arr)(putArry))
} }
|
11.8 課堂練習2
val sentence = "AAAAAAAAAABBBBBBBBCCCCCDDDDDDD"
使用映射集合,統計一句話中,各個字母出現的次數
提示:Map[Char, Int]()
看看java如何實現
使用scala的flodLeft折疊方式實現.
課后完成思考:使用Scala的傳統方法來實現(類似Java)
| package com.atguigu.chapter11.mapoper
object Exercise04 { def main(args: Array[String]): Unit = {
val sentence = "AAAAAGAAAAABBBBBBBBCCCCCDD" def charCount( map : Map[Char, Int], c : Char ): Map[Char, Int] = { map + (c -> (map.getOrElse(c, 0) + 1)) } val map2 = sentence.foldLeft(Map[Char, Int]())(charCount) println(map2)
} } |
11.9 擴展-拉鏈(合並)
11.9.1 基本介紹
在開發中,當我們需要將兩個集合進行 對偶元組合並,可以使用拉鏈。
11.9.2 應用實例
| package com.atguigu.chapter11.zippart
object ZipDemo {
def main(args: Array[String]): Unit = {
val list1 = List(1, 2 ,3) val list2 = List(4, 5, 6) val list3 = list1.zip(list2) //((1,4), (2,5),(3,6))
println("list3=" + list3)
}
}
|
11.10 擴展-迭代器
11.10.1 基本說明
通過iterator方法從集合獲得一個迭代器,通過while循環和for表達式對集合進行遍歷
11.10.2 應用案例
| package com.atguigu.chapter11.iterator
object IteratorDemo { def main(args: Array[String]): Unit = { val iterator = List(1, 2, 3, 4, 5).iterator // println("--------遍歷方式1 -----------------") while (iterator.hasNext) { println(iterator.next()) } println("--------遍歷方式2 for -----------------") for(enum <- iterator) { println(enum) // }
} }
|
11.10.3 對iterator的使用的說明
1) iterator 的構建實際是 AbstractIterator 的一個匿名子類,該子類提供了
/*
def iterator: Iterator[A] = new AbstractIterator[A] {
var these = self
def hasNext: Boolean = !these.isEmpty
def next(): A =
*/
2) 該AbstractIterator 子類提供了 hasNext next 等方法.
3) 因此,我們可以使用 while的方式,使用hasNext next 方法變量
11.11 擴展-流 Stream
11.11.1 基本說明
stream是一個集合。這個集合,可以用於存放無窮多個元素,但是這無窮個元素並不會一次性生產出來,而是需要用到多大的區間,就會動態的生產,末尾元素遵循lazy規則(即:要使用結果才進行計算的) 。
11.11.2 創建Stream對象
- 案例:
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1)
val stream1 = numsForm(1)
| package com.atguigu.chapter11.stream
object StreamDemo { def main(args: Array[String]): Unit = { //對下面的函數說明 //Stream 集合存放的數據類型是BigInt //numsForm 是自定義的一個函數,函數名是程序員指定的。 //創建的集合的第一個元素是 n , 后續元素生成的規則是 n + 1 //后續元素生成的規則是可以程序員指定的 ,比如 numsForm( n * 4)...
def numsForm(n: BigInt) : Stream[BigInt] = n #:: numsForm(n + 1) val stream1 = numsForm(1) println("stream1=" + stream1) //我希望在取一個流集合數據 println(stream1.tail) //(2,?) println("stream1=" + stream1) // (1,2,?) println("stream1.head=" + stream1.head)
//println("stream1.last" + stream1.last) //死循環 } }
|
- 說明
Stream 集合存放的數據類型是BigInt
numsForm 是自定義的一個函數,函數名是程序員指定的。
創建的集合的第一個元素是 n , 后續元素生成的規則是 n + 1
后續元素生成的規則是可以程序員指定的 ,比如 numsForm( n * 4)...
11.12 擴展-視圖 View
11.12.1 基本介紹
Stream的懶加載特性,也可以對其他集合應用view方法來得到類似的效果,具有如下特點:
view方法產出一個總是被懶執行的集合。
11.12.2 view不會緩存數據,每次都要重新計算
應用案例
請找到1-100 中,數字倒序排列 和它本身相同的所有數。
| package com.atguigu.chapter11.view
object ViewDemo { def main(args: Array[String]): Unit = {
//函數 將一個數字,原封不動的返回 def multiple(num: Int): Int = { num }
//判斷一個數字,交換順序后,是否相等 def eq(i: Int): Boolean = { i.toString.equals(i.toString.reverse) }
//說明: 沒有使用view val viewSquares1 = (1 to 100) .map(multiple) .filter(eq) println(viewSquares1) //(1,2,3,。。。。11,22)
//說明: 沒有使用view val viewSquares2 = (1 to 100) .view .map(multiple) .filter(eq) println("viewSquares2=" + viewSquares2) //(SeqView)lazy for (item <- viewSquares2) { print(item + ",") }
// //for (x <- viewSquares1) {} // //使用view // val viewSquares2 = (1 to 100) // .view // .map(multiple) // .filter(eq) // println(viewSquares2)
} }
|
11.13 擴展-線程安全的集合
11.14 擴展-並行集合
11.14.1 基本介紹
Scala為了充分使用多核CPU,提供了並行集合(有別於前面的串行集合),用於多核環境的並行計算。
- 主要用到的算法有:
Divide and conquer : 分治算法,Scala通過splitters,combiners等抽象層來實現,主要原理是將計算工作分解很多任務,分發給一些處理器去完成,並將它們處理結果合並返回
Work stealin算法,主要用於任務調度負載均衡(load-balancing),通俗點完成自己的所有任務之后,發現其他人還有活沒干完,主動(或被安排)幫他人一起干,這樣達到盡早干完的目的 - 應用案例
| package com.atguigu.chapter11.parallel
object ParallelDemo2 { def main(args: Array[String]): Unit = { val result1 = (0 to 100).map{case _ => Thread.currentThread.getName} val result2 = (0 to 100).par.map{case _ => Thread.currentThread.getName}.distinct println(result1) println(result2)
} }
|
11.15 擴展-操作符
11.15.1 基本介紹
這部分內容沒有必要刻意去理解和記憶,語法使用的多了,自然就會熟練的使用,該部分內容了解一下即可。
11.15.2 操作符擴展
1) 如果想在變量名、類名等定義中使用語法關鍵字(保留字),可以配合反引號反引號 [案例演示]
val `val` = 42
2) 中置操作符:A 操作符 B 等同於 A.操作符(B)
| val dog = new Dog dog.+(10) dog + 20
println(dog.age) // 40
class Dog { var age:Int = 10 def +(n:Int): Unit = { age += n } } |
3) 后置操作符:A操作符 等同於 A.操作符,如果操作符定義的時候不帶()則調用時不能加括號
| class Operate {
//定義函數/方法的時候,省略的() def ++ = "123"
}
//后置操作符 val oper = new Operate println(oper++) // println(oper.++) |
4) 前置操作符,+、-、!、~等操作符A等同於A.unary_操作符 [案例演示]
| val oper2 = new Operate2 !oper2 //
class Operate2 { // 聲明前置運算符 //unary :一元運算符 def unary_! = println("!!!!!!!") } |
5) 賦值操作符,A 操作符= B 等同於 A = A 操作符 B ,比如 A += B 等價 A = A + B
第 12 章
模式匹配
12.1 match
12.1.1 基本介紹
Scala中的模式匹配類似於Java中的switch語法,但是更加強大。
模式匹配語法中,采用match關鍵字聲明,每個分支采用case關鍵字進行聲明,當需要匹配時,會從第一個case分支開始,如果匹配成功,那么執行對應的邏輯代碼,如果匹配不成功,繼續執行下一個分支進行判斷。如果所有case都不匹配,那么會執行case _ 分支,類似於Java中default語句
12.1.2 Java swtich應用案例
12.1.3 scala的match應用案例
- 案例: 給你兩個數,根據運算符得到相應的結果,使用match-case
| package com.atguigu.chapter12.matchpart
object MatchDemo01 { def main(args: Array[String]): Unit = { /* 給你兩個數,根據運算符得到相應的結果,使用match-case */ val n1 = 10 val n2 = 20 var res = 0 val operChar = '+' operChar match { case '+' => { res = n1 + n2 print("xx") } case '-' => res = n1 - n2 case '*' => res = n1 * n2 case '/' => res = n1 / n2 case _ => println("你的運算符有誤..") } println("res=" + res) // 30 } } |
12.1.4 match的細節和注意事項
1) 如果所有case都不匹配,那么會執行case _ 分支,類似於Java中default語句
2) 如果所有case都不匹配,又沒有寫case _ 分支,那么會拋出MatchError
3) 每個case中,不用break語句,自動中斷case
4) 可以在match中使用其它類型(任意類型),而不僅僅是字符
5) => 等價於 java swtich 的 :
6) => 后面的代碼塊到下一個 case, 是作為一個整體執行,可以使用{} 擴起來,也可以不擴。
12.2 守衛
12.2.1 基本介紹
如果想要表達匹配某個范圍的數據,就需要在模式匹配中增加條件守衛
12.2.2 應用案例
| package com.atguigu.chapter12.matchpart
object MatchGuard { def main(args: Array[String]): Unit = { for (ch <- "+-3!") { //遍歷字符串 var sign = 0 var digit = 0 ch match { case '+' => sign = 1 case '-' => sign = -1 // 說明 // 1. 如果 ch.toString.equals("3") 為真,則表示匹配成功 case _ if ch.toString.equals("3") => digit = 3 case _ if ch.toInt > 4 => println("大於4") case _ => sign = 2 } println(ch + " " + sign + " " + digit) }
} }
|
12.2.3 課堂思考題
12.3 模式中的變量
12.3.1 基本介紹
如果在case關鍵字后跟變量名,那么match前表達式的值會賦給那個變量
12.3.2 案例
| package com.atguigu.chapter12.matchpart
object MatchVar { def main(args: Array[String]): Unit = { val ch = 'B' //說明 //1. match的前面是一個表達式(即任何有值的即可) ch+1+f1() match { // ch 變量 case '+' => println("ok~") //說明,當代碼走到 case mychar ,就會將 ch賦給mychar case mychar => println("ok~~~~~" + mychar) case _ => println ("ok~~") }
} def f1(): Char = { 'D' } }
|
12.4 類型匹配
12.4.1 基本介紹
可以匹配對象的任意類型,這樣做避免了使用isInstanceOf和asInstanceOf方法
12.4.2 應用案例
| package com.atguigu.chapter12.matchpart
object MatchType { def main(args: Array[String]): Unit = { //下面的代碼的作用是,根據 a的值不同,返回不同數據類型 val a = 3 val obj = if(a == 1) 1 else if(a == 2) "2" else if(a == 3) BigInt(3) else if(a == 4) Map("aa" -> 1) else if(a == 5) Map(1 -> "aa") else if(a == 6) Array(1, 2, 3) else if(a == 7) Array("aa", 1) else if(a == 8) Array("aa")
//使用類型匹配來進行操作 val result = obj match {
case a : Int => a //說明 case b : Map[String, Int] => "對象是一個字符串-數字的Map集合" //1. 先將 obj 賦給 b , b的名稱就是一個普通變量名,程序員指定 //2. Map[String, Int] 類型 //3. 如果類型匹配成功,就會執行 => 后面的代碼 case b : Map[String, Int] => "對象是一個字符串-數字的Map集合" case c : Map[Int, String] => "對象是一個數字-字符串的Map集合" case d : Array[String] => "對象是一個字符串數組" case e : Array[Int] => "對象是一個數字數組" case f : BigInt => Int.MaxValue case _ => "啥也不是" }
println("res=" + result)
} }
|
12.4.3 類型匹配注意事項
1) Map[String, Int] 和Map[Int, String]是兩種不同的類型,其它類推。
2) 在進行類型匹配時,編譯器會預先檢測是否有可能的匹配,如果沒有則報錯.
val obj = 10
val result = obj match {
case a : Int => a
case b : Map[String, Int] => "Map集合"
case _ => "啥也不是"
}
3) 如果 case _ 出現在match 中間,則表示隱藏變量名,即不使用,而不是表示默認匹配
| //使用類型匹配來進行操作 val result = obj match { //表示隱藏變量 case _ : Int => println("int被匹配") } |
12.5 匹配數組
12.5.1 基本介紹
1) Array(0) 匹配只有一個元素且為0的數組。
2) Array(x,y) 匹配數組有兩個元素,並將兩個元素賦值為x和y。當然可以依次類推Array(x,y,z) 匹配數組有3個元素的等等...
3) Array(0,_*) 匹配數組以0開始
12.5.2 應用案例
| package com.atguigu.chapter12.matchpart
object MatchArray { def main(args: Array[String]): Unit = { for (arr <- Array(Array(0), Array(1, 0), Array(0, 1, 0), Array(1, 1, 0), Array(1, 1, 0, 1),Array("hello", 90))) { // 對一個數組集合進行遍歷 val result = arr match { case Array(0) => "0" //匹配Array(0) 這個數組 // 說明 //1. 匹配有兩個元素的數組,然后將 將元素值賦給對應的x,y //2. => 使用xy case Array(x, y) => x + "=" + y //說明 //1. 以0開頭和數組 case Array(0, _*) => "以0開頭和數組" case _ => "什么集合都不是" } println("result = " + result) }
} }
|
12.6 匹配列表
12.6.1 應用案例
| package com.atguigu.chapter12.matchpart
object MatchList { def main(args: Array[String]): Unit = { //list是一個存放List集合的數組 //請思考,如果要匹配 List(88) 這樣的只含有一個元素的列表,並原值返回.應該怎么寫 for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0),List(88))) { val result = list match {
case 0 :: Nil => "0" //匹配List(0) case x :: y :: Nil => x + " " + y //匹配有兩個元素的List case 0 :: tail => "0 ..." //匹配以0開頭的,后面有0到多個元素的List case x :: Nil => x::Nil case _ => "something else"
} println(result) }
} }
|
12.7 匹配元組
12.7.1 應用案例
| package com.atguigu.chapter12.matchpart
object MatchTuple {
def main(args: Array[String]): Unit = { //對一個元組集合進行遍歷 for (pair <- Array((0, 1), (1, 0), (1, 1),(1,0,2))) { val result = pair match { // case (0, _) => "0 ..." //是第一個元素是0的元組 case (y, 0) => y // 匹配后一個元素是0的對偶元組 case (a,b) => a + " " + b case _ => "other" //.默認 } println(result) }
} }
|
12.8 對象匹配
12.8.1 基本介紹
對象匹配,什么才算是匹配呢?,規則如下:
1) case中對象的unapply方法(提取器)返回some集合則為匹配成功
2) 返回none集合則為匹配失敗
12.8.2 應用案例1
| package com.atguigu.chapter12.matchpart
object MatchObject { def main(args: Array[String]): Unit = { object Square { //我們要編寫出正確的構造器和對象提取器,前提是你的業務邏輯是正確 def unapply(z: Double): Option[Double] = Some(math.sqrt(z)) def apply(z: Double): Double = z * z } // 模式匹配使用: //val number: Double = 8.0 val n = Square(9.0) //構建了一個Square n match { //說明 // 1. 當case 后面有Square(n),調用 Square的 unapply,進行對象提取 // 注意:unapply方法的參數 unapply(z: Double)的z 是number // 2. 如果返回的unapply 返回的是Some,就認為匹配成功 // 3. 如果返回的是None ,就認為不成功 // 4. 匹配成功后,會把返回值,賦給 Square(n) 的n case Square(n) => println("匹配到=" + n) case _ => println("nothing matched") } } }
|
12.8.3 應用案例1的小結
1) 構建對象時apply會被調用 ,比如 val n1 = Square(5)
2) 當將 Square(n) 寫在 case 后時[case Square(n) => xxx],會默認調用unapply 方法(對象提取器)
3) number 會被 傳遞給def unapply(z: Double) 的 z 形參
4) 如果返回的是Some集合,則unapply提取器返回的結果會返回給 n 這個形參
5) case中對象的unapply方法(提取器)返回some集合則為匹配成功
6) 返回none集合則為匹配失敗
12.8.4 應用案例2
| package com.atguigu.chapter12.matchpart
object MatchObject02 { def main(args: Array[String]): Unit = {
val namesString = "Alice,Bob" //說明 // 1. 對namesString 進行對象匹配
namesString match { //1. 這里Names(first, second, third)就會調用 unapplySeq //2. unapplySeq 如果返回了some集合,就會一次將結果賦給first, second, third //3. unapplySeq 返回的some集合中的結果的個數要和 case 提取的參數匹配 //4. 返回none 表示匹配失敗,即對象提取失敗 case Names(first, second, third) => { println("the string contains three people's names ok!") // 打印字符串 println(s"$first $second $third") } case _ => println("nothing matched") }
} }
object Names { //條件滿足,返回 Some,條件不滿足,返回None def unapplySeq(str: String): Option[Seq[String]] = { if (str.contains(",")) Some(str.split(",")) else None } }
|
12.8.5 應用案例2的小結
1) 當case 后面的對象提取器方法的參數為多個,則會默認調用def unapplySeq() 方法
2) 如果unapplySeq返回是Some,獲取其中的值,判斷得到的sequence中的元素的個數是否是三個,如果是三個,則把三個元素分別取出,賦值給first,second和third
3) 其它的規則不變.
12.9 變量聲明中的模式
12.9.1 基本介紹
match中每一個case都可以單獨提取出來,意思是一樣的.
12.9.2 應用案例
| package com.atguigu.chapter12.matchpart
object VarPattern { def main(args: Array[String]): Unit = {
val (x, y) = (1, 2) // x = 1 y = 2 val (q, r) = BigInt(10) /% 3 //說明 q = BigInt(10) / 3 r = BigInt(10) % 3 val arr = Array(1, 7, 2, 9) val Array(first, second, _*) = arr println(first, second) println("q=" + q + " r=" + r) //案例演示+說明
} }
|
