1. 程式人生 > >Spark2.x學習筆記:2、Scala簡單例子

Spark2.x學習筆記:2、Scala簡單例子

2、 Scala簡單例子

2.1 互動式程式設計

spark-shell是Spark互動式執行模式,提供了互動式程式設計,邊敲程式碼邊執行,不需要建立程式原始檔,方便除錯程式,有利於快速學習Spark。

[[email protected] spark-2.2.0]# bin/spark-shell 
Using Spark's default log4j profile: org/apache/spark/log4j-defaults.properties
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For
SparkR, use setLogLevel(newLevel). 17/09/03 06:32:38 WARN NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable 17/09/03 06:32:56 WARN ObjectStore: Failed to get database global_temp, returning NoSuchObjectException Spark context Web UI available at http://192.168
.80.131:4040 Spark context available as 'sc' (master = local[*], app id = local-1504434761542). Spark session available as 'spark'. Welcome to ____ __ / __/__ ___ _____/ /__ _\ \/ _ \/ _ `/ __/ '_/ /___/ .__/\_,_/_/ /_/\_\ version 2.2.0 /_/ Using Scala version 2.11.8 (Java HotSpot(TM) 64
-Bit Server VM, Java 1.8.0_112) Type in expressions to have them evaluated. Type :help for more information. scala>

Spark內建了Scala環境,進入spark-shell後可以看到scala>,可以直接輸入scala語句,回車即執行。

2.3 資料型別

Scala 極度重用了 Java 型別,Scala 的 Int 型別代表了 Java 的原始整數型別 int,Float 代表了 float,Boolean 代表了 boolean,陣列被對映到 Java 陣列。Scala 同樣重用了許多標準 Java 庫型別。例如,Scala 裡的字串文字是 Java.lang.String,而丟擲的異常必須是 java.lang.Throwable 的子類。

scala> var x:Int =10
x: Int = 10

scala> var y:Double =3.14
y: Double = 3.14

scala> var s:String ="Hello,Scala!"
s: String = Hello,Scala!

scala> var b:Boolean=true
b: Boolean = true

scala>

備註:Scala語句的分號是可選的,且通常不寫

2.2 Scala 變數

在 Scala 中,使用關鍵詞 “var” 宣告變數,使用關鍵詞 “val” 宣告常量。在 Scala 中宣告變數和常量不一定要指明資料型別,在沒有指明資料型別的情況下,其資料型別是通過變數或常量的初始值推斷出來的。 
所以,如果在沒有指明資料型別的情況下宣告變數或常量必須要給出其初始值,否則將會報錯。

scala> var x=1000
x: Int = 1000

scala> var y=3.14
y: Double = 3.14

scala> val s="hello,world"
s: String = hello,world

scala>

備註:val 變數的值只能初始化一次,再次賦值會發生錯誤,var 和 Java 的變數相同,可以隨時修改。val 是函數語言程式設計的風格,變數一旦賦值就不要再做修改。

2.3 基本運算

(1)算術運算

scala> 1+2
res1: Int = 3

scala> 2*3-5/2
res2: Int = 4

scala> 7%3
res3: Int = 1

scala> 

(2)關係運算

scala> var a=5
a: Int = 5

scala> var b=3
b: Int = 3

scala> a==b
res7: Boolean = false

scala> a>=b
res8: Boolean = true

scala> a!=b
res9: Boolean = true

scala> 

(3)邏輯運算

scala> var a=true;
a: Boolean = true

scala> var b=false;
b: Boolean = false

scala> a && b
res4: Boolean = false

scala> a || b
res5: Boolean = true

scala> !(a&&b)
res6: Boolean = true

scala>

(4)賦值運算子

scala> var x=2
x: Int = 2

scala> x+=3

scala> println(x)
5

scala> x*=3

scala> println(x)
15

scala> 

備註:

  • Scala沒有提供++和–的預算符
  • println 是 Scala 預定義匯入的標準輸出函式,所以可以直接使用

(5)運算子過載 
Scala允許方法呼叫形式a.fun(b)簡寫為a fun b

scala> 1.to(10)
res15: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> 1 to 10
res16: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

scala> "hello" + ",world"
res17: String = hello,world

scala> "hello".+(",world")
res18: String = hello,world

scala>

也就是說

  • 字串的+運算實際上就是呼叫了+方法,運算子被過載了
  • to運算可以生成一個區間集合

2.4 分支語句

scala> var x=3
x: Int = 3

scala> if(x<10)
     |   println("x<10")
x<10

scala> if(x<10){
     |   println("x<10")
     | }else{
     |   println("x>10")
     | }
x<10

scala>
scala> var x=10
x: Int = 10

scala> if(x<0){
     |   println("x<0")
     | }else if(x<10){
     |   println("0<=x<10")
     | }else{
     |   println("x>=10")
     | }
x>=10

scala>

2.4 迴圈語句

(1)while迴圈

scala> var i=1
i: Int = 1

scala> while(i<=100){
     |   sum+=i
     |   i=i+1
     | }

scala> println(sum)
5050

scala> 

(2)for迴圈 
Scala的for迴圈與Java增強型for迴圈類似,基本形式是for a <- 集合,a相當於集合的通項元素,用於遍歷集合,<-箭頭符號類似於Java增強型for迴圈的冒號,<-表示生成器。

scala> var sum=0
sum: Int = 0

scala> for(i <- 1 to 100){
     |   sum+=i
     | }

scala> println(sum)
5050

scala>

備註:Scala在for迴圈中對迴圈變數i的賦值用了“<-”符號,1 to 100指定了一個範圍

在scala中還有一個和上面的to關鍵字有類似作用的關鍵字until,它的不同之處在於不包括最後一個元素

scala> for(i <- 1 until 10) {
     |     println("i is " + i);
     | }
i is 1
i is 2
i is 3
i is 4
i is 5
i is 6
i is 7
i is 8
i is 9

scala>

2.5 函式

首先,函式/變數同是一等公民,函式與變數同等地位,函式的定義可以單獨定義,可以不依賴於類、介面或者object,而且獨立存在,獨立使用,並且可以賦值給變數。 
Spark當中的計算都是用scala函數語言程式設計來做。 
(1)庫函式

import scala.math._
import scala.math._

scala> val x=2
x: Int = 2

scala> sqrt(x)
res4: Double = 1.4142135623730951

scala> 

備註:在Scala中,_字元是“萬用字元”,類似Java中的*

(2)自定義函式 
函式的定義用 def 開始。每個函式引數後面必須帶字首冒號的型別標註,因為 Scala 編譯器沒辦法推斷函式引數型別。Scala 函式定義格式如下:

def functionName ([引數列表]) : [return type] = {
   function body
   return [expr]
}

定義一個求解最大值的函式

scala> def max(x: Int, y: Int): Int = if(x < y) y else x
max: (x: Int, y: Int)Int

scala> max(3,5)
res9: Int = 5

scala> 

備註:Scala函式可以沒有return 語句,預設返回最後一個值。

如果函式的引數在函式體內只出現一次,則可以使用下劃線代替

scala> def mul(x:Int,y:Int)=x*y
mul: (x: Int, y: Int)Int

scala> mul(2,3)
res26: Int = 6

scala> def mul=(_:Int)*(_:Int)
mul: (Int, Int) => Int

scala> mul(3,4)
res27: Int = 12

scala> 

(3)可變引數 
Scala允許指定函式的最後一個引數可重複。

scala> def prints(args:String*)={
     |   for(arg <- args){
     |      println(arg)
     |   }
     | }
prints: (args: String*)Unit

scala> prints("aa","bb","cc")
aa
bb
cc

scala> 

備註:如果函式沒有返回值,可以返回為 Unit,這個類似於 Java 的 void

(4)函式賦值 
可以將一個函式賦值給一個變數, 
val 變數名 = 函式名+空格+_ 
這裡函式名後面必須要有空格,表明是函式的原型

scala> val fmax=max _
fmax: (Int, Int) => Int = <function2>

scala> fmax(3,5)
res7: Int = 5

scala>

(5)匿名函式 
匿名函式格式: 
val 變數名 = (引數:型別) => 函式體

scala> var increase = (x: Int) => x + 1  
increase: Int => Int = <function1>

scala> var n=1;
n: Int = 1

scala> println(increase(n))
2

scala> 

程式說明:

  • (x: Int) => x + 1定義了一個匿名函式,=>表示對左邊的引數進行右邊的加工
  • 將該匿名函式賦值到 increase變數,通過函式變數即可像普通函式操作了

(6)高階函式 
因為函式的引數可以是變數,而函式又可以賦值給變數,即函式和變數地位一樣,所以函式引數也可以是函式。Scala 中允許使用高階函式, 高階函式可以使用其他函式作為引數,或者使用函式作為輸出結果。

普通函式的定義語法如下:

def funName(para1:Type1,para2:Type2):Type = { do some things }

高階函式其實就是普通函式中的引數進一步推廣了,高階函式的引數可以是一個函式,引數名就是函式名,那麼該特殊引數對應的型別怎麼寫呢?這樣就把問題轉化為尋找函式的型別的問題。函式的型別,其實就是輸入輸出的型別。 
請看下面例子:

scala> import scala.math._
import scala.math._

scala> def valueFor(f:(Double)=>Double,value:Double)={
     |     f(value)
     | }
valueFor: (f: Double => Double, value: Double)Double

scala> valueFor(ceil _,0.25)
res1: Double = 1.0

scala> valueFor(sqrt _,0.25)
res2: Double = 0.5

scala> 

說明:

  • valueFor第一個引數是函式引數,f是引數名稱,(Double)=>Double 是引數的型別;
  • 第二個引數是普通引數,引數名是value,引數型別是Double;
  • valueFor函式定義可以簡寫成def valueFor(f:(Double)=>Double,value:Double)=f(value)

再看一個例子: 
map方法接受一個函式引數,將它應用到陣列中的每個元素,返回新的陣列。

scala> import scala.math._
import scala.math._

scala> val num  = 3.14
num: Double = 3.14

scala> val func = ceil _
func: Double => Double = <function1>

scala> val array = Array(1.0,3.14,4).map(func) 
array: Array[Double] = Array(1.0, 4.0, 4.0)

scala> for(i<-array)print(i+" ")
1.0 4.0 4.0 
scala> 

(7)閉包 
閉包可以簡單的認為是可以訪問一個函式裡面區域性變數的另外一個函式。

scala> var factor = 3 
factor: Int = 3

scala> val multiplier = (i:Int) => i * factor  
multiplier: Int => Int = <function1>

scala> println(multiplier(1))
3

scala> println(multiplier(2))
6

scala> 

(8)柯里化(Currying) 
柯里化函式是將原來接手兩個引數的函式轉變成新的接收一個引數的函式過程。新函式返回一個以原有第二個引數作為引數的函式。

柯里化函式定義

scala> def mul(x:Int)=(y:Int)=>x*y
mul: (x: Int)Int => Int

scala> mul(2)(3)
res19: Int = 6

scala> (mul(2))(3)
res23: Int = 6

scala> 

說明:mul(2)(3)實際上是按照(mul(2))(3)形式計算的,mul(2)的結果(y:Int)=>2*y,這個新函式又接收引數3得到結果6。

Scala可以簡寫柯里化函式

scala> def mul(x:Int)(y:Int)=x*y
mul: (x: Int)(y: Int)Int

scala> mul(5)(3)
res20: Int = 15

再看一個例子

scala> def strcat(s1: String)(s2: String) = s1 + s2
strcat: (s1: String)(s2: String)String

scala> strcat("hello")("world")
res22: String = helloworld

scala> 

2.6 字串

scala> val msg = "hello"
msg: String = hello

scala> println(msg.length)
5

scala> println(msg.charAt(0))
h

scala> println(msg.compareTo("hi"))
-4

scala> println(msg.equals("hello"))
true

scala> val s=msg+",spark"
s: String = hello,spark

scala> println(s.substring(6))
spark

scala>

2.7 陣列

scala> var a1 = Array("QQ", "Baidu", "Google")
a1: Array[String] = Array(QQ, Baidu, Google)

scala> for(x<-a1)println(x)
QQ
Baidu
Google

scala> a1.foreach(x => println(x))
QQ
Baidu
Google

scala> a1.foreach(println(_))
QQ
Baidu
Google

scala> a1.foreach(println)
QQ
Baidu
Google
scala> var a2 =new Array[String](3)
a2: Array[String] = Array(null, null, null)

scala> array(0)="hello"
scala> a2(0)="hello"

scala> a2(1)="spark"

scala> a2(2)="!"

scala> println(a2(1))
spark

scala> 

Array是定長陣列,而ArrayBuffer是可變陣列。ArrayBuffer對應於Java中的ArrayList。

scala> import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.ArrayBuffer

scala> val ab=ArrayBuffer[Int]()
ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer()

scala> ab += 1
res7: ab.type = ArrayBuffer(1)

scala> ab += (2,3,4,5)
res8: ab.type = ArrayBuffer(1, 2, 3, 4, 5)

scala> val array=Array(7,11,13,17)
array: Array[Int] = Array(7, 11, 13, 17)

scala> ab ++= array
res9: ab.type = ArrayBuffer(1, 2, 3, 4, 5, 7, 11, 13, 17)

scala> ab.foreach(println)
1
2
3
4
5
7
11
13
17

scala> val a1=ab.toArray
a1: Array[Int] = Array(1, 2, 3, 4, 5, 7, 11, 13, 17)

scala> val buffer=a1.toBuffer
buffer: scala.collection.mutable.Buffer[Int] = ArrayBuffer(1, 2, 3, 4, 5, 7, 11, 13, 17)

scala>

備註:

  • +=表示在ArrayBuffer尾部新增元素,尾部新增多個元素時用括號包起來
  • ++=表示追加任何集合
  • toArray表示將ArrayBuffer轉換為Array
  • toArrayBuffer表示將Array轉換為ArrayBuffer

2.8 List

Scala 列表類似於陣列,它們所有元素的型別都相同,但是它們也有所不同:列表是不可變的,值一旦被定義了就不能改變,其次列表 具有遞迴的結構(也就是連結表結構)而陣列不是。。 
列表的元素型別 T 可以寫成 List[T]。

Scala的List,scala.List,不同於Java的java.util.List,總是不可變的(而Java的List可變)。更通常的說法,Scala的List是設計給函式式風格的程式設計用的。

scala> val fruit: List[String] = List("apples", "oranges", "pears")
fruit: List[String] = List(apples, oranges, pears)

scala> println(fruit(0))
apples

scala> println(fruit.head)
apples

scala> println(fruit.tail)
List(oranges, pears)

scala> println(fruit.isEmpty)
false

scala> fruit.length
res24: Int = 3

scala> fruit.foreach(x => println(x))
apples
oranges
pears

scala> fruit.foreach(println)
apples
oranges
pears

scala>  

可以使用 ::: 運算子或 List.:::() 方法或 List.concat() 方法來連線兩個或多個列表

Nil 也可以表示為一個空列表。

scala> val newFruit="bananas"::fruit
newFruit: List[String] = List(bananas, apples, oranges, pears)

scala> println(newFruit(0))
bananas

scala>
scala> val nums =List(-19,-7,0,3,11,19)
nums: List[Int] = List(-19, -7, 0, 3, 11, 19)

scala> nums.filter(_>0)
res30: List[Int] = List(3, 11, 19)

scala> nums.exists(_<0)
res31: Boolean = true

scala> nums.partition(_>0)
res34: (List[Int], List[Int]) = (List(3, 11, 19),List(-19, -7, 0))

scala>

說明: 
集合中使用下劃線是最常用的形式,下劃線代表了集合中的“某(this)”一個元素。nums.filter(_>0)等價於nums.filter(x=>x>0),其中x=>x>0是匿名函式,且引數在函式體中只出現一次,可以簡寫成_>0

2.9 元祖

與列表一樣,元組也是不可變的,但與列表不同,元組可以包含不同型別的元素。而列表應該是List[Int]或List[String]的樣子,元組可以同時擁有Int和String。元組很有用,比方說,如果你需要在方法裡返回多個物件。Java裡你將經常建立一個JavaBean樣子的類去裝多個返回值,Scala裡你可以簡單地返回一個元組。而且這麼做的確簡單:例項化一個裝有一些物件的新元組,只要把這些物件放在括號裡,並用逗號分隔即可。一旦你已經例項化了一個元組,你可以用點號,下劃線和一個基於1的元素索引訪問它。

scala> val pa = (40,"Foo")
pa: (Int, String) = (40,Foo)

scala> val pair = (40,"Foo")
pair: (Int, String) = (40,Foo)

scala> println(pair)
(40,Foo)

scala> println(pair._1)
40

scala> println(pair._2)
Foo

scala> 

你或許想知道為什麼你不能像訪問List裡的元素那樣訪問元組的,就像pair(0)。那是因為List的apply方法始終返回同樣的型別,但是元組裡的或許型別不同。這些_N數字是基於1的,而不是基於0的,因為對於擁有靜態型別元組的其他語言,如Haskell和ML,從1開始是傳統的設定。

2.10 Map

Scala對映(Map)是一組鍵/值對的物件。 任何值都可以根據鍵來進行檢索。鍵在對映中是唯一的,但值不一定是唯一的。對映也稱為雜湊表。對映有兩種,不可變的和可變的。可變物件和不可變物件之間的區別在於,當物件不可變時,物件本身無法更改。

預設情況下,Scala使用不可變對映(Map)。如果要使用可變集合(Set),則必須明確匯入scala.collection.mutable.Map類。如果想同時使用可變的和不可變對映(Map),那麼可以繼續引用不可變對映(Map),但是可以將mutable集合引用mutable.Map。

scala> import scala.collection.mutable.Map
import scala.collection.mutable.Map

scala> var map = Map(1 -> "a", 2 -> "b", 3 -> "c") 
map: scala.collection.mutable.Map[Int,String] = Map(2 -> b, 1 -> a, 3 -> c)

scala> println(map.keys)
Set(2, 1, 3)

scala> println(map.values)
HashMap(b, a, c)

scala> println(map.isEmpty)
false

scala> map += (4 -> "d")   
res12: scala.collection.mutable.Map[Int,String] = Map(2 -> b, 4 -> d, 1 -> a, 3 -> c)

scala> map.foreach(value => print(value + " "))   
(2,b) (4,d) (1,a) (3,c) 
scala>

總結: 
scala提供了許多用於新增和移除元素的操作符,總結如下。

  • 向後(:+),向前(+:)追加元素到有序集合
  • 新增(+)元素到無序集合
  • 用-移除元素
  • 用++和–來批量新增和移除元素
  • 對於列表,優先使用::和:::

2.10 將函式對映到集合

任何一種函式式語言中,都有map函式與faltMap這兩個函式

  • map函式的用法,顧名思義,將一個函式傳入map中,然後利用傳入的這個函式,將集合中的每個元素處理,並將處理後的結果返回。
  • 而flatMap與map唯一不一樣的地方就是傳入的函式在處理完後返回值必須是List,其實這也不難理解,既然是flatMap,那除了map以外必然還有flat的操作,所以需要返回值是List才能執行flat這一步。
scala> val nums=List(1,2,3,4)
nums: List[Int] = List(1, 2, 3, 4)

scala> nums.map(x=>2+x)
res24: List[Int] = List(3, 4, 5, 6)

scala> nums.map(_+1)
res25: List[Int] = List(2, 3, 4, 5)

scala> 
scala> val data = List("Hadoop","Java","Spark")
data: List[String] = List(Hadoop, Java, Spark)

scala>  println(data.flatMap(_.toList))
List(H, a, d, o, o, p, J, a, v, a, S, p, a, r, k)

scala>

2.11 正則表示式

(1)模式匹配 
模式匹配包括一系列備選項,每個替代項以關鍵字大小寫為單位。每個替代方案包括一個模式和一個或多個表示式,如果模式匹配,將會進行評估計算。箭頭符號=>將模式與表示式分離。

scala> def matchTest(x: Int): String = x match {
     |       case 1 => "one"
     |       case 2 => "two"
     |       case _ => "many"
     | }
matchTest: (x: Int)String

scala> matchTest(1)
res37: String = one

scala> matchTest(2)
res38: String = two

scala> matchTest(3)
res39: String = many

scala>

(2)正則表示式 
scala.util.matching包中提供的Regex類支援和實現正則表示式。 
以下例項演示了使用正則表示式查詢單詞 Scala

scala> import scala.util.matching.Regex
import scala.util.matching.Regex

scala> val pattern = "Scala".r
pattern: scala.util.matching.Regex = Scala

scala> val str = "Scala is Scalable and cool"
str: String = Scala is Scalable and cool

scala> println(pattern findFirstIn str)
Some(Scala)

scala> 

說明:

  • 使用 String 類的 r() 方法構造了一個Regex物件。
  • 使用 findFirstIn 方法找到首個匹配項。
  • 如果需要檢視所有的匹配項可以使用 findAllIn 方法。

你可以使用 mkString( ) 方法來連線正則表示式匹配結果的字串,並可以使用管道(|)來設定不同的模式

scala> val pattern = new Regex("(S|s)cala")
pattern: scala.util.matching.Regex = (S|s)cala

scala> val str = "Scala is scalable and cool"
str: String = Scala is scalable and cool

scala> println((pattern findAllIn str).mkString(","))
Scala,scala

scala>

程式說明: 
(S|s)cala表示首字母可以是大寫 S 或小寫 s 
- mkString(“,”)使用逗號 , 連線返回結果

scala> val pattern = """(\d{1,3}\.){3}\d{1,3}""".r
pattern: scala.util.matching.Regex = (\d{1,3}\.){3}\d{1,3}

scala> val str="my ip is 192.168.1.81"
str: String = my ip is 192.168.1.81

scala> println(pattern findAllIn str toList)
warning: there was one feature warning; re-run with -feature for details
List(192.168.1.81)

scala> for(matchStr <- pattern.findAllIn(str)){
     |     println(matchStr)
     | }
192.168.1.81

scala> 

2.12 異常處理

scala> import java.io.FileReader
import java.io.FileReader

scala> import java.io.FileNotFoundException
import java.io.FileNotFoundException

scala> import java.io.IOException
import java.io.IOException

scala> try{
     |    val file=new FileReader("input.txt")
     | }catch{
     |    case ex: FileNotFoundException =>{
     |        println("Missing file exception")
     |    }
     |    case ex: IOException => {
     |        println("IO Exception")
     |    }
     | }
Missing file exception

scala> 

2.13 類與物件

(1)類的定義 
public 是 Scala 的預設訪問級別

scala> class Point(x1:Int,y1:Int){
     |     var x:Int=x1
     |     var y:Int=y1
     |     
     |     def move(dx:Int,dy:Int){
     |           x=x+dx;
     |           y=y+dy;
     |     }
     |     def print(){
     |           println("("+x+","+y+")")
     |     }
     | }
defined class Point

scala> var p=new Point(10,20)
p: Point = [email protected]3e7f7cd2

scala> p.move(2,3)

scala> p.print()
(12,23)

scala> p.print
(12,23)

程式說明:

  • 構造器的引數直接放到類名之後。
  • 由於public是預設的,所以Point類的x和y欄位是public欄位。
  • Scala對每個欄位都提供了getter和setter方法,在生成面向JVM的類時,每個欄位生成一個私有欄位以及對應的getter和setter方法,這兩個方法是public,在需要時可以重新定義getter和setter。(對於私有欄位生成的getter和setter方法也是私有的。)
  • 呼叫無參方法,可以省略圓括號。建議對於修改物件值的方法使用圓括號,只是讀值的方法不使用圓括號。

(2)輔助構造器

scala> class Point{
     |     var x:Int=0
     |     var y:Int=0
     |    
     |     def this(x:Int){
     |        this()
     |        this.x=x
     |     }
     | 
     |     def this(x:Int,y:Int){
     |        this(x)
     |        this.y=y
     |     }
     | }
defined class Point

scala> val p1=new Point
p1: Point = [email protected]6f694dd2

scala> var p2=new Point(1)
p2: Point = [email protected]7437ec41

scala> var p3=new Point(1,2)
p3: Point = [email protected]630643f6

scala> 

程式說明:

  • 一個類沒有顯示定義構造器,則自動擁有一個無參構造器
  • 輔助構造器的名稱是this

(3)單例物件 
Scala比Java更面向物件,因為在Scala中不能擁有靜態成員,Scala它使用單例物件。單例是一種只能有一個例項的物件。使用object關鍵字物件而不是class關鍵字建立單例。由於無法例項化單例物件,因此無法將引數傳遞給主建構函式。 
object下的成員都是靜態的,若有同名的class,這其作為它的伴生類。在object中一般可以為伴生類做一些初始化等操作

(4)伴生物件 
在Java或C++中,通常會用到既有例項方法也有靜態方法的類,在Scala中將靜態成員分離出來,形成與類同名的伴生物件(companion object)。類和它的伴生物件必須定義在同一個原始檔中。類被稱為是這個單例物件的伴生類(companion class)。

例子如下,直接將伴生類和伴生物件定義在同一原始檔中即可。

class Account {
    val id = Account.newUniqueNumber()
    private var balance = 0.0
    def deposit(amount: Double){ balance += amount }
}

object Account { //伴生物件
    private var lastNumber = 0
    def newUniqueNumber() = { lastNumber += 1; lastNumber}
}

在spark-shell模式下,要同時定義類和物件,必須用貼上模式。鍵入:paste,然後鍵入或貼上類和物件的定義,最後一Ctrl+D退出貼上模式。

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Account {
    val id = Account.newUniqueNumber()
    private var balance = 0.0
    def deposit(amount: Double){ balance += amount }
}

object Account { //伴生物件
    private var lastNumber = 0
    def newUniqueNumber() = { lastNumber += 1; lastNumber}
}

// Exiting paste mode, now interpreting.

defined class Account
defined object Account

scala> 

注意:類和他的伴生物件可以相互訪問私有成員,他們必須定義在同一個原始檔中。

(5)獨立物件 
不與伴生類共享名稱的單例物件稱為獨立物件。它可以用在很多地方,例如作為相關功能方法的工具類,或者定義Scala應用的入口點。

比如下面程式中的Demo就是獨立物件,其中包含了main方法,類似與Java的主類中的main方法,是應用程式的入口。

import java.io._

class Point(val xc: Int, val yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
   }
}

object Demo {
   def main(args: Array[String]) {
      val point = new Point(10, 20)
      printPoint

      def printPoint{
         println ("Point x location : " + point.x);
         println ("Point y location : " + point.y);
      }
   }
}

將上述程式儲存在原始檔:Demo.scala 中,使用Scala命令編譯和執行此程式。

(6)apply方法 
前面我們通過語句var a1 = Array("QQ", "Baidu", "Google")直接建立了陣列,為什麼不使用new呢,為什麼不使用Array的構造器呢?類似的語句還很多,比如List("apples", "oranges", "pears")。 
這是因為Scala在伴生物件中定義了apply方法,該方法返回的是伴生類的物件。

scala> :paste
// Entering paste mode (ctrl-D to finish)