Scala是一種多範式的編程語言,其設計的初衷是要集成面向對象編程和函數式編程的各類特性。Scala運行於Java平臺(Java虛擬機),併兼容現有的Java程序。java
學習Scala編程語言,爲後續學習Spark奠基基礎。程序員
l 優雅:這是框架設計師第一個要考慮的問題,框架的用戶是應用開發程序員,API是否優雅直接影響用戶體驗。算法
l 速度快:Scala語言表達能力強,一行代碼抵得上Java多行,開發速度快;Scala是靜態編譯的,因此和JRuby,Groovy比起來速度會快不少。編程
l 能融合到Hadoop生態圈:Hadoop如今是大數據事實標準,Spark並非要取代Hadoop,而是要完善Hadoop生態。JVM語言大部分可能會想到Java,但Java作出來的API太醜,或者想實現一個優雅的API太費勁。 數組
l 德國的一位資深Java專家Adam Bien在參加JavaOne 2008大會(他在會上有一個技術講座)期間,親耳聽到,有人問Java之父James Gosling「除了Java以外,你如今最但願在JVM上使用什麼語言?」的時候,他出人意料的回答:「Scala!」幾乎是脫口而出。安全
由於Scala是運行在JVM平臺上的,因此安裝Scala以前要安裝JDK,安裝JDK1.8版本網絡
① Windows安裝Scala編譯器數據結構
訪問Scala官網http://www.scala-lang.org/下載Scala編譯器安裝包,目前最新版本是2.12.x,可是目前大多數的框架都是用2.11.x編寫開發的,Spark2.x使用的就是2.11.x,因此這裏推薦2.11.x版本,下載scala-2.11.12.msi後點擊下一步就能夠了多線程
1) 安裝JDK閉包
2) 下載Scala:http://www.scala-lang.org/download/
3) 安裝Scala:
設置環境變量:SCALA_HOME和PATH路徑
4) 驗證Scala
② Linux安裝Scala編譯器
下載Scala地址http://downloads.typesafe.com/scala/2.11.12/scala-2.11.12.tgz而後解壓Scala到指定目錄
tar -zxvf scala-2.11.12.tgz -C /usr/java
配置環境變量,將scala加入到PATH中
vi /etc/profile
export JAVA_HOME=/usr/java/jdk1.8.0_111
export PATH=$PATH:$JAVA_HOME/bin:/usr/java/scala-2.11.12/bin
l REPL(Read Evaluate Print Loop):命令行
l IDE:圖形開發工具
n The Scala IDE (Based on Eclipse):http://scala-ide.org/
n IntelliJ IDEA with Scala plugin:http://www.jetbrains.com/idea/download/
n Netbeans IDE with the Scala plugin
因爲IDEA的Scala插件更優秀,大多數Scala程序員都選擇IDEA,能夠到http://www.jetbrains.com/idea/download/下載社區免費版,點擊下一步安裝便可,安裝時若是有網絡能夠選擇在線安裝Scala插件。這裏咱們使用離線安裝Scala插件:
l 安裝IDEA,點擊下一步便可。因爲咱們離線安裝插件,因此點擊Skip All and Set Defaul
l 下載IEDA的scala插件,地址http://plugins.jetbrains.com/plugin/1347-scala
1.安裝Scala插件:Configure -> Plugins -> Install plugin from disk -> 選擇Scala插件 -> OK -> 重啓IDEA
注意:在Scala中,任何數據都是對象。例如:
③ 數值類型:Byte,Short,Int,Long,Float,Double
l Byte: 8位有符號數字,從-128 到 127
l Short: 16位有符號數據,從-32768 到 32767
l Int: 32位有符號數據
l Long: 64位有符號數據
例如:
val a:Byte = 10
a+10
獲得:res9: Int = 20
這裏的res9是新生成變量的名字
val b:Short = 20
a+b
注意:在Scala中,定義變量能夠不指定類型,由於Scala會進行類型的自動推導。
④ 字符類型和字符串類型:Char和String
對於字符串,在Scala中能夠進行插值操做。
注意:前面有個s;至關於執行:"My Name is " + s1
⑤ Unit類型:至關於Java中的void類型
⑥ Nothing類型:通常表示在執行過程當中,產生了Exception
例如,咱們定義一個函數以下:
l 使用val和var申明變量
例如:scala> val answer = 8 * 3 + 2
能夠在後續表達式中使用這些名稱
l val:value 簡寫,表示的意思爲值,不可變
要申明其值可變的變量:val
l var:variable 簡寫,表示的變量,能夠改變值
要申明其值不可變的變量:var
l 例子
def main(args: Array[String]) {
//使用val定義的變量值是不可變的,至關於java裏用final修飾的變量
//使用var定義的變量是可變得,在Scala中鼓勵使用val
//Scala編譯器會自動推斷變量的類型,必要的時候能夠指定類型
//
變量名在前,類型在後
} }
|
注意:能夠不用顯式指定變量的類型,Scala會進行自動的類型推到
Scala的if/else語法結構和Java或C++同樣。
不過,在Scala中,if/else是表達式,有值,這個值就是跟在if或else以後的表達式的值。
def main(args: Array[String]) { val x = 1
//判斷x的值,將結果賦給y
//打印y的值
println
(y)
//支持混合類型表達式
//打印z的值
println
(z)
//若是缺失else,至關於if (x > 2) 1 else ()
println
(m)
//在scala中每一個表達式都有值,scala中有個Unit類,寫作(),至關於Java中的void
println
(n)
//if和else if
else if (x >= 1) 1 else -1
println
(k)} }
|
def main(args: Array[String]) { val x = 0
//在scala中{}中課包含一系列表達式,塊中最後一個表達式的值就是塊的值
//下面就是一個塊表達式
if (x < 0){ -1 } else if(x >= 1) { 1 } else { "error" } }
//result的值就是塊表達式的結果
println
(result)} }
|
Scala擁有與Java和C++相同的while和do循環
Scala中,可使用for和foreach進行迭代
注意:
(*) <- 表示Scala中的generator,即:提取符
(*)第三種寫法是第二種寫法的簡寫
在上面的案例中,咱們將list集合中的每一個元素轉換成了大寫,而且使用yield關鍵字生成了一個新的集合。
注意:在上面的例子中,foreach接收了另外一個函數(println)做爲值
當val被申明爲lazy時,它的初始化將被推遲,直到咱們首次對它取值。
一個更爲複雜一點的例子:讀取文件:
Scala異常的工做機制和Java或者C++同樣。直接使用throw關鍵字拋出異常。
使用try...catch...finally來捕獲和處理異常:
Scala數組的類型:
l 定長數組:使用關鍵字Array
l 變長數組:使用關鍵字ArrayBuffer
l 遍歷數組
l Scala數組的經常使用操做
l Scala的多維數組
l 和Java同樣,多維數組是經過數組的數組來實現的。
l 也能夠建立不規則的數組,每一行的長度各不相同。
① 定長數組和變長數組
import scala.collection.mutable.ArrayBuffer def main(args: Array[String]) {
//初始化一個長度爲8的定長數組,其全部元素均爲0
//直接打印定長數組,內容爲數組的hashcode值
println
(arr1)
//將數組轉換成數組緩衝,就能夠看到原數組中的內容了
//toBuffer會將數組轉換長數組緩衝
println
(arr1.toBuffer)
//注意:若是new,至關於調用了數組的apply方法,直接爲數組賦值
//初始化一個長度爲1的定長數組
Array
[Int](10)
println
(arr2.toBuffer)
//定義一個長度爲3的定長數組
Array
("hadoop", "storm", "spark")
//使用()來訪問元素
println
(arr3(2))
//////////////////////////////////////////////////
//變長數組(數組緩衝)
//若是想使用數組緩衝,須要導入import scala.collection.mutable.ArrayBuffer包
//向數組緩衝的尾部追加一個元素
//+=尾部追加元素
//追加多個元素
//追加一個數組++=
Array
(6, 7)
//追加一個數組緩衝
//打印數組緩衝ab
//在數組某個位置插入元素用insert
//刪除數組某個位置的元素用remove
println
(ab)} }
|
② 遍歷數組
object ForArrayTest {
//初始化一個數組
Array
(1,2,3,4,5,6,7,8)
//加強for循環
println
(i)
//好用的until會生成一個Range
//reverse是將前面生成的Range反轉
println
(arr(i))} }
|
③ 數組轉換
yield關鍵字將原始的數組進行轉換會產生一個新的數組,原始的數組不變
def main(args: Array[String]) {
//定義一個數組
Array
(1, 2, 3, 4, 5, 6, 7, 8, 9)
//將偶數取出乘以10後再生成一個新的數組
println
(res.toBuffer)
//更高級的寫法,用着更爽
//filter是過濾,接收一個返回值爲boolean的函數
//map至關於將數組中的每個元素取出來,應用傳進去的函數
println
(r.toBuffer)} }
|
④ 數組經常使用算法
在Scala中,數組上的某些方法對數組進行相應的操做很是方便!
在Scala中,把哈希表這種數據結構叫作映射
① 構建映射
② 獲取和修改映射中的值
好用的getOrElse
注意:在Scala中,有兩種Map,一個是immutable包下的Map,該Map中的內容不可變;另外一個是mutable包下的Map,該Map中的內容可變
例子:
注意:一般咱們在建立一個集合是會用val這個關鍵字修飾一個變量(至關於java中的final),那麼就意味着該變量的引用不可變,該引用中的內容是否是可變,取決於這個引用指向的集合的類型
例子:以下這段超簡單的循環便可遍歷映射中全部的鍵/值對偶
映射是K/V對偶的集合,對偶是元組的最簡單形式,元組能夠裝着多個不一樣類型的值。
① 建立元組
② 獲取元組中的值
③ 將對偶的集合轉換成映射
④ 拉鍊操做
zip命令能夠將多個值綁定在一塊兒
注意:若是兩個數組的元素個數不一致,拉鍊操做後生成的數組的長度爲較小的那個數組的元素個數
Scala中的+ - * / %等操做符的做用與Java同樣,位操做符 & | ^ >> <<也同樣。只是有
一點特別的:這些操做符其實是方法。例如:
a + b
是以下方法調用的簡寫:
a.+(b)
a 方法 b能夠寫成 a.方法(b)
① 定義方法
方法的返回值類型能夠不寫,編譯器能夠自動推斷出來,可是對於遞歸方法,必須指定返回類型
② 定義函數
③ 方法和函數的區別
在函數式編程語言中,函數是「頭等公民」,它能夠像任何其餘數據類型同樣被傳遞和操做
案例:首先定義一個方法,再定義一個函數,而後將函數傳遞到方法裏面
//定義一個方法
//方法m2參數要求是一個函數,函數的參數必須是兩個Int類型
//
返回值類型也是Int類型
f(2, 6) }
//定義一個函數f1,參數是兩個Int類型,返回值是一個Int類型
f1
= (x: Int, y: Int) => x + y
//再定義一個函數f2
f2
= (m: Int, n: Int) => m * n
//main方法
//調用m1方法,並傳入f1函數
m1
(
f1
)
println
(r1)
//調用m1方法,並傳入f2函數
m1
(
f2
)
println
(r2)} }
|
④ 將方法轉換成函數(神奇的下劃線)
l 可使用Scala的預約義函數
例如:求兩個值的最大值
l 也可使用def關鍵字自定義函數
語法:
示例:
l Call By Value:對函數實參求值,且僅求一次
l Call By Name:函數實參每次在函數體內被用到時都會求值
咱們來分析一下,上面兩個調用執行的過程:
一份複雜一點的例子:
l 默認參數
l 代名參數
l 可變參數
在Scala中,函數是「頭等公民」,就和數字同樣。能夠在變量中存放函數,即:將函數做爲變量的值(值函數)。
(*)首先,定義一個最普通的函數
(*)再定義一個高階函數
(*)分析這個高階函數調用的過程
在這個例子中,首先定義了一個普通的函數mytest,而後定義了一個高階函數myFunction;myFunction接收三個參數:第一個f是一個函數參數,第二個是x,第三個是y。而f是一個函數參數,自己接收兩個Int的參數,返回一個Int的值。
就是函數的嵌套,即:在一個函數定義中,包含另一個函數的定義;而且在內函數中能夠訪問外函數中的變量。
測試上面的函數:
柯里化函數(Curried Function)是把具備多個參數的函數轉換爲一條函數鏈,每一個節點上是單一參數。
一個簡單的例子:
在這個例子中,能夠被2整除的被分到一個分區;不能被2整除的被分到另外一個分區。
在這個例子中,分爲兩步:
(1)將(1,2,3)和(4,5,6)這兩個集合合併成一個集合
(2)再對每一個元素乘以2
把數據及對數據的操做方法放在一塊兒,做爲一個相互依存的總體——對象
面向對象的三大特徵:
封裝
繼承
多態
簡單類和無參方法:
案例:注意沒有class前面沒有public關鍵字修飾。
若是要開發main方法,須要將main方法定義在該類的伴生對象中,即:object對象中,(後續作詳細的討論)。
l 當定義屬性是private時候,scala會自動爲其生成對應的get和set方法
private var stuName:String = "Tom"
l 定義屬性:private var money:Int = 1000 但願money只有get方法,沒有set方法??
l private[this]的用法:該屬性只屬於該對象私有,就不會生成對應的set和get方法。若是這樣,就不能直接調用,例如:s1.stuName ---> 錯誤
咱們能夠在一個類的內部在定義一個類,以下:咱們在Student類中,再定義了一個Course類用於保存學生選修的課程。
開發一個測試程序進行測試:
類的構造器分爲:主構造器、輔助構造器
l 主構造器:和類的聲明結合在一塊兒,只能有一個主構造器
Student4(val stuName:String,val stuAge:Int)
(1) 定義類的主構造器:兩個參數
(2) 聲明瞭兩個屬性:stuName和stuAge 和對應的get和set方法
l 輔助構造器:能夠有多個輔助構造器,經過關鍵字this來實現
Scala沒有靜態的修飾符,但Object對象下的成員都是靜態的 ,如有同名的class,這其做爲它的伴生類。在Object中通常能夠爲伴生類作一些初始化等操做。
下面是Java中的靜態塊的例子。在這個例子中,咱們對JDBC進行了初始化。
而Scala中的Object就至關於Java中靜態塊。
Object對象的應用
單例對象
使用應用程序對象:能夠省略main方法;須要從父類App繼承。
遇到以下形式的表達式時,apply方法就會被調用:
Object(參數1,參數2,......,參數N)
一般,這樣一個apply方法返回的是伴生類的對象;其做用是爲了省略new 關鍵字
Object的apply方法舉例:
Scala和Java同樣,使用extends關鍵字擴展類。
l 案例一:Employee類繼承Person類
l 案例二:在子類中重寫父類的方法
l 案例三:使用匿名子類
l 案例四:使用抽象類。抽象類中包含抽象方法,抽象類只能用來繼承。
l 案例五:使用抽象字段。抽象字段就是一個沒有初始值的字段
trait就是抽象類。trait跟抽象類最大的區別:trait支持多重繼承
Scala的包和Java中的包或者C++中的命名空間的目的是相同的:管理大型程序中的名稱。
Scala中包的定義和使用:
包的定義
包的引入:Scala中依然使用import做爲引用包的關鍵字,例如
並且Scala中的import能夠寫在任意地方
包能夠包含類、對象和特質,但不能包含函數或者變量的定義。很不幸,這是Java虛擬機的侷限。
把工具函數或者常量添加到包而不是某個Utils對象,這是更加合理的作法。Scala中,包對象的出現正是爲了解決這個侷限。
Scala中的包對象:常量,變量,方法,類,對象,trait(特質)
l
l
其實這裏的source就指向了這個文件中的每一個字符。
l
l
l
Scala的集合有三大類:序列Seq、集Set、映射Map,全部的集合都擴展自Iterable特質
在Scala中集合有可變(mutable)和不可變(immutable)兩種類型,immutable類型的集合初始化後就不能改變了(注意與val修飾的變量進行區別)
l 可變集合
l 不可變集合:
集合從不改變,所以能夠安全地共享其引用。
甚至是在一個多線程的應用程序當中也沒問題。
l 不可變列表(List)
不可變列表的相關操做:
l 可變列表(LinkedList):scala.collection.mutable
經常使用的序列有:Vector和Range
Vector是ArrayBuffer的不可變版本,是一個帶下標的序列
Range表示一個整數序列
l 集Set是不重複元素的集合
l 和列表不一樣,集並不保留元素插入的順序。默認以Hash集實現
Scala有一個強大的模式匹配機制,能夠應用在不少場合:
switch語句
類型檢查
Scala還提供了樣本類(case class),對模式匹配進行了優化
模式匹配示例:
l 更好的switch
l Scala的守衛
l 模式匹配中的變量
l 類型模式
l 匹配數組和列表
簡單的來講,Scala的case class就是在普通的類定義前加case這個關鍵字,而後你能夠對這些類來模式匹配。
case class帶來的最大的好處是它們支持模式識別。
首先,回顧一下前面的模式匹配:
其次,若是咱們想判斷一個對象是不是某個類的對象,跟Java同樣可使用isInstanceOf
最後,在Scala中有一種更簡單的方式來判斷,就是case class
注意:須要在class前面使用case關鍵字。
和Java或者C++同樣,類和特質能夠帶類型參數。在Scala中,使用方括號來定義類型參數
測試程序:
函數和方法也能夠帶類型參數。和泛型類同樣,咱們須要把類型參數放在方法名以後。
注意:這裏的ClassTag是必須的,表示運行時的一些信息,好比類型。
類型的上界和下界,是用來定義類型變量的範圍。它們的含義以下:
S <: T
這是類型上界的定義。也就是S必須是類型T的子類(或自己,本身也能夠認爲是本身的子類。
U >: T
這是類型下界的定義。也就是U必須是類型T的父類(或自己,本身也能夠認爲是本身的父類)。
l 一個簡單的例子:
l 一個複雜一點的例子(上界):
l 再來看一個例子:
它比 <: 適用的範圍更廣,除了全部的子類型,還容許隱式轉換過去的類型。用 <% 表示。儘可能使用視圖界定,來取代泛型的上界,由於適用的範圍更加普遍。
示例:
l 上面寫過的一個列子。這裏因爲T的上界是String,當咱們傳遞100和200的時候,就會出現類型不匹配。
l 可是100和200是能夠轉成字符串的,因此咱們可使用視圖界定讓addTwoString方法接收更普遍的數據類型,即:字符串及其子類、能夠轉換成字符串的類型。
注意:使用的是 <%
l 但實際運行的時候,會出現錯誤:
這是由於:Scala並無定義如何將Int轉換成String的規則,因此要使用視圖界定,咱們就必須建立轉換的規則。
l 建立轉換規則
l 運行成功
l 協變:
Scala的類或特徵的範型定義中,若是在類型參數前面加入+符號,就可使類或特徵變爲協變了。
逆變:
在類或特徵的定義中,在類型參數以前加上一個-符號,就可定義逆變範型類和特徵了。
總結一下:Scala的協變:泛型變量的值能夠是自己類型或者其子類的類型
Scala的逆變:泛型變量的值能夠是自己類型或者其父類的類型
所謂隱式轉換函數指的是以implicit關鍵字申明的帶有單個參數的函數。
l 前面講視圖界定時候的一個例子:
l 再舉一個例子:咱們把Fruit對象轉換成了Monkey對象
使用implicit申明的函數參數叫作隱式參數。咱們也可使用隱式參數實現隱式的轉換
所謂隱式類: 就是對類增長implicit 限定的類,其做用主要是對類的功能增強!