一、Scala入門
1.1 概述
1.1.1 為什么學習Scala
主要基於以下幾個原因:
1) 大數據主要的批處理計算引擎框架Spark是基於Scala語言開發的
2) 大數據主要的流式計算引擎框架Flink也提供了Scala相應的API
3) 大數據領域中函數式編程的開發效率更高,更直觀,更容易理解
1.1.2 Java and Scala
Martin Odersky是狂熱的編譯器愛好者,長時間的編程后,希望開發一種語言,能夠讓寫程序的過程變得簡單,高效,所以當接觸到Java語言后,感受到了這門語言的魅力,決定將函數式編程語言的特性融合到Java語言中,由此產生了2門語言(Pizza & Scala),這兩種語言極大地推動了Java語言的發展
l JDK1.5的泛型,增強for循環,自動類型轉換等都是從Pizza語言引入的新特性
l JDK1.8的類型推斷,λ(lambda)表達式是從Scala語言引入的新特性由上可知,Scala語言是基於Java開發的,所以其編譯后的文件也是字節碼文件,並可以運行在JVM中。
1.2 快速使用
1.2.1 Scala環境安裝
1) 安裝JDK 1.8
2) 安裝Scala2.11
l 解壓文件:scala-2.11.8.zip,解壓目錄要求無中文無空格
l 配置環境變量
3) 環境測試
如果出現如下窗口內容,表示環境安裝成功
1.2.2 Scala插件安裝
默認情況下IDEA不支持Scala的開發,需要安裝Scala插件。
默認情況,IDEA中創建項目時也不支持Scala的開發,需要添加Scala框架的支持。
1.2.3 Hello Scala案例
1) 創建Maven項目
2) 增加Scala框架支持
3) 創建類
在main文件目錄中創建Scala類:com.atguigu.bigdata.scala.HelloScala
package com.atguigu.bigdata.scala object HelloScala { def main(args: Array[String]): Unit = { println("Hello Scala") } }
4) 代碼解析
l object
l def
l args : Array[String]
l Unit
l println
如果只是通過源碼來解析,並不能了解其實現的原理。scala語言是基於Java語言開發的,所以也會編譯為class文件,所以可以通過反編譯工具jd-gui.exe查看scala編譯后的代碼和java語言之間的關系,來掌握具體代碼的實現原理
5) 源碼關聯
在使用Scala過程中,為了搞清楚Scala底層的機制,需要查看源碼,那么就需要關聯和查看Scala的源碼包。
二、 變量和數據類型
2.1 注釋
Scala注釋使用和Java完全一樣。注釋是一個程序員必須要具有的良好編程習慣。將自己的思想通過注釋先整理出來,再用代碼去體現。
2.1.1 單行注釋
package com.atguigu.bigdata.scala object ScalaComment{ def main(args: Array[String]): Unit = { // 單行注釋 } }
2.1.2 多行注釋
package com.atguigu.bigdata.scala object ScalaComment{ def main(args: Array[String]): Unit = { /* 多行注釋 */ } }
2.1.3 文檔注釋
package com.atguigu.bigdata.scala /** * doc注釋 */ object ScalaComment{ def main(args: Array[String]): Unit = { } }
2.2 變量
變量是一種使用方便的占位符,用於引用計算機內存地址,變量創建后會占用一定的內存空間。基於變量的數據類型,操作系統會進行內存分配並且決定什么將被儲存在保留內存中。因此,通過給變量分配不同的數據類型,你可以在這些變量中存儲整數,小數或者字母。
2.2.1 語法聲明
變量的類型在變量名之后等號之前聲明。
object ScalaVariable { def main(args: Array[String]): Unit = { // var | val 變量名 :變量類型 = 變量值 // 用戶名稱 var username : String = "zhangsan" // 用戶密碼 val userpswd : String = "000000" } }
變量的類型如果能夠通過變量值推斷出來,那么可以省略類型聲明,這里的省略,並不是不聲明,而是由Scala編譯器在編譯時自動聲明編譯的。
object ScalaVariable { def main(args: Array[String]): Unit = { // 因為變量值為字符串,又因為Scala是靜態類型語言,所以及時不聲明類型 // Scala也能正確的判斷出變量的類型,這體現了Scala語言的簡潔的特性。 var username = "zhangsan" val userpswd = "000000" } }
2.2.2 變量初始化
Java語法中變量在使用前進行初始化就可以,但是Scala語法中是不允許的,必須顯示進行初始化操作。
object ScalaVariable { def main(args: Array[String]): Unit = { var username // Error val username = "zhangsan" // OK } }
2.2.3 可變變量
值可以改變的變量,稱之為可變變量,但是變量類型無法發生改變, Scala中可變變量使用關鍵字var進行聲明
object ScalaVariable { def main(args: Array[String]): Unit = { // 用戶名稱 var username : String = "zhangsan" username = "lisi" // OK username = true // Error } }
2.2.4 不可變變量
值一旦初始化后無法改變的變量,稱之為不可變變量。Scala中不可變變量使用關鍵字val進行聲明, 類似於Java語言中的final關鍵字
object ScalaVariable { def main(args: Array[String]): Unit = { // 用戶名稱 val username : String = "zhangsan" username = "lisi" // Error username = true // Error } }
2.3 標識符
Scala 可以使用兩種形式的標志符,字符數字和符號。
l 字符數字使用字母或是下划線開頭,后面可以接字母或是數字,符號"$"在 Scala 中也看作為字母。然而以"$"開頭的標識符為保留的 Scala 編譯器產生的標志符使用,應用程序應該避免使用"$"開始的標識符,以免造成沖突。
l Scala 的命名規則采用和 Java 類似的 camel 命名規則,首字符小寫,比如 toString。類名的首字符還是使用大寫。此外也應該避免使用以下划線結尾的標志符以避免沖突。
l Scala 內部實現時會使用轉義的標志符,比如:-> 使用 $colon$minus$greater 來表示這個符號。因此如果你需要在 Java 代碼中訪問:->方法,你需要使用 Scala 的內部名稱 $colon$minus$greater。
// 和Java一樣的標識符命名規則 val name = "zhangsan" // OK val name1 = "zhangsan0" // OK //val 1name = "zhangsan0" // Error val name$ = "zhangsan1" // OK val $name = "zhangsan2" // OK val name_ = "zhangsan3" // OK val _name = "zhangsan4" // OK val $ = "zhangsan5" // OK val _ = "zhangsan6" // OK //val 1 = "zhangsan6" // Error //val true = "zhangsan6" // Error // 和Java不一樣的標識符命名規則 val + = "lisi" // OK val - = "lisi" // OK val * = "lisi" // OK val / = "lisi" // OK val ! = "lisi" // OK //val @ = "lisi" // Error val @@ = "lisi" // OK //val # = "lisi" // Error val ## = "lisi" // OK val % = "lisi" // OK val ^ = "lisi" // OK val & = "lisi" // OK //val ( = "lisi" // Error //val ( = "lisi" // Error //val ) = "lisi" // Error //val = = "lisi" // Error val == = "lisi" // OK //val [ = "lisi" // Error //val ] = "lisi" // Error //val : = "lisi" // Error val :: = "lisi" // OK //val ; = "lisi" // Error //val ' = "lisi" // Error //val " = "lisi" // Error val "" = "lisi" // OK val < = "lisi" // OK val > = "lisi" // OK val ? = "lisi" // OK val | = "lisi" // OK val \ = "lisi" // OK //val ` = "lisi" // Error val ~ = "lisi" // OK val :-> = "wangwu" // OK val :-< = "wangwu" // OK // 切記,能聲明和能使用是兩回事
Scala 中的標識符也不能是關鍵字或保留字,那么Scala中有多少關鍵字或保留字呢?
2.4 字符串
在 Scala 中,字符串的類型實際上是 Java String,它本身沒有 String 類。
在 Scala 中,String 是一個不可變的對象,所以該對象不可被修改。這就意味着你如果修改字符串就會產生一個新的字符串對象。
object ScalaString { def main(args: Array[String]): Unit = { val name : String = "scala" val subname : String = name.substring(0,2) } }
2.4.1 字符串連接
object ScalaString { def main(args: Array[String]): Unit = { // 字符串連接 println("Hello " + name) } }
2.4.2 傳值字符串
object ScalaString { def main(args: Array[String]): Unit = { // 傳值字符串(格式化字符串) printf("name=%s\n", name) } }
2.4.3 插值字符串
object ScalaString { def main(args: Array[String]): Unit = { // 插值字符串 // 將變量值插入到字符串 println(s"name=$name") } }
2.4.4 多行字符串
object ScalaString { def main(args: Array[String]): Unit = { // 多行格式化字符串 // | 默認頂格符 println( s""" | Hello | ${name} """.stripMargin) } }
2.5 輸入輸出
2.5.1 輸入
l 從屏幕中獲取輸入
object ScalaIn { def main(args: Array[String]): Unit = { // 標准化屏幕輸入 val age : Int = scala.io.StdIn.readInt() println(age) } }
l 從文件中獲取輸入
object ScalaIn { def main(args: Array[String]): Unit = { scala.io.Source.fromFile("input/user.json").foreach( line => { print(line) } ) } }
2.5.2 輸出
Scala進行文件寫操作,用的都是 java中的I/O類
object ScalaOut {
def main(args: Array[String]): Unit = {
val writer = new PrintWriter(new File("output/test.txt" ))
writer.write("Hello Scala")
writer.close()
}
}
2.6 數據類型
Scala與Java有着相同的數據類型,但是又有不一樣的地方
2.6.1 Java數據類型
Java的數據類型包含基本類型和引用類型
l 基本類型:byte,short,char,int,long,float,double,boolean
l 引用類型:Object,數組,字符串,包裝類,集合,POJO對象等
2.6.1 Scala數據類型
Scala是完全面向對象的語言,所以不存在基本數據類型的概念,有的只是任意值對象(AnyVal)和任意引用對象(AnyRef)
2.7 類型轉換
2.7.1 自動類型轉化(隱式轉換)
object ScalaDataType { def main(args: Array[String]): Unit = { val b : Byte = 10 val s : Short = b val i : Int = s val lon : Long = i } }
2.7.2 強制類型轉化
l Java語言
int a = 10 byte b = (byte)a
l Scala語言
var a : Int = 10
Var b : Byte = a.toByte
2.7.3 字符串類型轉化
scala是完全面向對象的語言,所有的類型都提供了toString方法,可以直接轉換為字符串
lon.toString
三、運算符
scala運算符的使用和Java運算符的使用基本相同,只有個別細節上不同。
3.1 算數運算符
3.2 關系運算符
3.3 賦值運算符
3.4 邏輯運算符
假定變量 A 為 1,B 為 0
3.5 位運算符
如果指定 A = 60; 及 B = 13; 兩個變量對應的二進制為
3.6 運算符本質
在Scala中其實是沒有運算符的,所有運算符都是方法。
l scala是完全面向對象的語言,所以數字其實也是對象
l 當調用對象的方法時,點.可以省略
l 如果函數參數只有一個,或者沒有參數,()可以省略
object ScalaOper { def main(args: Array[String]): Unit = { val i : Int = 10 val j : Int = i.+(10) val k : Int = j +(20) val m : Int = k + 30 println(m) } }
四、流程控制
Scala程序代碼和所有編程語言代碼一樣,都會有特定的執行流程順序,默認情況下是順序執行,上一條邏輯執行完成后才會執行下一跳邏輯,其間也可以根據某些條件執行不同的分支邏輯代碼。
4.1 分支控制
讓程序有選擇的的執行,分支控制有三種:單分支、雙分支、多分支
if(布爾表達式) { // 如果布爾表達式為 true 則執行該語句塊 }
如果布爾表達式為 true 則執行大括號內的語句塊,否則跳過大括號內的語句塊,執行大括號之后的語句塊。
4.1.1 單分支
IF...ELSE 語句是通過一條或多條語句的執行結果(true或者false)來決定執行的代碼塊
object ScalaBranch { def main(args: Array[String]): Unit = { val b = true if ( b ) { println("true") } } }
4.1.2 雙分支
if(布爾表達式) { // 如果布爾表達式為 true 則執行該語句塊 } else { // 如果布爾表達式為 false 則執行該語句塊 }
如果布爾表達式為 true 則執行接着的大括號內的語句塊,否則執行else后的大括號內的語句塊。
object ScalaBranch { def main(args: Array[String]): Unit = { val b = true if ( b ) { println("true") } else { println("false") } } }
雙分支結構在某些情況下也可以代替Java中的三元運算符
4.1.3 多分支
if(布爾表達式1) { // 如果布爾表達式1為 true,則執行該語句塊 } else if ( 布爾表達式2 ) { // 如果布爾表達式2為 true,則執行該語句塊 }... } else { // 上面條件都不滿足的場合,則執行該語句塊 }
實現一個小功能:輸入年齡,如果年齡小於18歲,則輸出“童年”。如果年齡大於等於18且小於等於30,則輸出“青年”,如果年齡大於30小於等於50,則輸出”中年”,否則,輸出“老年”。
object ScalaBranch { def main(args: Array[String]): Unit = { val age = 30 if ( age < 18 ) { println("童年") } else if ( age <= 30 ) { println("青年") } else if ( age <= 50 ) { println("中年") } else { println("老年") } } }
實際上,Scala中的表達式都是有返回值的,所以上面的小功能還有其他的實現方式
object ScalaBranch { def main(args: Array[String]): Unit = { val age = 30 val result = if ( age < 18 ) { "童年" } else if ( age <= 30 ) { "青年" } else if ( age <= 50 ) { "中年" } else { "老年" } println(result) } }
4.2 循環控制
有的時候,我們可能需要多次執行同一塊代碼。一般情況下,語句是按順序執行的:函數中的第一個語句先執行,接着是第二個語句,依此類推。編程語言提供了更為復雜執行路徑的多種控制結構。循環語句允許我們多次執行一個語句或語句組
Scala語言提供了以下幾種循環類型
4.2.1 for循環
1) 基本語法
for ( 循環變量 <- 數據集 ) { 循環體 }
這里的數據集可以是任意類型的數據集合,如字符串,集合,數組等
object ScalaLoop { def main(args: Array[String]): Unit = { for ( i <- Range(1,5) ) { // 范圍集合 println("i = " + i ) } for ( i <- 1 to 5 ) { // 包含5 println("i = " + i ) } for ( i <- 1 until 5 ) { // 不包含5 println("i = " + i ) } } }
2) 循環守衛
循環時可以增加條件來決定是否繼續循環體的執行,這里的判斷條件我們認為是循環的守衛
object ScalaLoop {
def main(args: Array[String]): Unit = {
for ( i <- Range(1,5) if i != 3 ) {
println("i = " + i )
}
}
}
3) 循環步長
scala的集合也可以設定循環的增長幅度,也就是所謂的步長step
object ScalaLoop { def main(args: Array[String]): Unit = { for ( i <- Range(1,5,2) ) { println("i = " + i ) } for ( i <- 1 to 5 by 2 ) { println("i = " + i ) } } }
4) 循環嵌套
object ScalaLoop { def main(args: Array[String]): Unit = { for ( i <- Range(1,5); j <- Range(1,4) ) { println("i = " + i + ",j = " + j ) } for ( i <- Range(1,5) ) { for ( j <- Range(1,4) ) { println("i = " + i + ",j = " + j ) } } } }
請好好體會上面兩種嵌套方式的區別
5) 引入變量
object ScalaLoop { def main(args: Array[String]): Unit = { for ( i <- Range(1,5); j = i - 1 ) { println("j = " + j ) } } }
6) 返回值
scala所有的表達式都是有返回值的。但是這里的返回值並不一定都是有值的喲。
如果希望for循環表達式的返回值有具體的值,需要使用關鍵字yield
object ScalaLoop { def main(args: Array[String]): Unit = { val result = for ( i <- Range(1,5) ) yield { i * 2 } println(result) } }
4.2.2 while循環
1) 基本語法
當循環條件表達式返回值為true時,執行循環體代碼
while( 循環條件表達式 ) { 循環體 }
一種特殊的while循環就是,先執行循環體,再判斷循環條件是否成立
do { 循環體 } while ( 循環條件表達式 )
2) while循環
object ScalaLoop { def main(args: Array[String]): Unit = { var i = 0 while ( i < 5 ) { println(i) i += 1 } } }
3) do...while循環
object ScalaLoop { def main(args: Array[String]): Unit = { var i = 5 do { println(i) } while ( i < 5 ) } }
4.2.3 循環中斷
scala是完全面向對象的語言,所以無法使用break關鍵字這種方式來中斷循環邏輯,而是采用了函數式編程的方式代替了循環語法中的break和continue
object ScalaLoop { def main(args: Array[String]): Unit = { scala.util.control.Breaks.breakable { for ( i <- 1 to 5 ) { if ( i == 3 ) { scala.util.control.Breaks.break } println(i) } } } }
4.2.4 嵌套循環
循環中有循環,就是嵌套循環。通過嵌套循環可以實現特殊的功能,比如說九九乘法表,
這里我們來實現一個九層妖塔的小功能
Java版本
public class printTower { public static void main(String[] args) { for(int i = 1;i <= 9; i++){ for(int j = 0;j <= 9 - i;j++){ System.out.print(" "); } for(int j =0;j < 2*i-1;j++){ System.out.print("*"); } System.out.println( ); } } }
Scala版本
object printTower { def main(args: Array[String]): Unit = { //方式一: for(i <- 1 to 9){ for(j <- 1 to 9-i){ print(" ") } for(j <- 1 to 2*i-1){ print("*") } println() } for(i <- 1 to 18 by 2;j <- 2*i-1){ println("" * j + "*" * i) } //方式二: for ( i <- 1 to 18 by 2;j = (18-i)/2 ){ println( " " * j + "*" * i ) } } }
五、函數式編程
在之前的學習中,我們一直學習的就是面向對象編程,所以解決問題都是按照面向對象的方式來處理的。比如用戶登陸,但是接下來,我們會學習函數式編程,采用函數式編程的思路來解決問題。scala編程語言將函數式編程和面向對象編程完美地融合在一起了。
l 面向對象編程
分解對象,行為,屬性,然后通過對象的關系以及行為的調用來解決問題
l 函數式編程
將問題分解成一個一個的步驟,將每個步驟進行封裝(函數),通過調用這些封裝好的步驟,解決問題。
5.1 基礎函數編程
5.1.1 基本語法
[修飾符] def 函數名 ( 參數列表 ) [:返回值類型] = { 函數體 } private def test( s : String ) : Unit = { println(s) }
5.1.2 函數&方法
l scala 有方法與函數的概念,二者在語義上的區別很小。scala 方法是類的一部分,而函數是一個對象可以賦值給一個變量。換句話來說在類中定義的函數即是方法。scala 中的方法跟 Java 的類似,方法是組成類的一部分。scala 中的函數則是一個完整的對象
l Scala中的方法和函數一般不好區分,所以簡單的理解就是:方法也是函數。只不過類中聲明的函數就稱之為方法,而類中的方法是有重載和重寫的。其他場合聲明的就是函數了,那可就沒有重載和重寫的概念哦,而且函數是可以嵌套聲明使用的,但是方法就沒有辦法聲明方法了。
5.1.3 函數定義
1) 無參,無返回值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun1(): Unit = { println("函數體") } fun1() } }
2) 無參,有返回值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun2(): String = { "zhangsan" } println( fun2() ) } }
3) 有參,無返回值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun3( name:String ): Unit = { println( name ) } fun3("zhangsan") } }
4) 有參,有返回值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun4(name:String): String = { "Hello " + name } println( fun4("zhangsan") ) } }
5) 多參,無返回值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun5(hello:String, name:String): Unit = { println( hello + " " + name ) } fun5("Hello", "zhangsan") } }
6) 多參,有返回值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun6(hello:String, name:String): String = { hello + " " + name } println( fun6("Hello", "zhangsan")) } }
5.1.4 函數參數
1) 可變參數
object ScalaFunction { def main(args: Array[String]): Unit = { def fun7(names:String*): Unit = { println(names) } fun7() fun7( "zhangsan" ) fun7( "zhangsan", "lisi" ) } }
可變參數不能放置在參數列表的前面,一般放置在參數列表的最后
object ScalaFunction { def main(args: Array[String]): Unit = { // Error //def fun77(names:String*, name:String): Unit = { //} def fun777( name:String, names:String* ): Unit = { println( name ) println( names ) } } }
2) 參數默認值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun8( name:String, password:String = "000000" ): Unit = { println( name + "," + password ) } fun8("zhangsan", "123123") fun8("zhangsan") } }
3) 帶名參數
object ScalaFunction { def main(args: Array[String]): Unit = { def fun9( password:String = "000000", name:String ): Unit = { println( name + "," + password ) } fun9("123123", "zhangsan" ) fun9(name="zhangsan") } }
5.1.5 函數至簡原則
所謂的至簡原則,其實就是Scala的編譯器為了提高開發效率。幫助我們將函數聲明中能簡化的地方全部都進行了簡化。也就是說將函數聲明中那些能省的地方全部都省掉。所以簡單來說就是:能省則省
1) 省略return關鍵字
object ScalaFunction { def main(args: Array[String]): Unit = { def fun1(): String = { return "zhangsan" } def fun11(): String = { "zhangsan" } } }
2) 省略花括號
object ScalaFunction { def main(args: Array[String]): Unit = { def fun2(): String = "zhangsan" } }
3) 省略返回值類型
object ScalaFunction { def main(args: Array[String]): Unit = { def fun3() = "zhangsan" } }
4) 省略參數列表
object ScalaFunction { def main(args: Array[String]): Unit = { def fun4 = "zhangsan" } }
5) 省略等號
如果函數體中有明確的return語句,那么返回值類型不能省略
object ScalaFunction { def main(args: Array[String]): Unit = { def fun5(): String = { return "zhangsan" } println(fun5()) } }
如果函數體返回值類型明確為Unit, 那么函數體中即使有return關鍵字也不起作用
object ScalaFunction { def main(args: Array[String]): Unit = { def fun5(): Unit = { return "zhangsan" } println(fun5()) } }
如果函數體返回值類型聲明為Unit, 但是又想省略,那么此時就必須連同等號一起省略
object ScalaFunction { def main(args: Array[String]): Unit = { def fun5() { return "zhangsan" } println(fun5()) } }
6) 省略名稱和關鍵字
object ScalaFunction { def main(args: Array[String]): Unit = { () => { println("zhangsan") } } }
5.2 高階函數編程
5.2.1 函數作為值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun1(): String = { "zhangsan" } val a = fun1 val b = fun1 _ println(a) println(b) } }
5.2.2 函數作為參數
object ScalaFunction { def main(args: Array[String]): Unit = { def fun2( i:Int ): Int = { i * 2 } def fun22( f : Int => Int ): Int = { f(10) } println(fun22(fun2)) } }
5.2.3 函數作為返回值
object ScalaFunction { def main(args: Array[String]): Unit = { def fun3( i:Int ): Int = { i * 2 } def fun33( ) = { fun3 _ } println(fun33()(10)) } }
5.2.4 匿名函數
object ScalaFunction { def main(args: Array[String]): Unit = { def fun4( f:Int => Int ): Int = { f(10) } println(fun4((x:Int)=>{x * 20})) println(fun4((x)=>{x * 20})) println(fun4((x)=>x * 20)) println(fun4(x=>x * 20)) println(fun4(_ * 20)) } }
5.2.5 閉包
object ScalaFunction { def main(args: Array[String]): Unit = { def fun5() = { val i = 20 def fun55() = { i * 2 } fun55 _ } }
}
5.2.6 函數柯里化
object ScalaFunction { def main(args: Array[String]): Unit = { def fun6(i:Int)(j:Int) = { i * j } } }
5.2.7 控制抽象
object ScalaFunction { def main(args: Array[String]): Unit = { def fun7(op: => Unit) = { op } fun7{ println("xx") } } }
5.2.8 遞歸函數
object ScalaFunction { def main(args: Array[String]): Unit = { def fun8(j:Int):Int = { if ( j <= 1 ) { 1 } else { j * fun8(j-1) } } println(fun8(5)) } }
5.2.9 惰性函數
當函數返回值被聲明為lazy時,函數的執行將被推遲,直到我們首次對此取值,該函數才會執行。這種函數我們稱之為惰性函數。
object ScalaFunction { def main(args: Array[String]): Unit = { def fun9(): String = { println("function...") "zhangsan" } lazy val a = fun9() println("----------") println(a) } }
六、面向對象編程
Scala是一門完全面向對象的語言,摒棄了Java中很多不是面向對象的語法。雖然如此,但其面向對象思想和Java的面向對象思想還是一致的
6.1 基礎面向對象編程
6.1.1 包
1) 基本語法
Scala中基本的package包語法和Java完全一致
package com.atguigu.bigdata.scala
2) 擴展語法
Java中package包的語法比較單一,Scala對此進行擴展
l Scala中的包和類的物理路徑沒有關系
l package關鍵字可以嵌套聲明使用
package com package atguigu { package bigdata { package scala { object ScalaPackage { def test(): Unit = { println("test...") } } } } }
l 子包可以直接訪問父包中的內容,而無需import
package com package atguigu { package bigdata { class Test { } package scala { object ScalaPackage { def test(): Unit = { new Test() } } } } }
l Scala中package也可以看作對象,並聲明屬性和函數
package com package object atguigu { val name : String = "zhangsan" def test(): Unit = { println( name ) } } package atguigu { package bigdata { package scala { object ScalaPackage { def test(): Unit = { } } } } }
6.1.2 導入
1) 基本語法
Scala中基本的import導入語法和Java完全一致
import java.util.List import java.util._ // Scala中使用下划線代替Java中的星號
2) 擴展語法
Java中import導入的語法比較單一,Scala對此進行擴展
l Scala中的import語法可以在任意位置使用
object ScalaImport{ def main(args: Array[String]): Unit = { import java.util.ArrayList new ArrayList() } }
l Scala中可以導包,而不是導類
object ScalaImport{ def main(args: Array[String]): Unit = { import java.util new util.ArrayList() } }
l Scala中可以在同一行中導入多個類,簡化代碼
import java.util.{List, ArrayList}
l Scala中可以屏蔽某個包中的類
import java.util._ import java.sql.{ Date=>_, Array=>_, _ }
l Scala中可以給類起別名,簡化使用
import java.util.{ArrayList=>AList} object ScalaImport{ def main(args: Array[String]): Unit = { new AList() } }
l Scala中可以使用類的絕對路徑而不是相對路徑
import _root_.java.util.ArrayList
l 默認情況下,Scala中會導入如下包和對象
import java.lang._ import scala._ Import scala.Predef._
6.1.3 類
Java中類可以看成一個模板,而對象可以看成是根據模板所創建的具體事物
1) 基本語法
// 聲明類:訪問權限 class 類名 { 類主體內容 } class User { } // 對象:new 類名(參數列表) new User()
2) 擴展語法
Scala中一個源文件中可以聲明多個類
6.1.4 屬性
1) 基本語法
class User { var name : String = _ // 類屬性其實就是類變量 var age : Int = _ // 下划線表示類的默認初始化 }
2) 擴展語法
Scala中的屬性其實在編譯后也是方法
class User { var name : String = _ val age : Int = 30 private val email : String = _ @BeanPropetry var address : String = _ }
6.1.5 訪問權限
Scala中的訪問權限和Java中的訪問權限類似,但是又有區別:
private : 私有訪問權限 private[包名]: 包訪問權限 Protected : 受保護權限
6.1.6 方法
Scala中的類的方法其實就是函數,所以聲明方式完全一樣,但是使用方式必須使用對象
object ScalaMethod{ def main(args: Array[String]): Unit = { val user = new User user.login("zhangsan", "000000") } } class User { def login( name:String, password:String ): Boolean = { false } }
6.1.7 對象
Scala中的對象和Java是類似的
val | var 對象名 [:類型] = new 類型() var user : User = new User()
6.1.8 構造方法
和Java一樣,Scala構造對象也需要調用構造方法,並且可以有任意多個構造方法.但是Scala中構造方法主要分為2大類:主構造函數和輔助構造函數
class User() { // 主構造函數 var username : String = _ def this( name:String ) { // 輔助構造函數 this() // 輔助構造函數應該直接或間接調用主構造函數 username = name } def this( name:String, password:String ) { this(name) // 構造器調用其他另外的構造器,要求被調用構造器必須提前聲明 } }
6.2 高階面向對象編程
6.2.1 繼承
和Java一樣,Scala的繼承也是單繼承,且使用extends關鍵字。
class Person { } class User extends Person { }
構造對象時需要考慮構造方法的順序
6.2.2 封裝
封裝就是把抽象出的數據和對數據的操作封裝在一起,數據被保護在內部,程序的其它部分只有通過被授權的操作(成員方法),才能對數據進行操作。
1) 將屬性進行私有化
2) 提供一個公共的set方法,用於對屬性賦值
3) 提供一個公共的get方法,用於獲取屬性的值
6.2.3 抽象
l Scala將一個不完整的類稱之為抽象類。
abstract class Person { }
l Scala中如果一個方法只有聲明而沒有實現,那么是抽象方法
abstract class Person { def test():Unit }
l Scala中如果一個屬性值只有聲明沒有初始化,那么是抽象屬性
abstract class Person { var name:String }
l 子類如果繼承抽象類,必須實現抽象方法或補全抽象屬性,否則也為抽象
abstract class Person { var name:String } class User extends Person { var name : String = "zhangsan" }
6.2.4 單例對象
l 所謂的單例對象,就是在程序運行過程中,指定類的對象只能創建一個,而不能創建多個。這樣的對象可以由特殊的設計方式獲得,也可以由語言本身設計得到,比如object伴生對象
l Scala語言是完全面向對象的語言,所以並沒有靜態的操作(即在Scala中沒有靜態的概念)。但是為了能夠和Java語言交互(因為Java中有靜態概念),就產生了一種特殊的對象來模擬類對象,該對象為單例對象。若單例對象名與類名一致,則稱該單例對象這個類的伴生對象,這個類的所有“靜態”內容都可以放置在它的伴生對象中聲明。
class User { // 伴生類 } object User { // 伴生對象 def apply() = new User() // 構造伴生類對象 } ... val user1 = new User()// 通過構造方法創建對象 val user2 = User() // 通過伴生對象構建伴生類對象
6.2.5 特質
Scala將多個類的相同特征從類中剝離出來,形成一個獨立的語法結構,稱之為“特質”(特征)。這種方式在Java中稱之為接口,但是Scala中沒有接口的概念。
Scala中采用特殊的關鍵字trait來聲明特質, 如果一個類符合某一個特征,那么就可以將特質“混入”到類中。
1) 基本語法
trait 特質名稱
class 類名 extends 父類(特質1) with 特質2 with特質3
trait Operator { } trait DB{ } class MySQL extends Operator with DB{ }
2) 動態混入
object ScalaTrait{ def main(args: Array[String]): Unit = { val mysql = new MySQL with Operator mysql.insert() } } trait Operator { def insert(): Unit = { println("insert data...") } } class MySQL { }
3) 初始化疊加
object ScalaTrait{ def main(args: Array[String]): Unit = { val mysql = new MySQL } } trait Operator { println("operator...") } trait DB { println("db...") } class MySQL extends DB with Operator{ println("mysql...") }
4) 功能疊加
object ScalaTrait { def main(args: Array[String]): Unit = { val mysql: MySQL = new MySQL mysql.operData() } } trait Operate{ def operData():Unit={ println("操作數據。。") } } trait DB extends Operate{ override def operData(): Unit = { print("向數據庫中。。") super.operData() } } trait Log extends Operate{ override def operData(): Unit = { super.operData() } } class MySQL extends DB with Log { }
6.2.6 擴展
l 類型檢查和轉換
class Person{ } object Person { def main(args: Array[String]): Unit = { val person = new Person //(1)判斷對象是否為某個類型的實例 val bool: Boolean = person.isInstanceOf[Person] if ( bool ) { //(2)將對象轉換為某個類型的實例 val p1: Person = person.asInstanceOf[Person] println(p1) } //(3)獲取類的信息 val pClass: Class[Person] = classOf[Person] println(pClass) } }
l 枚舉類和應用類
object Test { def main(args: Array[String]): Unit = { println(Color.RED) } } // 枚舉類 object Color extends Enumeration { val RED = Value(1, "red") val YELLOW = Value(2, "yellow") val BLUE = Value(3, "blue") } // 應用類 object AppTest extends App { println("application"); }
l Type定義新類型
使用type關鍵字可以定義新的數據數據類型名稱,本質上就是類型的一個別名
object Test { def main(args: Array[String]): Unit = { type S =String var v : S = "abc" } }
七、集合
7.1 簡介
Scala的集合有三大類:序列Seq、集Set、映射Map,所有的集合都擴展自Iterable特質。對於幾乎所有的集合類,Scala都同時提供了可變和不可變的版本,分別位於以下兩個包
可變集合可以在適當的地方被更新或擴展。這意味着你可以修改,添加,移除一個集合的元素。而不可變集合類,相比之下,永遠不會改變。不過,你仍然可以模擬添加,移除或更新操作。但是這些操作將在每一種情況下都返回一個新的集合,同時使原來的集合不發生改變。
可變集合和不可變集合,如何區分呢?我們一般可以根據集合所在包名進行區分:
l scala.collection.immutable
l scala.collection.mutable
7.2 數組
7.2.1 不可變數組
1) 基本語法
object ScalaCollection{ def main(args: Array[String]): Unit = { //(1)數組定義 val arr01 = new Array[Int](4) println(arr01.length) // 4 //(2)數組賦值 //(2.1)修改某個元素的值 arr01(3) = 10 val i = 10 arr01(i/3) = 20 //(2.2)采用方法的形式修改數組的值 arr01.update(0,1) //(3)遍歷數組 //(3.1)查看數組 println(arr01.mkString(",")) //(3.2)普通遍歷 for (i <- arr01) { println(i) } //(3.3)簡化遍歷 def printx(elem:Int): Unit = { println(elem) } arr01.foreach(printx) arr01.foreach((x)=>{println(x)}) arr01.foreach(println(_)) arr01.foreach(println) } }
2) 基本操作
object ScalaCollection{ def main(args: Array[String]): Unit = { // 創建數組的另外一種方式 val arr1 = Array(1,2,3,4) val arr2 = Array(5,6,7,8) // 添加數組元素,創建新數組 val arr3: Array[Int] = arr1 :+ 5 println( arr1 == arr3 ) // false val arr4: Array[Int] = arr1 ++: arr2 // 添加集合 val arr5: Array[Int] = arr1 ++ arr2 arr4.foreach(println) println("****************") arr5.foreach(println) println("****************") // 多維數組 var myMatrix = Array.ofDim[Int](3,3) myMatrix.foreach(list=>list.foreach(println)) // 合並數組 val arr6: Array[Int] = Array.concat(arr1, arr2) arr6.foreach(println) // 創建指定范圍的數組 val arr7: Array[Int] = Array.range(0,2) arr7.foreach(println) } }
7.2.2 可變數組
1) 基本語法
import scala.collection.mutable.ArrayBuffer object ScalaCollection{ def main(args: Array[String]): Unit = { val buffer = new ArrayBuffer[Int] // 增加數據 buffer.append(1,2,3,4) // 修改數據 buffer.update(0,5) buffer(1) = 6 // 刪除數據 val i: Int = buffer.remove(2) buffer.remove(2,2) // 查詢數據 println(buffer(3)) // 循環集合 for ( i <- buffer ) { println(i) } } }
2) 基本操作
import scala.collection.mutable.ArrayBuffer object ScalaCollection{ def main(args: Array[String]): Unit = { val buffer1 = ArrayBuffer(1,2,3,4) val buffer2 = ArrayBuffer(5,6,7,8) val buffer3: ArrayBuffer[Int] = buffer1 += 5 println( buffer1 eq buffer3 ) // true // 使用 ++ 運算符會產生新的集合數組 val buffer4: ArrayBuffer[Int] = buffer1 ++ buffer2 // 使用 ++= 運算符會更新之前的集合,不會產生新的數組 val buffer5: ArrayBuffer[Int] = buffer1 ++= buffer2 println( buffer1 eq buffer4 ) // false println( buffer1 eq buffer5 ) // true } }
7.2.3 可變數組和不可變數組轉換
import scala.collection.mutable import scala.collection.mutable.ArrayBuffer object ScalaCollection{ def main(args: Array[String]): Unit = { val buffer = ArrayBuffer(1,2,3,4) val array = Array(4,5,6,7) // 將不可變數組轉換為可變數組 val buffer1: mutable.Buffer[Int] = array.toBuffer // 將可變數組轉換為不可變數組 val array1: Array[Int] = buffer.toArray } }
7.3 Seq集合
1) 基本語法
object ScalaCollection{ def main(args: Array[String]): Unit = { // Seq集合 val list = List(1,2,3,4) // 增加數據 val list1: List[Int] = list :+ 1 println(list1 eq list) list1.foreach(println) val list2: List[Int] = 1 +: list list2.foreach(println) println("*****************") val list3: List[Int] = list.updated(1,5) println(list eq list3) List3.foreach(println) } }
2) 基本操作
object ScalaCollection{ def main(args: Array[String]): Unit = { // Seq集合 val list1 = List(1,2,3,4) // 空集合 val list2: List[Nothing] = List() val nil = Nil println(list2 eq nil) // 創建集合 val list3: List[Int] = 1::2::3::Nil val list4: List[Int] = list1 ::: Nil // 連接集合 val list5: List[Int] = List.concat(list3, list4) list5.foreach(println) // 創建一個指定重復數量的元素列表 val list6: List[String] = List.fill[String](3)("a") list6.foreach(println) } }
7.3.2 可變List
1) 基本語法
import scala.collection.mutable.ListBuffer object ScalaCollection{ def main(args: Array[String]): Unit = { // 可變集合 val buffer = new ListBuffer[Int]() // 增加數據 buffer.append(1,2,3,4) // 修改數據 buffer.update(1,3) // 刪除數據 buffer.remove(2) buffer.remove(2,2) // 獲取數據 println(buffer(1)) // 遍歷集合 buffer.foreach(println) } }
2) 基本操作
import scala.collection.mutable.ListBuffer object ScalaCollection{ def main(args: Array[String]): Unit = { // 可變集合 val buffer1 = ListBuffer(1,2,3,4) val buffer2 = ListBuffer(5,6,7,8) // 增加數據 val buffer3: ListBuffer[Int] = buffer1 :+ 5 val buffer4: ListBuffer[Int] = buffer1 += 5 val buffer5: ListBuffer[Int] = buffer1 ++ buffer2 val buffer6: ListBuffer[Int] = buffer1 ++= buffer2 println( buffer5 eq buffer1 ) println( buffer6 eq buffer1 ) val buffer7: ListBuffer[Int] = buffer1 - 2 val buffer8: ListBuffer[Int] = buffer1 -= 2 println( buffer7 eq buffer1 ) println( buffer8 eq buffer1 ) } }
7.3.3 可變集合和不可變集合轉換
import scala.collection.mutable import scala.collection.mutable.ListBuffer object ScalaCollection{ def main(args: Array[String]): Unit = { val buffer = ListBuffer(1,2,3,4) val list = List(5,6,7,8) // 可變集合轉變為不可變集合 val list1: List[Int] = buffer.toList // 不可變集合轉變為可變集合 val buffer1: mutable.Buffer[Int] = list.toBuffer } }
7.4 Set集合
7.4.1 不可變Set
1) 基本語法
object ScalaCollection{ def main(args: Array[String]): Unit = { val set1 = Set(1,2,3,4) val set2 = Set(5,6,7,8) // 增加數據 val set3: Set[Int] = set1 + 5 + 6 val set4: Set[Int] = set1.+(6,7,8) println( set1 eq set3 ) // false println( set1 eq set4 ) // false set4.foreach(println) // 刪除數據 val set5: Set[Int] = set1 - 2 - 3 set5.foreach(println) val set6: Set[Int] = set1 ++ set2 set6.foreach(println) println("********") val set7: Set[Int] = set2 ++: set1 set7.foreach(println) println(set6 eq set7) } }
2) 基本操作
object ScalaCollection{ def main(args: Array[String]): Unit = { val set1 = Set(1,2,3,4) val set2 = Set(5,6,7,8) // 增加數據 val set3: Set[Int] = set1 + 5 + 6 val set4: Set[Int] = set1.+(6,7,8) println( set1 eq set3 ) // false println( set1 eq set4 ) // false set4.foreach(println) // 刪除數據 val set5: Set[Int] = set1 - 2 - 3 set5.foreach(println) val set6: Set[Int] = set1 ++ set2 set6.foreach(println) println("********") val set7: Set[Int] = set2 ++: set1 set7.foreach(println) println(set6 eq set7) } }
7.4.2 可變Set
1) 基本語法
import scala.collection.mutable object ScalaCollection{ def main(args: Array[String]): Unit = { val set1 = mutable.Set(1,2,3,4) val set2 = mutable.Set(5,6,7,8) // 增加數據 set1.add(5) // 添加數據 set1.update(6,true) println(set1.mkString(",")) // 刪除數據 set1.update(3,false) println(set1.mkString(",")) // 刪除數據 set1.remove(2) println(set1.mkString(",")) // 遍歷數據 set1.foreach(println) } }
2) 基本操作
import scala.collection.mutable object ScalaCollection{ def main(args: Array[String]): Unit = { val set1 = mutable.Set(1,2,3,4) val set2 = mutable.Set(4,5,6,7) // 交集 val set3: mutable.Set[Int] = set1 & set2 println(set3.mkString(",")) // 差集 val set4: mutable.Set[Int] = set1 &~ set2 println(set4.mkString(",")) } }
7.5 Map集合
Map(映射)是一種可迭代的鍵值對(key/value)結構。所有的值都可以通過鍵來獲取。Map 中的鍵都是唯一的。
7.5.1 不可變Map
1) 基本語法
object ScalaCollection{ def main(args: Array[String]): Unit = { val map1 = Map( "a" -> 1, "b" -> 2, "c" -> 3 ) val map2 = Map( "d" -> 4, "e" -> 5, "f" -> 6 ) // 添加數據 val map3 = map1 + ("d" -> 4) println(map1 eq map3) // false // 刪除數據 val map4 = map3 - "d" println(map4.mkString(",")) val map5: Map[String, Int] = map1 ++ map2 println(map5 eq map1) println(map5.mkString(",")) val map6: Map[String, Int] = map1 ++: map2 println(map6 eq map1) println(map6.mkString(",")) // 修改數據 val map7: Map[String, Int] = map1.updated("b", 5) println(map7.mkString(",")) // 遍歷數據 map1.foreach(println) } }
2) 基本操作
object ScalaCollection{ def main(args: Array[String]): Unit = { val map1 = Map( "a" -> 1, "b" -> 2, "c" -> 3 ) val map2 = Map( "d" -> 4, "e" -> 5, "f" -> 6 ) // 創建空集合 val empty: Map[String, Int] = Map.empty println(empty) // 獲取指定key的值 val i: Int = map1.apply("c") println(i) println(map1("c")) // 獲取可能存在的key值 val maybeInt: Option[Int] = map1.get("c") // 判斷key值是否存在 if ( !maybeInt.isEmpty ) { // 獲取值 println(maybeInt.get) } else { // 如果不存在,獲取默認值 println(maybeInt.getOrElse(0)) } // 獲取可能存在的key值, 如果不存在就使用默認值 println(map1.getOrElse("c", 0)) } }
7.5.2 可變Map
1) 基本語法
import scala.collection.mutable object ScalaCollection{ def main(args: Array[String]): Unit = { val map1 = mutable.Map( "a" -> 1, "b" -> 2, "c" -> 3 ) val map2 = mutable.Map( "d" -> 4, "e" -> 5, "f" -> 6 ) // 添加數據 map1.put("d", 4) val map3: mutable.Map[String, Int] = map1 + ("e" -> 4) println(map1 eq map3) val map4: mutable.Map[String, Int] = map1 += ("e" -> 5) println(map1 eq map4) // 修改數據 map1.update("e",8) map1("e") = 8 // 刪除數據 map1.remove("e") val map5: mutable.Map[String, Int] = map1 - "e" println(map1 eq map5) val map6: mutable.Map[String, Int] = map1 -= "e" println(map1 eq map6) // 清除集合 map1.clear() } }
2) 基本操作
import scala.collection.mutable object ScalaCollection{ def main(args: Array[String]): Unit = { val map1 = mutable.Map( "a" -> 1, "b" -> 2, "c" -> 3 ) val map2 = mutable.Map( "d" -> 4, "e" -> 5, "f" -> 6 ) val set: Set[(String, Int)] = map1.toSet val list: List[(String, Int)] = map1.toList val seq: Seq[(String, Int)] = map1.toSeq val array: Array[(String, Int)] = map1.toArray println(set.mkString(",")) println(list.mkString(",")) println(seq.mkString(",")) println(array.mkString(",")) println(map1.get("a")) println(map1.getOrElse("a", 0)) println(map1.keys) println(map1.keySet) println(map1.keysIterator) println(map1.values) println(map1.valuesIterator) } }
7.6 元組
在Scala語言中,我們可以將多個無關的數據元素封裝為一個整體,這個整體我們稱之為:元素組合,簡稱元組。有時也可將元組看成容納元素的容器,其中最多只能容納22個
object ScalaCollection{ def main(args: Array[String]): Unit = { // 創建元組 val tuple = (1, "zhangsan", 30) // 根據順序號訪問元組的數據 println(tuple._1) println(tuple._2) println(tuple._3) // 迭代器 val iterator: Iterator[Any] = tuple.productIterator // 根據索引訪問元素 tuple.productElement(0) // 獲取整體 println(tuple.x) // 如果元組的元素只有兩個,那么我們稱之為對偶元組,也稱之為鍵值對 val kv: (String, Int) = ("a", 1) val kv1: (String, Int) = "a" -> 1 println( kv eq kv1 ) }
}
7.7 隊列
Scala也提供了隊列(Queue)的數據結構,隊列的特點就是先進先出。進隊和出隊的方法分別為enqueue和dequeue。
import scala.collection.mutable object ScalaCollection{ def main(args: Array[String]): Unit = { val que = new mutable.Queue[String]() // 添加元素 que.enqueue("a", "b", "c") val que1: mutable.Queue[String] = que += "d" println(que eq que1) // 獲取元素 println(que.dequeue()) println(que.dequeue()) println(que.dequeue()) } }
7.8 並行
Scala為了充分使用多核CPU,提供了並行集合(有別於前面的串行集合),用於多核環境的並行計算
object ScalaCollection{ def main(args: Array[String]): Unit = { val result1 = (0 to 100).map{x => Thread.currentThread.getName} val result2 = (0 to 100).par.map{x => Thread.currentThread.getName} println(result1) println(result2) } }
7.9 常用方法
1) 常用方法
object ScalaCollection{ def main(args: Array[String]): Unit = { val list = List(1,2,3,4) // 集合長度 println("size =>" + list.size) println("length =>" + list.length) // 判斷集合是否為空 println("isEmpty =>" + list.isEmpty) // 集合迭代器 println("iterator =>" + list.iterator) // 循環遍歷集合 list.foreach(println) // 將集合轉換為字符串 println("mkString =>" + list.mkString(",")) // 判斷集合中是否包含某個元素 println("contains =>" + list.contains(2)) // 取集合的前幾個元素 println("take =>" + list.take(2)) // 取集合的后幾個元素 println("takeRight =>" + list.takeRight(2)) // 查找元素 println("find =>" + list.find(x => x % 2== 0)) // 丟棄前幾個元素 println("drop =>" + list.drop(2)) // 丟棄后幾個元素 println("dropRight =>" + list.dropRight(2)) // 反轉集合 println("reverse =>" + list.reverse) // 去重 println("distinct =>" + list.distinct) } }
2) 衍生集合
object ScalaCollection{ def main(args: Array[String]): Unit = { val list = List(1,2,3,4) val list1 = List(3,4,5,6) // 集合頭 println("head => " + list.head) // 集合尾 println("tail => " + list.tail) // 集合尾迭代 println("tails => " + list.tails) // 集合初始化 println("init => " + list.init) // 集合初始化迭代 println("inits => " + list.inits) // 集合最后元素 println("last => " + list.last) // 集合並集 println("union => " + list.union(list1)) // 集合交集 println("intersect => " + list.intersect(list1)) // 集合差集 println("diff => " + list.diff(list1)) // 切分集合 println("splitAt => " + list.splitAt(2)) // 滑動 println("sliding => " + list.sliding(2)) // 滾動 println("sliding => " + list.sliding(2,2)) // 拉鏈 println("zip => " + list.zip(list1)) // 數據索引拉鏈 println("zipWithIndex => " + list.zipWithIndex) } }
3) 計算函數
object ScalaCollection{ def main(args: Array[String]): Unit = { val list = List(1,2,3,4) val list1 = List(3,4,5,6) // 集合最小值 println("min => " + list.min) // 集合最大值 println("max => " + list.max) // 集合求和 println("sum => " + list.sum) // 集合乘積 println("product => " + list.product) // 集合簡化規約 println("reduce => " + list.reduce((x:Int,y:Int)=>{x+y})) println("reduce => " + list.reduce((x,y)=>{x+y})) println("reduce => " + list.reduce((x,y)=>x+y)) println("reduce => " + list.reduce(_+_)) // 集合簡化規約(左) println("reduceLeft => " + list.reduceLeft(_+_)) // 集合簡化規約(右) println("reduceRight => " + list.reduceRight(_+_)) // 集合折疊 println("fold => " + list.fold(0)(_+_)) // 集合折疊(左) println("foldLeft => " + list.foldLeft(0)(_+_)) // 集合折疊(右) println("foldRight => " + list.foldRight(0)(_+_)) // 集合掃描 println("scan => " + list.scan(0)(_+_)) // 集合掃描(左) println("scanLeft => " + list.scanLeft(0)(_+_)) // 集合掃描(右) println("scanRight => " + list.scanRight(0)(_+_)) } }
4) 功能函數
object ScalaCollection{ def main(args: Array[String]): Unit = { val list = List(1,2,3,4) // 集合映射 println("map => " + list.map(x=>{x*2})) println("map => " + list.map(x=>x*2)) println("map => " + list.map(_*2)) // 集合扁平化 val list1 = List( List(1,2), List(3,4) ) println("flatten =>" + list1.flatten) // 集合扁平映射 println("flatMap =>" + list1.flatMap(list=>list)) // 集合過濾數據 println("filter =>" + list.filter(_%2 == 0)) // 集合分組數據 println("groupBy =>" + list.groupBy(_%2)) // 集合排序 println("sortBy =>" + list.sortBy(num=>num)(Ordering.Int.reverse)) println("sortWith =>" + list.sortWith((left, right) => {left < right})) } }
7.10 案例實操 - WordCount
7.10.1 數據准備
Hello Scala
Hello Spark
Hello Hadoop
7.10.2 功能實現
object WordCount { def main(args: Array[String]): Unit = { // TODO - Scala - WordCount 案例 // TODO 1.讀取文件的數據 val lineList: List[String] = Source.fromFile("input/word.txt").getLines().toList println(lineList) //List(Hello Hadoop Scala Spark, Hello Hadoop Scala, Hello Hadoop Scala, Hello) // TODO 2.將文件中的讀取的數據拆分成一個一個的單詞 val worList: List[String] = lineList.flatMap( line => { line.split(" ") } ) println(worList) //List(Hello, Hadoop, Scala, Spark, Hello, Hadoop, Scala, Hello, Hadoop, Scala, Hello) // TODO 3.將相同的單詞進行分組 // k(單詞) => v (相同單詞的集合) val word2List: Map[String, List[String]] = worList.groupBy( word => word ) println(word2List) //Map(Hadoop -> List(Hadoop, Hadoop, Hadoop), Hello -> List(Hello, Hello, Hello, Hello), Spark -> List(Spark), Scala -> List(Scala, Scala, Scala)) // TODO 4.將分組后的單詞數據進行次數的統計 //(word, list) => (word, count) val word2CountMap: Map[String, Int] = word2List.map( kv => { val word = kv._1 val list = kv._2 (word, list.size) } ) println(word2CountMap) // Map(Hadoop -> 3, Hello -> 4, Spark -> 1, Scala -> 3) // TODO 5.將統計的結果進行排序,並取前3名 // map事無序的無法排序,所以要先把map集合改為序的list val word2CountList: List[(String, Int)] = word2CountMap.toList //降序 val sortList: List[(String, Int)] = word2CountList.sortBy( t => t._2 )(Ordering.Int.reverse) println(sortList) // List((Hello,4), (Hadoop,3), (Scala,3), (Spark,1)) //取前3 val top3List: List[(String, Int)] = sortList.take(3) println(top3List) // List((Hello,4), (Hadoop,3), (Scala,3)) // TODO 6. 將統計結果打印到控制台 println(top3List.mkString(",")) // (Hello,4),(Hadoop,3),(Scala,3) } }
八、模式匹配
8.1 簡介
8.1 簡介
Scala中的模式匹配類似於Java中的switch語法,但是scala從語法中補充了更多的功能,所以更加強大。
int i = 20 switch (i) { default : System.out.println("other number"); break; case 10 : System.out.println("10"); //break; case 20 : System.out.println("20"); break; }
8.2 基本語法
模式匹配語法中,采用match關鍵字聲明,每個分支采用case關鍵字進行聲明,當需要匹配時,會從第一個case分支開始,如果匹配成功,那么執行對應的邏輯代碼,如果匹配不成功,繼續執行下一個分支進行判斷。如果所有case都不匹配,那么會執行case _分支,類似於Java中default語句。
object ScalaMatch{ def main(args: Array[String]): Unit = { var a: Int = 10 var b: Int = 20 var operator: Char = 'd' var result = operator match { case '+' => a + b case '-' => a - b case '*' => a * b case '/' => a / b case _ => "illegal" } println(result) } }
8.3 匹配規則
8.3.1 匹配常量
def describe(x: Any) = x match { case 5 => "Int five" case "hello" => "String hello" case true => "Boolean true" case '+' => "Char +" }
8.3.2 匹配類型
def describe(x: Any) = x match { case i: Int => "Int" case s: String => "String hello" case m: List[_] => "List" case c: Array[Int] => "Array[Int]" case someThing => "something else " + someThing }
8.3.3 匹配數組
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) 這個數組 case Array(x, y) => x + "," + y //匹配有兩個元素的數組,然后將將元素值賦給對應的x,y case Array(0, _*) => "以0開頭的數組" //匹配以0開頭和數組 case _ => "something else" } println("result = " + result) }
8.3.4 匹配列表
for (list <- Array(List(0), List(1, 0), List(0, 0, 0), List(1, 0, 0), List(88))) { val result = list match { case List(0) => "0" //匹配List(0) case List(x, y) => x + "," + y //匹配有兩個元素的List case List(0, _*) => "0 ..." case _ => "something else" } println(result) } val list: List[Int] = List(1, 2, 5, 6, 7) list match { case first :: second :: rest => println(first + "-" + second + "-" + rest) case _ => println("something else") }
8.3.5 匹配元組
for (tuple <- Array((0, 1), (1, 0), (1, 1), (1, 0, 2))) { val result = tuple match { case (0, _) => "0 ..." //是第一個元素是0的元組 case (y, 0) => "" + y + "0" // 匹配后一個元素是0的對偶元組 case (a, b) => "" + a + " " + b case _ => "something else" //默認 } println(result) }
8.3.6 匹配對象
class User(val name: String, val age: Int) object User{ def apply(name: String, age: Int): User = new User(name, age) def unapply(user: User): Option[(String, Int)] = { if (user == null) None else Some(user.name, user.age) } } val user: User = User("zhangsan", 11) val result = user match { case User("zhangsan", 11) => "yes" case _ => "no" }
8.3.7 樣例類
l 樣例類就是使用case關鍵字聲明的類
l 樣例類仍然是類,和普通類相比,只是其自動生成了伴生對象,並且伴生對象中自動提供了一些常用的方法,如apply、unapply、toString、equals、hashCode和copy。
l 樣例類是為模式匹配而優化的類,因為其默認提供了unapply方法,因此,樣例類可以直接使用模式匹配,而無需自己實現unapply方法。
l 構造器中的每一個參數都成為val,除非它被顯式地聲明為var(不建議這樣做)
case class User(name: String, age: Int) object ScalaCaseClass { def main(args: Array[String]): Unit = { val user: User = User("zhangsan", 11) val result = user match { case User("zhangsan", 11) => "yes" case _ => "no" } println(result) } }
8.4 應用場景
8.4.1 變量聲明
object ScalaMatch { def main(args: Array[String]): Unit = { val (x, y) = (1, 2) println(s"x=$x,y=$y") val Array(first, second, _*) = Array(1, 7, 2, 9) println(s"first=$first,second=$second") val Person(name, age) = Person("zhangsan", 16) println(s"name=$name,age=$age") } case class Person(name: String, age: Int) }
8.4.2 循環匹配
object ScalaMatch { def main(args: Array[String]): Unit = { val map = Map("A" -> 1, "B" -> 0, "C" -> 3) for ((k, v) <- map) { //直接將map中的k-v遍歷出來 println(k + " -> " + v) //3個 } println("----------------------") //遍歷value=0的 k-v ,如果v不是0,過濾 for ((k, 0) <- map) { println(k + " --> " + 0) // B->0 } println("----------------------") //if v == 0 是一個過濾的條件 for ((k, v) <- map if v >= 1) { println(k + " ---> " + v) // A->1 和 c->33 } } }
8.4.3 函數參數
object ScalaMatch { def main(args: Array[String]): Unit = { val list = List( ("a", 1), ("b", 2), ("c", 3) ) val list1 = list.map { case ( k, v ) => { (k, v*2) } } println(list1) } }
8.5 偏函數
所謂的偏函數,其實就是對集合中符合條件的數據進行處理的函數
偏函數也是函數的一種,通過偏函數我們可以方便的對輸入參數做更精確的檢查。例如該偏函數的輸入類型為List[Int],而我們需要的是第一個元素是0的集合,這就是通過模式匹配實現的。
8.5.1 基本語法
val second: PartialFunction[List[Int], Option[Int]] = { case x :: y :: _ => Some(y) }
8.5.2 案例實操
將該List(1,2,3,4,5,6,"test")中的Int類型的元素加一,並去掉字符串。
l 不使用偏函數
List(1,2,3,4,5,6,"test").filter(_.isInstanceOf[Int]).map(_.asInstanceOf[Int] + 1).foreach(println)
l 使用偏函數
List(1, 2, 3, 4, 5, 6, "test").collect { case x: Int => x + 1 }.foreach(println)
九、異常
9.1 簡介
語法處理上和Java類似,但是又不盡相同。
try { int a = 10; int b = 0; int c = a / b; }catch (ArithmeticException e){ // catch時,需要將范圍小的寫到前面 e.printStackTrace(); }catch (Exception e){ e.printStackTrace(); }finally { System.out.println("finally"); }
Scala中的異常沒有所謂的編譯時異常和運行時異常,所以也無需顯示拋出方法異常
9.2 基本函數
object ScalaException { def main(args: Array[String]): Unit = { try { var n= 10 / 0 }catch { case ex: ArithmeticException=>{ // 發生算術異常 println("發生算術異常") } case ex: Exception=>{ // 對異常處理 println("發生了異常1") println("發生了異常2") } }finally { println("finally") } } }
十、隱式轉換
10.1 簡介
Scala中在程序編譯錯誤時,可以通過內置的類型轉換機制進行二次編譯,嘗試將本身錯誤的代碼通過類型轉換后編譯通過。慢慢地,這也形成了一種擴展功能的轉換機制
10.2 隱式函數
object ScalaImplicit { def main(args: Array[String]): Unit = { implicit def transform( d : Double ): Int = { d.toInt } var d : Double = 2.0 val i : Int = d println(i) } }
10.3 隱式參數 & 隱式變量
object ScalaImplicit { def main(args: Array[String]): Unit = { def transform( implicit d : Double ) = { d.toInt } implicit val dd : Double = 2.0 println(transform) } }
10.4 隱式類
在Scala2.10后提供了隱式類,可以使用implicit聲明類,隱式類的非常強大,同樣可以擴展類的功能,在集合中隱式類會發揮重要的作用。
l 其所帶的構造參數有且只能有一個
隱式類必須被定義在“類”或“伴生對象”或“包對象”里,即隱式類不能是頂級的
object ScalaImplicit { def main(args: Array[String]): Unit = { val emp = new Emp() emp.insertUser() } class Emp { } implicit class User( emp : Emp) { def insertUser(): Unit = { println("insert user...") } } }
10.5 隱式機制
l 首先會在當前代碼作用域下查找隱式實體(隱式方法、隱式類、隱式對象)。(一般是這種情況)
l 如果第一條規則查找隱式實體失敗,會繼續在隱式參數的類型的作用域里查找。類型的作用域是指與該類型相關聯的全部伴生對象以及該類型所在包的包對象。
十一、泛型
11.1 簡介
Scala的泛型和Java中的泛型表達的含義都是一樣的,但是Scala提供了更加強大的功能
11.2 泛型轉換
Scala的泛型可以根據功能進行改變
11.2.1 泛型不可變
object ScalaGeneric { def main(args: Array[String]): Unit = { val test1 : Test[User] = new Test[User] // OK val test2 : Test[User] = new Test[Parent] // Error val test3 : Test[User] = new Test[SubUser] // Error } class Test[T] { } class Parent { } class User extends Parent{ } class SubUser extends User { } }
11.2.2 泛型協變
object ScalaGeneric { def main(args: Array[String]): Unit = { val test1 : Test[User] = new Test[User] // OK val test2 : Test[User] = new Test[Parent] // Error val test3 : Test[User] = new Test[SubUser] // OK } class Test[+T] { } class Parent { } class User extends Parent{ } class SubUser extends User { } }
11.2.3 泛型逆變
object ScalaGeneric {
def main(args: Array[String]): Unit = {
val test1 : Test[User] = new Test[User] // OK
val test2 : Test[User] = new Test[Parent] // OK
val test3 : Test[User] = new Test[SubUser] // Error
}
class Test[-T] {
}
class Parent {
}
class User extends Parent{
}
class SubUser extends User {
}
}
11.3泛型邊界
Scala的泛型可以根據功能設定類樹的邊界
object ScalaGeneric { def main(args: Array[String]): Unit = { val parent : Parent = new Parent() val user : User = new User() val subuser : SubUser = new SubUser() test[User](parent) // Error test[User](user) // OK test[User](subuser) // OK } def test[A]( a : A ): Unit = { println(a) } class Parent { } class User extends Parent{ } class SubUser extends User { } }
11.3.1 泛型上限
object ScalaGeneric { def main(args: Array[String]): Unit = { val parent : Parent = new Parent() val user : User = new User() val subuser : SubUser = new SubUser() test[Parent](parent) // Error test[User](user) // OK test[SubUser](subuser) // OK } def test[A<:User]( a : A ): Unit = { println(a) } class Parent { } class User extends Parent{ } class SubUser extends User { } }
11.3.2 泛型下限
object ScalaGeneric { def main(args: Array[String]): Unit = { val parent : Parent = new Parent() val user : User = new User() val subuser : SubUser = new SubUser() test[Parent](parent) // OK test[User](user) // OK test[SubUser](subuser) // Error } def test[A>:User]( a : A ): Unit = { println(a) } class Parent { } class User extends Parent{ } class SubUser extends User { } }
11.4 上下文限定
上下文限定是將泛型和隱式轉換的結合產物,以下兩者功能相同,使用上下文限定[A : Ordering]之后,方法內無法使用隱式參數名調用隱式參數,需要通過implicitly[Ordering[A]]獲取隱式變量,如果此時無法查找到對應類型的隱式變量,會發生出錯誤。
object ScalaGeneric { def main(args: Array[String]): Unit = { def f[A : Test](a: A) = println(a) implicit val test : Test[User] = new Test[User] f( new User() ) } class Test[T] { } class Parent { } class User extends Parent{ } class SubUser extends User { } }
十二、正則表達式
12.1 簡介
正則表達式(regular expression)描述了一種字符串匹配的模式(pattern),可以用來檢查一個串是否含有某種子串、將匹配的子串替換或者從某個串中取出符合某個條件的子串等。
12.2 基本語法
object ScalaRegex { def main(args: Array[String]): Unit = { // 構建正則表達式 val pattern = "Scala".r val str = "Scala is Scalable Language" // 匹配字符串 - 第一個 println(pattern findFirstIn str) // 匹配字符串 - 所有 val iterator: Regex.MatchIterator = pattern findAllIn str while ( iterator.hasNext ) { println(iterator.next()) } println("***************************") // 匹配規則:大寫,小寫都可 val pattern1 = new Regex("(S|s)cala") val str1 = "Scala is scalable Language" println((pattern1 findAllIn str1).mkString(",")) } }
12.3 案例實操
l 手機號正則表達式驗證方法
object ScalaRegex { def main(args: Array[String]): Unit = { // 構建正則表達式 println(isMobileNumber("18801234567")) println(isMobileNumber("11111111111")) } def isMobileNumber(number: String): Boolean ={ val regex = "^((13[0-9])|(14[5,7,9])|(15[^4])|(18[0-9])|(17[0,1,3,5,6,7,8]))[0-9]{8}$".r val length = number.length regex.findFirstMatchIn(number.slice(length-11,length)) != None } }
l 提取郵件地址的域名部分
object ScalaRegex { def main(args: Array[String]): Unit = { // 構建正則表達式 val r = """([_A-Za-z0-9-]+(?:\.[_A-Za-z0-9-\+]+)*)(@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*(?:\.[A-Za-z]{2,})) ?""".r println(r.replaceAllIn("abc.edf+jianli@gmail.com hello@gmail.com.cn", (m => "*****" + m.group(2)))) } }