[TOC]html
Scala是一門多範式(multi-paradigm)的編程語言,設計初衷是要集成面向對象編程和函數式編程的各類特性。java
Scala運行在Java虛擬機上,併兼容現有的Java程序。linux
Scala源代碼被編譯成Java字節碼,因此它能夠運行於JVM之上,並能夠調用現有的Java類庫。程序員
函數編程範式更適合用於Map/Reduce和大數據模型,它摒棄了數據與狀態的計算模型,着眼於函數自己,而非執行的過程的數據和狀態的處理。函數範式邏輯清晰、簡單,很是適合用於處理基於不變數據的批量處理工做,這些工做基本都是經過map和reduce操做轉換數據後,生成新的數據副本,而後再進行處理。es6
像Spark,Flink等都是採用Scala開發的,因此學習好大數據,掌握scala是必要的。算法
官網:http://scala-lang.org/ 數據庫
一、安裝JDK 二、JAVA_HOME, PATH 三、Maven 四、SCALA SDK 下載地址:http://scala-lang.org/download/all.html 這裏選擇的版本爲Scala 2.10.5,分爲windows和linux版本 五、配置SCALA_HOME 在windows環境變量中添加SCALA_HOME 六、驗證 scala -version Scala code runner version 2.10.5 -- Copyright 2002-2013, LAMP/EPFL
object HelloWorld { def main(args:Array[String]):Unit = { println("Hello World!") } }
保存爲HelloWorld.scala
,而後再執行下面兩步便可:apache
scalac HelloWorld.scala scala HelloWorld
1.可拓展 面向對象 函數式編程 2.兼容JAVA 類庫調用 互操做 3.語法簡潔 代碼行短 類型推斷 抽象控制 4.靜態類型化 可檢驗 安全重構 5.支持併發控制 強計算能力 自定義其餘控制結構
一、都是基於JVM虛擬機運行的編程
Scala編譯以後的文件也是.class,都要轉換爲字節碼,而後運行在JVM虛擬機之上。windows
二、Scala和Java相互調用
在Scala中能夠直接調用Java的代碼,同時在Java中也能夠直接調用Scala的代碼
三、Java8 VS Scala
1)Java8(lambda)沒有出來以前,Java只是面向對象的一門語言,可是Java8出來之後,Java就是一個面向對象和麪向函數的混合語言了。
2)首先咱們要對Scala進行精肯定位,從某種程度上講,Scala並非一個純粹的面向函數的編程語言,有人認爲Scala是一個帶有閉包的靜態面嚮對象語言),更準確地說,Scala是面向函數與面向對象的混合。
3)Scala設計的初衷是面向函數FP,而Java起家是面向對象OO,如今二者都是OO和FP的混合語言,是否能夠這麼認爲:Scala= FP + OO,而Java =OO + FP?
因爲面向對象OO和麪向函數FP兩種範式是相似橫座標和縱座標的二者不一樣座標方向的思考方式,相似數據庫和對象之間的不匹配阻抗關係,二者若是結合得很差恐怕就不會產生1+1>2的效果。
面向對象是最接近人類思惟的方式,而面向函數是最接近計算機的思惟方式。若是你想讓計算機爲人的業務建模服務,那麼以OO爲主;若是你但願讓計算機能本身經過算法從大數據中自動建模,那麼以FP爲主。因此,Java可能還會在企業工程類軟件中佔主要市場,而Scala則會在科學計算大數據分析等領域搶佔Java市場,好比Scala的Spark大有替代Java的Hadoop之趨勢。
一、Scala解釋器讀到一個表達式,對它進行求值,將它打印出來,接着再繼續讀下一個表達式。這個過程被稱作讀取--求值--打印--循環,即:REPL。
從技術上講,scala程序並非一個解釋器。實際發生的是,你輸入的內容被快速地編譯成字節碼,而後這段字節碼交由Java虛擬機執行。正由於如此,大多數scala程序員更傾向於將它稱作「REPL」
二、scala
scala>"Hello" res1: String = Hello scala> 1+2 res5: Int = 3 scala>"Hello".filter(line=>(line!='l')) res2: String = Heo
你應該注意到了在咱們輸入解釋器的每一個語句後,它會輸出一行信息,相似res0:
java.lang.String
= Hello。輸出的第一部分是REPL給表達式起的變量名。在這幾個例子裏,REPL爲每一個表達式定義了一個新變量(res0到res3)。輸出的第二部分(:後面的部分)是表達式的靜態類型。第一個例子的類型是java.lang.String,最後一個例子的類型則是scala.util.matching.Regex。輸出的最後一部分是表達式求值後的結果的字符串化顯示。通常是對結果調用toString方法獲得的輸出,JVM給全部的類都定義了toString方法。
一、Scala中沒有static的類,可是他有一種相似的伴生對象
二、字段:
字段/變量的定義Scala中使用 var/val 變量/不變量名稱 : 類型的方式進行定義,例如:
var index1 : Int = 1 val index2 : Int = 1
其中var與val的區別在於,var是變量,之後的值還能夠改變,val的值只能在聲明的時候賦值,可是val不是常量,只能說是不變量或只讀變量。
三、你們確定會以爲這種var/val名稱 : 類型的聲明方式太過於繁瑣了,有另一種方式
因此你在聲明字段的時候,可使用編譯器自動推斷類型,即不用寫: 類型
,例如:
var index1 = 1 (類型推斷) val index2 = 1
這個例子演示了被稱爲類型推斷(type inference)的能力,它能讓scala自動理解你省略了的類型。這裏,你用int字面量初始化index1,所以scala推斷index2的類型是Int。對於能夠由Scala解釋器(或編譯器)自動推斷類型的狀況,就沒有必要非得寫出類型標註不可。
四、a.方法(b)
這裏的方法是一個帶有2個參數的方法(一個顯示的和一個隱式的)
1.to(10), 1 to 10, 1 until 10 scala> 1.to(10) res19: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> 1 to 10 res20: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> 1 until 10 res21: scala.collection.immutable.Range = Range(1, 2, 3, 4, 5, 6, 7, 8, 9)
五、其實根據函數式編程思想中,var變量是個很差的存在,Scala中推薦你們儘量的採用val的不變量,主要緣由是
scala擁有和java同樣的數據類型,和java的數據類型的內存佈局徹底一致,精度也徹底一致。
下面表格中是scala支持的數據類型:
上表中列出的數據類型都是對象,也就是說scala沒有java中的原生類型。也就是說scala沒有基本數據類型與包裝類型的概念。
Scala裏,你能夠捨棄方法調用的空括號。例外就是若是方法帶有反作用就加上括號,如println(),不過若是方法沒有反作用就能夠去掉括號,如String上調用的toLowerCase:
scala> "Hello".toLowerCase res22: String = hello scala> "Hello".toLowerCase() res23: String = hello
Scala裏,你能夠捨棄方法調用的空括號。例外就是若是方法帶有反作用就加上括號,如println(),不過若是方法沒有反作用就能夠去掉括號,如String上調用的toLowerCase:
你能夠經過中綴操做符,加號(+),減號(-),乘號(*),除號(/)和餘數(%),在任何數類型上調用數學方法, scala中的基礎運算與java一致
scala> 1.2 + 2.3 res6: Double = 3.5 scala> 3 - 1 res7: Int = 2 scala> 'b' - 'a' res8: Int = 1 scala> 2L * 3L res9: Long = 6 scala> 11 / 4 res10: Int = 2 scala> 11 % 4 res11: Int = 3 scala> 11.0f / 4.0f res12: Float = 2.75 scala> 11.0 % 4.0 res13: Double = 3.0
你能夠用關係方法:大於(>),小於(<),大於等於(>=)和小於等於(<=)比較數類型,像等號操做符那樣,產生一個Boolean結果。另外,你可使用一元操做符!(unary_!方法)改變Boolean值
scala> 1 > 2 res16: Boolean = false scala> 1 < 2 res17: Boolean = true scala> 1.0 <= 1.0 res18: Boolean = true scala> 3.5f >= 3.6f res19: Boolean = false scala> 'a' >= 'A' res20: Boolean = true scala> val thisIsBoring = !true thisIsBoring: Boolean = false scala> !thisIsBoring res21: Boolean = true
scala> ("he" + "llo") == "hello" res33: Boolean = true === 比較2個不一樣的對象 scala> 1 == 1.0 res34: Boolean = true
其比較的是值,而不是Java概念中的地址值。另外第二個例子,1 == 1.0,會爲true,是由於1會作類型的提高變爲1.0
一、Scala的if/else語法結構和Java或者C++同樣,不過,在Scala中if/else表達式有值,這個值就是跟在if或else以後的表達式的值
val x = 3 if(x > 0) 1 else -1
上述表達式的值是1或-1,具體是哪個取決於x的值,同時你也能夠將if/else表達式的值複製給變量
val s = if(x>0) 1 else -1 // 這與以下語句的效果是同樣的 if(x >0) s = 1 else s =-1
不過,第一種寫法更好,由於它能夠用來初始化一個val,而在第二種寫法當中,s必須是var
二、val result = if(personAge > 18) "Adult" else 0
其中一個分支是java.lang.string,另一個類型是Int, 因此他們的公共超類是Any
三、若是else丟失了
if(x>0) 1
那麼有可能if語句沒有輸出值,可是在Scala中,每一個表達式都有值,這個問題的解決方案是引入一個Unit類,寫做(),不帶else語句的if語句等同於if(x>0) 1else ()
一、在Java和C++中,每一個語句都已分號結束。可是在Scala中—與JavaScript和其它語言相似—行尾的位置不須要分號。一樣,在}、else以及相似的位置也不須要寫分號。
不過,若是你想在單行中寫下多個語句,就須要將他們以分號隔開
If(n>0){r = r *n ; n-=1}
咱們須要用分號將r = r *n 和n -=1隔開,因爲有},在第二個語句以後並不須要寫分號。
二、分行顯示
If(n>0){ r = r *n n-=1 }
scala> val n = 9 n: Int = 9 scala> var f = 0 f: Int = 0 scala> var m = 5 m: Int = 5 scala> val d = if(n < 18){f = f + n ; m = m + n ; f + m} d: AnyVal = 23
若是要打印一個值,咱們用print或println函數。後者在打印完內容後會追加一個換行符。舉例來講,
print("Answer:") println(42)
與下面的代碼輸出的內容相同:
println("Answer:" + 42)
另外,還有一個帶有C風格格式化字符串的printf函數:system.in
printf("Hello,%s! You are %d years old.\n", "Fred", 42)
你能夠用readLine函數從控制檯讀取一行輸入。若是要讀取數字、Boolean或者是字符,能夠用readInt、readDouble、readByte、readShort、readLong、readFloat、readBoolean或者readChar。與其餘方法不一樣,readLine帶一個參數做爲提示字符串:
scala> val name = readLine("Please input your name:") Please input your name:name: String = xpleaf
一個簡單的輸入輸出案例以下:
val name = readLine("What's Your name: ") print("Your age: ") val age = readInt() if(age > 18) { printf("Hello, %s! Next year, your will be %d.\n", name, age + 1) }else{ printf("Hello, %s! You are still a children your will be %d.\n", name, age + 1 +" Come up!!!") } }
Scala擁有與Java和C++相同的while和do循環。例如:
object _04LoopDemo { def main(args: Array[String]):Unit = { var sum = 0 var i = 1 while(i < 11) { sum += i i += 1 } } }
Scala 也有do-while循環,它和while循環相似,只是檢查條件是否知足在循環體執行以後檢查。例如:
object _04LoopDemo { def main(args: Array[String]):Unit = { var sum = 0 var i = 1 do { sum += i i += 1 } while(i <= 10) println("1+...+10=" + sum) } }
登陸用戶名密碼的遊戲:三次機會,從控制檯輸入輸入用戶名密碼,若是成功登陸,返回登陸成功,失敗,則反饋錯誤信息!以下:
object _05LoopTest { def main(args: Array[String]):Unit = { val dbUser = "zhangyl" val dbPassword = "uplooking" var count = 3 while(count > 0) { val name = readLine("親,請輸入用戶名:") val pwd = readLine("請輸入密碼:") if(name == dbUser && pwd == dbPassword) { println("登錄成功,正在爲您跳轉到主頁吶," + name + "^_^") // count = 0 return } else { count -= 1 println("連用戶名和密碼都記不住,你一天到底在弄啥嘞!您還有<" + count + ">次機會") } } } }
Scala沒有與for(初始化變量;檢查變量是否知足某條件;更新變量)
循環直接對應的結構。若是你須要這樣的循環,有兩個選擇:一是使用while循環,二是使用以下for語句:
for (i <- 表達式)
讓變量i
遍歷< -
右邊的表達式的全部值。至於這個遍歷具體如何執行,則取決於表達式的類型。對於Scala集合好比Range而言,這個循環會讓i依次取得區間中的每一個值。
遍歷字符串或數組時,你一般須要使用從0到n-1的區間。這個時候你能夠用until方法而不是to方法。util方法返回一個並不包含上限的區間。
val s = "Hello" var sum = 0 for (i <- 0 until s.length) { // i的最後一個取值是s.length - 1 sum += s(i) // 注意此時爲對應的ASCII值相加 }
在本例中,事實上咱們並不須要使用下標。你能夠直接遍歷對應的字符序列:
var sum = 0 for (ch <- "Hello") sum += ch
說明:Scala並無提供break或continue語句來退出循環。那麼若是須要break時咱們該怎麼作呢?有以下幾個選項:
object _06LoopBreakTest { def main(args: Array[String]):Unit = { import scala.util.control.Breaks._ var n = 15 breakable { for(c <- "Spark Scala Storm") { if(n == 10) { println() break } else { print(c) } n -= 1 } } } }
一、除了for循環的基本形態以外,Scala也提供了其它豐富的高級特性。好比能夠在for循環括號裏同時包含多組變量 <- 表達式
結構,組之間用分號分隔
for (i <- 1 to 3;j <- 1 to 3) print ((10 * i +j) + " ")
for循環的這種結構相似Java中的嵌套循環結構。
例如要實現一個九九乘法表,使用基本的for循環形態時,代碼以下:
object _07LoopForTest { def main(args: Array[String]):Unit = { for(i <- 1 to 9) { for(j <- 1 to i){ var ret = i * j print(s"$i*$j=$ret\t") } // println() System.out.println() } } }
而使用高級for循環時,以下:
object _07LoopForTest { def main(args: Array[String]):Unit = { // for(i <- 1 to 9; j <- 1 to 9 if j <= i) { for(i <- 1 to 9; j <- 1 to i) { var ret = i * j print(s"$i*$j=$ret\t") if(i == j) { println } } } }
二、if循環守衛
能夠爲嵌套循環經過if表達式添加條件:
for (i <- 1 to 3; j <- 1 to 3 if i != j) print ((10 * i + j) + " ")
if表達式是否添加括號,結果無變化:
for (i <- 1 to 3; j <- 1 to 3 if (i != j)) print ((10 * i + j) + " ")
注意:注意在if以前並無分號。
三、For推導式
Scala中的yield不像Ruby裏的yield,Ruby裏的yield象是個佔位符。Scala中的yield的主要做用是記住每次迭代中的有關值,並逐一存入到一個數組中。用法以下:
for {子句} yield {變量或表達式}
1)若是for循環的循環體以yield開始,則該循環會構造出一個集合,每次迭代生成集中的一個值:
scala> for(i <- 1 to 10) yield println(i % 3) 1 2 0 1 2 0 1 2 0 1 res47: scala.collection.immutable.IndexedSeq[Unit] = Vector((), (), (), (), (), (), (), (), (), ()) scala> val ret1 = for(i <- 1 to 10) yield(i) ret1: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) scala> for(i <- ret1) println(i) 1 2 3 4 5 6 7 8 9 10
2) for 循環中的 yield 會把當前的元素記下來,保存在集合中,循環結束後將返回該集合
Scala中for循環是有返回值的。若是被循環的是Map,返回的就是Map,被循環的是List,返回的就是List,以此類推。
yield關鍵字的簡短總結:
1.針對每一次for循環的迭代, yield會產生一個值,被循環記錄下來(內部實現上,像是一個緩衝區) 2.當循環結束後, 會返回全部yield的值組成的集合 3.返回集合的類型與被遍歷的集合類型是一致的
Scala的異常處理和其它語言好比Java相似,一個方法能夠經過拋出異常的方法而不返回值的方式終止相關代碼的運行。調用函數能夠捕獲這個異常做出相應的處理或者直接退出,在這種狀況下,異常會傳遞給調用函數的調用者,依次向上傳遞,直到有方法處理這個異常。
object _01ExceptionDemo { def main(args:Array[String]):Unit = { import scala.io.Source import java.io.FileNotFoundException try { val line = Source.fromFile("./wordcount.txt").mkString val ret = 1 / 0 println(line) } catch { case fNFE:FileNotFoundException => { println("FileNotFoundException:文件找不到了,傳的路徑有誤。。。") } case e:Exception => { println("Exception: " + e.getMessage) } case _ => println("default處理方式") } finally { println("this is 必需要執行的語句") } } }
Scala除了方法外還支持函數。方法對對象進行操做,函數不是。要定義函數,你須要給出函數的名稱、參數和函數體,就像這樣:
下面是須要注意的問題:
def myFirstFunction(name:String, age:Int) : String ={ println("name=> " + name +"\t age=> " + age) "Welcome " + name + " to our world, your age is => " + age }
在本例中咱們並不須要用到return。咱們也能夠像Java或C++那樣使用return,來當即從某個函數中退出,不過在Scala中這種作法並不常見。
提示:雖然在帶名函數中使用return並無什麼不對(除了浪費7次按鍵動做外),咱們最好適應沒有return的日子。很快,你就會使用大量匿名函數,這些函數中return並不返回值給調用者。它跳出到包含它的帶名函數中。咱們能夠把return當作是函數版的break語句,僅在須要時使用。
def myFirstFunctionWithoutFeedBackValues: Unit ={ println("This is our first function without feedback values") }
def printMsg(name:String) = println("Hello, " + name +", welcome happy day!!!")
上面所對應函數的案例以下:
/** 注意: 1.scala中若是要給一個函數作返回值,能夠不用return語句, 使用也是能夠的,可是scala語言強烈建議你們不要用 由於scala崇尚的是簡約而不簡單 通常也就是將這個return當作break來使用 2.在scala中在同一條語句中,變量名不能和引用函數名重複 3.使用遞歸時,須要指定返回值類型 */ object _02FunctionDemo { def main(args:Array[String]):Unit = { // val ret = myFirstFunc("xpleaf", 23) // println(ret) // show("xpleaf", 23) // val ret = singleLineFunc("xpleaf") // println(ret) val ret = factorial(5) println(ret) } def testFunc(num:Int) = { if(num == 1) { 1 } 10 } // 使用遞歸來求解5的階乘 def factorial(num:Int):Int = { if(num == 1) { 1 } else { num * factorial(num - 1) } // 若是num * factorial(num - 1)不寫在else裏面,則須要return,不然會有異常, // testFunc中則不會這樣,因此猜想遞歸函數纔會有此問題 } // 單行函數 def singleLineFunc(name:String) = "hello, " + name // 有"="號,有返回值,自動判斷返回值類型 // def singleLineFunc(name:String) {"hello, " + name} // 沒有「=」號,沒有返回值 // def singleLineFunc(name:String):Unit = "hello, " + name // 有"="號,但指定返回值爲空,至關於沒有返回值 // def singleLineFunc(name:String) = println("hello, " + name) // 沒有返回值,println並非一個表達式 // 沒有返回值的函數 def show(name:String, age:Int):Unit = { println(s"My name is $name, and I'm $age years old.") } // 有返回值的函數 def myFirstFunc(name:String, age:Int):String = { println(s"My name is $name, and I'm $age years old.") // return "Welcome you " + name + ", make yourself..." "Welcome you " + name + ", make yourself..." } }
一、咱們在調用某些函數時並不顯式地給出全部參數值,對於這些函數咱們可使用默認參數。
def sayDefaultFunc(name: String, address: String = "Beijing", tellphone: String ="139****") ={ println(name +"address=> " + address +"\t tellphone=> " + tellphone) }
二、不指定具體參數時:給出默認值
sayDefaultFunc("Garry")
三、若是相對參數的數量,你給出的值不夠,默認參數會從後往前逐個應用進來。
sayDefaultFunc("Garry","Shanhai")
四、給出所有的參數值
sayDefaultFunc("Garry","Shanhai","13709872335")
五、帶名參數可讓函數更加可讀。它們對於那些有不少默認參數的函數來講也頗有用。
sayDefaultFunc(address ="上海", tellphone="12109876543",name="Tom")
六、你能夠混用未命名參數和帶名參數,只要那些未命名的參數是排在前面的便可:
sayDefaultFunc("Tom",tellphone="12109876543",address= "上海")
前面咱們介紹的函數的參數是固定的,本篇介紹Scala函數支持的可變參數列表,命名參數和參數缺省值定義。
重複參數
Scala在定義函數時容許指定最後一個參數能夠重複(變長參數),從而容許函數調用者使用變長參數列表來調用該函數,Scala中使用「*」來指明該參數爲重複參數。例如
Scala:
def echo(args: String*) = { for (arg <- args) println(arg) }
Java:
public staticvoid echo(String ...args){ for(String str: args){ System.out.println(str) } }
一、在函數內部,變長參數的類型,實際爲一數組,好比上例的String* 類型實際爲 Array[String]。
然而,現在你試圖直接傳入一個數組類型的參數給這個參數,編譯器會報錯:
val arr= Array("Spark","Scala","AKKA") Error message as bellows: error: type mismatch;
二、爲了不這種狀況,你能夠經過在變量後面添加_*來解決,這個符號告訴Scala編譯器在傳遞參數時逐個傳入數組的每一個元素,而不是數組總體
val arr= Array("Spark","Scala","AKKA") echo(arr:_*)
一個例子以下:
object _04FunctionDemo { def main(args:Array[String]):Unit = { show("Spark", "Strom", "Hadoop") var arr = Array("Spark", "Strom", "Hadoop") show(arr:_*) } def show(strs:String*) { for(str <- strs) { println(str) } } }
一、Scala對於不返回值的函數有特殊的表示法。若是函數體包含在花括號當中但沒有前面的=號,那麼返回類型就是Unit。這樣的函數被稱作過程(procedure)。過程不返回值,咱們調用它僅僅是爲了它的反作用。
案例:
如:咱們須要打印一些圖案,那麼能夠定義一個過程:
def draw(str:String) { println("-------") println("|"+" "+"|") println("|"+ str +"|") println("|"+" "+"|") println("-------") }
二、咱們也能夠顯示指定的函數的返回值:Unit
一、當val被聲明爲lazy時,它的初始化將被推遲,直到咱們首次對它取值。例如,
lazy val lines= scala.io.Source.fromFile("D:/test/scala/wordcount.txt").mkString
二、若是程序從不訪問lines ,那麼文件也不會被打開。但故意拼錯文件名。在初始化語句被執行的時候並不會報錯。不過,一旦你訪問words,就將會獲得一個錯誤提示:文件未找到。
三、懶值對於開銷較大的初始化語句而言十分有用。它們還能夠應對其餘初始化問題,好比循環依賴。更重要的是,它們是開發懶數據結構的基礎。(spark 底層嚴重依賴這些lazy)
四、加載(使用它的時候纔會被加載)
println(lines)
一個例子以下:
object _05LazyDemo { def main(args:Array[String]):Unit = { import scala.io.Source import java.io.FileNotFoundException try { lazy val line = Source.fromFile("./wordcountq.txt").mkString // wordcountq.txt這個文件並不存在 // println(line) } catch { case fNFE:FileNotFoundException => { println("FileNotFoundException:文件找不到了,傳的路徑有誤。。。") } case e:Exception => { println("Exception: " + e.getMessage) } } finally { println("this is 必需要執行的語句") } } }