Scala編程進(jìn)階

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特性

  1. 同一個包定義焕济,可以在不同的scala源文件中纷妆;一個scala源文件內(nèi),可以包含兩個包

  2. 子包中的類晴弃,可以訪問父包中的類


  3. 相對包名與絕對包名


  4. 定義package對象(使用較少)

    package內(nèi)的成員掩幢,可以直接訪問package對象內(nèi)的成員


  5. package可見性

package com.spark.scala.
class Person{
  private[scala] val name = "leo"   
  private[spark] val age = 25  // 只在spark包下面的成員可見
}

import特性

  1. 用import xxx.xxx._的格式可以導(dǎo)入包下所有成員
  2. scala與java不同之處在于,任何地方都可以使用import上鞠,比如類內(nèi)际邻、方法內(nèi),這種方式的好處在于芍阎,可以在一定作用域范圍內(nèi)使用導(dǎo)入
  3. 選擇器世曾、重命名、隱藏
import spark.scala.{aaa, bbb} 
import spark.scala.{HashMap => scalaHashMap} // 重命名
import scala.util.{HashMap => _, _} // 導(dǎo)入scala.util包下所有的類谴咸,但是隱藏掉HashMap類
  1. 隱式導(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() 為空
  1. 子類PEStudent的構(gòu)造函數(shù)(無參)調(diào)用父類Student的構(gòu)造函數(shù)(無參)
  2. 父類的構(gòu)造函數(shù)初始化field(結(jié)果正確)
  3. 父類的構(gòu)造函數(shù)使用field執(zhí)行其他構(gòu)造代碼(classScores)岭佳,但是此時其他構(gòu)造代碼如果使用了該field血巍,而且field要被子類重寫,那么它的getter方法被自動重寫,返回初始化的值0(比如Int),因此pes.classScores返回長度為0的數(shù)組
  4. 子類的構(gòu)造函數(shù)再執(zhí)行鞭铆,重寫field(pes.classNum = 3)
  5. 但是此時子類從父類繼承的其他構(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 集合元素操作

集合的常用操作

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))))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市认境,隨后出現(xiàn)的幾起案子胚委,更是在濱河造成了極大的恐慌,老刑警劉巖叉信,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件篷扩,死亡現(xiàn)場離奇詭異,居然都是意外死亡茉盏,警方通過查閱死者的電腦和手機(jī)鉴未,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸠姨,“玉大人铜秆,你說我怎么就攤上這事⊙惹ǎ” “怎么了连茧?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長巍糯。 經(jīng)常有香客問我啸驯,道長,這世上最難降的妖魔是什么祟峦? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任罚斗,我火速辦了婚禮,結(jié)果婚禮上宅楞,老公的妹妹穿的比我還像新娘针姿。我一直安慰自己,他們只是感情好厌衙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布距淫。 她就那樣靜靜地躺著,像睡著了一般婶希。 火紅的嫁衣襯著肌膚如雪榕暇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天,我揣著相機(jī)與錄音彤枢,去河邊找鬼狰晚。 笑死,一個胖子當(dāng)著我的面吹牛堂污,可吹牛的內(nèi)容都是我干的家肯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼盟猖,長吁一口氣:“原來是場噩夢啊……” “哼讨衣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起式镐,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤反镇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后娘汞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歹茶,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年你弦,在試婚紗的時候發(fā)現(xiàn)自己被綠了惊豺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡禽作,死狀恐怖尸昧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情旷偿,我是刑警寧澤烹俗,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站萍程,受9級特大地震影響幢妄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茫负,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一蕉鸳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧朽褪,春花似錦置吓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽友题。三九已至嗤堰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背踢匣。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工告匠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人离唬。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓后专,卻偏偏與公主長得像,于是被迫代替她去往敵國和親输莺。 傳聞我的和親對象是個殘疾皇子戚哎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

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