Gradle入门系列(一)——groovy基础语法

groovy基础语法

一、变量

1、类型

对象类型、基本类型(本质是对象类型)

int x = 0
println x.class
double y = 3.14
println y.class

输出:

class java.lang.Integer
class java.lang.Double

结论:
从log输出可以得出,groovy中实质上是没有基本类型的,本质是java中的装箱对象类型。

2、定义

强类型定义方式、弱类型def定义方式

def x_1 = 11
println x_1.class
def y_1 = 3.1415
println y_1.class
def name = 'Android'
println name.class

输出:

class java.lang.Integer
class java.math.BigDecimal
class java.lang.String

二、字符串

String、GString

1、常用的三种定义方式

// 单引号:与java中的String是一样的
def name = 'a single \'a\' string'
println name.class
// 三引号:可以直接指定格式(如:换行,不需要加\n,代码表示上也不需要用+号拼接)
def thupleName = '''three single string'''
println thupleName.class
// 三引号:需要每行文字声明不同行(即:三引号后不要紧接文字内容),可以用\。
def thupleName1 = '''\
line one
line two
line three'''
println thupleName1
// 双引号:可扩展字符串(即:可包含变量、表达式等)
def doubleName = "this a common String" // String
println doubleName.class
def doubleName1 = "Hello: ${name}" // GString
println doubleName1.class
// 单引号
class java.lang.String
// 三引号
class java.lang.String
line one
line two
line three
// 双引号
class java.lang.String
class org.codehaus.groovy.runtime.GStringImpl

2、新增操作符

方法来源:java.lang.String、DefaultGroovyMethods、StringGroovyMethods(普通类型的参数、闭包类型的参数)

def str = "groovy"
def str2 = "gro"
// center(numberOfChars, padding):使用padding对已有字符串两边进行填充
// center(numberOfChars):使用空格对已有字符串两边进行填充
println str.center(8,'a')
// padLeft(numberOfChars, padding):使用padding对已有字符串左边进行填充
println str.padLeft(8,'a')
// 比较:可以使用>、<符号来直接比较,而不需要使用compareTo()
println str > str2
// 获取字符:string[index](相当于string.getAt(index))
println str[0]
println str[0..1]
// 减去字符串:可以直接使用-号,效果与minus()一致
println str - str2 // str.minus(str2)
// 倒序
println str.reverse()
// 首字母大写
println str.capitalize()
// 判断是否是数字
println str.isNumber()

输出:

// center
agroovya
// padLef
aagroovy
// str > str2
true
// str[0]
g
// str[0..1]
gr
// str - str2
ovy
// reverse
yvoorg
// capitalize
Groovy
// isNumber
false

三、新增API讲解

逻辑控制:顺序逻辑(单步往下执行)、条件逻辑(if/else、switch/case)、循环逻辑(while、for)

// ============= 条件逻辑 =============
def x = 1.23
def result
switch(x){
  case 'foo':
    result = 'found foo'
    break
  case [4,5,6,'inlist']: // 列表
    result = 'list'
    break
  case 12..30: // 范围
    result = "range"
    break
  case Integer:
    result = 'integer'
    break
  case BigDecimal:
    result = 'big decimal'
    break
  default:
    result = 'default'
    break
}
println result

// ============= 循环逻辑 =============
def sum = 0
// 对范围的for循环
for(i in 0..9){sum += i}
// 对List的循环
for(i in [0,1,2,3,4,5,6,7,8,9]){sum += i}
// 对Map的循环
for(i in ["lili":1, "luck":2, "xiaoming":3]){
  sum += i.value
}

输出:

// x = 1.23
big decimal
// x = 4
list

groovy闭包讲解

一、基础

闭包概念(定义、调用)、闭包参数(普通参数、隐式参数)、闭包返回值(总是有返回值的)

// ============= 闭包概念 =============
// 定义
def clouser = {println 'Hello groovy!'}
// 调用1:
clouser.call()
// 调用2:
clouser()

// ============= 闭包参数 =============
// 普通参数
def clouser = {String name, int age -> println "Hello ${name}, my age is ${age}"}
clouser('groovy!', 18) // clouser.call('groovy!', 18) 
// 隐式参数(it是所有闭包都拥有的默认参数,当有显式声明参数时,it失效)
def clouser = {println "Hello ${it}"}
clouser('groovy!')

// ============= 闭包返回值 =============
def clouser = {return 'Hello, groovy!'} // 返回 Hello, groovy
def result = clouser()
println result

def clouser = {println 'Hello groovy!'} // 返回 null
def result = clouser()
println result

二、使用

闭包的用法:与基本类型的结合使用、与String结合使用、与数据结构结合使用、与文件等结合使用

// ============= 与基本类型的结合使用 =============
int x = fab(5)
println x
// 用来求指定number的阶乘
int fab(int number){
  int result = 1
  1.upto(number, {num -> result *= num}) // upto()是DefaultGroovyMethods中封装好的方式
  return result
}
int fab2(int number){
  int result = 1
  number.downto(1){       // 闭包可以不写在括号内,可以写在括号外
    num -> result *= num
  }
  return result
}
// 从0循环到number
int cal(int number){
  int result = 1
  number.times { // times(Closure)只接收一个闭包,把闭包写到括号外,括号可以省略
    num -> result += num
  }
  return result
}

// ============= 与String结合使用包参数 =============
String str = 'the 2 and 3 is 5'
// each:遍历每个字符
str.each {
  String temp -> print temp
}
// find:查找符合条件的第一个
println str.find {
  String s -> s.isNumber()
}
// findAll:查找符合条件的所有字符
def list = str.findAll {
  String s -> s.isNumber()
}
println list.toListString()
// any:遍历每个字符,只要满足条件就返回true
def result= str.any {
  String s -> s.isNumber()
}
println result
// every:遍历每个字符,所有都要满足条件才会返回true
println str.every {
  String s -> s.isNumber
}
// collect:遍历每个字符,经过闭包处理后,添加进list中返回
def list = str.collect { it.toUpperCase() }
println list.toListString()

// ============= 与数据结构结合使用 =============
// ============= 与文件等结合使用 =============
// 后续篇章中会涉及

输出:

// ============= 与String结合使用包参数 =============
// each
the 2 and 3 is 5
// find
2
// final All
[2, 3, 5]
// any
true
// every
false
// collect
[T, H, E,  , 2,  , A, N, D,  , 3,  , I, S,  , 5]

三、进阶

闭包关键变量(this、owner、delegate)

// this == owner == delegate
def scriptClosure = {
  println "scriptClosure this:"+this // 代表闭包定义处的类
  println "scriptClosure owner:"+owner // 代表闭包定义处的类或者对象
  println "scriptClosure delegate:"+delegate // 代码任意对象,默认与owner一致
}
scriptClosure.call()

// this != owner == delegate
def nestClosure = {
  def innerClosure = {
    prinln "innerClosure this:"+this
    prinln "innerClosure owner:"+owner
    prinln "innerClosure delegate:"+delegate
  }
  innerClosure.call()
}

// this != owner != delegate
def nestClosure = {
  def innerClosure = {
    prinln "innerClosure this:"+this
    prinln "innerClosure owner:"+owner
    prinln "innerClosure delegate:"+delegate
  }
  innerClosure.delegate = new Person() // 手动修改delegate的值
  innerClosure.call()
}

输出

// this == owner == delegate
scriptClosure this:variable.closurestudy@2ef3eef9
scriptClosure owner:variable.closurestudy@2ef3eef9
scriptClosure delegate:variable.closurestudy@2ef3eef9

// this != owner == delegate
innerClosure this:variable.closurestudy@2ef3eef9
innerClosure owner:variable.closurestudy$_run_closure2@402bba4f
innerClosure delegate:variable.closurestudy$_run_closure2@402bba4f

// this != owner != delegate
innerClosure this:variable.closurestudy@2ef3eef9
innerClosure owner:variable.closurestudy$_run_closure2@402bba4f
innerClosure delegate:variable.Person@795cd85e

结论:

  • 在大多数据情况下,this、owner、delegate的值是一样的。
  • 在闭包中定义闭包时,this与owner的值是不一样的。(this指的是闭包定义处的类对象,owner指的是闭包定义处类中的闭包对象)
  • 在手动修改了闭包delegate时,owner与delegate的值才会不一样。

闭包委托策略(Closure.OWNER_FIRST、Closure.OWNER_ONLY、Closure.DELEGATE_FIRST、Closure.DELEGATE_ONLY)

class Studen{
  String name
  def pretty = { "My name is ${name}" }
  String toString(){
    pretty.call()
  }
}

Class Teacher{
  String name1 // 注意:与Student中的name变量名不一样!!
}

def stu = new Studen(name: "Lqr")
def tea = new Teacher(name1: "Lxf")

// 1、Normal
println stu.toString()

// 2、优先委托delegate
stu.pertty.delegate = tea
stu.pertty.resolveStrategy = Closure.DELEGATE_FIRST
println stu.toString()

// 3、只委托delegate
stu.pertty.delegate = tea
stu.pertty.resolveStrategy = Closure.DELEGATE_ONLY
println stu.toString()

输出:

// 1、Normal
My name is Lqr

// 2、优先委托delegate
My name is Lqr // 如果Teacher中的name1改为name,则输出变为:My name is Lxf

// 3、只委托delegate
报错:No Such property: name for class Teacher

groovy数据结构

一、列表

定义、操作(增删查排)

// 列表的定义
def list = [1, 2, 3, 4, 5] // groovy中的列表就是ArrayList
println list.class
println list.size()
// 数组的定义
def array = [1, 2, 3, 4, 5] as int[] // 使用as int[]转换
int[] array = [1, 2, 3, 4, 5] // 使用强类型定义

// ============= 列表的添加 =============
list.add(6)
list.leftShift(7)
list << 8
println list.toListString()
def plusList = list + 9
println plusList.toListString()

// ============= 列表的删除 =============
list.remove(7)
list.remove((Object)7)
list.removeAt(7)
list.removeElement(6)
list.removeAll { return it % 2 == 0}
println list - [6, 7]
println list.toListString()
 
// ============= 列表的排序 =============
def sortList = [6 -3, 9, 2, -7, 1, 5]
sortList.sort() // java中:Collections.sort(sortList)
println sortList
// 自定义排序规则:按绝对值反方向排序
sortList.sort { a,b ->
  a == b ? 0 : Math.abs(a) < Math.abs(b) ? 1 : -1
}
// 自定义排序规则:按字符串长度排序
def sortStringList = ['abc', 'z', 'Hello', 'groovy', 'java']
sortStringList.sort { it -> return it.size()}
println sortStringList

// ============= 列表的查找 =============
def findList = [-3, 9, 6, 2, -7, 1, 5]
// find() : 返回第一个符合条件的元素
int result = findList.find { return it % 2 == 0}
println result
// findAll() : 返回所有符合条件的元素
def resultfindList.findAll{ return it % 2 != 0}
println result.toListString()
// any() : 若列表中有一个满足条件就返回true
def result = findList.any { return it % 2 != 0} 
println result
// every() : 列表中所有元素都满足条件才返回true
def result = findList.every {return it % 2 != 0}
println result
// min() : 返回最小值
println findList.min() // findList.min { return Math.abs(it) } 查找最小绝对值
// max() : 返回最大值
println findList.max() // findList.max { return Math.abs(it) } 查找最大绝对值
// count() : 统计符合条件的元素个数
def num = findList.count { return it % 2 == 0}
println num

输出

// 列表的定义
class java.util.ArrayList
5

// ============= 列表的排序 =============
[-7, -3, 1, 2, 5, 6, 9]
// 自定义排序规则:按绝对值反方向排序
[9, -7, 6, 5, -3, 2, 1]
// 自定义排序规则:按字符串长度排序
['z', 'abc', 'java', 'Hello', 'groovy']

// ============= 列表的查找 =============
// find
6
// findAll() 
[-3, 9, -7, 1, 5]
// any() 
true
// every() 
false
// min()
1
// max() 
9
// count() 
2

二、映射

// 定义
def colors = [
  red : 'ff0000',
  green : '00ff00',
  blue : '0000ff'
]
println colors.getClass() // 注:不能直接使用colors.class,因为这样会查找key为class的元素
// 强行指定类型:def colors = [...] as HashMap 或 HashMap colors = [...]

// 索引方式
println colors['red']
println colors.red
println colors.blue
// 添加元素
colors.yellow = 'ffff00'    // 添加同类型key-value
colors.complex = [a:1, b:2] // 添加任意类型key-value
println colors.toMapString()
// 删除元素
colors.remove(red)

def students = [
  1: [number: '0001', name: 'Bob', score: 55, sex: 'male'],
  2: [number: '0002', name: 'Johnny', score: 62, sex: 'female']
  3: [number: '0003', name: 'Claire', score: 73, sex: 'female']
  4: [number: '0004', name: 'Amy' ,score: 66, sex: 'male']
]

// 遍历
students.each { def student -> 
  println "the key is ${student.key}, "+
    "the value is ${student.value}"
}
// 带索引遍历
students.eachWithIndex { def student, int index ->
  println "the index is ${index}, "+
    "the key is ${student.key}, "+
    "the value is ${student.value}"
}
// 直接遍历key-value
students.each { key, value -> ...}
students.eachWithIndex { key, value, index -> ...}

// 查找
def entry = students.find { def student ->
  return student.value.score >= 60
}
println entry
def entrys = students.findAll { def student ->
  return student.value.score >= 60
}
println entrys
// 统计及格男生个数
def count =  students.count { def student ->
  return student.value.score >= 60 && student.value.sex == 'male'
}
println count
// 过滤:获取所有及格同学的姓名
def names = students.findAll { def student ->
  return student.value.score >= 60
}.collect{ // 过滤出元素指定属性列
  return it.value.name
}
println names.toListString()
// 分组:对及格与不及格学生进行分组
def group = students.groupBy {def student ->
  return student.value.score >= 60 ? '及格' : '不及格'
}
println group.toMapString()

// 排序
def sort = students.sore { def student1, def students ->
  Number score1 = student1.value.score
  Number score2 = student2.value.score
  return score1 == score2 ? 0 : score1 < score2 ? -1 : 1
}
println sort
// 定义
class java.util.LinkedHashMap

// 索引方式
ff0000
ff0000
0000ff
// 添加元素
[red:ff0000, green:00ff00, blue:0000ff, yellow:ffff00, complex:[a:1, b:2]]

// 遍历
the key is 1, thie value is [number:0001, name:Bob, score:55, sex:male]
the key is 2, thie value is [number:0002, name:Johnny, score:62, sex:female]
the key is 3, thie value is [number:0003, name:Claire, score:73, sex:female]
the key is 4, thie value is [number:0004, name:Amy, score:66, sex:male]
// 带索引遍历
the index is 0, the key is 1, thie value is [number:0001, name:Bob, score:55, sex:male]
the index is 1, the key is 2, thie value is [number:0002, name:Jhonny, score:62, sex:female]
the index is 2, the key is 3, thie value is [number:0003, name:Claire, score:73, sex:female]
the index is 3, the key is 4, thie value is [number:0004, name:Amy, score:66, sex:male]

// 查找
// find
2={number=0002, name=Johnny, score=62, sex=female}
// findAll
[2:[number:0002, name:Johnny, score:62, sex:female], 3:[number:0003, name:Claire, score:73, sex:female], 4:[number:0004, name:Amy, score:66, sex:male]]]
// count
1
// collect
[Johnny, Claire, Amy]
// groupBy
[不及格:[1:[number:0001, name:Bob, score:55, sex:male]], 及格:[2:[number:0002, name:Johnny, score:62, sex:female], 3:[number:0003, name:Claire, score:73, sex:female], 4:[number:0004, name:Amy, score:66, sex:male]]]]

// 排序
[1:[number:0001, name:Bob, score:55, sex:male], 2:[number:0002, name:Johnny, score:62, sex:female], 4:[number:0004, name:Amy, score:66, sex:male]], 3:[number:0003, name:Claire, score:73, sex:female]]

其他:

  • map定义时,key通常是用不可变字符串或number来定义。
  • 字符串不使用单引号时,groovy默认会认为是不可变的单引号字符串。(如:red 与 'red' 一样)

三、范围

Range定义、操作(each、switch-case)

def range = 1..10           // Range是一个继承于List的接口,即,本质是列表
println range[0]            // 范围第一个数值
println range.contains(10)  // 范围是否包含10
println range.from          // 范围中第一个值
println range.to            // 范围中最后一个值

// 遍历
range.each {
  println it
}
for(i in range){
  println i
}

// switch-case
def getGrade(Number number){
  def result
  switch(number){
    case 0..<60: // [0, 60)
      result = '不及格'
      break;
    case 60..<70:
      result = '及格'
      break;
    case 70..<80:
      result = '良好'
      break;
    case 80..100: // [80, 100]
      result = '优秀'
      break;
  }
  result // 相当于 return result。此处可以省略return,groovy中方法会默认返回最后一行的结果
}

groovy面向对象

一、类、接口等的定义和使用

// 类的定义
class Person{
  String name
  
  Integer age // int与Integer是一样的,groovy中,int本质就是Integer
  
  def increaseAge(Integer years){
    this.name += years
  }
}

// 创建对象
def person = new Person(name: 'Lqr', age: 18) // 也可以只直接一部分,如:new Person(name: 'lqr')或不指定
println "the name is ${person.name}, the age is ${person.age}"
person.increaseAge(10)

// 接口定义
interface Action {
  void eat()
  void drink()
  void play()
}

// trait类定义(与java中的抽象类差不多)
trait DefaultAction {
  abstract void eat()
  void play(){
    println ' i can play.'
  }
}

结论

  • groovy中默认都是public(类、成员属性、方法等)
  • groovy类继承自groovy.lang.GroovyObject(而java类则继承自Object)
  • def定义的方法,其返回值就是Object
  • groovy类会默认会成员变量生成getter与setter方法。
  • 无论你是直接.还是调用get/set,最终都是调用get/set方法,如:person.name相当于person.getName()
  • groovy接口中不允许定义非public的方法(如:protected void eat()是不行的)

二、元编程

groovy运行时类方法调用流程:


在java中,如果对象调用了一个类中没有定义过的方法时,连编译都编译不过,但是在groovy中,情况则不同(可以编译通过),根据图中流程可以知道,运行期间,当对象调用了一个类中没有的方法时,会依次调用metaClass中的同名方法,类中的methodMissing(String name, Object args)方法,类中的invokeMethod(String name, Object args)方法,执行到其中一个便停止后续方法查找调用。

1、定义invokeMethod

当只定义类的invokeMethod(String name, Object args)方法时,运行时调用对象一个不存在的方法时,会执行invokeMethod()。

class Person{
  ...
  // 一个方法找不到时,调用它代替
  def invokeMethod(String name, Object args){
    return "the method is ${name}, the params is ${args}"
  }
}

def person = new Person(name: 'Lqr', age: 18)
person.cry()

输出:

the method is cry, the params is []

2、定义methodMissing

当同时定义了类中的invokeMethod()、methodMissing()方法时,优先执行methodMissing(String name, Object args)

class Person{
  ...
  // 一个方法找不到时,调用它代替
  def invokeMethod(String name, Object args){
    return "the method is ${name}, the params is ${args}"
  }
  
  def methodMissing(String name, Object args){
    return "the method ${name} is missing"
  }
}

def person = new Person(name: 'Lqr', age: 18)
person.cry()

输出:

the method cry is missing

3、metaClass

metaClass便是groovy中的元编程核心。在groovy中,可以使用metaClass为类在运行时动态添加 属性 和 方法

用处:对第三方库中的final类进行扩展。

// 为类动态添加一个属性
Person.metaClass.sex = 'male' // 同时设置动态属性sex的默认值为male
def Person = new Person(name: 'Lqr', age: 18)
println person.sex
person.sex = 'female'
println "the new sex is:" + person.sex

// 为类动态添加方法
Person.metaClass.sexUpperCase = { -> sex.toUpperCase() }
def person2 = new Person(name: 'Lqr', age: 18)
println person2.sexUpperCase()

// 为类动态添加静态方法
Person.metaClass.static.createPerson = {
  String name, int age -> new Person(name: name, age: age)
}
def person3 = Person.createPerson('Lqr', 18)
println person3.name + ' and ' + person.age

输出:

// 为类动态添加一个属性
male
the new sex is:female
// 为类动态添加方法
MALE
// 为类动态添加静态方法
Lqr and 18

以上可以看到groovy的metaClass功能十分强大,但它本身有一个限制需要我们注意一下,即:默认情况下metaClass注入的属性与方法只是短暂的(准确来说是非全局的)。

举个例子在ClassA中对Person进行了metaClass扩展并正常调用动态注入的属性和方法,但是在ClassB中,若也要使用前面动态注入的属性和方法是不行的,因为在groovy中metaClass动态注入的属性和方法默认是非全局的,你可以有如下2种做法:

  1. 在ClassB中再使用metaClass再动态注入一次属性和方法。
  2. 使用ExpandoMetaClass.enableGlobally()。

4、ExpandoMetaClass.enableGlobally()

使用ExpandoMetaClass.enableGlobally()开启metaClass动态全局注入 属性、方法 功能。

// 在ApplicationManager调用ExpandoMetaClass.enableGlobally(),并对Person进行扩展
class ApplicationManager{
  static void init(){
    ExpandoMetaClass.enableGlobally()
    // 为第三方类添加方法
    Person.metaClass.static.createPerson = { String name, int age ->
      new Person(name: name, age: age)
    }
  }
}

// 在ClassB中可以使用Person动态注入的扩展方法了
class ClassB{
  def test(){
    def person = Person.createPerson('Lqr', 18) // 因为只是普通方法,不是构造函数,所以不能这样:createPerson(name: 'Lqr', age: 18) ,切记!!
    println "the person name is ${person.name} and the age is ${person.age}"
  }
}

// Entry模拟一个App的运行入口
class Entry{
  static void main(String[] args){
    ApplicationManager.init()
    new ClassB().test()
  }
}

输出:

the person name is Lqr and the age is 18
欢迎关注微信公众号:全栈行动
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容