ScalaDoc的使用

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)(_ - _)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市姨丈,隨后出現(xiàn)的幾起案子畅卓,更是在濱河造成了極大的恐慌,老刑警劉巖蟋恬,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翁潘,死亡現(xiàn)場離奇詭異,居然都是意外死亡歼争,警方通過查閱死者的電腦和手機(jī)拜马,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沐绒,“玉大人俩莽,你說我怎么就攤上這事∏钦冢” “怎么了扮超?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長蹋肮。 經(jīng)常有香客問我瞒津,道長,這世上最難降的妖魔是什么括尸? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任巷蚪,我火速辦了婚禮,結(jié)果婚禮上濒翻,老公的妹妹穿的比我還像新娘屁柏。我一直安慰自己啦膜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布淌喻。 她就那樣靜靜地躺著僧家,像睡著了一般。 火紅的嫁衣襯著肌膚如雪裸删。 梳的紋絲不亂的頭發(fā)上八拱,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天,我揣著相機(jī)與錄音涯塔,去河邊找鬼肌稻。 笑死,一個胖子當(dāng)著我的面吹牛匕荸,可吹牛的內(nèi)容都是我干的爹谭。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼榛搔,長吁一口氣:“原來是場噩夢啊……” “哼诺凡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起践惑,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤腹泌,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后尔觉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體真屯,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年穷娱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了绑蔫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡泵额,死狀恐怖配深,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嫁盲,我是刑警寧澤篓叶,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站羞秤,受9級特大地震影響缸托,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘾蛋,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一俐镐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧哺哼,春花似錦佩抹、人聲如沸叼风。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽无宿。三九已至,卻和暖如春枢里,著一層夾襖步出監(jiān)牢的瞬間孽鸡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工栏豺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彬碱,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓冰悠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親配乱。 傳聞我的和親對象是個殘疾皇子溉卓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容