2.2 跳出循環(huán)的三種方法
1. 使用boolean控制變量
var flag = true
var res = 0
var n = 0
while(flag){
res += n
n += 1
if (n==5) flag = false
}
/****************/
for(i <- 0 until 10 if flag){
res += 1
if (i==4) flag = false
}
2. 在嵌套函數(shù)里面使用return
def outer() = {
var res = 0
def inner(){
for (i<-0 until 10){
if (i==5) return
res += 1
}
}
inner()
res
}
3. 使用Breaks對象的break方法
import scala.util.control.Breaks._
var res = 0
breakable{
for(i<-0 until 10){
if (i==5) break;
res += i
}
}
2.3 多維數(shù)組竭望、Java數(shù)組與Scala數(shù)組的隱式轉(zhuǎn)換
多維數(shù)組
//構(gòu)造行與列的二維數(shù)組:Array.ofDim方法
val multiDimArr = Array.ofDim[Double](3,4)
multiDimArr(0)(0) = 1.0 // 柯里表達(dá)式
// 構(gòu)造不規(guī)則多維數(shù)組
val multiDimArr2 = new Array[Array[Int]](3)
multiDimArr2(0) = new Array[Int](1)
multiDimArr2(1) = new Array[Int](2)
multiDimArr2(2) = new Array[Int](3)
multiDimArr2(1)(1) = 1
Java數(shù)組與Scala數(shù)據(jù)的隱式轉(zhuǎn)換
// Scala中的list是ArrayBuffer井誉,無法直接傳入Java的API
import scala.collection.JavaConversions.bufferAsJavaList
import scala.collection.mutable.ArrayBuffer
val command = ArrayBuffer("javac", "filePath/test.java")
val processBuilder = new ProcessBuilder(command) // ProcessBuilder是java的類,command作為ArrayBuffer是無法直接傳入的椎瘟,只能通過bufferAsJavaList隱式轉(zhuǎn)換
val process = processBuilder.start()
val res = process.waitFor()
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable.Buffer
val cmd:Buffer[String] = processBuilder.command() // 逆方向轉(zhuǎn)換
2.4 Tuple拉鏈操作未桥、Java與Scala的Map隱式轉(zhuǎn)換
Tuple拉鏈操作
// Tuple拉鏈操作就是zip操作栽渴,是Array類的方法所森,用于將兩個Array合并為一個Array
// 比如Array(v1)和Array(v2)囱持,使用zip操作合并后的格式為Array((v1, v2)),合并后的Array元素類型為Tuple
val students = Array("leo","jake")
val scores = Array(80,60)
val studentScores = students.zip(scores)
for ((stu, sco) <- studentScores) println(stu+" "+sco)
val studentScoreMap = studentScores.toMap
studentScoreMap("leo")
Java Map和Scala Map的隱式轉(zhuǎn)換
import scala.collection.JavaConversions.mapAsScalaMap
val javaScoreMap = new java.util.HashMap[String, Int]()
javaScoreMap.put("leo",80)
javaScoreMap.put("jack",70)
val scalaScoreMap: Scala.collection.immutable.Map[String, Int] = javaScoreMap
import scala.collection.JavaConversions.mapAsJavaMap
import java.awt.font.TextAttribute._
val scalaAttrMap = Map(FAMILY -> "Serif", SIZE -> 12)
val font = new java.awt.Font(scalaAttrMap)
2.6 package與import實戰(zhàn)詳解
package定義
// package第一種定義方式:多層級package定義(不可取)
package com {
package spark {
package scala {
class Test {}
}
}
}
// package第二種定義方式:串聯(lián)式package定義(也不可取)
package com.spark.scala {
package service {
class Test {}
}
}
//package第三種定義方式:文件頂部package定義
package com.spark.scala.service
class Test{}
package特性
同一個包定義焕济,可以在不同的scala源文件中纷妆;一個scala源文件內(nèi),可以包含兩個包
-
子包中的類晴弃,可以訪問父包中的類
-
相對包名與絕對包名
-
定義package對象(使用較少)
package內(nèi)的成員掩幢,可以直接訪問package對象內(nèi)的成員
package可見性
package com.spark.scala.
class Person{
private[scala] val name = "leo"
private[spark] val age = 25 // 只在spark包下面的成員可見
}
import特性
- 用import xxx.xxx._的格式可以導(dǎo)入包下所有成員
- scala與java不同之處在于,任何地方都可以使用import上鞠,比如類內(nèi)际邻、方法內(nèi),這種方式的好處在于芍阎,可以在一定作用域范圍內(nèi)使用導(dǎo)入
- 選擇器世曾、重命名、隱藏
import spark.scala.{aaa, bbb}
import spark.scala.{HashMap => scalaHashMap} // 重命名
import scala.util.{HashMap => _, _} // 導(dǎo)入scala.util包下所有的類谴咸,但是隱藏掉HashMap類
- 隱式導(dǎo)入
// 每個scala程序默認(rèn)都會隱式導(dǎo)入以下幾個包所有的成員
import java.lang._
import scala._
import Predef._
2.7 重寫field的提前定義轮听、Scala繼承層級、對象相等性
重寫field的提前定義
class Student{
val classNum: Int = 10
val classScores: Array[Int] = new Array[Int](classNum)
}
class PEStudent extends Student{
override val classNum: Int = 3
}
val s = new Student()
val pes = new PEStudent()
s.classScores // Array[Int] = Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
pes.classScores // Array[Int] = Array() 為空
- 子類PEStudent的構(gòu)造函數(shù)(無參)調(diào)用父類Student的構(gòu)造函數(shù)(無參)
- 父類的構(gòu)造函數(shù)初始化field(結(jié)果正確)
- 父類的構(gòu)造函數(shù)使用field執(zhí)行其他構(gòu)造代碼(classScores)岭佳,但是此時其他構(gòu)造代碼如果使用了該field血巍,而且field要被子類重寫,那么它的getter方法被自動重寫,返回初始化的值0(比如Int),因此pes.classScores返回長度為0的數(shù)組
- 子類的構(gòu)造函數(shù)再執(zhí)行鞭铆,重寫field(pes.classNum = 3)
- 但是此時子類從父類繼承的其他構(gòu)造代碼,已經(jīng)出現(xiàn)錯誤了(pes.classScores)
此時辨赐,只能用Scala對象繼承的一個高級特性:提前定義,在父類構(gòu)造函數(shù)執(zhí)行前京办,先執(zhí)行子類的構(gòu)造函數(shù)中的某些代碼
class PEStudent extends{
override val classNum: Int = 3
} with Student
Scala繼承層級
Scala中掀序,最頂端的兩個trait是Nothing和Null,Null trait唯一的對象就是null惭婿,其次是繼承了Nothing trait的Any類不恭,接著AnyVal trait和AnyRef類叶雹,都繼承自Any類。
Any類是一個比較重要的類换吧,其中定義了inInstanceOf和asInstanceOf等方法折晦,以及equals、hashCode等對象的基本方法沾瓦。
AnyRef類满着,增加了一些多線程的方法,比如wait贯莺、notify/notifyAll风喇、synchronized等。
對象相等性
如何判斷兩個引用變量缕探,是否指向同一個對象實例魂莫?
AnyRef的eq方法用于檢查兩個變量是否指向同一個對象實例,AnyRef的equals方法默認(rèn)調(diào)用eq方法實現(xiàn)爹耗,也就是說耙考,默認(rèn)情況下,判斷兩個變量相等鲸沮,要求必須指向同一個對象實例琳骡。
此外锅论,定義equals方法時讼溺,也最好使用同樣的fields,重寫hashCode方法最易。
如果只是想簡單通過是否指向同一個對象實例怒坯,判定變量是否相等,那么直接使用==即可
class Product(val name: String, val price: Double){
final override def equals(other: Any) = {
val that = other.asInstanceOf[Product]
if(that == null) false
else name == that.name && price == that.price
}
final override def hashCode = 13 * name.hashCode + 17 * price.hashCode
}
val p1 = new Product("p1", 1.0)
val p2 = new Product("p2", 2.0)
val p3 = p1 // p3 == p1 true
val p4 = new Product("p1", 1.0)
p4 == p1 // true
2.8 文件操作實戰(zhàn)
遍歷文件中的每一行
import scala.io.Source
/***1. 使用Source.getLines返回***/
val source = Source.fromFile(filePath,"UTF-8")
val lineIterator = source.getLines // 注意getLines調(diào)用后指針在文件尾部藻懒,再次調(diào)用返回空
for (line <- lineIterator) println(line)
/***2. 將Source.getLines返回的迭代器剔猿,轉(zhuǎn)換成數(shù)組***/
val source = Source.fromFile(filePath,"UTF-8")
val lines = source.getLines.toArray
for (line <- lines) println(line)
/***3. 調(diào)用Source.mkString,返回文本中所有的內(nèi)容***/
val source = Source.fromFile(filePath,"UTF-8")
val lines = source.mkString
source.close()
遍歷一個文件中的每個字符
val source = Source.fromFile(filePath,"UTF-8")
for (c <- source) print(c)
從URL以及字符串中讀取字符
val html = Source.fromURL("http://www.baidu.com", "UTF-8")
val str = Source.fromString("Hello World")
結(jié)合Java IO流嬉荆,文件拷貝
import java.io._
val file = new File(filePath)
var len = file.length.toInt
val bytes = new Array[Byte](len)
val fis = new FileInputStream(file)
val fos = new FileOutputStream(new File(outputFilePath))
fis.read(buf)
fos.write(buf, 0, len)
fis.close()
fos.close()
遞歸遍歷子目錄
import java.io.File
def getDir(dir: File): Iterator[File] = {
val childDirs = dir.listFiles.filter{_.isDirectory}
childDirs.toIterator ++ childDirs.toIterator.flatMap(getDir _)
}
val iterators = getDir("/home/xxx/test")
for (i <- iterators) println(i)
序列化與反序列化(Java)
// 要實現(xiàn)序列化归敬,一定要有@SerialVersionUID注解,定義一個版本號鄙早,并繼承Serializable trait
@SerialVersionUID(1L) class Person(val name: String) extends Serializable
val leo = new Person("leo")
import java.io._
val oos = new ObjectOutputStream(new FileOutputStream(filePath))
oos.writeObject(leo)
oos.close()
val ois = new ObjectInputStream(new FileOutputStream(filePath))
val restoredLeo = ois.readObject().asInstanceOf[Person]
restoredLeo.name // restoredLeo與Leo不是一個對象
2.9 偏函數(shù)實戰(zhàn)詳解
偏函數(shù)
// 偏函數(shù)是沒有定義好明確的輸入?yún)?shù)的函數(shù)汪茧,函數(shù)體是一連串的case語句
// 偏函數(shù)是PartialFunction[A, B]類的一個實例,該類有兩個方法限番,一個是apply()方法舱污,直接調(diào)用可以通過函數(shù)體內(nèi)的case進(jìn)行匹配,返回結(jié)果弥虐;另一個是isDefinedAt()方法扩灯,可以返回一個輸入媚赖,是否跟任何一個case語句匹配
/***學(xué)生成績查詢案例***/
val getStudentGrade: PartialFunction[String, Int] = {
case "Leo" => 90; case "Jack" => 85; case "Marry" => 95
}
getStudentGrade("Leo")
getStudentGrade.isDefinedAt("Tom") // False
2.10 執(zhí)行外部命令
scala的程序是運(yùn)行在jvm進(jìn)程中的,如果scala程序想要執(zhí)行scala所在進(jìn)程之外的珠插,比如本地os的命令時惧磺,即執(zhí)行外部命令
import sys.process._
"ls -la .." ! //
2.11 正則表達(dá)式支持
// 定義一個正則表達(dá)式,使用String類的r方法
val pattern1 = "[a-z]+".r
// 獲取一個字符串中捻撑,匹配正則表達(dá)式的部分豺妓,使用findAllIn
for (matchString <- pattern1.findAllIn(str)) println(matchString)
// 同理,使用findFirstIn布讹,可以獲取第一個匹配正則表達(dá)式的部分
pattern1.findFirstIn(str)
// 使用replaceAllIn琳拭,可以將匹配正則的部分替換掉
pattern1.replaceAllIn("hello world", "replacement")
// 使用replaceFirstIn,可以將第一個匹配正則的部分替換掉
pattern1.replaceFirstIn("hello world", "replacement")
2.11 提取器
提取器就是包含了一個unapply方法的對象描验,跟apply方法正好相反白嘁。
apply方法,是接受一堆參數(shù)膘流,然后構(gòu)造出一個對象絮缅。
unapply方法,是接受一個字符串呼股,然后解析出對象的屬性值耕魄。
class Person(val name: String, val age: Int)
Object Person{
def apply(name: String, age: Int) = new Person(name, age)
def unapply(str: String) = {
val splitIndex = str.indexOf(" ") // 按空格切割
if (splitIndex == -1) None // 如果沒有空格
else Some((str.substring(0, splitIndex), str.substring(splitIndex + 1)))
}
}
val Person(name, age) = "leo 25"
name
age
樣例類的提取器
// case class類似于java中的javaBean,javaBean包含了一堆屬性(field)彭谁,每個field都有一對getter和setter
// scala默認(rèn)給case class創(chuàng)建getter和setter方法
case class Person(name: String, age: Int)
val p = Person("leo", 25) // 樣例類默認(rèn)提供apply和unapply方法
p match {
// 模式匹配吸奴,如果是Person類,調(diào)用Person的unapply方法
case Person(name, age) => println(name + ": "+ age)
}
只有一個參數(shù)的提取器
如果你的類只有一個字段缠局,即字符串里面只有一個字段则奥,解析出來的字段是沒有辦法放在tuple中的,因為scala的tuple規(guī)定要2個及以上的值狭园。
這個時候读处,在unapply方法中,只能將一個字段值唱矛,封裝在Some對象中返回罚舱。
class Person(val name: String)
Object Person {
def unapply(input: String): Option[String] = Some(input)
}
val Person(name) = "leo"
2.15 注解
在scala中可以給類、方法绎谦、field管闷、?variable、parameter添加注解燥滑,并且scala支持給某個對象添加多個注解渐北。
// 特例:如果要給類的主構(gòu)造函數(shù)添加注解,那么需要在構(gòu)造函數(shù)前添加注解铭拧,并加上一對圓括號赃蛛。
class Person @Unchecked() (val name: String, val age: Int)
// 還可以給表達(dá)式添加注解恃锉,此時需要在表達(dá)式后面加上冒號以及注解,如:
val scores = Map("Leo" -> 90, "Jack" -> 85)
(scores.get("Leo"): @unchecked) match { case score => println(score) }
// 開發(fā)注解:要自己開發(fā)一個注解呕臂,就必須擴(kuò)展Annotation trait
class Test extends annotation.Annotation
@Test
class myTest
// 注解參數(shù)
class Test(var timeout: Int) extends annotation.Annotation
@Test(timeout = 100)
class myTest
常用注解介紹
@volatile var name = "leo" // 輕量級的java多線程并發(fā)安全控制
jvm中破托,多線程的每個線程都有自己的工作區(qū),還有一塊兒所有線程共享的工作區(qū)歧蒋,每次一個線程拿到一個公共的變量土砂,都需要從共享區(qū)中拷貝一個副本到自己的工作區(qū)中使用和修改,修改完以后谜洽,再在一個合適的時機(jī)萝映,將副本的值寫回到共享區(qū)中。這里就會出現(xiàn)多線程并發(fā)訪問的安全問題阐虚。
volatile關(guān)鍵字修飾的變量序臂,可以保證一個線程在從共享區(qū)獲取一個變量的副本時,都會強(qiáng)制刷新一下這個變量的值实束,保證自己獲取到的變量的副本值是最新的奥秆。但這樣也不是百分百保險,仍可能出現(xiàn)錯誤的風(fēng)險咸灿。
@transient var name = "leo" // 瞬態(tài)字段构订,不會序列化這個字段
@SerialVersionUID(value) // 標(biāo)記類的序列化版本號(類可能在序列化保存之后改變了,這樣反序列化時會報錯)
@native // 標(biāo)注用c實現(xiàn)的本地方法
@throws(classOf[Exception]) def test(){} // 給方法標(biāo)記要拋出的checked異常
@varargs def test(args: String*){} // 標(biāo)記方法接受的是變長參數(shù)
@BeanProperty // 標(biāo)記生成JavaBean風(fēng)格的getter和setter方法
@BooleanBeanProperty // 標(biāo)記生成is風(fēng)格的getter方法避矢,用于boolean類型的field
@deprecated(message = "") // 讓編譯器提示警告
@unchecked // 讓編譯器提示類型轉(zhuǎn)換的警告
2.17 XML基礎(chǔ)操作
scala中定義xml
val books = <books><book>my book</book></books> // scala.xml.Elem悼瘾,即一個xml元素
val books = <book>my book</book><book>your book</book> // 多個平級的元素,scala.xml.Nodeseq谷异,節(jié)點(diǎn)序列
XML節(jié)點(diǎn)類型
Node類是所有XML節(jié)點(diǎn)類型的父類型分尸,兩個重要的子類型是Text和Elem。
Elem表示一個XML元素歹嘹,也就是一個XML節(jié)點(diǎn)。scala.xml.Elem類型的label屬性孔庭,返回的是標(biāo)簽名尺上,child屬性,返回的是子元素圆到。
scala.xml.NodeSeq類型怎抛,是一個元素序列,可以用for循環(huán)芽淡,直接遍歷它马绝。可以通過scala.xml.NodeBuffer類型挣菲,手動創(chuàng)建一個節(jié)點(diǎn)序列富稻。
val booksBuffer = new scala.xml.NodeBuffer
booksBuffer += <book>book1</book>
booksBuffer += <book>book2</book>
val books: scala.xml.NodeSeq = booksBuffer
xml元素的屬性
// scala.xml.Elem.attributes屬性掷邦,可以返回xml元素的屬性,是Seq{scala.xml.Node}類型的椭赋,繼續(xù)調(diào)用text屬性抚岗,可以獲取屬性的值
val book = <book id="1" price="10.0">book1</book>
val bookID = book.attributes("id").text
// 遍歷屬性
for (attr <- book.attributes) println(attr)
// 獲取屬性Map
book.attributes.asAttrMap
xml中嵌入scala代碼
val books = Array("book1", "book2")
<books><book>{books(0)}</book><book>{books(1)}</book></books>
<books>{ for (book <- books) yield <book>{book}</book>}</books>
// xml屬性中嵌入scala代碼
<book id="{ books(0) }">{ books(0) }</book>
xml修改元素
// scala中的xml表達(dá)式默認(rèn)是不可改變的,必須拷貝一份再修改
val books = <books><book>book1</book></books>
val booksCopy = books.copy(child = books.child ++ <book>book2</book>)
var book = <book id="1">book1</book>
import scala.xml._
// 修改一個屬性
val bookCopy = book % Attribute(null, "id", "2", Null)
// 添加一個屬性
val bookCopy = book % Attribute(null, "id", "2", Attribute(null, "price", "10.0", Null))
xml加載和寫入外部文檔
import scala.xml._
import java.io._
// 使用scala的XML類加載
val books = XML.loadFile(filePath)
// 使用Java的FileInputStream類加載
val books = XML.load(new FileInputStream(filePath))
// 用Java的InputStreamReader類加載
val books = XML.load(new InputStreamReader(new FileInputStream(filePath), "UTF-8"))
// 寫入外部xml文檔
XML.save(outFilePath, bookss)
2.21 集合元素操作
![](https://ww3.sinaimg.cn/large/006tNbRwgy1fdz4mai1czj30m20dy183.jpg)
集合的常用操作
head last tail
length isEmpty
sum max min
count exists filter filterNot
takeWhile dropWhile
takeRight dropRight
sclie
contains startsWith endsWith
indexOf
intersect diff
map
val scoreMap = Map("leo" -> 90, "jack" -> 60)
val names = List("leo", "jack", "tom")
names.map(scoreMap(_)) // names映射為成績集合
flatMap
val scoreMap = Map("leo" -> List(80, 90), "jack" -> List(60, 70))
names.flatMap(scoreMap(_)) // List(80, 90, 60, 70)
collect
"abc".collect { case 'a' => 1; case 'b' => 2; case 'c' => 3} // Vector(1, 2, 3)哪怔,"abc"中每個元素與偏函數(shù)匹配
foreach
names.foreach(println _)
reduce
List(1, 2, 3, 4).reduceLeft(_ - _) // 1-2-3-4
List(1, 2, 3, 4).reduceRight(_ - _) // 4-3-2-1
fold
List(1, 2, 3, 4).foldLeft(10){ (m, n) => println(m+": "+n); m -n } // 給定起始元素 10-1-2-3-4
List(1, 2, 3, 4).foldRight(10)(_ - _) // 不一樣宣蔚,1-(2-(3-(4-10))))