2019-09-20: 九: Flutter之Dart第六節(jié)(類和對象)?

九: Flutter之Dart第六節(jié)(類和對象)?

Dart 是一個面向?qū)ο蟮恼Z言、面向?qū)ο笾蟹浅V匾母拍罹褪穷愂逅臁㈩惍a(chǎn)生了對象他炊。
這一節(jié),我們就具體來學(xué)習(xí)類和對象已艰、但是Dart對類進(jìn)行了很多其他語言沒有的特性痊末、一起探討。

9.1: 類的定義

在Dart中哩掺、定義類用class關(guān)鍵字凿叠。
類通常有兩部分組成:成員(member) 和 方法 (method)。
定義類的偽代碼如下:

class 類名 {
   類型 成員名
   返回值類型 方法名(參數(shù)列表) {
     方法體
   }
 }

編寫一個簡單的Person類

  • 這里有一個注意點(diǎn):我們在方法中使用屬性(成員變量/實(shí)例變量)時嚼吞、并沒有加this盒件;
  • Dart的開發(fā)風(fēng)格中、在方法中通常使用屬性時舱禽、會省略this炒刁、但是有命名沖突時、this不能省略誊稚。
class Person {
 String name;

 eat() {
   print('$name在吃東西');
 }
}

main(List<String> args) {
 // 2: 我們在方法中使用屬性并沒有使用this翔始、一般我們都是省略的罗心、但是如果在方法中使用屬性的時候、如果有命名
 // 沖突的話城瞎、this不能省略

 // 1: 創(chuàng)建類的對象, 直接使用Person() 也可以創(chuàng)建
 var p = new Person();

 // 2: 給對象的屬性賦值
 p.name = 'lishengbing';

 // 3: 調(diào)用對象方法
 p.eat();

 //打硬趁啤: lishengbing在吃東西
}

9.2: 構(gòu)造方法

9.2.1: 普通構(gòu)造方法

我們知道、當(dāng)通過類創(chuàng)建一個對象時全谤、會調(diào)用這個類的構(gòu)造方法

  • 當(dāng)類中沒有明確指定的構(gòu)造方法時肤晓、將默認(rèn)擁有一個無參的構(gòu)造方法

  • 前面的Person中我們就是在調(diào)用這個構(gòu)造方法、我們也可以根據(jù)自己的需求认然、定義自己的構(gòu)造方法补憾。

  • 注意1: 當(dāng)有了自己的構(gòu)造方法時、默認(rèn)的構(gòu)造方法將會失效卷员、不能使用
    -- 當(dāng)然盈匾、你可能希望明確的寫一個默認(rèn)的構(gòu)造方法、但是會和我們自定義的構(gòu)造方法沖突
    -- 這是因為Dart語言本身不支持函數(shù)的重載(名稱相同毕骡、參數(shù)不同的方式)

  • 注意2: 這里我還實(shí)現(xiàn)了toString方法削饵。

class Person {
 String name;
 int age;

 // 明確指定了構(gòu)造方法、如果沒有指定默認(rèn)是Person()
 Person(String name, int age) {
   this.name = name;
   this.age = age;
 }

 @override
 String toString() {
   return 'name = ${name}, age = ${age}';
 }
}

另外未巫、在實(shí)現(xiàn)構(gòu)造方法時窿撬、通常做的事情就是通過 **參數(shù)給屬性 **賦值,如上this.name = name
為了簡化了這一過程叙凡、Dart提供了一種更加簡潔的語法糖形式
上面的構(gòu)造方法可以優(yōu)化成下面的寫法:

// 明確指定了構(gòu)造方法劈伴、如果沒有指定默認(rèn)是Person()
 Person(String name, int age) {
   this.name = name;
   this.age = age;
 }

 // 等同于上面優(yōu)化構(gòu)造方法
 Person(this.name, this.age);

9.2.2: 命名構(gòu)造方法

但是在開發(fā)中、我們確實(shí)希望實(shí)現(xiàn)更多的構(gòu)造方法握爷、怎么辦呢跛璧?

  • 因為不支持方法(函數(shù))的重載、所以我們沒辦法創(chuàng)建相同名稱的構(gòu)造方法新啼。我們需要使用命名構(gòu)造方法追城。
class People {
 String name;
 int age;

 People() {
  name = '';
   age = 0;
 }

 // 命名構(gòu)造方法, withArgments可以隨便變化
 People.withArgments(String name, int age) {
   this.name = name;
   this.age = age;
 }

// 優(yōu)化上面的構(gòu)造方法
 //People.withArgments(this.name, this.age);

 @override
 String toString() {
     return 'name=${name} age=${age}';
  }
}

/*
* name= age=0
 name=lishengbing age=28
*/
main(List<String> args) {
 var p = new People();
 print(p);
 var p1 = new People.withArgments('lishengbing', 28);
 print(p1);
}

在之后的開發(fā)中、我們也可以利用命名構(gòu)造方法燥撞、提供更加便捷的創(chuàng)建對象的方式

  • 比如開發(fā)中座柱、我們需要經(jīng)常將一個Map轉(zhuǎn)成對象、可以提供如下的構(gòu)造方法
// 新的構(gòu)造方法
 People.fromMap(Map<String, Object> map) {
   this.name = map['name'];
   this.age = map['age'];
 }
var p3 = new People.fromMap({'name': 'lishengbing', 'age': 29});
 // name=lishengbing age=29
 print(p3);

9.2.3: 初始化列表

我們來重新定義一個類Point物舒、傳入x/y辆布、可以得到它的距離distance:

初始化列表

class Point {
 final num x;
 final num y;
 final distance;

// 錯誤寫法

Point(this.x, this.y) {
  distance = sqrt(x * x + y * y);
}*/

 // 正確寫法
 Point(this.x, this.y) : distance = sqrt(x * x + y * y);

}

上面這種初始化變量的方法、我們稱之為初始化列表(Initializer list)

9.2.4: 重定向構(gòu)造方法

在某些情況下茶鉴、我們希望在一個構(gòu)造方法中去調(diào)用另一個構(gòu)造方法、這時候我們就可以使用重定向構(gòu)造方法景用。

  • 在一個構(gòu)造函數(shù)中涵叮、去調(diào)用另一個構(gòu)造函數(shù)(注意:是在冒號后面使用this調(diào)用)
class Person01 {
 String name;
 int age;

 Person01(this.name, this.age);
 Person01.fromName(String name) : this('lishengbing', 30);
}

9.2.5: 常量構(gòu)造方法

在某些時候下惭蹂、傳入相同值時、我們希望返回同一個對象割粮、這個時候盾碗、可以使用常量構(gòu)造方法
默認(rèn)情況下、創(chuàng)建對象時舀瓢、即使傳入相同的參數(shù)廷雅、創(chuàng)建出來的對象也不是同一個對象,看下面的代碼:

  • 我們使用identical(對象1京髓, 對象2)函數(shù)來判斷兩個對象是否是同一個對象
class Person02 {
 String name;
 Person02(this.name);
}

main(List<String> args) {
 var p1 = Person02('lishengbing');
 var p2 = Person02('lishengbing');

 // false
 print(identical(p1, p2));
}
  • 但是如果將構(gòu)造方法前加上 const進(jìn)行修飾的話航缀、那么可以保證同一個參數(shù)、創(chuàng)建出來的對象是相同的堰怨。
    這樣的構(gòu)造函數(shù)就叫做常量構(gòu)造方法
class Person02 {
final  String name;
const  Person02(this.name);
}

main(List<String> args) {
 var p1 = const Person02('lishengbing');
 var p2 = const Person02('lishengbing');

 // true
 print(identical(p1, p2));
}
常量構(gòu)造方法有一些注意點(diǎn):
  • 注意一:擁有常量構(gòu)造方法的類中芥玉、所有的成員變量必須是final學(xué)校修飾的、如final String name;
  • 注意二:為了可以使用常量構(gòu)造方法备图、創(chuàng)建出相同的對象灿巧,不再使用new 關(guān)鍵字、而是使用const關(guān)鍵字
    -- 如果是將結(jié)果賦值給const修飾的標(biāo)識符時揽涮、const可以省略抠藕。

9.2.5: 工廠構(gòu)造方法

Dart 提供了factory關(guān)鍵字、用于通過工廠去獲取對象蒋困。
工廠獲取得到的對象傳入同一個參數(shù)盾似、得到的是同一個對象
傳入不同的參數(shù)、獲取的對象不是同一個對象家破。

main(List<String> args) {
 var p1 = Person03('object');
 var p2 = Person03('object');

 // 工廠構(gòu)造方法獲取對象比較=true
 print('工廠構(gòu)造方法獲取對象比較=${identical(p1, p2)}');
}

class Person03 {
 String name;

 static final Map<String, Person03> _cache = <String, Person03>{};

 factory Person03(String name) {
   if (_cache.containsKey(name)) {
       return _cache[name];
  }else {
       final p = Person03._internal(name);
       _cache[name] = p;
       return p;
   }
 }

 Person03._internal(this.name);

}

9.2.6: setting & getting

默認(rèn)情況下颜说、Dart中類定義的屬性是可以被外界直接訪問的
但是某些情況下、我們希望監(jiān)控這個類的屬性被訪問的過程汰聋、這個時候就可以使用setting 和 getting了

main(List<String> args) {
 final d = Dog('黃色');

 //打印就是: dog color = 黑色
 d.setColor = '黑色';
 
 //打印就是: dog color = 紅色
 d.color = '紅色';
 print('dog color = ${d.getColor}');
}


class Dog {
 String color;

 String get getColor {
   return color;
 }

 set setColor(String color) {
   this.color = color;
 }

 Dog(this.color);
}

9.2.7: 類的繼承(僅支持單繼承门粪、多繼承不可以)

1: 面向?qū)ο蟮钠渲幸淮筇匦跃褪抢^承、繼承不僅可以減少我們的代碼量烹困、也是多態(tài)的使用前提

2: dart 中的繼承使用extends 關(guān)鍵字玄妈、子類中使用super來訪問父類;

3: 父類中的所有成員變量和方法都會被繼承髓梅、但是構(gòu)造方法除外.

main(List<String> args) {
 var p = new Person04();
 p.age = 10;
 p.run();
 /// 奔跑ing
 /// 繼承=10
 print('繼承=${p.age}');
}

class Animal {
 int age;
 run() {
  print('奔跑ing');
 }
}

class Person04 extends Animal {

}
9.2.7(1): 子類可以擁有自己的成員變量拟蜻、并且可以對父類的方法進(jìn)行重寫
class Person04 extends Animal {
 @override
 run() {
  // TODO: implement run
   1: 如果子類重寫了父類的方法,打印父類的方法
   //我是Person04類的重寫run方法
   //繼承=10
   print('我是Person04類的重寫run方法');
 }
}
9.2.7(2): 子類中可以調(diào)用父類的構(gòu)造方法枯饿、對某些屬性進(jìn)行初始化
  • 子類的構(gòu)造方法在執(zhí)行前酝锅, 將隱式調(diào)用父類的默認(rèn)的無參數(shù)的構(gòu)造方法(沒有參數(shù)且類同名的構(gòu)造方法)
  • 如果父類沒有無參默認(rèn)構(gòu)造方法、則子類的構(gòu)造方法必須在初始化列表中通過super顯式調(diào)用父類的某個構(gòu)造方法奢方。
class Animal {
 int age;
 run() {
   print('奔跑ing');
 }

 // 只要這樣寫搔扁、該類就沒有了無參默認(rèn)構(gòu)造函數(shù)
 Animal(this.age);
}

class Person04 extends Animal {

 String name;

 Person04(String name, int age) : name=name, super(age);

 @override
 run() {
   // TODO: implement run
   // 1: 如果子類重寫了父類的方法爸舒,打印父類的方法
   // 我是Person04類的重寫run方法
   // 繼承=10
   print('我是Person04類的重寫run方法');
 }

 @override
 String toString() {
     print('toString-');
  }
}

9.2.8: 抽象類

我們知道、繼承是多態(tài)使用的前提
所以在定義很多通用的通用接口時我們通常會讓調(diào)用者傳入父類稿蹲、通過多態(tài)類實(shí)現(xiàn)更加靈活的調(diào)用方式扭勉。

但是、父類本身可能并不需要對某些方法進(jìn)行具體的實(shí)現(xiàn)苛聘、所以父類中定義的方法涂炎、我們可以定義為抽象類。

什么是抽象方法设哗?在Dart中沒有具體實(shí)現(xiàn)的方法(沒有方法體)唱捣、就是抽象方法。

  • 抽象方法熬拒、必須存在于抽象類中爷光。
  • 抽象類是使用abstract 聲明的類。
    下面這個Shape類就是一個抽象類澎粟、其中包含了一個抽象方法
abstract class Shape {
  getArea();
}

class Circle extends Shape {

  double r;
  Circle(this.r);

  @override
  getArea() {
    // TODO: implement getArea
    return r * r * 3.14;
  }
}

class Reactangle extends Shape {

  double w;
  double h;
  Reactangle(this.w, this.h);

  @override
  getArea() {
    // TODO: implement getArea
    return w * h;
  }
}
注意事項:
  • 注意一:** 抽象類中不能實(shí)例化蛀序;
  • 注意二:** 抽象類中的抽象方法必須被子類實(shí)現(xiàn)、抽象類中的已經(jīng)被實(shí)現(xiàn)方法活烙、可以不被子類重寫徐裸;

9.2.9: 隱式接口

Dart中的接口比較特殊、沒有一個專門的關(guān)鍵字來聲明接口
默認(rèn)情況下啸盏、定義的每一個類都相當(dāng)于默認(rèn)也聲明了一個接口重贺、可以由其他的類來實(shí)現(xiàn)(因為Dart不支持多繼承)

在開發(fā)中、我們通常將用于給別人來實(shí)現(xiàn)的類聲明為抽象類:

abstract class Runner {
  run();
}

abstract class Flyer {
  fly();
}

class SuperMan implements Runner, Flyer {
  @override
  run() {
    // TODO: implement run
    print('超人在跑');
  }

  @override
  fly() {
    // TODO: implement fly
    print('超人在飛');
  }
}

9.2.10: Mixin混入

在通過implements實(shí)現(xiàn)某個類時回懦、類中所有的方法都必須被重載實(shí)現(xiàn)(無論這個類原來是否已經(jīng)實(shí)現(xiàn)過該方法)

但是在某些情況下气笙、一個類可能希望直接復(fù)用之前類的原有實(shí)現(xiàn)方案、怎么做呢怯晕?

  • 使用繼承嗎潜圃?但是Dart只支持單繼承、那么意味著你只能復(fù)用一個類的實(shí)現(xiàn)

Dart提供了另外一種方案:Mixin混入的方式

  • 除了可以class 定義類之外舟茶、也可以通過mixin關(guān)鍵字來定義一個類
  • 只是通過mixin定義的類用于被其他類混入使用谭期、通過with關(guān)鍵字來進(jìn)行混入。
main(List<String> args) {
  var superMan = SuperMain();
  superMan.run();
  superMan.fly();
  /*
  在奔跑...
  在飛翔... 
  */
}

mixin Runner1 {
  run() {
    print('在奔跑...');
  }
}

mixin Flyer1 {
  fly() {
    print('在飛翔...');
  }
}

/// implements的方式必須要求對其中的方法進(jìn)行重新實(shí)現(xiàn)
class SuperMain0 implements Runner1, Flyer1 {

  @override
  run() {
    // TODO: implement run
    return null;
  }

  @override
  fly() {
    // TODO: implement fly
    return null;
  }
}

class SuperMain with Runner1, Flyer1 {

}

9.2.11: 類成員和方法

前面我們在類中定義的成員和方法都屬于對象級別的吧凉、在開發(fā)中隧出、我們有時候也需要定義類級別的成員和方法

在Dart中我們使用static關(guān)鍵字類定義:

main(List<String> args) {
  var stu = Student();
  stu.name = 'lishengbing';
  stu.age = 66;
  stu.study();

  Student.time = '早上10點(diǎn)';
  Student.attendClass();
  // lishengbing在學(xué)習(xí)
  // 去上課
}

class Student {
  String name;
  int age;

  static String time;

  study() {
    print('$name在學(xué)習(xí)');
  }

  static attendClass() {
    print('去上課');
  }
}

9.2.12: 枚舉的類型

枚舉在開發(fā)中也非常常見、枚舉也是一種特殊的類阀捅、通常用于表示數(shù)量的常量值胀瞪。

1: 枚舉的定義

枚舉使用enum關(guān)鍵字來進(jìn)行定義

// 1: 枚舉的定義

main(List<String> args) {
  // Colors.red
  print(Colors.red);
  // values, [Colors.red, Colors.green, Colors.blue]
  print(Colors.values);
  // index, 0
  print(Colors.red.index);
}

enum Colors {
  red,
  green,
  blue
}
2: 枚舉的屬性

枚舉類型中有兩個比較常見的屬性

  • index: 用于表示每個枚舉常量的索引、從0開始
    如:print(Colors.red.index);
  • values:包含每個枚舉值的List
    如:print(Colors.values);
3: 枚舉的注意事項
  • 注意1: 您不能子類化饲鄙、混合或者實(shí)現(xiàn)枚舉赏廓。
  • 注意2: 不能顯式實(shí)例化一個枚舉涵紊。

9.3: 泛型

1: 為什么使用范型?

......

2: List和Map的范型
main(List<String> args) {
// 1: 創(chuàng)建List的方式
var name1 = ['lishengbing', 'zhangsan', 'wangxiao', 111];
// List<String>
// List<Object>
print(name1.runtimeType);

// 限制類型
var name2 = <String>['1', '2', '3'];
// List<String>
print(name2.runtimeType);



// 2: Map使用泛型的寫法
var info1 = {'name': 'li', 1: 'one', 'age': 20};
// _InternalLinkedHashMap<Object, Object>
print(info1.runtimeType);


// 2: 對類型進(jìn)行限制
Map<String, String> info2 = {'name': 'li', 'age': '20'};
// 限制寫法1
// info2=_InternalLinkedHashMap<String, String>
print('info2=${info2.runtimeType}');

var info3 = <String, String>{'name': 'li', 'age': '20'};
// 限制寫法2
// info3=_InternalLinkedHashMap<String, String>
print('info3=${info3.runtimeType}');

}
3: 類定義的泛型

如果我們需要定義一個類、用于存儲位置信息Location幔摸、但是并不確定使用者使用的是int類型、還是double類型颤练、甚至是一個字符串類型既忆、這個時候該怎么定義呢?

  • 一種方案是使用Object類型嗦玖、但是在之后使用時候非常不方便
  • 另一種方案就是使用泛型
    Location類的定義:Object類 || 泛型方式
main13(List<String> args) {
  Location l = Location(10, 20);
  /// int
  print(l.x.runtimeType);
}

class Location {
  Object x;
  Object y;

  Location(this.x, this.y);
}


main(List<String> args) {
  Location2 l1 = Location2<int>(10, 30);
  // Location2<int>
  print('int-l1=${l1.runtimeType}');

  Location2 l2 = Location2<String>('30', '40');
  // Location2<String>
  print('String-l2=${l2.runtimeType}');
}

// 泛型寫法
class Location2<T> {
  T x;
  T y;

  Location2(this.x, this.y);
}

  • 如果我們希望類型只能是num類型患雇、怎么做呢?
// 如果希望類型只能是num類型


main(List<String> args) {
  Location3 l3 = Location3(10, 20);
  // Location3<num>
  print(l3.runtimeType);
}


class Location3<T extends num> {
  T x;
  T y;

  Location3(this.x, this.y);
}
4: 泛型方法的定義

最初宇挫、Dart僅僅在類中支持泛型苛吱、后來一種稱之為泛型方法的新語法允許在方法和函數(shù)中使用類型參數(shù)。

main(List<String> args) {
  var names = ['1', '2'];
  var first = getFirst(names);
  /// first = 1, type=String
  print('first = ${first}, type=${first.runtimeType}');
}

T getFirst<T>(List<T> ts) {
    return ts[0];
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末器瘪,一起剝皮案震驚了整個濱河市翠储,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌橡疼,老刑警劉巖援所,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異欣除,居然都是意外死亡住拭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門历帚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來滔岳,“玉大人,你說我怎么就攤上這事挽牢∑酌海” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵卓研,是天一觀的道長趴俘。 經(jīng)常有香客問我,道長奏赘,這世上最難降的妖魔是什么寥闪? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮磨淌,結(jié)果婚禮上疲憋,老公的妹妹穿的比我還像新娘。我一直安慰自己梁只,他們只是感情好缚柳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布埃脏。 她就那樣靜靜地躺著,像睡著了一般秋忙。 火紅的嫁衣襯著肌膚如雪彩掐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天灰追,我揣著相機(jī)與錄音堵幽,去河邊找鬼。 笑死弹澎,一個胖子當(dāng)著我的面吹牛朴下,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播苦蒿,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼殴胧,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了佩迟?” 一聲冷哼從身側(cè)響起团滥,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎音五,沒想到半個月后惫撰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡躺涝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年厨钻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坚嗜。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡夯膀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出苍蔬,到底是詐尸還是另有隱情诱建,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布碟绑,位于F島的核電站俺猿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏格仲。R本人自食惡果不足惜押袍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望凯肋。 院中可真熱鬧谊惭,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驱敲,卻和暖如春铁蹈,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背癌佩。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工木缝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人围辙。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像放案,于是被迫代替她去往敵國和親姚建。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

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