Scala編程進階

跳出循環語句的3種方法... 2java

多維數組... 3正則表達式

Java數組與Scala數組緩衝的隱式轉換... 3編程

Java MapScala Map的隱式轉換... 3數組

Tuple拉鍊操做... 4安全

內部類的做用域:外部類對象... 4多線程

擴大內部類做用域:伴生對象... 4併發

擴大內部類做用域:類型投影... 5app

內部類獲取外部類的引用... 5jvm

package定義... 5ide

package特性... 6

import. 9

重寫field的提早定義... 10

Scala的繼承層級... 11

對象相等性... 11

文件操做... 11

序列化以及反序列化... 13

偏函數... 13

執行外部命令... 14

正則表達式支持... 14

提取器... 14

樣例類的提取器... 15

只有一個參數的提取器... 16

註解... 16

經常使用註解... 17

xml 18

XML節點類型... 18

xml元素的屬性... 19

xml中嵌入scala代碼... 19

修改xml元素... 19

加載和寫入外部xml文件... 20

集合元素操做符... 20

集合的經常使用方法... 21

   

跳出循環語句的3種方法

方法一:使用boolean控制變量

while循環:

var flag = true

var res = 0

var n = 0

while(flag) {

  res += n

  n += 1

  if (n == 5) {

    flag = false

  }

}

 

for循環:(高級for循環,加上了if守衛)

var flag = true

var res = 0

for (i <- 0 until 10 if flag) {

  res += i

  if (i == 4) flag = false

}

方法二:在嵌套函數中使用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

  }

}

多維數組

什麼是多維數組?:數組的元素,仍是數組,數組套數組,就是多維數組

構造指定行與列的二維數組:Array.ofDim方法

val multiDimArr1 = Array.ofDim[Double](3, 4)

multiDimArr1(0)(0) = 1.0

構造不規則多維數組:

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數組與Scala數組緩衝的隱式轉換

Scala代碼中,直接調用JDKJava)的API,好比調用一個Java類的方法,勢必可能會傳入Java類型的list,此時若是直接把ScalaArrayBuffer傳入Java接收ArrayList的方法,確定不行。這能夠先將Scala中的Buffer轉換爲Java中的List便可

import scala.collection.JavaConversions.bufferAsJavaList

import scala.collection.mutable.ArrayBuffer

val command = ArrayBuffer("javac", "C:\\Users\\Administrator\\Desktop\\HelloWorld.java") // 調用操做系統命令編譯源碼

// ProcessBuilderJDK中的類,構造函數爲:ProcessBuilder(List<String> command),要求的是List,因此須要將ScalaBuffer轉換爲Java中的List,才能在Java中使用

val processBuilder = new ProcessBuilder(command)

val process = processBuilder.start()

val res = process.waitFor()

 

下面是將Java返回的List轉換爲Buffer

import scala.collection.JavaConversions.asScalaBuffer

import scala.collection.mutable.Buffer

// ProcessBuildercommand()方法返回的是List<String>,因此須要將List<String>隱式轉換爲Buffer[String],才能在Scala中使用

val cmd: Buffer[String] = processBuilder.command()

Java MapScala Map的隱式轉換

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 // Java Map自動隱式轉換 Scala Map

 

import scala.collection.JavaConversions.mapAsJavaMap

import java.awt.font.TextAttribute._

val scalaAttrMap = Map(FAMILY -> "Serif", SIZE -> 12)

val font = new java.awt.Font(scalaAttrMap) // Scala Map自動隱式轉換 Java Map

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,調用ArraytoMap方法,能夠將Array轉換爲Map

studentScores.toMap

內部類的做用域:外部類對象

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 // error: type mismatch;

擴大內部類做用域:伴生對象

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

擴大內部類做用域:類型投影

class Class {

  class Student(val name: String)

  val students = new ArrayBuffer[Class#Student] // 明確說明使用的是Class類型中的Student類,而非Class對象中的

  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

內部類獲取外部類的引用

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定義

由於要對多個同名的類進行命名空間的管理,避免同名類發生衝突

好比說,scala.collection.mutable.Mapscala.collection.immutable.Map

 

 

package定義的第一種方式: 多層級package定義(比較差的作法,通常不這麼幹)

package com {

         package sn {

                   package scala {

                            class Test {}

                   }

         }

}

 

package定義的第二種方式: 串聯式package定義(也不怎麼樣,通常也不這麼幹)

package com.sn.scala {

         package service {

                   class Test {}

         }

}

 

package定義的第三種方式: 文件頂部package定義

package com.sn.scala.service

class Test {

}

package特性

同一個包定義,能夠在不一樣的scala源文件中的:

Test1.scala

package com {

  package sn {

    package scala {

      class Test1

    }

  }

}

 

Test2.scala

package com {

  package sn {

    package scala {

      class Test2

    }

  }

}

一個scala源文件內,能夠包含兩個包:

Test3.scala

package com {

  package sn {

    package scala1 {

      class Test

    }

  }

}

 

package com {

  package sn {

    package scala2 {

      class Test

    }

  }

}

子包中的類,能夠訪問父包中的類:

Test.scala

package com {

  package sn {

    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 T {

  def main(args: Array[String]) {

    import com.sn.scala.service._

    new MyService().sayHello("")

    new MyService().sayHello("leo")

  }

}

 

相對包名與絕對包名:

package com {

  package sn {

    package scala {

      object Utils {

        def isNotEmpty(str: String): Boolean = str != null && str != ""

      }

 

      class Test

 

      package collection {}

 

      package service {

        class MyService {

          // 報錯,默認使用相對報名,從com.sn.scala.collection包中,尋找mutable包下的ArrayBuffer

          // 可是找不到,因此會報錯

          // 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對象內的成員

package com.sn.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 com.sn {

  package scala {

    class Person {

      //com.sn.scala包下可見

      private[scala] val name = "leo"

      //com.sn包下可見

      private[sn] val age = 25

    }

    object T1 {

      new Person().name

    }

  }

  object T2 {

    import com.sn.scala.Person

    new Person().age

  }

}

import

package com.sn.scala

package service {

  class MyService {

    def sayHello(name: String) {}

  }

}

 

import com.sn.scala.service.MyService;

object MainClass {

  def main(args: Array[String]): Unit = {

    val service = new MyService

  }

}

 

 

import特性一: import com.sn.scala.service._這種格式,能夠導入包下全部的成員

 

import特性二: scalajava不一樣之處在於,任何地方均可以使用import,好比類內、方法內,這種方式的好處在於,能夠在必定做用域範圍內使用導入

object MainClass {

  def main(args: Array[String]): Unit = {

    import com.sn.scala.service._

    val service = new MyService

  }

}

 

import特性三: 選擇器、重命名、隱藏

 

import com.sn.scala.service.{ MyService },僅僅導入com.sn.scala.service包下的MyService類,其它不導入

import com.sn.scala.service.{ MyService => MyServiceImpl },將導入的類進行重命名

import com.sn.scala.service.{ MyService => _, _ },導入com.sn.scala.service包下全部的類,可是隱藏掉MyService

 

import特性四: 隱式導入

每一個scala程序默認都會隱式導入如下幾個包下全部的成員

import java.lang._

import scala._

import Predef._

重寫field的提早定義

默認狀況下,若是父類中的構造函數代碼用到了被子類重寫(或實現)的field,那麼可能會出現未被正確初始化的問題:

當父類的構造函數執行時,若是使用到了被子類實現或重寫過的fieldfield重寫或實現至關於對應的getter方法被重寫),會調用子類重寫或實現過的field,因爲子類構造器尚未執行,因此會返回子類中被重寫或實現過的該field的初始值(好比Int0Stringnull)。詳細能夠參數《Scala編程基礎》中的「trait field的初始化」相應章節

class Student {

  val classNumber: Int = 10

  val classScores: Array[Int] = new Array[Int](classNumber) // 會調用子類被重寫過的classNumber字段

}

 

class PEStudent extends Student {

  override val classNumber: Int = 3

}

scala> new PEStudent().classScores

res42: Array[Int] = Array()

原本咱們指望的是,PEStudent,能夠從Student繼承來一個長度爲3classScores數組,結果PEStudent對象,只有一個長度爲0classScores數組

 

此時只能使用Scala對象繼承的一個高級特性: 提早定義,在父類構造函數執行以前,先執行子類的構造函數中的某些代碼

class Student {

  val classNumber: Int = 10

  val classScores: Array[Int] = new Array[Int](classNumber) // 會調用子類被重寫過的classNumber字段

}

class PEStudent extends {

  override val classNumber: Int = 3

} with Student

scala> new PEStudent().classScores

res43: Array[Int] = Array(0, 0, 0)

 

也能夠這樣,但Student需定義成trait

trait Student {

  val classNumber: Int = 10

  val classScores: Array[Int] = new Array[Int](classNumber)

}

class PEStudent extends Student {

}

var ps = new {override val classNumber: Int = 3} with PEStudent with Student

ps.classScores

Scala的繼承層級

這裏咱們大概知道一下Scala的繼承層級,咱們寫的全部的Scala traitclass,都是默認繼承自一些Scala根類的,有一些基礎的方法

 

Scala中,最頂端的兩個traitNothingNullNull trait惟一的對象就是null

其次是繼承了Nothing traitAny

接着Anyval traitAnyRef類,都繼承自Any

 

Any類是個比較重要的類,其中定義了isInstanceOfasInstanceOf等方法,以及equalshashCode等對象的基本方法

Any類,有點像Java中的Object基類

AnyRef類,增長了一些多線程的方法,好比waitnotify/notifyAllsynchronized等,也是屬於Java Object類的一部分

對象相等性

這裏,咱們要知道,在scala中,你如何判斷兩個引用變量,是否指向同一個對象實例

 

AnyRefeq方法用於檢查兩個變量是否指向同一個對象實例

AnyRefequals方法默認調用eq方法實現,也就是說,默認狀況下,判斷兩個變量相等,要求必須指向同一個對象實例

 

一般狀況下,本身能夠重寫equals方法,根據類的fields來斷定是否相等

此外,定義equals方法時,也最好使用一樣的fields,重寫hashCode方法

 

若是隻是想要簡單地經過是否指向同一個對象實例,斷定變量是否至關,那麼直接使用==操做符便可,默認判斷null,而後調用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

}

文件操做

必須導入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返回的迭代器轉換成數組

val source = Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")

val lines = source.getLines.toArray

for(line <- lines) println(line)

這裏說明一點: 一個BufferedSource對象的getLines方法,只能調用一次,一次調用完以後,遍歷了迭代器裏全部的內容,就已經把文件裏的內容讀取完了

若是反覆調用source.getLines,是獲取不到內容的。此時,必須從新建立一個BufferedSource對象

 

方法三: 調用Source.mkString,返回文本中全部的內容

val source = Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")

val lines = source.mkString

 

使用完BufferedSource對象以後,調用BufferedSource.close方法,關閉IO流資源

 

遍歷一個文件中的每個字符

val source = Source.fromFile("C://Users//Administrator//Desktop//test.txt", "UTF-8")

for (c <- source) print(c)// BufferedSource,也實現了一個Iterator[Char]的一個trait

 

URL以及字符串中讀取字符

val source = Source.fromURL("http://www.baidu.com", "UTF-8")

val source = Source.fromString("Hello World")

 

結合Java IO流,讀取任意文件

案例: 結合java IO流,作一個文件拷貝的案例

import java.io._

val f = new File("C:/Users/Administrator/Desktop/1.txt")

val fis = new FileInputStream(f)

val fos = new FileOutputStream(new File("C:/Users/Administrator/Desktop/2.txt"))

val buf = new Array[Byte](f.length.toInt)

fis.read(buf)

fos.write(buf)

fis.close()

fos.close()


 

結合Java IO流,寫文件

val pw = new PrintWriter("C://Users//Administrator//Desktop//test4.txt")

pw.println("Hello World")

pw.close()

 

遞歸遍歷指定目錄的全部子目錄:

import java.io._

def getSubdirIterator(dir: File): Iterator[File] = {

  //filter:對數組進行過濾,只要目錄的。filter返回的爲數組

  val childDirs = dir.listFiles.filter(_.isDirectory)

  //childDirs.toIterator:數組轉迭代器。++:兩個迭代器相加

  childDirs.toIterator ++ childDirs.toIterator.flatMap(getSubdirIterator _)//對子目錄進行遞歸

}

val iterator = getSubdirIterator(new File("C://Users//Administrator//Desktop"))

for (d <- iterator) println(d)

序列化以及反序列化

仍是要藉助於Java序列化和反序列化機制

若是要序列化,那麼就必須讓類,有一個@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

偏函數

偏函數,是一種高級的函數形式

 

簡單來講,偏函數是什麼,其實就是沒有定義好明確的輸入參數的函數(或是不能處理全部狀況的函數),函數體就是一連串的case語句

 

通常的函數:

def getStudentGrade(name: String) = {

         ...

}

 

偏函數是PartialFunction[A, B]類的一個實例

這個類有兩個方法,一個是apply()方法,直接調用能夠經過函數體內的case進行匹配,返回結果;

另外一個是isDefinedAt()方法,能夠返回一個輸入,是否跟任何一個case語句匹配

 

學生成績查詢案例:

val getStudentGrade: PartialFunction[String, Int] = {

         case "Leo" => 90; case "Jack" => 85; case "Marry" => 95

}

getStudentGrade("Leo")

getStudentGrade.isDefinedAt("Tom")

執行外部命令

import sys.process._

 

"javac HelloWorld.java" !

"java HelloWorld" !

正則表達式支持

定義一個正則表達式,使用String類的r方法

此時返回的類型是scala.util.matching.Regex類的對象

val pattern1 = "[a-z]+".r

 

val str = "hello 123 world 456"

 

獲取一個字符串中,匹配正則表達式的部分,使用findAllIn,會獲取到一個Iterator,迭代器

而後就能夠去遍歷各個匹配正則的部分,去進行處理

for (matchString <- pattern1.findAllIn(str)) println(matchString)

 

同理,使用findFirstIn,能夠獲取第一個匹配正則表達式的部分

pattern1.findFirstIn(str)

 

使用replaceAllIn,能夠將匹配正則的部分,替換掉

pattern1.replaceFirstIn("hello world", "replacement")

 

使用replaceFirstIn,能夠將第一個匹配正則的部分,替換掉

pattern1.replaceAllIn("hello world", "replacement")

提取器

apply方法:

伴生類和伴生對象的概念,companion classcompanion object

伴生對象裏面,能夠定義一個apply方法

直接調用類(參數),方式,就至關於在調用apply方法

此時在apply方法中,一般來講(也不必定),會建立一個伴生類的對象,返回回去

 

這種方式,有一個好處,建立對象呢,很是的方便

不要每次都是new (參數),而是 (參數)

 

提取器 unapply方法:

apply方法,顧名思義,那就是反過來

apply方法,能夠理解爲,接收一堆參數,而後返回一個對象

unapply方法,能夠理解爲,接收一個字符串,解析成一個對象的各個字段

 

提取器就是一個包含了unapply方法的對象,跟apply方法正好相反

apply方法,是接收一堆參數,而後構造出來一個對象

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)))

  } 

}

 

scala> val Person(name, age) = "leo 25"// 會調用unapply方法進行解析,將"leo 25"解析成相應成員

name: String = leo

age: String = 25

樣例類的提取器

scala中的樣例類,相似於java中的javabean

java中的JavaBean,包含了一堆屬性,field; 每一個field都有一對gettersetter方法:

 

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中的樣例類,默認就是提供apply方法和unapply方法的

 

case class Person(name: String, age: Int)

 

val p = Person("leo", 25)

 

p match {

         case Person(name, age) => println(name + ": " + age)

}

只有一個參數的提取器

以前,已經跟你們講解過普通的提取器

至關因而,好比說,接收一個字符串,做爲參數

而後從字符串裏面解析出來多個字段值,而後將多個字段值封裝在一個tuple

做爲Some類型的對象,返回

 

若是你的類只有一個字段,字符串裏面只有一個字段,

解析出來的一個字段,是沒有辦法放在tuple中的,由於scala中的tuple,規定了,必需要兩個以及兩個以上的值

 

這個時候,在提取器,unapply方法中,只能將一個字段值,封裝在Some對象中,直接返回

 

class Person(val name: String)

 

object Person {

  def unapply(input: String): Option[String] = Some(input)

}

 

val Person(name) = "leo"

註解

代碼中,加入一些特殊的標記。在代碼編譯或運行時,在碰到註解的時候,作一些特殊的操做

 

scala中,能夠給類、方法、fieldlocal variableconstructor / method / function parameter添加註解

並且scala是支持給某個目標添加多個註解的

 

這裏有一些特例:若是要給類的主構造函數添加註解,那麼須要在構造函數前添加註解,並加上一對圓括號

好比說

class Person @Unchecked() (val name: String, val age: Int)

 

還能夠給表達式添加註解,此時須要在表達式後面加上冒號以及註解,好比

 

val scores = Map("Leo" -> 90, "Jack" -> 60)

(scores.get("Leo"): @unchecked) match { case score => println(score) }

 

除此以外,還能夠給類型參數和變量的類型定義添加註解

 

 

要本身動手開發一個註解,就必須擴展Annotation trait,好比

class Test extends annotation.Annotation

@Test

class myTest

 

註解中,是能夠有參數的,好比

class Test(var timeout: Int) extends annotation.Annotation

@Test(timeout = 100) class myTest

若是註解只有一個參數的話,那麼也能夠不用指定註解的參數名,好比

@Test(100) class myTest

經常使用註解

@volatile var name = "leo"                                 輕量級的java多線程併發安全控制

 

jvmjava虛擬機中,能夠有多個線程

每一個線程都有本身的工做區,還有一起全部線程共享的工做區

每次一個線程拿到一個公共的變量,都須要從共享區中拷貝一個副本到本身的工做區中使用和修改

而後修改完之後,再在一個合適的時機,將副本的值,寫回到共享區中

 

這裏就會出現一個多線程併發訪問安全的問題

多個線程若是同時拷貝了變量副本,都作了不一樣的修改

而後依次將副本修改的值,寫回到共享區中,會依次覆蓋掉以前的一些副本值

就會出現變量的值,是不符合預期的

 

volatile關鍵字修飾的變量

它能夠保證,一個線程在從共享區獲取一個變量的副本時,都會強制刷新一下這個變量的值

保證本身獲取到的變量的副本值是最新的

因此這樣子作呢,是一種輕量級的多線程併發訪問控制辦法

 

可是也不是百分之百保險的,仍是有可能會出現錯誤的風險

 

@transient var name = "leo"                             瞬態字段,不會序列化這個字段

 

以前講序列化,默認會將一個對象中全部的字段的值,都序列化到磁盤文件中去

而後反序列化的時候,還能夠獲取這些字段的值

加了transient的字段,是瞬態的,序列化的時候,不會序列化這個字段

反序列化的時候,這個字段也就沒有值了

 

@SerialVersionUID(value)                                  標記類的序列化版本號

 

序列化版本號,這個什麼意思

若是咱們將一個類的對象序列化到磁盤文件上了

結果過了一段時間之後,這個類在代碼中改變了,此時若是你想將磁盤文件中的對象反序列化回來

就會報錯,由於你的序列化的對象的結構與代碼中的類結構已經不同了

 

針對這種問題,就應該有一個序列化版本號

若是你的類改變了,就從新生成一個序列化版本號

反序列化的時候,就會發現序列化類型的版本號和代碼中的類的版本號,不同

 

@native                                                                    標註用c實現的本地方法

@throws(classOf[Exception]) def test() {}       給方法標記要拋出的checked異常

@varargs def test(args: String*) {}                   標記方法接收的是變長參數

@BeanProperty                                                      標記生成JavaBean風格的gettersetter方法

@BooleanBeanProperty                                      標記生成is風格的getter方法,用於boolean類型的field

@deprecated(message = "")                              讓編譯器提示警告

@unchecked                                                            讓編譯器不提示警告

xml

scalaxml有很好的支持,能夠直接在scala代碼中定義一個xml文檔元素

scala> val books = <books><book>my first scala book</book></books>

books: scala.xml.Elem = <books><book>my first scala book</book></books>

此時doc的類型是scala.xml.Elem,也就是一個xml元素

 

scala還能夠直接定義多個同級別的xml元素

scala> val books = <book>my first scala book</book><book>my first spark book</book>

books: scala.xml.NodeBuffer = ArrayBuffer(<book>my first scala book</book>, <book>my first spark book</book>)

此時doc的類型是scala.xml.NodeBuffer,也就是一個xml節點序列

XML節點類型

Node類是全部XML節點類型的父類型,兩個重要的子類型是TextElem

Elem表示一個XML元素,也就是一個XML節點。scala.xml.Elem類型的label屬性,返回的是標籤名,child屬性,返回的是子元素。

scala.xml.NodeSeq類型,是一個元素序列,能夠用for循環,直接遍歷它。

 

scala> val books = <books><book></book></books>

books: scala.xml.Elem = <books><book></book></books>

scala> books.label

res10: String = books

 

能夠經過scala.xml.NodeBuffer類型,來手動建立一個節點序列:

scala> val booksBuffer = new scala.xml.NodeBuffer

booksBuffer: scala.xml.NodeBuffer = ArrayBuffer()

 

scala> booksBuffer += <book>book1</book>

res2: booksBuffer.type = ArrayBuffer(<book>book1</book>)

 

scala> booksBuffer += <book>book2</book>

res3: booksBuffer.type = ArrayBuffer(<book>book1</book>, <book>book2</book>)val books: scala.xml.NodeSeq = booksBuffer

 

scala> for(bb <- booksBuffer) println(bb)

<book>book1</book>

<book>book2</book>

 

scala> val books: scala.xml.NodeSeq = booksBuffer

books: scala.xml.NodeSeq = NodeSeq(<book>book1</book>, <book>book2</book>)

 

scala> for(b <- books) println(b)

<book>book1</book>

<book>book2</book>

xml元素的屬性

scala.xml.Elem.attributes屬性,能夠返回這兒xml元素的屬性,是Seq[scala.xml.Node]類型的,繼續調用text屬性,能夠拿到屬性的值

 

scala> val book = <book id="1" price="10.0">book1</book>

book: scala.xml.Elem = <book id="1" price="10.0">book1</book>

 

scala> val bookId = book.attributes("id").text

bookId: String = 1

 

還能夠遍歷屬性

scala> for(attr <- book.attributes) println(attr)

 id="1" price="10.0"

 price="10.0"

 

還能夠調用book.attributes.asAttrMap,獲取一個屬性Map

xml中嵌入scala代碼

scala> val books = Array("book1", "book2")

books: Array[String] = Array(book1, book2)

 

scala> <books><book>{ books(0) }</book><book>{ books(1) }</book></books>

res12: scala.xml.Elem = <books><book>book1</book><book>book2</book></books>

 

scala> <books>{ for (book <- books) yield <book>{book}</book> }</books>

res13: scala.xml.Elem = <books><book>book1</book><book>book2</book></books>

 

還能夠在xml屬性中嵌入scala代碼:

<book id={ books(0) }>{ books(0) }</book>

修改xml元素

默認狀況下,scala中的xml表達式是不可改變的;若是要修改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))

加載和寫入外部xml文件

import scala.xml._

import java.io._

 

使用scalaXML類加載

val books = XML.loadFile("C://Users//Administrator//Desktop//books.xml")

 

使用JavaFileInputStream類加載

val books = XML.load(new FileInputStream("C://Users//Administrator//Desktop//books.xml"))

 

使用JavaInputStreamReader類指定加載編碼

val books = XML.load(new InputStreamReader(new FileInputStream("C://Users//Administrator//Desktop//books.xml"), "UTF-8"))

 

將內存中的xml對象,寫入外部xml文檔

XML.save("C://Users//Administrator//Desktop//books2.xml", books)

集合元素操做符

col :+ ele                     將元素添加到集合尾部                   Seq

ele +: col                     將元素添加到集合頭部                   Seq

col + ele                      在集合尾部添加元素                       SetMap

col + (ele1, ele2)       將其餘集合添加到集合的尾部     SetMap

col - ele                       將元素從集合中刪除                       SetMapArrayBuffer

col - (ele1, ele2)        將子集合從集合中刪除                   SetMapArrayBuffer

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             取兩個setdiff                                Set

col += ele                    給集合添加一個元素                       可變集合

col += (ele1, ele2)    給集合添加一個集合                       可變集合

col ++= col2               給集合添加一個集合                       可變集合

col -= ele                     從集合中刪除一個元素                   可變集合

col -= (ele1, ele2)     從集合中刪除一個子集合              可變集合

col = col2              從集合中刪除一個子集合              可變集合

ele +=: col                  向集合頭部添加一個元素              ArrayBuffer

col2 ++=: col              向集合頭部添加一個集合              ArrayBuffer

集合的經常使用方法

headlasttail

lengthisEmpty

summaxmin

countexistsfilterfilterNot

takeWhiledropWhile

takedropsplitAt

takeRightdropRight

sclie

containsstartsWithendsWith

indexOf

intersectdiff

 

 

map操做,一對一映射

scala> val scoreMap = Map("leo" -> 90, "jack" -> 60, "tom" -> 70)

scoreMap: scala.collection.immutable.Map[String,Int] = Map(leo -> 90, jack -> 60, tom -> 70)

 

scala> val names = List("leo", "jack", "tom")

names: List[String] = List(leo, jack, tom)

 

scala> names.map(scoreMap(_)) // 根據Key索引從Map中取對應的value

res14: List[Int] = List(90, 60, 70)

 

 

flatMap操做,一對多映射

scala> val scoreMap = Map("leo" -> List(80, 90, 60), "jack" -> List(70, 90, 50), "tom" -> List(60,70,40))

scoreMap: scala.collection.immutable.Map[String,List[Int]] = Map(leo -> List(80, 90, 60), jack -> List(70, 90, 50), tom -> List(60, 70, 40))

 

scala> names.map(scoreMap(_))

res15: List[List[Int]] = List(List(80, 90, 60), List(70, 90, 50), List(60, 70, 40))

 

scala> names.flatMap(scoreMap(_)) // 展平

res16: List[Int] = List(80, 90, 60, 70, 90, 50, 60, 70, 40)

 

 

collect操做,結合偏函數使用

scala> "abc".collect { case 'a' => 1; case 'b' => 2; case 'c' => 3 }

res17: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3)

 

 

foreach操做,遍歷

names.foreach(println _)

 

 

reduce操做

scala> List(1, 2, 3,4).reduceLeft(_ - _) //  1 - 2 - 3 - 4

res21: Int = -8

 

scala> List(1, 2, 3,4).reduceRight(_ - _) //  1 - ( 2 - (3 - 4) )

res22: Int = 2

 

 

fold操做

scala> List(1, 2, 3,4).foldLeft(10)(_ - _) // 10 - 1 - 2 - 3

res23: Int = 0

 

scala> List(1, 2, 3,4).foldRight(10)(_ - _) // 1 - ( 2 - ( 3 - (4 - 10) ) )

res24: Int = 8

 

實際上,咱們能夠直接使用reduce,而不用reduceLeft,這時,默認採用的是reduceLeft,以下:

scala> List(1,2,3,4) .reduce(_ - _)

res5: Int = -8

附件列表

相關文章
相關標籤/搜索