包含內(nèi)容
- 1基礎(chǔ)
- 2控制結(jié)構(gòu)和函數(shù)
- 3數(shù)組相關(guān)操作
- 4映射和元組
- 5類
- 6對象
- 7.包和引入(x)
- 8繼承
1基礎(chǔ)
- sqrt(2)、pow這些數(shù)學(xué)函數(shù)在scala.math包中定義,當(dāng)然該包中還包含一些類,不僅有函數(shù)。
- import scala.math._ 等于import math._,scala可以省略,代表引入該包下所有東西。
- import java.util.{HashMap => JavaHashMap} //重命名
- import scala.math.{BigInt =>_ , _} 引入除BigInt外的所有成員
- "_" 是scala中通配符,類似java的"*"
- RichInt、RichDouble、RichChar比Int、Double、Char提供了更豐富功能
- apply方法:伴生對象構(gòu)建對象的常用方法。
- a.方法(b) 等于 a 方法 b 。如a + b = a.+(b)
- StringOps的def count (p :(Char) => Boolean):Int 該方法需要傳入一個接受單個Char并返回true或false的函數(shù),用于指定哪些字符串應(yīng)當(dāng)被清點。
- scala底層用java.lang.String類來表示字符串,不過通過StirngOps類給字符串追加了上百種操作,
- "hello".intersect("world") ,這個表達式中java.lang.String對象"hello"被隱式轉(zhuǎn)換成StringOps對象,接著調(diào)用StringOps類的intersect方法。
- Unit類型等同于java或c++中的void
- ScalaDoc文檔中,每個類旁邊的O和C,分別對應(yīng)類(C)或伴生對象(O)
- scala中沒有++操作,如value++,不允許
2控制結(jié)構(gòu)和函數(shù)
REPL
- 在命令行鍵入scala進入REPL模式
- REPL中鍵入:paste可以把代碼粘貼進去,然后Ctrl+D,REPL就會把代碼塊當(dāng)做一個整體來分析。
- Tab鍵提示
表達式、塊表達式和賦值
scala中表達式(eg:3+4)和語句(eg:if)都有值。
val s = if( x>5) 22 else 2 //if語句給變量賦值
-
塊表達式{},包含一些了表達式,塊中最后一個表達式就是塊的值。
- val distance={ val ds = x-x0; val dy =y -y0; sqrt( dx * dx + dy * dy) } //{}的值取最后一個表達式的值
-
賦值語句的值為Unit,val d = { r = 5 } //此時塊表達式的值為Unit,所以d的值為Unit。
- var y = 3; val x = y = 1 ;//別這樣做,結(jié)果:y = 1,但是x = Unit.
輸出:print、println、printf("hello,%s! you are %d years old.\n","liang",23)
輸入:readLine、readInt、readFloat、readBoolean、readChar etc.
循環(huán):
基本語法結(jié)構(gòu):for( 變量 <- 表達式) ,eg:for( i<- 0.until(s.length) )、for(ch <- "Hello" )
高級for循環(huán):以變量 <- 表達式的形式提供多個生成器,用分號隔開它們,每個生成器都可以帶一個守衛(wèi):以if開頭的Boolean表達式。
for( i < 1 to 3 ;j <- 1 to 3 ) print( (10* i +j ) + " ")
//打?。?1 12 13 21 22 23 31 32 33 ,每個i都會經(jīng)歷j<-1 to 3for( i < 1 to 3 ; from = 4 -i ;j <- from to 3 ) print( (10* i +j ) + " ")
//打?。?13 22 23 31 32 33 ,每個i都會經(jīng)歷from = 4 -i ;每個form都會經(jīng)過j <- from to 3for( i < 1 to 3 ;j <- 1 to 3 if i != j) print( (10* i +j ) + " ") //帶守衛(wèi),注守衛(wèi)沒有分號
//打?。?12 13 21 23 31 33 ,每個i都會經(jīng)歷j <- 1 to 3 if i != j-
for推導(dǎo)式:循環(huán)體以yield開始,則循環(huán)會構(gòu)造出一個集合,每次迭代生成集合中的一個值。且集合與它的第一個生成是類型兼容的。
- for( c <- "Hello";i<-0 to 1) yield (c+i).toChar
// String = "HIeflmlmop" - for( i <-0 to 1; c <-"Hello") yield (c+i).toChar
//Vector(H, e, l, l, o, I, f, m, m, p)
- for( c <- "Hello";i<-0 to 1) yield (c+i).toChar
-
沒有break語句,但是Breaks對象提供了break方法。
函數(shù)
方法對對象(不是類)進行操作,函數(shù)不是。在java中我們用靜態(tài)方法來模擬函數(shù)。scala.math中的sqrt就是函數(shù)。
定義函數(shù):函數(shù)名、參數(shù)、函數(shù)體
def abs( x:Double ) = if( x >= 0 ) x else -x;
參數(shù)類型必須給出。
函數(shù)體有多個表達式,可以使用代碼塊。
返回類型:函數(shù)不是遞歸的就不需要指定返回類型,可以根據(jù)=符號右側(cè)表達推斷出來。
默認參數(shù)
定義: def decorate(str :String, left: String ="[", right: String ="]") = left + str +right
調(diào)用:decorate("hello") //返回"[hell0"變長參數(shù):語法
def sum(args: Int* )={
var result = 0
for (arg <- args ) result += arg
result
}
- sum函數(shù)被調(diào)用時傳入的單個參數(shù)必須是整數(shù),而不是一個整數(shù)區(qū)間,如果傳一個區(qū)間需追加:_*,告訴編譯器你希望這個參數(shù)被當(dāng)做參數(shù)序列處理。
- val s = sum(1,4,3,3) 或 sum( 1 to 5:_*)//將1 to 5當(dāng)做參數(shù)序列處理。
- 當(dāng)調(diào)用變長參數(shù)且參數(shù)類型為Object的java方法,如MessageFormat的format方法,需要手工對基本類型進行轉(zhuǎn)換,如:
val ss = MessageFormat.format("the answer to {0} is{1}","everything",42.asInstanceOf[AnyRef]);
public static String format(String pattern, Object ... arguments)
過程
- 過程:如果函數(shù)體包含在花括號中單沒有前面的=號,那么返回類型就是Unit,這樣的函數(shù)稱作過程。過程沒有返回值。
def procedure(){
print("ddd");
}
或者
def procedure():Unit={
print("ddd");
}
懶值
- 當(dāng)val被聲明為lazy時,他的初始化將被推遲,直到我們首次對他取值。
- 對于開銷較大的初始化語句十分有用
- 懶值介于val和def的中間狀態(tài)。
異常
- scala沒有受檢異常,不需要聲明函數(shù)或方法可能會拋出某種異常。
- throw表達式的返回類型是Nothing。
- 對于if/else,如果有一個分支是Nothing,那么if/else表達式類型就是另一個分支。
- if( x >= 0 ) { sqrt(x) } else throw new IllegalArgumentException("x should not be negative" ) 類型為Double
-
捕獲異常采用模式匹配。
捕獲異常 - 如果不使用異常則可用_來代替變量名
- try/finally可以釋放資源
快學(xué)scala第三章習(xí)題答案
3數(shù)組相關(guān)操作
- 長度固定使用Array,長度變化用ArrayBuffer。用toBuffer、toArray方法相互轉(zhuǎn)化。
- val nums = new ArrayInt //數(shù)組初始化為0
- val b = ArrayBufferInt
- b +=1 , b+=(1,2,5),b++= Array(1,2,3,8)
- 提供初始值是不要用new
- val s = Array("hello","world")
- 用()來訪問元素
- nums(2)
- 用for( elem <- arr )來變量元素
- 用for( ele<-arr if ...) ...yield ...來將原數(shù)組轉(zhuǎn)型為新數(shù)組,原數(shù)組不變。
- if( elem <- a if( elem % 2 == 0 ) yield 2 * elem //對每個偶數(shù)翻倍,并存在新集合中
- a.filter ( _ % 2 ==0 ).map(2 * _) 另一個寫法
- scala數(shù)組和java數(shù)據(jù)可以互操作,用ArrayBuff,使用scala.collection.javaConversions中的轉(zhuǎn)函數(shù)。
常用算法
- Array(1,2,3).sum 求和,元素是數(shù)值類型
- Array("dd","a","zh").max、min //求最大最小元素
- Array("dd","a","zh").sorted返回排序的數(shù)組或緩沖數(shù)組,原數(shù)組不變
- scala.util.Sorting.quickSort(a) :對數(shù)組排序,但不適用緩沖數(shù)組
- min、max、quickSort操作,元素必須支持比較操作,包括數(shù)字、字符串、及帶Ordered特質(zhì)的類型
- mkString:顯示數(shù)組或緩沖數(shù)組內(nèi)容,允許指定元素間的分隔符
解讀Scaladoc
-
對Array類的操作方法列在ArrayOps相關(guān)條目下,在數(shù)組上應(yīng)用這些操作之前,數(shù)組都會被轉(zhuǎn)化成ArrayOps對象。
數(shù)組函數(shù)解讀
數(shù)組函數(shù)解讀
二維數(shù)組
- Double的二維數(shù)組類型為:Array[Array[Double]]。
- 構(gòu)造方法:val matrix = Array.ofDim[Double](3,4) //三行,四列
- 訪問元素:matrix(row)(column)
與java的互操作
- scala數(shù)組是由java數(shù)組實現(xiàn)的,你可以在java和scala之間來回傳遞。
- 引入import scala.collection.JavaConversions.bufferAsJavaList 可以實現(xiàn)Scala到Java的轉(zhuǎn)換
import scala.collection.JavaConversions.bufferAsJavaList
val command = ArrayBuffer("ls","-al");
val pb = new ProcessBuilder(command);
其中ProcessBuilder為java中的類, 構(gòu)造函數(shù)為public ProcessBuilder(List<String> command)
- 引入import scala.collection.JavaConversions.asScalaBuffer 可以實現(xiàn)Java集合到Scala的轉(zhuǎn)換
import scala.collection.JavaConversions.asScalaBuffer
val pb = new ProcessBuilder(command);
val cmd:Buffer[String] = pb.command();
其中ProcessBuilder為java中的類, public List<String> command()
4映射和元組
- 不可變映射和可變映射
- 默認情況下得到的是一個哈希映射,不過可以指明要樹形映射
創(chuàng)建映射
- val scores = scala.collection.immutable.Map("a"->10,"b"->5) //不可變的Map[String,Int],其值不可變
- val s = scala.collection.mutable.Map("a"->20) //可變映射
- val d = new scala.collection.mutable.HashMap[String,Int] //空映射
- 映射是對偶的集合
- ->操作符和(key,value)來創(chuàng)建對偶 : "dd"->2 或者 ("dd",2)
獲取映射值
- 使用()符號,s("a")
-
如果映射不存在請求使用的健,會拋出異常
- 檢測映射中是否包含鍵:contains方法
- s.getOrElse("b",0) //如果不存在b鍵則返回0.
更新映射值
- 針對可變映射
- s("a") = 6 //更新a鍵的值位6,或者是增加鍵值對"a"->6
- s+= ("w"->3,"ss"->9) //增加多個
- s-= "a" //移除鍵a
迭代映射
- 語法:
for( (k,v)<- 映射 ) 處理k和v -
for( v <- s.values ) 處理v//只訪問值,鍵用keySet -
for( (k,v) <- 映射 ) yield (v,k)//交換鍵和值得位置
已排序映射
- 默認情況下映射的實現(xiàn)是一個哈希映射,不過可以指明要樹形映射。
- 輸出的內(nèi)容是有序的而不是根據(jù)輸入順序,按照字典序排序
- 樹形映射同樣有可變和不可變的。
var s = collection.mutable.SortedMap("bc"->5) //可變映射,可變變量s
s("b")=3 // b->3 ,bc- > 5
s+=("a"->2) //a->2 , b->3 ,bc- > 5
與java互操作
- 之前是引入scala
- 引入import scala.collection.JavaConverters._對象
val source = new java.util.HashMap[String,Int]
val s:scala.collection.mutable.Map[String,Int] = source.asScala
val source = new scala.collection.mutable.ListBuffer[Int]
val target: java.util.List[Int] = source.asJava
val other: scala.collection.mutable.Buffer[Int] = target.asScala
assert(source eq other)
- 過時方法:
import scala.collection.JavaConversions.mapAsScalaMap
import scala.collection.JavaConversions.propertiesAsScalaMap val
val map:Map[String,Int] = new TreeMap[String,Int]
props:scala.collection.Map[String,String] = System.getProperties()
元組(tuple)
- 不同類型值的聚集
- 如:val s =(1, 2.3, "ddd") 對應(yīng)類型為:Tuple3[Int,Double,java.lang.String]或(Int,Double,java.lang.String)
- 獲取元組值
- _序號 訪問組元,元組的各組元從1開始,而不是0
- 如: s._2 或 s _2(空格) //2.3
- 模式匹配獲取
- val (first,second,_) = s // 在不需要的位置上用_,
拉鏈操作zip
-
把key和value組合在一起
拉鏈操作
"Hello".zip("World")
res0: scala.collection.immutable.IndexedSeq[(Char, Char)] = Vector((H,W), (e,o), (l,r), (l,l), (o,d))
5類
- 一個主構(gòu)造器,多個輔助構(gòu)造器,輔助構(gòu)造器叫做this
getter和setter屬性
- 類中字段自動帶有g(shù)etter、setter方法,并且對應(yīng)的getter和setter方法為:age和age_=。(假設(shè)屬性為age)。屬性getter、setter方法的公有和私有性與字段有關(guān)。
- 可通過javap -c 字節(jié)碼文件:查看生成的信息。
class Person(val name:String,var sex:Boolean){ //公有name的get,公有sex的get、set方法
var age =0 //公有字段,公有的getter、setter方法
private var address="china" //私有字段,私有的getter、setter方法
val school="cnu" //只getter方法
private[this] var tel="1134423" //對象私有字段,不會生產(chǎn)getter和setter方法
}
- 定制getter、setter方法
class Person(){
private var myAge =0 //公有字段,公有的age、age_=方法
def age = myAge //為myAge定制get、set方法
def age_=(newAge:Int){
if( newValue > myAge ) myAge = newAge
}
}


Bean屬性
- JavaBeans規(guī)范把java屬性定義為一對getFoo/setFoo方法(或者對只讀屬性而言單個getFoo方法),而scala提供的getter、setter方法的名稱并不是java所預(yù)期的。但是許多java工作都依賴javaBeans的名稱習(xí)慣,所以通過將字段標(biāo)注為@BeanProperty時,就會生產(chǎn)javaBeans版的getter、setter方法。

輔助構(gòu)造器
- 名稱為this
- 每一個輔助構(gòu)造器必須以一個對先前已定義的其它輔助構(gòu)造器或主構(gòu)造器的調(diào)用開始。

主構(gòu)造器
-
主構(gòu)造器參數(shù)直接放置在類名之后
image.png -
主構(gòu)造器參數(shù)生成字段和方法
主構(gòu)造器 注意:class Person(name :String){}//對于這種name沒有被方法使用的情況,Person不會生成該字段。
嵌套類
- Java中的內(nèi)部類從屬于外部類。但是Scala中,每個實例都有自己的內(nèi)部類,就像自己的字段一樣。
class Person{
private val members = new ArrayBuffer[Member]
class Member(val name:String){
}
}
val zhangsan = new Person
val lisi = new Person
- 其中zhansan.Member和lisi.Member是兩個不同的類。
- 如果不希望這種效果:(即zhansan.Member和lisi.Member是兩個不同的類。),有2種方式解決:
- 1.將Member類移到別處:推薦位置是伴生對象,即Person的伴生對象中。
object Person{
class Member(val name:String){
}
}
class Person{
private val members = new ArrayBuffer[Person.Member] //注意
}
- 2.類型投影:Person#Member, 含義:“任何Person的Member”
- 內(nèi)嵌類中,可以通過 外部類.this的方式來訪問外部類的this引用。
- 也可以使用class Person { outer=>語法來使outer變量指向Person.this。

快學(xué)scala第五章習(xí)題答案
6對象
用對象作為單例或存放工具方法
object Accounts{
private var lastNumber =0
def newUniqueNumber() ={lastNumber += 1 ;lastNumber }
}
- scala沒有靜態(tài)方法和字段,用object來達到同樣目的,對象定義了某個類的單個實例。對象的構(gòu)造器在該對象唄第一次使用時調(diào)用,如果一個對象從未被使用,那么構(gòu)造器不會執(zhí)行。

類可以擁有一個同名的伴生對象(在同一個源文件中)
- java中經(jīng)常會用到既有實例方法又有靜態(tài)方法的類,在Scala中通過類與同名的“伴生”對象來達到同樣目的。
class Accounts{
val id = Accounts.newUniqueNumber()
private var balance =0.0
def deposit(amount: Double){balance += amount}
}
object Accounts{
private var lastNumber =0
def newUniqueNumber() ={lastNumber += 1 ;lastNumber }
}
- 類和它的伴生對象可以相互訪問私有特性,他們必須存在同一個源文件中
對象可以擴展類或特質(zhì)
- object對象可擴展類及多個特質(zhì)
- image.png
- val acions = Map("open"->DoNothingAction, "save"-> DoNothingAction)
對象的apply方法通常用來構(gòu)造伴生類的新實例
- Object(參數(shù)1,...,參數(shù)N) 表達式就會調(diào)用apply方法。

- 因為Account定義了apply方法,val acct = Account(1000.0)就變得可行了。
如果不想顯示定義main方法,可以擴展App特質(zhì)的對象
你可以通過擴展Enumeration對象來實現(xiàn)枚舉
7.包和引入
包
- 同一個包可以定義在多個文件當(dāng)中,源文件目錄和包也沒有強制關(guān)聯(lián)關(guān)系:Utils.scala可以不在com/horstmann目錄下。
package com {
package horstmann{
class Utils{}
pacakge impatient{
class Employee{ } //作用域:可以訪問Utils類中方法
}
}
}
等同于
package com.horstmann
class Utils{}
package impatient
class Employee{}
串聯(lián)式包語句
- 這樣的包語句限定了可見成員,com.horstmann、com的成員在這里不在可見
package com.horstmann.impatient{
package people{
class Person
}
}
包對象
package com.horstmann.impatient
package object people{
val defaultName = "John Public"
}
package people{
class Man{
val name = defaultName //從包對象拿到常量值,因為在同一個包中不需要使用com....people.defaultName訪問
}
}
- 可以把工具函數(shù)或常量添加到包而不是某個Utils對象。
- 每個包都可以有一個包對象,你需要在父包中定義它,且名稱與子包一樣。
- 使用:不在同一個包中使用 com.horstmann.impatient.people.defaultName訪問
包可見性
-
Java中被default(默認)修飾的成員在包含該類的包中是可見的。Scala中可以通過修飾符可以使類中成員在包中可見。
類成員可見度
類私有字段、對象私有字段
- scala中方法默認可以訪問該類的所有對象的私有字段。
class Counter{
private var value =0 //對象私有字段,Counter類方法可以訪問Counter類所有對象的value字段
private[this] var age =0 //對象私有字段,Counter類方法只能訪問到當(dāng)前對象的age字段
def increment() = {value+=1}
//可以訪問other.value字段,因為other也是Counter類。
def isLess(other:Counter ) = value < other.value
//下面other.age訪問是錯誤的,因為age是對象私有的
def isLessAge(other:Counter ) = age < other.age
}
引入
- 重命名和隱藏
import java.util.{HashMap => JavaHashMap} //重命名
import java.util.{HashMap =>_,_} //隱藏,第二個“_”符號是java.util._的意思
- 隱式引入
- 但是scala下的類會覆蓋java.lang下的類,而不是報錯
import java.lang._
import scala._
import Predef._
- 在任何地方都可以聲明引入,import的效果一直延伸到包含該語句塊的末尾。
class Manager{
import scala.collection.mutable._ //該引入只在Manager{}語句塊中有用
val sub = new ArrayBuffer[Employee]
}
8繼承
- extends、final關(guān)鍵字和java中相同
- 類、字段、方法聲明為final就不能被繼承和重寫。(而java中字段final只是表明不可變,可以被重寫)
- 重寫方法、字段時必須用override,重寫超類抽象方法則不需要


- 調(diào)用超類方法同樣用super。eg:super.toString()
- 只有主構(gòu)造器可以調(diào)用超類的主構(gòu)造器
class Employee(name:String,age:Int,val salary:Double) extends Person(name,age)
- 與java不同protected的成員對于類所屬包是不可見的,可以通過protected[包]實現(xiàn)
類型檢查和轉(zhuǎn)換
| Scala | Java | 說明 |
|---|---|---|
| obj.isInstanceOf[CI] | obj instanceof CI | obj是否屬于CI類及其子類 |
| obj.asInstanceOf[CI] | (CI)obj | 將obj轉(zhuǎn)換成CI類,如果obj是CI子類會出錯 |
| obj.getClass == classOf[CI] | CI.class | 測試obj是否是CI類,但又不是其子類 |
抽象類
- abstract關(guān)鍵字,抽象方法省去其方法體,存在至少一個抽象方法,則必須聲明為abstract
- 重寫超類抽象方法則不需要override
構(gòu)造順序和提前定義
- 當(dāng)你在子類中重寫val并且在超類的構(gòu)造器中使用該值的話,會出現(xiàn)不符合預(yù)期的結(jié)果。(和java一樣)
- 舉例:下面的Ant類繼承Creature,并且重寫range字段,getter方法也被重寫。在實例化Ant之前會先實例化父類Creature,在執(zhí)行Createture構(gòu)造函數(shù)中,先設(shè)置range字段為10 ,然后為了初始化env數(shù)組,用到了range的getter方法,getter方法被重寫,所以調(diào)用子類getter方法,此時Ant類還未進行實例化,range字段的值為零值,所以返回0,所以env的數(shù)組長度為0.
class Creature{
val range:Int = 10
val env:Array[Int] = new Array[Int](range)
}
class Ant extends Creature{
override val range = 2
}
//下面代碼寫在main函數(shù)中
val ant = new Ant
print(ant.env.length) //返回0
- 解決上面問題明白2個原理:1.類加載中,父類先與子類之前執(zhí)行初始化動作。類加載的初始化階段會執(zhí)行<cinit>函數(shù),即對類變量(static修飾的)進行賦值。2.父類的的實例化先于子類實例化,非類變量在實例化中會被賦值。
- 解決方法:
- 將超類val聲明為final,則在scala中該字段就不能被重寫(注:java中可以),將子類重寫的字段聲明為final可以得到正確的值,對于本例就是2
- 將超類的env聲明為lazy,即env在使用時才初始化,而此時ant早已實例化完成。
- 在子類使用提前定義語法:讓你可以在超類的構(gòu)造器執(zhí)行之前初始化子類的val字段。
- class Ant extends { override val range = 2 } with Creature
Scala繼承層級

對象相等性
- AnyRef的eq方法檢查兩個引用是否指向同一個對象,AnyRef的equals調(diào)用eq。
- 重寫equals,提供一個自然的與實際相稱的相等性判斷,語法定義:加上final,參數(shù)類型必須為Any.
- final override def equals(other: Any)={}
- 在程序中,只要用==就可以,對于應(yīng)用類型而言,他會在做完必要的null檢查后調(diào)用equals方法。







