Flutter學(xué)習(xí)筆記

Dart基礎(chǔ)語法

一、選擇開發(fā)工具

首先需要安裝Dart SDK(https://www.dart-china.org/t/topic/480#heading--manual-install),SDK安裝完成后,選擇一款編輯工具。這里就先只推薦兩款工具 VS Code ,Android Studio。工具下載完成后,安裝dart插件,這個時候就可以編寫dart代碼了。先新建一個hello.dart文件,然后在文件中輸入下方的代碼,點擊運行按鈕就可以執(zhí)行代碼。

// 在dart中,main方法是代碼運行的入口方法,一切都是從main方法開始的。。
void main (){
  // 點擊IDE運行按鈕 在打印臺中會打印出 hello Dart。 接下來的例子都是在該方法下執(zhí)行的(除特殊說明)。
  print('hello Dart');
}
二、常用的數(shù)據(jù)類型
  1. num num是數(shù)字類型的父類,包含了兩個子類 int和double。

  2. String String可以用 '' 或 "" 來創(chuàng)建,也可以使用''' 或者"""來創(chuàng)建可變行的字符串。

    eg.num與String的一些常用操作

    // String -> num
    int one = int.parse('1');
    print(one + 2); // 輸出 3 double類型同理
    int one = int.parse('hello');
    print(one + 2); // 報錯
    
    // int -> String
    String oneStr = 1.toString();
    print(oneStr); // 輸出 1
    
    // double -> String
    String oneStr = 3.1455.toStringAsFixed(2);
    print(oneStr); // 截取兩位小數(shù), 輸出3.15 四舍五入
    String twoStr = 3.toStringAsFixed(2);
    print(twoStr); // 輸出3.00
    
    // 可以使用${表達(dá)式}將表達(dá)式的值放入字符串中。如果表達(dá)式是標(biāo)識符,{}可省略
    String oneStr = 'hello';
    String twoStr = "dart";
    print('$oneStr $twoStr'); //輸出 hello dart
    print('$oneStr ${twoStr.toLowerCase()}'); //輸出 hello dart
    print('$oneStr $twoStr.toLowerCase()'); //輸出 hello dart.toLowerCase()
    
  3. bool Dart是強bool類型檢查。

    • 只有兩個對象具有bool類型:true和false,它們都是編譯時常量

    • if(非bool類型) 報錯,assert( 非bool類型)報錯,也就是說需要bool類型的,如果我們用int 等非bool類型的值都會報錯。

      if (1){} // 報錯
      
  4. List 列表

    var list_1 = new List(); // 非固定長度 new可以省略
    
    // 添加元素
    list_1.add('1');
    
    // 添加多個元素
    list_1.addAll(['1', '2']);
    
    // 獲取list長度
    print(list_1.length); // 輸出2
    
    // 獲取list最后一個元素
    print(list_1.last); //輸出 2
    
    // 獲取list第一個元素
    print(list_1.first); // 輸出 1
    
    // 查詢某位置的元素,index從0開始
    print(list_1[0]); // 輸出 1
    
    // 根據(jù)某個元素獲取對應(yīng)的index
    print(list_1.indexOf("1")); // 輸出 0
    
    // 刪除元素
    list_1.removeAt(0); // 通過索引刪除
    list_1.remove("1"); // 通過對象刪除 如果有兩個 "1" 那么會刪除第一個
    list_1.removeLast(); // 刪除最后一個元素
    list_1.removeRange(start,end); // 刪除范圍內(nèi)的數(shù)據(jù)
    list_1.removeWhere((item) => item.length > 6);// 刪除元素長度大于6的元素 這種寫法可見下面箭頭函數(shù)的詳解
    list_1.clear(); // 清除所有元素
    
    /*注意*/
    List list_2 = new List(2); // 固定長度
    list_2.add('1'); // 報錯 報錯信息 Cannot add to a fixed-length list
    
    // 另外一種定義方式
    List list_3 = ['1','2','3'];
    print(list_3); // 輸出 [1,2,3]
    
  1. Set 集合 Set是沒有順序且不能重復(fù)的集合,所以不能通過索引去獲取值

    Set set_1 = new Set();
    Set set_2 = new Set(2); // 報錯 set沒有固定長度的定義
    set_1.add('1');
    set_1.add('2');
    set_1.add('1');
    print(set_1); // 輸出 {1, 2}
    // 可以和list一樣通過contains來判斷是否包含某個元素
    print(set_1.contains("1")); // 輸出 true 
    set_1.addAll(['b','c']);
    print(set_1); // 輸出 {1, 2, b, c}
    
  1. Map 映射是無序的鍵值對。鍵和值都可以是任何類型的對象。

    // 常用的兩種定義方式
    Map map_1 = Map();
    Map map_2 = {"key_1":"value_1","key_2":"value_2"};
    print(map_1); // 輸出 {}
    print(map_2); // 輸出 {key_1: value_1, key_2: value_2}
    
    // 賦值
    map_1["1"] = "one";
    map_1["2"] = "two";
    print(map_1); // 輸出 {1: one, 2: two}
    map_1["1"] = "first";
    print(map_1); // 輸出 {1: first, 2: two}
    map_1[1] = "one"; // 在map中key必須要保持唯一 value可以相同 key類型可以為任意其他類型
    print(map_1); // 輸出 {1: one, 2: two, 1: one}
    
    // 常用api
    map_1.remove(1); // 刪除key為1的元素
    map_1.containsKey(1); // 判斷是否存在key為1的元素
    
三、函數(shù)/方法
  1. 內(nèi)置函數(shù)/方法

    print("hello Dart");
    

    這個是常用的將數(shù)據(jù)打印到控制臺的方法,這是系統(tǒng)提供給我們的內(nèi)置函數(shù),可以直接調(diào)用。

  2. 自定義方法

    自定義方法的基本格式:

    返回類型 方法名稱 (參數(shù)1,參數(shù)2,...){

    ? 方法體

    ? return 返回值;

    }

    int getMaxCount(int a, int b){
     if (a > b){
         return a;
     }
      return b;
    }
    print(getMaxCount(2, 1)); // 輸出 2
    
    // 在dart中 返回值和參數(shù)類型是可以省略的
    getMinCount(a,b){
      if (a < b){
        return a;
      }
      return b;
    }
    print(getMinCount(2, 1)); // 輸出 1
    
  1. 方法的傳參

    // 可選參數(shù)
    String printUserInfo(String username,[int age]){ // username和age表示是形參 其中age表示可選參數(shù)
      return "姓名:$username  年齡:$age";
    }
    print(printUserInfo("張三",30)); // 張三 和 30 表示實參
    print(printUserInfo("張三")); // 這里可以不用傳入age的參數(shù),如果age不是可選參數(shù)的話,那么age是必須要傳的
    
    // 默認(rèn)參數(shù)
    String printUserInfoDefalut(String username,[int age,String sex = "男"]){
      return "姓名:$username  年齡:$age  性別:$sex";
    }
    print(printUserInfoDefalut("李四",30)); // 輸出 姓名:李四  年齡:30  性別:男
    /*注意
    對于可選參數(shù)的傳遞是按照參數(shù)的順序進(jìn)行的 print(printUserInfoDefalut("李四","女")); 這里會報錯。雖然sex和age都是可選參數(shù),但是當(dāng)對一個形參賦值時,會默認(rèn)選擇第一個。這里可選參數(shù)的第一個形參是age int類型的,這里傳遞一個"女"是string類型的,所以會報錯。
    */
    // 命名參數(shù)
    String printUserInfoName(String username,{int age,String sex = "男"}){
      return "姓名:$username  年齡:$age  性別:$sex";
    }
    print(printUserInfoDefalut("李四",age:30,sex:"男")); // 輸出 姓名:李四  年齡:30  性別:男
    // 這樣我們通過指定參數(shù)名稱實現(xiàn)我們想要傳遞的參數(shù)。這個比較常用。
    
    // 將方法作為參數(shù)
    fun_1(){
      print("方法1");
    }
    
    fun_2 (Function fun){ // fun方法其實就是一個Function對象
      fun();
    }
    
    fun_2(fun_1); // 輸出 方法1
    
  1. 箭頭函數(shù)

    // 遍歷數(shù)組
    List list = ["h","e","l","l","0"];
    // 正常寫法
    list.forEach((element){
      print(element);
    });
    // 箭頭函數(shù)的方式 箭頭后跟的方法體只能是一行 這只是一種簡寫的方式
    list.forEach((element)=>print(element));
    list.forEach((element) => {
      print(element) // 這里不能寫分號,也是只能執(zhí)行一行代碼
    });
    
    List list_1 = [1,3,5,1,3,7];
    // 這也是數(shù)組的一種遍歷并處理的方式
    List list_2 = list_1.map((element){
      if(element >3){
        return element*3;
      }
      return element;
    }).toList();
    print(list_2);
    List list_3 = list_1.map((element)=>element>3?element*3:element).toList();
    print(list_3);
    
  1. 自執(zhí)行與自調(diào)用

    //實際上自執(zhí)行方法我們可以理解為一個匿名函數(shù)的自調(diào)用
    ((){})();// 格式 將一個匿名函數(shù)用()包起來 然后執(zhí)行
    
    ((int n){
      print(n);
    })(12); //輸出12
    
    // 方法可以自己調(diào)用自己
    int sum = 1;
    fn(n){
      sum*=n;
      if (n == 1){
        return;
      }
      fn(n-1);
    }
    fn(5); // 這樣通過方法的自調(diào)用可以實現(xiàn)5的階乘
    
  1. 閉包

    • 全局變量特點:全局變量常駐內(nèi)存,會污染全局。

    • 局部變量特點:不常駐內(nèi)存會被垃圾回收機(jī)制回收,不會污染全局。

    • 閉包:常駐內(nèi)存,不會污染全局。寫法:函數(shù)嵌套函數(shù),并return內(nèi)層函數(shù)。

      //閉包
      fun(){
        // 雖然這里定義的是一個局部變量,但是通過閉包的寫法就不會被垃圾回收機(jī)制回收,且不會污染全局
        int a = 1;
        return (){
          a++;
          print(a);
        };
      }
      Function b = fun(); // 因為fun方法返回的就是一個方法,所以下面直接調(diào)用b方法。
      b();
      b();
      b();
      // 輸出 2 3 4
      
四、語法規(guī)則
  1. Dart中一切皆對象,都是繼承自O(shè)bject類。所以在Dart中不存在基本數(shù)據(jù)類型,我們定義的任何一個對象,如果不將其賦值的話,那么這個對象就是一個null對象。

    int a;
    int b = 0;
    print(a); // 輸出 null
    print(b); // 輸出 0
    
  1. Dart是動態(tài)型語言,如果沒有指定其類型時,則默認(rèn)時dynamic類型。在運行時會自動推導(dǎo)出具體的類型。

    var str = 'shsh';
    str = 1; // 報錯
    
  2. 運算符及修飾符

    • static:用于修飾類成員變量,這個變量是屬于類的,通過類名直接調(diào)用,而不是通過對象調(diào)用。這個與java類似。非靜態(tài)方法可以訪問靜態(tài)成員以及非靜態(tài)成員。靜態(tài)方法無法訪問非靜態(tài)成員,也無法調(diào)用非靜態(tài)方法。

    • final:用于修飾變量,表示單賦值(single-assignment),使用final修飾的變量必須進(jìn)行初始化,一旦被賦值之后,不能夠再次被賦值,否則編譯會報錯。

    • const:與final有一點類似,即只能被賦值一次。但是其修飾的對象有一定的限制。const修飾的對象的狀態(tài)是完全可以在編譯期間就能確定的,并且是不可變的。

      const n = 1+2;// 可行 因為這個是在編譯期間我們就知道n = 3的
      const list = new List(); // 不可行
      const list_1 = [1,2,3]; // 可行
      list_1 = [3,4]; // 不可行
      
  • 常用的運算符和表達(dá)式

    //加減乘除
    int a = 10;
    int b = 3;
    print(a/b); // 3.3333333333333335 不用判斷除數(shù)和被除數(shù)的類型,默認(rèn)返回double類型
    print(a~/b); // 3  整除取整
    print(a*b); // 30
    print(a+b); // 13
    print(a-b); // 7
    print(a%b); // 1 取余
    
    //as 定型
    Person per = new Person(); // 我們定義一個Person類,關(guān)于類的定義下文會介紹
    // 如果per是Person類型的話 那么就給Person的name屬性賦值,如果不是Person類型則不賦值 并
    (per as Person).name = "張三";
    
    // is 如果對象是指定的類型 那么就返回true 否則就返回false
    if (per is Person){
      per.name = "張三";
    }
    /// 注意:以上代碼不相等。如果per為空或不為Person,第二個示例(帶is)什么也不做;第一個(帶有as)拋出異常。
    
    // ??與三目運算符
    String str_1;
    String str_2 = "張三";
    print(str_1??str_2); // 輸出 張三
    print((str_1 is! Null)?str_1:str_2); // 輸出張三 在dart中 關(guān)于條件的判斷必須是bool類型
    
    // 級聯(lián)符號 可以避免創(chuàng)建臨時變量的繁瑣步驟
    Person() ..name ="張三";
    // 等價于
    Person per = new Person();
    per.name = "張三";
    
    // ?. 有條件的成員屬性訪問
    Person per; // 這里per就是null
    // 如果直接調(diào)用成員會報錯
    per.name = "張三"; // 報錯
    per?.name = "張三"; // 不會報錯
    
五、類與對象
  1. 類的創(chuàng)建

    // 與上面的例子不同,類的定義需要與mian方法同級
    class Person{
      String name; // 屬性
      void study (){
        print("學(xué)習(xí)");
     } // 方法
    }
    
    void main (){
      // 實例化,也就是創(chuàng)建對象
      Person per = new Person(); // per就是一個對象
      per.name = "張三";
      print("${per.name}"); // 輸出 張三 調(diào)用對象的屬性
      per.study(); // 輸出 學(xué)習(xí) 調(diào)用方法
    }
    
  1. 類的構(gòu)造函數(shù)

    • 默認(rèn)構(gòu)造函數(shù)。每個類都有一個默認(rèn)的構(gòu)造函數(shù),默認(rèn)是不用寫的,如果需要在構(gòu)造函數(shù)做操作的話,則重寫一下默認(rèn)構(gòu)造函數(shù)。

      class Person{
        String name;
        //構(gòu)造參數(shù)名與類名要相同
        Person(){ // 無參
          print("我需要在這里做一些操作");
        }
        // 初始化列表
        person():name = "張三"{
          // 在執(zhí)行構(gòu)造函數(shù)運行之前 初始化一些實例變量
        }
      }
      
      class Student{
        String name;
        Student(String name){ // 有參數(shù) 參數(shù)必傳
          this.name = name;
        }
        void printStudentInfo (){
          print("學(xué)生的姓名:${this.name}");
        }
      }
      
      class Coder{
        String name;
        Coder(this.name); // 簡寫的有參構(gòu)造函數(shù) 參數(shù)必傳
        void printCoderInfo (){
          print("程序員的姓名:${this.name}");
        }
      }
      
      class Engineer{
        String name;
        Engineer({this.name}); // 簡寫的有參構(gòu)造函數(shù) 參數(shù)非必傳
        void printEngineerInfo (){
          print("工程師的姓名:${this.name}");
        }
      }
      
      void main (){
        // 實例化,也就是創(chuàng)建對象
        Person per = new Person(); // 輸出  我需要在這里做一些操作。
        
        // 報錯 因為我們修改了默認(rèn)的構(gòu)造函數(shù),所以之前的無參構(gòu)造函數(shù)就不能使用了。
        // Student stu = new Student(); 
        
        Student stu_1 = new Student('張三');
        stu_1.printStudentInfo();
        
        Coder coder = Coder("張三");
        coder.printCoderInfo();
        
        Engineer eng = new Engineer();
        eng.printEngineerInfo(); // 輸出 工程師的姓名:null
        
         Engineer eng_1 = new Engineer(name: "張三");
        eng_1.printEngineerInfo(); // 輸出 工程師的姓名:張三
      }
      
  • 命名構(gòu)造函數(shù)。類是可以有多個構(gòu)造函數(shù)的,通過不同的命名來實現(xiàn)不同功能的構(gòu)造函數(shù)

    class Engineer{
      String name;
      String hairStyle; //發(fā)型 
      Engineer({this.name,this.hairStyle = "茂密的頭發(fā)"}); // 簡寫的有參構(gòu)造函數(shù) 參數(shù)非必傳
      Engineer.seniorEngineer(String name){ //命名構(gòu)造函數(shù)
        this.name = name;
        this.hairStyle = "禿了";
      }
      void printEngineerInfo (){
        print("工程師的姓名:${this.name} 發(fā)型:${this.hairStyle}");
      }
    }
    
    void main (){
      Engineer eng_1 = Engineer(name:"張三");
      eng_1.printEngineerInfo();
      
      Engineer eng_2 = Engineer.seniorEngineer("李四");
      eng_2.printEngineerInfo();
    }
    
  • 類中的setter和getter方法

    class Rect{
      num height;
      num width;
      Rect(this.height,this.width);
      // getter方法
      get area{
        return this.height*this.width;
      }
      // setter方法
      set areaHeight(num height){
        this.height = height;
      }
    }
    
    void main(){
      Rect re = Rect(10,10);
      print("面積:${re.area}"); // 輸出 面積:100
      re.areaHeight = 5;
      print("面積:${re.area}"); // 輸出 面積:50
    }
    
  1. 類的繼承

    • 子類使用 extends 關(guān)鍵字來繼承父類

    • 子類會繼承父類中可見你的屬性和方法,但是不會繼承構(gòu)造函數(shù)

    • 子類能復(fù)寫父類的方法 getter和setter

      class Person{
        String name;
        num age;
        Person(this.name,this.age);
        void printPersonInfo(){
          print("姓名:${this.name} 年齡:${this.age}");
         }
        
        void work(){
          print("我正在工作");
        }
      }
      
      class Coder extends Person{
        // 因為Person是重寫了構(gòu)造函數(shù),所以這里也需要寫一下與父類關(guān)聯(lián)的構(gòu)造函數(shù)
        // 如果父類有多個構(gòu)造函數(shù) 則需要選一個自己需要的
        Coder(String name,num age,String grade):super(name,age){
          // 通過代碼可以看出,這么寫的目的是當(dāng)我們在實例化Coder時也會將值傳遞給父類
          // grade 是子類的屬性,所以不用將參數(shù)傳遞給父類
          this.grade = grade;
         }
        String grade;
        void printCoderInfo(){
          super.work(); // 調(diào)用父類的方法
          print("姓名:${this.name} 年齡:${this.age} 等級:${this.grade}");
         }
        
        @override // 重寫父類方法
        void work() {
          print("我正在寫代碼");
        }
      }
      
      void main(){
        Coder code = Coder("zhangsan",20,"初級碼農(nóng)");
        code.printCoderInfo(); // 輸出 姓名:zhangsan 年齡:20 等級:初級碼農(nóng)
        code.work(); // 輸出 我正在寫代碼
      }
      
  1. 抽象類。Dart中的抽象類主要是用于定義標(biāo)準(zhǔn),子類可以繼承抽象類,然后實現(xiàn)抽象類的接口。

    • 抽象類通過abstract關(guān)鍵字來定義。

    • 抽象方法不能用abstract聲明,我們將沒有方法體的方法稱為抽象方法。

    • 如果子類繼承抽象類必須實現(xiàn)里面的抽象方法。

    • 如果把抽象類當(dāng)作接口實現(xiàn)的話必須得實現(xiàn)抽象類里定義的所有屬性和方法。

    • 抽象類不能被實例化,子類可以。

    • 如果要實現(xiàn)抽象方法約束子類的話我們需要用extends繼承抽象類。

    • 如果只是把抽象類作為一個標(biāo)準(zhǔn)的話我們需要用implements實現(xiàn)抽象類。

      abstract class Person{
        String name;
        int age;
        Person(this.name,this.age);
        work(); // 沒有實現(xiàn)方法體 這是一個抽象方法 抽象方法是必須要在子類中實現(xiàn)的
        eat(){
          print('吃飯');
        }
      }
      
      class Coder extends Person{
        Coder(String name, int age) : super(name, age);
        @override
        work(){
          print("${this.name}在搬運代碼,他今年已經(jīng)${this.age}了");
        }
      }
      /*
      在dart中普通類和抽象類都可以作為接口被實現(xiàn) 使用關(guān)鍵字 implements 
      dart的接口會將普通類或者是抽象類中的屬性或者方法都重寫一遍,所以我們一般采用抽象類實現(xiàn)接口。
      因為只有抽象類才可以創(chuàng)建抽象方法。
      */
      class Engineer implements Person{
        // 這里就不用像繼承一樣需要實現(xiàn)父類的構(gòu)造方法。
        @override
        int age;
      
        @override
        String name;
      
        @override
        eat() {
          print("工程師在吃飯");
        }
      
        @override
        work() {
          print("${this.name}在搞算法,他今年才${this.age}");
        }
      }
      
      void main (){
        Coder code = Coder("張三", 30);
        code.work(); // 輸出 張三在搬運代碼,他今年已經(jīng)30了
        
        Engineer eng = Engineer()
          ..name = "李四"
          ..age = 20
          ..work(); // 輸出 李四在搞算法,他今年才20
      }
      
  1. 多接口和mixins

    • 通過implements的方式,可以實現(xiàn)多接口。

      abstract class A{
        String a;
        printA();
      }
      abstract class B{
        String b;
        printB();
      }
      class C implements A,B{
        @override
        String a;
      
        @override
        String b;
      
        @override
        printA() {
        // TODO: implement printA
        return null;
        }
      
        @override
        printB() {
        // TODO: implement printB
        return null;
        }
      }
      // 這樣C類的對象就可以同時實現(xiàn) A B的的抽象方法
      
  • 通過mixins實現(xiàn)多繼承,Dart是不支持多繼承,是無法實現(xiàn)類似C++的多繼承的功能。

    class A{
    // mixins 不能有顯示的構(gòu)造方法 其實這個也好理解 因為使用mixins繼承多個類的話,它也不知道用什么構(gòu)造方法 只能使用默認(rèn)的了
    //  A.withName(){
    //
    //  }
      a(){
        print('A.a()');
      }
    }
    
    class B{
      a(){
        print('B.a()');
      }
      b(){
        print('B.b()');
      }
    }
    
    class C{
      a(){
        print('C.a()');
      }
      b(){
        print('C.b()');
      }
      c(){
        print('C.c()');
      }
    }
    
    class D extends A with B,C{
      int count; // 可以定義自己的屬性
    }
    void main (){
      var d = new D();
      d.a(); // 輸出 C.a() 默認(rèn)調(diào)用最后一個方法
    }
    /*mixins的方式組合的類 是必須要繼承object的 A B C 就是不能繼承與任何的類 除了默認(rèn)的object 如果想實現(xiàn)類似繼承的效果 我們可以使用接口的方式*/
    
六、泛型
  1. 泛型方法

    T getData<T>(value){
      return value;
    }
    print(getData("xingming")); // 輸出 xingming
    print(getData(20)); // 輸出 20
    print(getData<String>(20)); // 報錯 因為指定的是string類型 但是傳入的是int類型
    
    // 通過泛型方法我們就可以不用區(qū)分傳入的參數(shù)類型和輸出的參數(shù)類型。
    var names = List<String>();
    names.addAll(['1', '2', '3']);
    names.add(42); // 報錯
    
  1. 泛型類

    通過指定類的泛型類型,來確定輸入的數(shù)據(jù)類型是否正確

    void main (){
      LogList loglist = LogList<int>(); // 確定泛型類型為int類型,后續(xù)類中的類型判斷就是int類型
      loglist.addData(1);
      loglist.addData("1"); // 報錯
      loglist.printList();
      
      LogList loglist_1 = LogList(); // 沒有確定泛型類型
      loglist_1.addData(1);
      loglist_1.addData("1"); // 沒有報錯
      loglist_1.printList();
    }
    
    class LogList<T>{
      List list = List<T>();
      void addData(T value){
        list.add(value);
      }
    
      void printList(){
        for (int i = 0;i<list.length;i++){
          print(list[i]);
        }
      }
    }
    
  1. 泛型接口

    void main (){
      B b = B<int>();
      b.pirntA(1);
      //b.pirntA("12"); // 報錯
    }
    
    abstract class A<T>{
      pirntA(T value);
    }
    
    class B<T> implements A<T>{
      @override
      pirntA(T value) {
        print(value);
      }
    }
    
七、結(jié)尾

? 上面簡單的介紹了一些Dart語言的基礎(chǔ)語法知識,關(guān)于Dart中更深層次的內(nèi)容,我會在以后的文章中再慢慢的剖析。關(guān)注好享家技術(shù)團(tuán)隊公眾號,閱讀更多精彩技術(shù)文章。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容