Scaladoc是什么:scala api文檔坪郭,包含了scala所有的api以及使用說明吭产,class侣监、object、trait臣淤、function橄霉、method、implicit等
為什么要查閱Scaladoc:如果只是寫一些普通的Scala程序邑蒋,課程中講解(Scala編程詳解)的內(nèi)容基本夠用了姓蜂;但是如果(在現(xiàn)在按厘,或者未來,實(shí)際的工作環(huán)境中)要編寫復(fù)雜的scala程序钱慢,那么還是需要參考Scaladoc的逮京。(純粹用scala開發(fā)spark應(yīng)用程序,應(yīng)該不會特別復(fù)雜束莫;用scala構(gòu)建類似于spark的公司內(nèi)的分布式的大型系統(tǒng))
通過url:http://www.scala-lang.org/api/current/#package懒棉,可以在線瀏覽Scaladoc
以下是一些Scaladoc使用的tips(小貼士,小備注):
1览绿、直接在左上角的搜索框中策严,搜索你需要的尋找的包、類即可
2饿敲、O和C妻导,分別代表了某個類的伴生對象以及伴生類的概念
3、標(biāo)記為implicit的方法怀各,代表的是隱式轉(zhuǎn)換
4倔韭、舉例:搜索StringOps,可以看到String的增強(qiáng)類渠啤,StringOps的所有方法說明
Scala編程進(jìn)階:跳出循環(huán)語句的3種方法
方法一:使用boolean控制變量
while循環(huán):
var flag = true
var res = 0
var n = 0
while(flag) {
res += n
n += 1
if (n == 5) {
flag = false
}
}
for循環(huán):(高級for循環(huán)狐肢,加上了if守衛(wèi))
var flag = true
var res = 0
for (i <- 0 until 10 if flag) {
res += i
if (i == 4) flag = false
}
方法二:在嵌套函數(shù)中使用return
def add_outer() = {
var res = 0
def add_inner() {
for (i <- 0 until 10) {
if (i == 5) {
return
}
res += i
}
}
add_inner()
res
}
方法三:使用Breaks對象的break方法
跟java里面的break比較類似,相對來說沥曹,比較靈活好用份名;與breakable代碼塊配合使用
import scala.util.control.Breaks._
var res = 0
breakable {
for (i <- 0 until 10) {
if (i == 5) {
break;
}
res += i
}
}
Scala編程進(jìn)階:多維數(shù)組、Java數(shù)組與Scala數(shù)組的隱式轉(zhuǎn)換
多維數(shù)組
什么是多維數(shù)組妓美?:數(shù)組的元素僵腺,還是數(shù)組,數(shù)組套數(shù)組壶栋,就是多維數(shù)組
構(gòu)造指定行與列的二維數(shù)組:Array.ofDim方法
val multiDimArr1 = Array.ofDim[Double](3, 4)
multiDimArr1(0)(0) = 1.0
構(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ù)組緩沖的隱式轉(zhuǎn)換
Scala代碼中辰如,直接調(diào)用JDK(Java)的API,比如調(diào)用一個Java類的方法贵试,勢必可能會傳入Java類型的list琉兜;Scala中構(gòu)造出來的list,其實(shí)是ArrayBuffer毙玻;你直接把Scala的ArrayBuffer傳入Java接收ArrayList的方法豌蟋,肯定不行。
import scala.collection.JavaConversions.bufferAsJavaList
import scala.collection.mutable.ArrayBuffer
val command = ArrayBuffer("javac", "C:\\Users\\Administrator\\Desktop\\HelloWorld.java")
val processBuilder = new ProcessBuilder(command)
val process = processBuilder.start()
val res = process.waitFor()
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.mutable.Buffer
val cmd: Buffer[String] = processBuilder.command()
Tuple拉鏈操作桑滩、Java Map與Scala Map的隱式轉(zhuǎn)換
Tuple拉鏈操作
Tuple拉鏈操作指的就是zip操作
zip操作梧疲,是Array類的方法,用于將兩個Array,合并為一個Array
比如Array(v1)和Array(v2)幌氮,使用zip操作合并后的格式為Array((v1,v2))
合并后的Array的元素類型為Tuple
val students = Array("Leo", "Jack", "Jen")
val scores = Array(80, 100, 90)
val studentScores = students.zip(scores)
for ((student, score) <- studentScores)
println(student + " " + score)
如果Array的元素類型是個Tuple缭受,調(diào)用Array的toMap方法,可以將Array轉(zhuǎn)換為Map
studentScores.toMap
Java Map與Scala Map的隱式轉(zhuǎn)換
import scala.collection.JavaConversions.mapAsScalaMap
val javaScores = new java.util.HashMap[String, Int]()
javaScores.put("Alice", 10)
javaScores.put("Bob", 3)
javaScores.put("Cindy", 8)
val scalaScores: scala.collection.mutable.Map[String, Int] = javaScores
import scala.collection.JavaConversions.mapAsJavaMap
import java.awt.font.TextAttribute._
val scalaAttrMap = Map(FAMILY -> "Serif", SIZE -> 12)
val font = new java.awt.Font(scalaAttrMap)
擴(kuò)大內(nèi)部類作用域的2種方法该互、內(nèi)部類獲取外部類引用
內(nèi)部類的作用域:外部類對象
import scala.collection.mutable.ArrayBuffer
class Class {
class Student(val name: String)
val students = new ArrayBuffer[Student]
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack
擴(kuò)大內(nèi)部類作用域:伴生對象
object Class {
class Student(val name: String)
}
class Class {
val students = new ArrayBuffer[Class.Student]
def register(name: String) = {
new Class.Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack
擴(kuò)大內(nèi)部類作用域:類型投影
class Class {
class Student(val name: String)
val students = new ArrayBuffer[Class#Student]
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class
val leo = c1.register("leo")
c1.students += leo
val c2 = new Class
val jack = c2.register("jack")
c1.students += jack
內(nèi)部類獲取外部類的引用
class Class(val name: String) { outer =>
class Student(val name: String) {
def introduceMyself = "Hello, I'm " + name + ", I'm very happy to join class " + outer.name
}
def register(name: String) = {
new Student(name)
}
}
val c1 = new Class("c1")
val leo = c1.register("leo")
leo.introduceMyself
package與import實(shí)戰(zhàn)詳解
為什么要有package的概念米者?
因?yàn)橐獙Χ鄠€同名的類進(jìn)行命名空間的管理,避免同名類發(fā)生沖突
比如說宇智,scala.collection.mutable.Map和scala.collection.immutable.Map
package
package定義的第一種方式: 多層級package定義(比較差的做法塘雳,一般不這么干)
package com {
package ibeifeng {
package scala {
class Test {}
}
}
}
package定義的第二種方式: 串聯(lián)式package定義(也不怎么樣,一般也不這么干)
package com.ibeifeng.scala {
package service {
class Test {}
}
}
package定義的第三種方式: 文件頂部package定義
package com.ibeifeng.scala.service
class Test {
}
package定義的第四種方式: IDE自動生成包
- package特性一: 同一個包定義普筹,可以在不同的scala源文件中的; 一個scala源文件內(nèi),可以包含兩個包
//Test1.scala
package com {
package ibeifeng {
package scala {
class Test1
}
}
}
//Test2.scala
package com {
package ibeifeng {
package scala {
class Test2
}
}
}
//Test3.scala
package com {
package ibeifeng {
package scala1 {
class Test
}
}
}
package com {
package ibeifeng {
package scala2 {
class Test
}
}
}
- package特性二: 子包中的類隘马,可以訪問父包中的類
//Test.scala
package com {
package ibeifeng {
package scala {
object Utils {
def isNotEmpty(str: String): Boolean = str != null && str != ""
}
class Test
package service {
class MyService {
def sayHello(name: String) {
if(Utils.isNotEmpty(name)) {
println("Hello, " + name)
} else {
println("Who are you?")
}
}
}
}
}
}
}
object MainClass {
def main(args: Array[String]): Unit = {
val service = new com.ibeifeng.scala.service.MyService
service.sayHello("leo")
service.sayHello("")
}
}
- package特性三: 相對包名與絕對包名
package com {
package ibeifeng {
package scala {
object Utils {
def isNotEmpty(str: String): Boolean = str != null && str != ""
}
class Test
package collection {}
package service {
class MyService {
// 這會報(bào)錯太防,默認(rèn)使用相對報(bào)名,從com.ibeifeng.scala.collection包中酸员,尋找mutable包下的ArrayBuffer類
// 但是找不到蜒车,所以會報(bào)錯
// val names = new scala.collection.mutable.ArrayBuffer[String]
// 正確的做法是使用_root_,引用絕對包名
val names = new _root_.scala.collection.mutable.ArrayBuffer[String]
def sayHello(name: String) {
if(Utils.isNotEmpty(name)) {
println("Hello, " + name)
} else {
println("Who are you?")
}
}
}
}
}
}
}
- package特性四: 定義package對象(比較少)
package內(nèi)的成員幔嗦,可以直接訪問package對象內(nèi)的成員
package com.ibeifeng.scala
package object service {
val defaultName = "Somebody"
}
package service {
class MyService {
def sayHello(name: String) {
if(name != null && name != "") {
println("Hello, " + name)
} else {
println("Hello, " + defaultName)
}
}
}
}
- package特性五: package可見性
package com.ibeifeng.scala
class Person {
private[scala] val name = "leo"
private[ibeifeng] val age = 25
}
import
如果沒有import酿愧,那么。邀泉。嬉挡。你每次創(chuàng)建某個包下的類的對象,都得用new com.ibeifeng.scala.service.MyService這種冗長的格式汇恤。庞钢。。
所以如果用了import因谎,那么基括。。财岔。你只要先import com.ibeifeng.scala.service.MyService风皿,然后再new MyService,即可匠璧。桐款。。
import com.ibeifeng.scala.service.MyService;
object MainClass {
def main(args: Array[String]): Unit = {
val service = new MyService
service.sayHello("leo")
service.sayHello("")
}
}
import特性一: 用import com.ibeifeng.scala.service._這種格式患朱,可以導(dǎo)入包下所有的成員
import特性二: scala與java不同之處在于鲁僚,任何地方都可以使用import,比如類內(nèi)、方法內(nèi)冰沙,這種方式的好處在于侨艾,可以在一定作用域范圍內(nèi)使用導(dǎo)入
object MainClass {
def main(args: Array[String]): Unit = {
import com.ibeifeng.scala.service._
val service = new MyService
service.sayHello("leo")
service.sayHello("")
}
}
import特性三: 選擇器、重命名拓挥、隱藏
import com.ibeifeng.scala.service.{ MyService }唠梨,僅僅導(dǎo)入java.awt包下的Color和Font類
import com.ibeifeng.scala.service.{ MyService => MyServiceImpl },將導(dǎo)入的類進(jìn)行重命名
import com.ibeifeng.scala.service.{ MyService => _, _ }侥啤,導(dǎo)入java.util包下所有的類当叭,但是隱藏掉HashMap類import特性四: 隱式導(dǎo)入
每個scala程序默認(rèn)都會隱式導(dǎo)入以下幾個包下所有的成員
import java.lang._
import scala._
import Predef._
重寫field的提前定義、Scala繼承層級盖灸、對象相等性
重寫field的提前定義
默認(rèn)情況下蚁鳖,如果父類中的構(gòu)造函數(shù)代碼,用到了會被子類重寫的field; 那么出出現(xiàn)令人意想不到的一幕:
1赁炎、子類的構(gòu)造函數(shù)(無參)調(diào)用父類的構(gòu)造函數(shù)(無參)
2醉箕、父類的構(gòu)造函數(shù)初始化field(結(jié)果正確)
3、父類的構(gòu)造函數(shù)使用field執(zhí)行其他構(gòu)造代碼徙垫,但是此時(shí)其他構(gòu)造代碼如果使用了該field讥裤,而且field要被子類重寫,那么它的getter方法被重寫姻报,返回0(比如Int)
4己英、子類的構(gòu)造函數(shù)再執(zhí)行,重寫field(結(jié)果也正確)
5吴旋、但是此時(shí)子類從父類繼承的其他構(gòu)造代碼损肛,已經(jīng)出現(xiàn)了錯誤了
class Student {
val classNumber: Int = 10
val classScores: Array[Int] = new Array[Int](classNumber)
}
class PEStudent {
override val classNumber: Int = 3
}
本來我們期望的是,PEStudent邮府,可以從Student繼承來一個長度為3的classScores數(shù)組
結(jié)果荧关。。褂傀。PEStudent對象忍啤,只有一個長度為0的classScores數(shù)組
此時(shí)只能使用Scala對象繼承的一個高級特性: 提前定義,在父類構(gòu)造函數(shù)執(zhí)行之前仙辟,先執(zhí)行子類的構(gòu)造函數(shù)中的某些代碼
class PEStudent extends student {
override val classNumber: Int = 3
} with Student
Scala的繼承層級
這里我們大概知道一下Scala的繼承層級同波,我們寫的所有的Scala trait和class,都是默認(rèn)繼承自一些Scala根類的叠国,有一些基礎(chǔ)的方法
Scala中未檩,最頂端的兩個trait是Nothing和Null,Null trait唯一的對象就是null
其次是繼承了Nothing trait的Any類
接著Anyval trait和AnyRef類粟焊,都繼承自Any類
Any類是個比較重要的類冤狡,其中定義了isInstanceOf和asInstanceOf等方法孙蒙,以及equals、hashCode等對象的基本方法
Any類悲雳,有點(diǎn)像Java中的Object基類
AnyRef類挎峦,增加了一些多線程的方法,比如wait合瓢、notify/notifyAll坦胶、synchronized等,也是屬于Java Object類的一部分
對象相等性
這里晴楔,我們要知道顿苇,在scala中,你如何判斷兩個引用變量税弃,是否指向同一個對象實(shí)例
AnyRef的eq方法用于檢查兩個變量是否指向同一個對象實(shí)例
AnyRef的equals方法默認(rèn)調(diào)用eq方法實(shí)現(xiàn)纪岁,也就是說,默認(rèn)情況下则果,判斷兩個變量相等蜂科,要求必須指向同一個對象實(shí)例
通常情況下,自己可以重寫equals方法短条,根據(jù)類的fields來判定是否相等
此外导饲,定義equals方法時(shí)摘昌,也最好使用同樣的fields,重寫hashCode方法
如果只是想要簡單地通過是否指向同一個對象實(shí)例晶伦,判定變量是否相當(dāng)赋访,那么直接使用==操作符即可可都,默認(rèn)判斷null,然后調(diào)用equals方法
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
}
文件操作實(shí)戰(zhàn)詳解
遍歷一個文件中的每一行
必須導(dǎo)入scala.io.Source類: import scala.io.Source
方法一: 使用Source.getLines返回的迭代器
val source = Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")
val lineIterator = source.getLines
for (line <- lineIterator) println(line)
方法二: 將Source.getLines返回的迭代器蚓耽,轉(zhuǎn)換成數(shù)組
這里說明一點(diǎn): 一個BufferedSource對象的getLines方法渠牲,只能調(diào)用一次,一次調(diào)用完之后步悠,遍歷了迭代器里所有的內(nèi)容签杈,就已經(jīng)把文件里的內(nèi)容讀取完了
如果反復(fù)調(diào)用source.getLines,是獲取不到內(nèi)容的
此時(shí)鼎兽,必須重新創(chuàng)建一個BufferedSource對象
val source = Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")
val lines = source.getLines.toArray
for(line <- lines) println(line)
方法三: 調(diào)用Source.mkString答姥,返回文本中所有的內(nèi)容
val source = Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")
val lines = source.mkString
使用完BufferedSource對象之后,調(diào)用BufferedSource.close方法谚咬,關(guān)閉IO流資源
遍歷一個文件中的每一個字符
BufferedSource鹦付,也實(shí)現(xiàn)了一個Iterator[Char]的這么一個trait
val source = Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")
for(c <- source) print(c)
從URL以及字符串中讀取字符
val source = Source.fromURL("http://www.baidu.com", "UTF-8")
val source = Source.fromString("Hello World")
結(jié)合Java IO流,讀取任意文件
這里說明一點(diǎn)择卦,大家千萬不要以為敲长,spark就是會scala就可以了
也千萬不要以為郎嫁,scala,就是跟java一點(diǎn)關(guān)系都沒有祈噪,甚至于完全可以替代java
上述說法泽铛,都是很荒謬的,都是門外漢才會這么認(rèn)為
如果你真的深入讀了spark的源代碼
真的對scala掌握的很深入钳降,你就會知道一點(diǎn)
spark的源碼實(shí)際上是由scala和java共同編寫而成的厚宰,Java的多線程
scala,本身的編程語言的功能遂填,就不是特別的強(qiáng)大和完善铲觉,比如說,scala甚至不能很方便地寫文件吓坚,必須依賴于java的io流才可以
所以說撵幽,scala,其實(shí)主要就是針對某些特定領(lǐng)域的一些復(fù)雜系統(tǒng)的礁击,比較適用的一種編程語言而已
完全無法替代java的盐杂,scala和java是相輔相成,榮辱與共的這么一種哆窿,共生關(guān)系
可以這么跟大家說
scala還有一種作用链烈,可以用scala,編寫spark的作業(yè)
但是問題是挚躯,為什么强衡,我們要用java開發(fā)hive udf、mapreduce码荔、hbase client漩勤、zookeeper client,用Java開發(fā)storm的作業(yè)
然后作為一個大數(shù)據(jù)工程師缩搅,偏偏用到spark的時(shí)候越败,一定要用scala開發(fā)呢?
用spark開發(fā)作業(yè)硼瓣,用java究飞,個人認(rèn)為,個人觀點(diǎn)堂鲤,是最合適的噪猾,最通用的,最可移植的筑累,最方便維護(hù)的
scala袱蜡,這套課程里,scala編程詳解慢宗、scala編程進(jìn)階
1坪蚁、有些公司的技術(shù)leader奔穿,要求用scala開發(fā)spark作業(yè),我也沒辦法敏晤,我是極力反對的; 保證學(xué)員贱田,學(xué)了這套課程以后,可以用scala去工作和面試
2嘴脾、有些同學(xué)男摧,可能壓根兒不會java; 大多數(shù)是上學(xué)的時(shí)候,主要是搞算法的译打,或者只會c++耗拓,只會python; 這套課程學(xué)了,不用會java奏司,那么也可以精通和使用spark
3乔询、最重要的一點(diǎn),深入掌握scala所有的初中高級語法韵洋,才能透徹和深入的理解和閱讀spark的源碼
4竿刁、也有,但是很少搪缨,就是有些公司食拜,可能會用scala,開發(fā)復(fù)雜的大型分布式后端系統(tǒng)
案例: 結(jié)合java IO流副编,做一個文件拷貝的案例
import java.io._
val fis = new FileInputStream(new File("C://Users//Administrator//Desktop//test.txt"))
val fos = new FileOutputStream(new File("C://Users//Administrator//Desktop//test3.txt"))
val buf = new Array[Byte](1024)
fis.read(buf)
fos.write(buf, 0, 1024)
fis.close()
fos.close()
結(jié)合Java IO流监婶,寫文件
val pw = new PrintWriter("C://Users//Administrator//Desktop//test4.txt")
pw.println("Hello World")
pw.close()
遞歸遍歷子目錄
def getSubdirIterator(dir: File): Iterator[File] = {
val childDirs = dir.listFiles.filter(_.isDirectory)
childDirs.toIterator ++ childDirs.toIterator.flatMap(getSubdirIterator _)
}
val iterator = getSubdirIterator(new File("C://Users//Administrator//Desktop"))
for(d <- iterator) println(d)
序列化以及反序列化(Java序列化和反序列化機(jī)制)
如果要序列化,那么就必須讓類齿桃,有一個@SerialVersionUID,定義一個版本號
要讓類繼承一個Serializable trait
@SerialVersionUID(42L) class Person(val name: String) extends Serializable
val leo = new Person("leo")
import java.io._
val oos = new ObjectOutputStream(new FileOutputStream("C://Users//Administrator//Desktop//test.obj"))
oos.writeObject(leo)
oos.close()
val ois = new ObjectInputStream(new FileInputStream("C://Users//Administrator//Desktop//test.obj"))
val restoredLeo = ois.readObject().asInstanceOf[Person]
restoredLeo.name
偏函數(shù)實(shí)戰(zhàn)詳解
偏函數(shù)煮盼,是一種高級的函數(shù)形式
簡單來說短纵,偏函數(shù)是什么,其實(shí)就是沒有定義好明確的輸入?yún)?shù)的函數(shù)僵控,函數(shù)體就是一連串的case語句
一般的函數(shù)
def getStudentGrade(name: String) = {
...
}
偏函數(shù)是PartialFunction[A, B]類的一個實(shí)例
這個類有兩個方法香到,一個是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")
執(zhí)行外部命令
scala執(zhí)行外部命令
咱們的scala程序充易,實(shí)際上梗脾,寫好以后,跑起來盹靴,關(guān)鍵是炸茧,跑在哪里瑞妇?
scala程序是運(yùn)行在java虛擬機(jī)中的,也就是咱們平時(shí)常說的jvm梭冠,所以我們之前能夠看到辕狰,scala可以直接調(diào)用jdk
jdk: java development kit,java基礎(chǔ)的開發(fā)api
scala的程序控漠,是運(yùn)行在一個進(jìn)程中的
運(yùn)行在什么進(jìn)程中蔓倍?是運(yùn)行在jvm虛擬機(jī)進(jìn)程中的
比如說,如果說盐捷,我們的scala程序偶翅,希望去執(zhí)行scala所在進(jìn)程之外的,比如說毙驯,本地操作系統(tǒng)的一個命令
也許執(zhí)行的本地操作系統(tǒng)的命令倒堕,會啟動一個新的進(jìn)程,也許也不會
但是爆价,如果想要實(shí)現(xiàn)這樣的效果和功能垦巴,scala能不能夠做到?
這個是可以的
scala實(shí)際上铭段,是提供了這樣的支持的
也就是說骤宣,咱們的scala程序,運(yùn)行在一個獨(dú)立的進(jìn)程中序愚,但是可以隨心所欲地執(zhí)行外部操作系統(tǒng)的其他命令
甚至是說憔披,啟動其他的進(jìn)程
案例: 使用scala編譯和執(zhí)行外部的java程序
//HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World");
System.out.println("Hello Leo");
}
}
import sys.process._
"javac HelloWorld.java" !
"java HelloWorld" !
正則表達(dá)式支持
scala的正則表達(dá)式支持
正則表達(dá)式是什么?
一種語法爸吮,用一個表達(dá)式芬膝,來匹配一系列的字符串
[a-z]+: 一個或多個a~z范圍的26個小寫英文字母,比如hello形娇,world
強(qiáng)調(diào)一點(diǎn)锰霜,咱們不會去給大家詳細(xì)介紹正則表達(dá)式的語法
正則表達(dá)式這個東西,語法比較多
有興趣桐早,有需要的話癣缅,那么建議直接百度一下,自己看一下介紹哄酝,就知道是怎么回事了
大致學(xué)習(xí)一下正則表達(dá)式的語法即可
我們這里呢友存,講解一下scala中,對這個正則表達(dá)式的支持
定義一個正則表達(dá)式陶衅,使用String類的r方法
此時(shí)返回的類型是scala.util.matching.Regex類的對象
val pattern1 = "[a-z]+".r
拿到一個正則表達(dá)式以后屡立,我們一般會用它來做什么事情?
比如搀军,我們會用正則表達(dá)式來匹配一些字符串侠驯,比如來看看抡秆,某個字符串是否符合表達(dá)式規(guī)定的范圍之內(nèi)
比如,從一個長的字符串中吟策,提取出來儒士,匹配正則表達(dá)式的各個部分
val str = "hello 123 world 456"
獲取一個字符串中,匹配正則表達(dá)式的部分檩坚,使用findAllIn着撩,會獲取到一個Iterator,迭代器
然后就可以去遍歷各個匹配正則的部分匾委,去進(jìn)行處理
for (matchString <- pattern1.findAllIn(str)) println(matchString)
同理拖叙,使用findFirstIn,可以獲取第一個匹配正則表達(dá)式的部分
pattern1.findFirstIn(str)
使用replaceAllIn赂乐,可以將匹配正則的部分薯鳍,替換掉
pattern1.replaceFirstIn("hello world", "replacement")
使用replaceFirstIn,可以將第一個匹配正則的部分挨措,替換掉
pattern1.replaceAllIn("hello world", "replacement")
提取器實(shí)戰(zhàn)詳解
apply方法
伴生類和伴生對象的概念挖滤,companion class和companion object
伴生對象里面,可以定義一個apply方法
直接調(diào)用類(參數(shù))浅役,方式斩松,就相當(dāng)于在調(diào)用apply方法
此時(shí)在apply方法中,通常來說(也不一定)觉既,會創(chuàng)建一個伴生類的對象惧盹,返回回去
這種方式,有一個好處瞪讼,創(chuàng)建對象呢钧椰,非常的方便
不要每次都是new 類(參數(shù)),類(參數(shù))
unapply方法
和apply方法符欠,顧名思義嫡霞,那就是反過來
apply方法,可以理解為背亥,接收一堆參數(shù),然后返回一個對象
unapply方法悬赏,可以理解為狡汉,接收一個字符串,解析成一個對象的各個字段
提取器就是一個包含了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
樣例類的提取器實(shí)戰(zhàn)詳解
樣例類的提取器
scala中的樣例類橄仆,說白了,也很簡單
類似于java中的javabean衅斩,java中的JavaBean盆顾,是什么東東?
包含了一堆屬性畏梆,field; 每個field都有一對getter和setter方法
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
scala中的樣例類您宪,默認(rèn)就是提供apply方法和unapply方法的
case class Person(name: String, age: Int)
val p = Person("leo", 25)
p match {
case Person(name, age) => println(name + ": " + age)
}
只有一個參數(shù)的提取器
之前,已經(jīng)跟大家講解過普通的提取器
相當(dāng)于是奠涌,比如說宪巨,接收一個字符串,作為參數(shù)
然后從字符串里面解析出來多個字段值溜畅,然后將多個字段值封裝在一個tuple中
作為Some類型的對象捏卓,返回
現(xiàn)在我們來想一下,如果你的類只有一個字段
字符串里面只有一個字段
解析出來的一個字段慈格,是沒有辦法放在tuple中的怠晴,因?yàn)閟cala中的tuple,規(guī)定了峦椰,必須要兩個以及兩個以上的值
這個時(shí)候龄寞,在提取器,unapply方法中汤功,只能將一個字段值物邑,封裝在Some對象中,直接返回
class Person(val name: String)
object Person {
def unapply(input: String): Option[String] = Some(input)
}
val Person(name) = "leo"
注解實(shí)戰(zhàn)詳解
什么是注解滔金?
注解其實(shí)說白了色解,就是在我們的代碼中,加入一些特殊的標(biāo)記
特殊的標(biāo)記大概長什么樣子呢餐茵?
我們之前學(xué)過一個很常用科阎,和很經(jīng)典的一個注解,其實(shí)就是@BeanProperty忿族,讓編譯器自動生成屬性的JavaBean風(fēng)格的getter和setter方法
除此之外锣笨,還在《文件操作實(shí)戰(zhàn)詳解》那一講,講過一個序列化的這個東西道批,@SerialUID(可能是錯誤的)错英,指定一個序列化的版本號
注解是用來干嘛的?
然后我們的scala編譯器隆豹,就可以在編譯的時(shí)候椭岩,在碰到注解的時(shí)候,做一些特殊的操作。一個非常經(jīng)典的例子就是
@BeanProperty注解判哥,我們之前講解過献雅,給某個field添加了這個注解之后,scala編譯器就會給field編譯出新的JavaBean風(fēng)格的getter和setter方法
scala中塌计,在哪些地方可以添加注解呢挺身?
scala中,可以給類夺荒、方法瞒渠、field、local variable技扼、constructor / method / function parameter添加注解
而且scala是支持給某個目標(biāo)添加多個注解的
這里有一些特例:如果要給類的主構(gòu)造函數(shù)添加注解伍玖,那么需要在構(gòu)造函數(shù)前添加注解,并加上一對圓括號
比如說
class Person @Unchecked() (val name: String, val age: Int)
還可以給表達(dá)式添加注解剿吻,此時(shí)需要在表達(dá)式后面加上冒號以及注解窍箍,比如
val scores = Map("Leo" -> 90, "Jack" -> 60)
(scores.get("Leo"): @unchecked) match { case score => println(score) }
除此之外,還可以給類型參數(shù)和變量的類型定義添加注解
Scala中開發(fā)注解
要自己動手開發(fā)一個注解丽旅,就必須擴(kuò)展Annotation trait椰棘,比如
class Test extends annotation.Annotation
@Test
class myTest
注解的參數(shù)
注解中,是可以有參數(shù)的榄笙,比如
class Test(var timeout: Int) extends annotation.Annotation
@Test(timeout = 100) class myTest
如果注解的參數(shù)是value的話邪狞,那么也可以不用指定注解的參數(shù)名,比如
class Test(var value: String) extends annotation.Annotation
常用注解介紹
dscala提供的常用注解
其實(shí)里面很多都是針對java中的概念和概念提供的
跟大家提示一下茅撞,再次證明了帆卓,你搞scala,真想搞好的話米丘,先學(xué)和精通java
scala和java的關(guān)系是唇齒相依的
scala依賴java
java并不依賴scala
scala中剑令,常用的一些注解,全部是針對java的一些概念
所以呢拄查,在這里我們沒辦法給大家詳細(xì)講解java的概念吁津,就直接介紹scala中針對java的一些注解
@volatile var name = "leo" 輕量級的java多線程并發(fā)安全控制
jvm,java虛擬機(jī)中堕扶,可以有多個線程
每個線程都有自己的工作區(qū)碍脏,還有一塊兒所有線程共享的工作區(qū)
每次一個線程拿到一個公共的變量,都需要從共享區(qū)中拷貝一個副本到自己的工作區(qū)中使用稍算,和修改
然后修改完以后典尾,再在一個合適的時(shí)機(jī),將副本的值邪蛔,寫回到共享區(qū)中
這里就會出現(xiàn)一個多線程并發(fā)訪問安全的問題
多個線程如果同時(shí)拷貝了變量副本急黎,都做了不同的修改
然后依次將副本修改的值,寫回到共享區(qū)中侧到,會依次覆蓋掉之前的一些副本值
就會出現(xiàn)變量的值勃教,是不符合預(yù)期的
咱們的系統(tǒng),出現(xiàn)了錯誤和bug
volatile關(guān)鍵字修飾的變量
它可以保證匠抗,一個線程在從共享區(qū)獲取一個變量的副本時(shí)故源,都會強(qiáng)制刷新一下這個變量的值
保證自己獲取到的變量的副本值是最新的
所以這樣子做呢,是一種輕量級的多線程并發(fā)訪問控制辦法
但是也不是百分之百保險(xiǎn)的汞贸,還是有可能會出現(xiàn)錯誤的風(fēng)險(xiǎn)
@transient var name = "leo" 瞬態(tài)字段绳军,不會序列化這個字段
之前講序列化,默認(rèn)會將一個對象中所有的字段的值矢腻,都序列化到磁盤文件中去
然后反序列化的時(shí)候门驾,還可以獲取這些字段的值
加了transient的字段,是瞬態(tài)的多柑,序列化的時(shí)候奶是,不會序列化這個字段
反序列化的時(shí)候,這個字段也就沒有值了
@SerialVersionUID(value) 標(biāo)記類的序列化版本號
序列化版本號竣灌,這個什么意思
如果我們將一個類的對象序列化到磁盤文件上了
結(jié)果過了一段時(shí)間以后聂沙,這個類在代碼中改變了,此時(shí)如果你想將磁盤文件中的對象反序列化回來
就會報(bào)錯初嘹,因?yàn)槟愕男蛄谢膶ο蟮慕Y(jié)構(gòu)與代碼中的類結(jié)構(gòu)已經(jīng)不一樣了
針對這種問題及汉,就應(yīng)該有一個序列化版本號
如果你的類改變了,就重新生成一個序列化版本號
反序列化的時(shí)候屯烦,就會發(fā)現(xiàn)序列化類型的版本號和代碼中的類的版本號坷随,不一樣
@native 標(biāo)注用c實(shí)現(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)換的警告
XML基礎(chǔ)操作實(shí)戰(zhàn)詳解
scala中定義xml
scala對xml有很好的支持漫贞,可以直接在scala代碼中定義一個xml文檔元素
val books = <books><book>my first scala book</book></books>
此時(shí)doc的類型是scala.xml.Elem甸箱,也就是一個xml元素
scala還可以直接定義多個同級別的xml元素
val books = <book>my first scala book</book><book>my first spark book</book>
此時(shí)doc的類型是scala.xml.NodeBuffer,也就是一個xml節(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)
還可以調(diào)用book.attributes.asAttrMap象迎,獲取一個屬性Map
XML中嵌入scala代碼
在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修改元素實(shí)戰(zhàn)詳解
修改xml元素
默認(rèn)情況下,scala中的xml表達(dá)式是不可改變的呛踊;如果要修改xml元素的話砾淌,必須拷貝一份再修改
val books = <books><book>book1</book></books>
添加一個子元素
val booksCopy = books.copy(child = books.child ++ <book>book2</book>)
val 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))
說點(diǎn)閑話
如果大家真的對java比較精通的話
然后過來學(xué)習(xí)這個scala,就會發(fā)現(xiàn)有個特點(diǎn)
java的功能是非常強(qiáng)大的
但是谭网,從各個方面來看汪厨,比如io、xml操作愉择、第三方類庫的支持骄崩、socket、gui界面編程薄辅、jdbc訪問數(shù)據(jù)庫等等要拂,scala都比java差很多
之所以現(xiàn)在scala有點(diǎn)火,有些人推崇這個scala
其實(shí)主要是因?yàn)閟park是用scala作為主要的語言開發(fā)的(但是spark底層的源碼站楚,其實(shí)都是java)
類加載器脱惰、線程、反射窿春、線程池等等這些東西拉一,全部都是java底層,外部命令的執(zhí)行(ProcessBuilder)
XML加載和寫入外部文檔
加載和寫入外部xml文件
import scala.xml._
import java.io._
使用scala的XML類加載
val books = XML.loadFile("C://Users//Administrator//Desktop//books.xml")
使用Java的FileInputStream類加載
val books = XML.load(new FileInputStream("C://Users//Administrator//Desktop//books.xml"))
使用Java的InputStreamReader類指定加載編碼
val books = XML.load(new InputStreamReader(new FileInputStream("C://Users//Administrator//Desktop//books.xml"), "UTF-8"))
將內(nèi)存中的xml對象旧乞,寫入外部xml文檔
XML.save("C://Users//Administrator//Desktop//books2.xml", books)
集合元素操作
col :+ ele 將元素添加到集合尾部 Seq
ele +: col 將元素添加到集合頭部 Seq
col + ele 在集合尾部添加元素 Set蔚润、Map
col + (ele1, ele2) 將其他集合添加到集合的尾部 Set、Map
col - ele 將元素從集合中刪除 Set尺栖、Map嫡纠、ArrayBuffer
col - (ele1, ele2) 將子集合從集合中刪除 Set、Map延赌、ArrayBuffer
col1 ++ col2 將其他集合添加到集合尾部 Iterable
col2 ++: col1 將其他集合添加到集合頭部 Iterable
ele :: list 將元素添加到list的頭部 List
list2 ::: list1 將其他list添加到list的頭部 List
list1 ::: list2 將其他list添加到list的尾部 List
set1 | set2 取兩個set的并集 Set
set1 & set2 取兩個set的交集 Set
set1 &~ set2 取兩個set的diff Set
col += ele 給集合添加一個元素 可變集合
col += (ele1, ele2) 給集合添加一個集合 可變集合
col ++= col2 給集合添加一個集合 可變集合
col -= ele 從集合中刪除一個元素 可變集合
col -= (ele1, ele2) 從集合中刪除一個子集合 可變集合
col —= col2 從集合中刪除一個子集合 可變集合
ele +=: col 向集合頭部添加一個元素 ArrayBuffer
col2 ++=: col 向集合頭部添加一個集合 ArrayBuffer
集合的常用操作方法
head除盏、last、tail
length挫以、isEmpty
sum者蠕、max、min
count掐松、exists踱侣、filter粪小、filterNot
takeWhile、dropWhile
take抡句、drop糕再、splitAt
takeRight、dropRight
sclie
contains玉转、startsWith、endsWith
indexOf
intersect殴蹄、diff
map究抓、flatMap、collect袭灯、foreach實(shí)戰(zhàn)詳解
map操作刺下,一對一映射
val scoreMap = Map("leo" -> 90, "jack" -> 60, "tom" -> 70)
val names = List("leo", "jack", "tom")
names.map(scoreMap(_))
flatMap操作,一對多映射
val scoreMap = Map("leo" -> List(80, 90, 60), "jack" -> List(70, 90, 50), "tom" -> List(60,70,40))
names.map(scoreMap(_))
names.flatMap(scoreMap(_))
collect操作稽荧,結(jié)合偏函數(shù)使用
"abc".collect { case 'a' => 1; case 'b' => 2; case 'c' => 3 }
foreach操作橘茉,遍歷
names.foreach(println _)
reduce和fold實(shí)戰(zhàn)詳解
reduce操作
List(1, 2, 3, 4).reduceLeft(_ - _)
List(1, 2, 3, 4).reduceRight(_ - _)
fold操作
List(1, 2, 3, 4).foldLeft(10)(_ - _)
List(1, 2, 3, 4).foldRight(10)(_ - _)