Scala之——Scala程式設計基礎
1. Scala 基礎
1.1.宣告變數
package com.lyz.scala /** * Created by liuyazhuang */ object VariableDemo { def main(args: Array[String]) { //使用val定義的變數值是不可變的,相當於java裡用final修飾的變數 val i = 1 //使用var定義的變數是可變得,在Scala中鼓勵使用val var s = "hello" //Scala編譯器會自動推斷變數的型別,必要的時候可以指定型別 //變數名在前,型別在後 val str: String = "liuyazhuang" } }
1.2.常用型別
Scala 和 Java 一樣,有 7 種數值型別 Byte、 Char、 Short、 Int、 Long、 Float 和 Double(無包裝型別)和一個 Boolean 型別
1.3.條件表示式
Scala 的的條件表示式比較簡潔,例如:package com.lyz.scala /** * Created by liuyazhuang. */ object ConditionDemo { def main(args: Array[String]) { val x = 1 //判斷x的值,將結果賦給y val y = if (x > 0) 1 else -1 //列印y的值 println(y) //支援混合型別表示式 val z = if (x > 1) 1 else "error" //列印z的值 println(z) //如果缺失else,相當於if (x > 2) 1 else () val m = if (x > 2) 1 println(m) //在scala中每個表示式都有值,scala中有個Unit類,寫做(),相當於Java中的void val n = if (x > 2) 1 else () println(n) //if和else if val k = if (x < 0) 0 else if (x >= 1) 1 else -1 println(k) } }
1.4.塊表示式
package com.lyz.scala /** * Created by liuyazhuang. */ object BlockExpressionDemo { def main(args: Array[String]) { val x = 0 //在scala中{}中課包含一系列表示式,塊中最後一個表示式的值就是塊的值 //下面就是一個塊表示式 val result = { if (x < 0){ -1 } else if(x >= 1) { 1 } else { "error" } } //result的值就是塊表示式的結果 println(result) } }
1.5.迴圈
在 scala 中有 for 迴圈和 while 迴圈,用 for 迴圈比較多for 迴圈語法結構: for (i <- 表示式/陣列/集合)
package com.lyz.scala
/**
* Created by liuyazhuang.
*/
object ForDemo {
def main(args: Array[String]) {
//for(i <- 表示式),表示式1 to 10返回一個Range(區間)
//每次迴圈將區間中的一個值賦給i
for (i <- 1 to 10)
println(i)
//for(i <- 陣列)
val arr = Array("a", "b", "c")
for (i <- arr)
println(i)
//高階for迴圈
//每個生成器都可以帶一個條件,注意:if前面沒有分號
for(i <- 1 to 3; j <- 1 to 3 if i != j)
print((10 * i + j) + " ")
println()
//for推導式:如果for迴圈的迴圈體以yield開始,則該迴圈會構建出一個集合
//每次迭代生成集合中的一個值
val v = for (i <- 1 to 10) yield i * 10
println(v)
}
}
1.6.呼叫方法和函式
Scala 中的+ - * / %等操作符的作用與 Java 一樣,位操作符 & | ^ >> <<也一樣。只是有一點特別的: 這些操作符實際上是方法。例如:
a + b
是如下方法呼叫的簡寫:
a.+(b)
a 方法 b 可以寫成 a.方法(b)
1.7.定義方法和函式
1.7.1. 定義方法
方法的返回值型別可以不寫,編譯器可以自動推斷出來,但是對於遞迴函式,必須指定返回型別
1.7.2. 定義函式
1.7.3. 方法和函式的區別
在函數語言程式設計語言中,函式是“頭等公民”, 它可以像任何其他資料型別一樣被傳遞和操作
案例:首先定義一個方法,再定義一個函式,然後將函式傳遞到方法裡面
package com.lyz.scala
/**
* Created by Liuyazhuang
*/
object MethodAndFunctionDemo {
//定義一個方法
//方法m2引數要求是一個函式,函式的引數必須是兩個Int型別
//返回值型別也是Int型別
def m1(f: (Int, Int) => Int) : Int = {
f(2, 6)
}
//定義一個函式f1,引數是兩個Int型別,返回值是一個Int型別
val f1 = (x: Int, y: Int) => x + y
//再定義一個函式f2
val f2 = (m: Int, n: Int) => m * n
//main方法
def main(args: Array[String]) {
//呼叫m1方法,並傳入f1函式
val r1 = m1(f1)
println(r1)
//呼叫m1方法,並傳入f2函式
val r2 = m1(f2)
println(r2)
}
}
1.7.4. 將方法轉換成函式(神奇的下劃線)
2. 陣列、對映、元組、集合
2.1.陣列
2.1.1. 定長陣列和變長陣列
package com.lyz.scala
import scala.collection.mutable.ArrayBuffer
/**
* Created by Liuyazhuang
*/
object ArrayDemo {
def main(args: Array[String]) {
//初始化一個長度為8的定長陣列,其所有元素均為0
val arr1 = new Array[Int](8)
//直接列印定長陣列,內容為陣列的hashcode值
println(arr1)
//將陣列轉換成陣列緩衝,就可以看到原陣列中的內容了
//toBuffer會將陣列轉換長陣列緩衝
println(arr1.toBuffer)
//注意:如果new,相當於呼叫了陣列的apply方法,直接為陣列賦值
//初始化一個長度為1的定長陣列
val arr2 = Array[Int](10)
println(arr2.toBuffer)
//定義一個長度為3的定長陣列
val arr3 = Array("hadoop", "storm", "spark")
//使用()來訪問元素
println(arr3(2))
//////////////////////////////////////////////////
//變長陣列(陣列緩衝)
//如果想使用陣列緩衝,需要匯入import scala.collection.mutable.ArrayBuffer包
val ab = ArrayBuffer[Int]()
//向陣列緩衝的尾部追加一個元素
//+=尾部追加元素
ab += 1
//追加多個元素
ab += (2, 3, 4, 5)
//追加一個數組++=
ab ++= Array(6, 7)
//追加一個數組緩衝
ab ++= ArrayBuffer(8,9)
//列印陣列緩衝ab
//在陣列某個位置插入元素用insert
ab.insert(0, -1, 0)
//刪除陣列某個位置的元素用remove
ab.remove(8, 2)
println(ab)
}
}
2.1.2. 遍歷陣列
1.增強 for 迴圈2.好用的 until 會生成腳標, 0 until 10 包含 0 不包含 10
package com.lyz.scala
/**
* Created by Liuyazhuang
*/
object ForArrayDemo {
def main(args: Array[String]) {
//初始化一個數組
val arr = Array(1,2,3,4,5,6,7,8)
//增強for迴圈
for(i <- arr)
println(i)
//好用的until會生成一個Range
//reverse是將前面生成的Range反轉
for(i <- (0 until arr.length).reverse)
println(arr(i))
}
}
2.1.3. 陣列轉換
yield 關鍵字將原始的陣列進行轉換會產生一個新的陣列,原始的陣列不變package com.lyz.scala
/**
* Created by Liuyazhuang
*/
object ArrayYieldDemo {
def main(args: Array[String]) {
//定義一個數組
val arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
//將偶數取出乘以10後再生成一個新的陣列
val res = for (e <- arr if e % 2 == 0) yield e * 10
println(res.toBuffer)
//更高階的寫法,用著更爽
//filter是過濾,接收一個返回值為boolean的函式
//map相當於將陣列中的每一個元素取出來,應用傳進去的函式
val r = arr.filter(_ % 2 == 0).map(_ * 10)
println(r.toBuffer)
}
}
2.1.4. 陣列常用演算法
在 Scala 中,陣列上的某些方法對陣列進行相應的操作非常方便!2.2.對映
在 Scala 中,把雜湊表這種資料結構叫做對映2.2.1. 構建對映
2.2.2. 獲取和修改對映中的值
好用的 getOrElse
注意:在 Scala 中,有兩種 Map,一個是 immutable 包下的 Map,該 Map 中的內容不可變;
另一個是 mutable 包下的 Map,該 Map 中的內容可變
例子:
注意:通常我們在建立一個集合是會用 val 這個關鍵字修飾一個變數(相當於 java 中的 final),
那麼就意味著該變數的引用不可變,該引用中的內容是不是可變,取決於這個引用指向的集
合的型別
2.3.元組
對映是 K/V 對偶的集合,對偶是元組的最簡單形式,元組可以裝著多個不同型別的值。
2.3.1. 建立元組
2.3.2. 獲取元組中的值
2.3.3. 將對偶的集合轉換成對映
2.3.4. 拉鍊操作
zip 命令可以將多個值繫結在一起
注意:如果兩個陣列的元素個數不一致,拉鍊操作後生成的陣列的長度為較小的那個陣列的
元素個數
2.4.集合
Scala 的集合有三大類:序列 Seq、集 Set、對映 Map,所有的集合都擴充套件自 Iterable 特質
在 Scala 中集合有可變(mutable)和不可變(immutable)兩種型別, immutable 型別的集合
初始化後就不能改變了(注意與 val 修飾的變數進行區別)
2.4.1. 序列
不可變的序列 import scala.collection.immutable._
在 Scala 中列表要麼為空(Nil 表示空列表)要麼是一個 head 元素加上一個 tail 列表。
9 :: List(5, 2) :: 操作符是將給定的頭和尾建立一個新的列表
注意: :: 操作符是右結合的,如 9 :: 5 :: 2 :: Nil 相當於 9 :: (5 :: (2 :: Nil))
package com.lyz.collect
object ImmutListDemo {
def main(args: Array[String]) {
//建立一個不可變的集合
val lst1 = List(1,2,3)
//將0插入到lst1的前面生成一個新的List
val lst2 = 0 :: lst1
val lst3 = lst1.::(0)
val lst4 = 0 +: lst1
val lst5 = lst1.+:(0)
//將一個元素新增到lst1的後面產生一個新的集合
val lst6 = lst1 :+ 3
val lst0 = List(4,5,6)
//將2個list合併成一個新的List
val lst7 = lst1 ++ lst0
//將lst1插入到lst0前面生成一個新的集合
val lst8 = lst1 ++: lst0
//將lst0插入到lst1前面生成一個新的集合
val lst9 = lst1.:::(lst0)
println(lst9)
}
}
可變的序列import scala.collection.mutable._
package com.lyz.collect
import scala.collection.mutable.ListBuffer
object MutListDemo extends App{
//構建一個可變列表,初始有3個元素1,2,3
val lst0 = ListBuffer[Int](1,2,3)
//建立一個空的可變列表
val lst1 = new ListBuffer[Int]
//向lst1中追加元素,注意:沒有生成新的集合
lst1 += 4
lst1.append(5)
//將lst1中的元素最近到lst0中, 注意:沒有生成新的集合
lst0 ++= lst1
//將lst0和lst1合併成一個新的ListBuffer 注意:生成了一個集合
val lst2= lst0 ++ lst1
//將元素追加到lst0的後面生成一個新的集合
val lst3 = lst0 :+ 5
}
2.5. Set
不可變的 Setpackage com.lyz.collect
import scala.collection.immutable.HashSet
object ImmutSetDemo extends App{
val set1 = new HashSet[Int]()
//將元素和set1合併生成一個新的set,原有set不變
val set2 = set1 + 4
//set中元素不能重複
val set3 = set1 ++ Set(5, 6, 7)
val set0 = Set(1,3,4) ++ set1
println(set0.getClass)
}
可變的 Set package com.lyz.collect
import scala.collection.mutable
object MutSetDemo extends App{
//建立一個可變的HashSet
val set1 = new mutable.HashSet[Int]()
//向HashSet中新增元素
set1 += 2
//add等價於+=
set1.add(4)
set1 ++= Set(1,3,5)
println(set1)
//刪除一個元素
set1 -= 5
set1.remove(2)
println(set1)
}
2.6. Map
package com.lyz.collect
import scala.collection.mutable
object MutMapDemo extends App{
val map1 = new mutable.HashMap[String, Int]()
//向map中新增資料
map1("spark") = 1
map1 += (("hadoop", 2))
map1.put("storm", 3)
println(map1)
//從map中移除元素
map1 -= "spark"
map1.remove("hadoop")
println(map1)
}
3. 類、物件、繼承、特質
Scala 的類與 Java、 C++的類比起來更簡潔,學完之後你會更愛 Scala!!!3.1.類
3.1.1. 類的定義
//在Scala中,類並不用宣告為public。
//Scala原始檔中可以包含多個類,所有這些類都具有公有可見性。
class Person {
//用val修飾的變數是隻讀屬性,有getter但沒有setter
//(相當與Java中用final修飾的變數)
val id = "9527"
//用var修飾的變數既有getter又有setter
var age: Int = 18
//類私有欄位,只能在類的內部使用
private var name: String = "唐伯虎"
//物件私有欄位,訪問許可權更加嚴格的,Person類的方法只能訪問到當前物件的欄位
private[this] val pet = "小強"
}
3.1.2. 構造器
注意:主構造器會執行類定義中的所有語句/**
*每個類都有主構造器,主構造器的引數直接放置類名後面,與類交織在一起
*/
class Student(val name: String, val age: Int){
//主構造器會執行類定義中的所有語句
println("執行主構造器")
try {
println("讀取檔案")
throw new IOException("io exception")
} catch {
case e: NullPointerException => println("列印異常Exception : " + e)
case e: IOException => println("列印異常Exception : " + e)
} finally {
println("執行finally部分")
}
private var gender = "male"
//用this關鍵字定義輔助構造器
def this(name: String, age: Int, gender: String){
//每個輔助構造器必須以主構造器或其他的輔助構造器的呼叫開始
this(name, age)
println("執行輔助構造器")
this.gender = gender
}
}
/**
*構造器引數可以不帶val或var,如果不帶val或var的引數至少被一個方法所使用,
*那麼它將會被提升為欄位
*/
//在類名後面加private就變成了私有的
class Queen private(val name: String, prop: Array[String], private var age: Int = 18){
println(prop.size)
//prop被下面的方法使用後,prop就變成了不可變得物件私有欄位,等同於private[this] val prop
//如果沒有被方法使用該引數將不被儲存為欄位,僅僅是一個可以被主構造器中的程式碼訪問的普通引數
def description = name + " is " + age + " years old with " + prop.toBuffer
}
object Queen{
def main(args: Array[String]) {
//私有的構造器,只有在其伴生物件中使用
val q = new Queen("hatano", Array("蠟燭", "皮鞭"), 20)
println(q.description())
}
}
3.2.物件
3.2.1. 單例物件
在 Scala 中沒有靜態方法和靜態欄位,但是可以使用 object 這個語法結構來達到同樣的目的1.存放工具方法和常量
2.高效共享單個不可變的例項
3.單例模式
package com.lyz.scala
import scala.collection.mutable.ArrayBuffer
/**
* Created by Liuyazhuang.
*/
object SingletonDemo {
def main(args: Array[String]) {
//單例物件,不需要new,用【類名.方法】呼叫物件中的方法
val session = SessionFactory.getSession()
println(session)
}
}
object SessionFactory{
//該部分相當於java中的靜態塊
var counts = 5
val sessions = new ArrayBuffer[Session]()
while(counts > 0){
sessions += new Session
counts -= 1
}
//在object中的方法相當於java中的靜態方法
def getSession(): Session ={
sessions.remove(0)
}
}
class Session{
}
3.2.2. 伴生物件
在 Scala 的類中,與類名相同的物件叫做伴生物件,類和伴生物件之間可以相互訪問私有的方法和屬性
package com.lyz.scala
/**
* Created by Liuyazhuang.
*/
class Dog {
val id = 1
private var name = "liuyazhuang"
def printName(): Unit ={
//在Dog類中可以訪問伴生物件Dog的私有屬性
println(Dog.CONSTANT + name )
}
}
/**
* 伴生物件
*/
object Dog {
//伴生物件中的私有屬性
private val CONSTANT = "汪汪汪 : "
def main(args: Array[String]) {
val p = new Dog
//訪問私有的欄位name
p.name = "123"
p.printName()
}
}
3.2.3. apply 方法
通常我們會在類的伴生物件中定義 apply 方法,當遇到類名(引數 1,...引數 n)時 apply 方法會被呼叫
package com.lyz.scala
/**
* Created by Liuyazhuang.
*/
object ApplyDemo {
def main(args: Array[String]) {
//呼叫了Array伴生物件的apply方法
//def apply(x: Int, xs: Int*): Array[Int]
//arr1中只有一個元素5
val arr1 = Array(5)
println(arr1.toBuffer)
//new了一個長度為5的array,數組裡麵包含5個null
var arr2 = new Array(5)
}
}
3.2.4. 應用程式物件
Scala 程式都必須從一個物件的 main 方法開始,可以通過擴充套件 App 特質,不寫 main 方法package com.lyz.scala
/**
* Created by Liuyazhuang.
*/
object AppObjectDemo extends App{
//不用寫main方法
println("I love you Scala")
}
3.3.繼承
3.3.1. 擴充套件類
在 Scala 中擴充套件類的方式和 Java 一樣都是使用 extends 關鍵字3.3.2. 重寫方法
在 Scala 中重寫一個非抽象的方法必須使用 override 修飾符3.3.3. 型別檢查和轉換
Scala | Java |
obj.isInstanceOf[C] | obj instanceof C |
obj.asInstanceOf[C] | (C)obj |
classOf[C] | C.class |
3.3.4. 超類的構造
package com.lyz.scala
/**
* Created by Liuyazhuang.
*/
object ClazzDemo {
def main(args: Array[String]) {
//val h = new Human
//println(h.fight)
}
}
trait Flyable{
def fly(): Unit ={
println("I can fly")
}
def fight(): String
}
abstract class Animal {
def run(): Int
val name: String
}
class Human extends Animal with Flyable{
val name = "abc"
//列印幾次"ABC"?
val t1,t2,(a, b, c) = {
println("ABC")
(1,2,3)
}
println(a)
println(t1._1)
//在Scala中重寫一個非抽象方法必須用override修飾
override def fight(): String = {
"fight with 棒子"
}
//在子類中重寫超類的抽象方法時,不需要使用override關鍵字,寫了也可以
def run(): Int = {
1
}
}
4. 模式匹配和樣例類
Scala 有一個十分強大的模式匹配機制,可以應用到很多場合:如 switch 語句、型別檢查等。並且 Scala 還提供了樣例類,對模式匹配進行了優化,可以快速進行匹配
4.1.匹配字串
package com.lyz.cases
import scala.util.Random
object CaseDemo01 extends App{
val arr = Array("YoshizawaAkiho", "YuiHatano", "AoiSola")
val name = arr(Random.nextInt(arr.length))
name match {
case "YoshizawaAkiho" => println("吉澤老師...")
case "YuiHatano" => println("波多老師...")
case _ => println("真不知道你們在說什麼...")
}
}
4.2.匹配型別
package com.lyz.cases
import scala.util.Random
object CaseDemo01 extends App{
//val v = if(x >= 5) 1 else if(x < 2) 2.0 else "hello"
val arr = Array("hello", 1, 2.0, CaseDemo)
val v = arr(Random.nextInt(4))
println(v)
v match {
case x: Int => println("Int " + x)
case y: Double if(y >= 0) => println("Double "+ y)
case z: String => println("String " + z)
case _ => throw new Exception("not match exception")
}
}
注意: case y: Double if(y >= 0) => ...模式匹配的時候還可以新增守衛條件。如不符合守衛條件,將掉入 case _中
4.3.匹配陣列、元組
package com.lyz.cases
object CaseDemo03 extends App{
val arr = Array(1, 3, 5)
arr match {
case Array(1, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
case Array(0, _*) => println("0 ...")
case _ => println("something else")
}
val lst = List(3, -1)
lst match {
case 0 :: Nil => println("only 0")
case x :: y :: Nil => println(s"x: $x y: $y")
case 0 :: tail => println("0 ...")
case _ => println("something else")
}
val tup = (2, 3, 7)
tup match {
case (1, x, y) => println(s"1, $x , $y")
case (_, z, 5) => println(z)
case _ => println("else")
}
}
注意:在 Scala 中列表要麼為空(Nil 表示空列表)要麼是一個 head 元素加上一個 tail 列表。9 :: List(5, 2) :: 操作符是將給定的頭和尾建立一個新的列表
注意: :: 操作符是右結合的,如 9 :: 5 :: 2 :: Nil 相當於 9 :: (5 :: (2 :: Nil))
4.4.樣例類
在 Scala 中樣例類是一中特殊的類,可用於模式匹配。 case class 是多例的,後面要跟構造參數, case object 是單例的
package com.lyz.cases
import scala.util.Random
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
object CaseDemo04 extends App{
val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))
arr(Random.nextInt(arr.length)) match {
case SubmitTask(id, name) => {
println(s"$id, $name")//前面需要加上s, $id直接取id的值
}
case HeartBeat(time) => {
println(time)
}
case CheckTimeOutTask => {
println("check")
}
}
}
4.5. Option 型別
在 Scala 中 Option 型別樣例類用來表示可能存在或也可能不存在的值(Option 的子類有 Some和 None)。 Some 包裝了某個值, None 表示沒有值
package com.lyz.cases
object OptionDemo {
def main(args: Array[String]) {
val map = Map("a" -> 1, "b" -> 2)
val v = map.get("b") match {
case Some(i) => i
case None => 0
}
println(v)
//更好的方式
val v1 = map.getOrElse("c", 0)
println(v1)
}
}
4.6.偏函式
被包在花括號內沒有 match 的一組 case 語句是一個偏函式,它是 PartialFunction[A, B]的一個例項, A 代表引數型別, B 代表返回型別,常用作輸入模式匹配
package com.lyz.cases
object PartialFuncDemo {
def func1: PartialFunction[String, Int] = {
case "one" => 1
case "two" => 2
case _ => -1
}
def func2(num: String) : Int = num match {
case "one" => 1
case "two" => 2
case _ => -1
}
def main(args: Array[String]) {
println(func1("one"))
println(func2("one"))
}
}