吳麗麗-201871010123 《面向對象程序設計(java)》第6、七週學習總結

                                                 吳麗麗-201871010123 《面向對象程序設計(java)》第6、七週學習總結html

項目 內容
這個做業屬於哪一個課程 https://www.cnblogs.com/nwnu-daizh/
這個做業要求在哪裏  

https://www.cnblogs.com/nwnu-daizh/p/11605051.htmljava

做業的學習目標  
  1. 深刻理解程序設計中算法與程序的關係;
  2. 深刻理解java程序設計中類與對象的關係;
  3. 理解OO程序設計的第2個特徵:繼承、多態;
  4. 學會採用繼承定義類設計程序(重點、難點);
  5. 可以分析與設計至少包含3個自定義類的程序;
  6. 掌握利用父類定義子類的語法規則及對象使用要求。

第一部分:理論知識部分算法

                                                                第五章     繼承(inheritance)編程

5.1 類、超類和子類數組

5.2 Object:全部類的超類安全

5.3 泛型數組列表ide

5.4 對象包裝器和自動打包函數

5.5 參數變量可變的方法學習

5.6 枚舉類測試

5.7 繼承設計的技巧

   5.1 類、超類和子類

繼承的特色:具備層次結構;子類繼承了父類的域和方法。

a)類繼承的格式:

         class 新類名 extends 已有類名 

b)已有類稱爲:超類(superclass)、基類(base class) 或父類(parent  class)

     新類稱做:子類(subclass)、派生類(derived  class)或孩子類(child class)

c)通常來講,子類比超類擁有的功能更豐富。

d)super是一個指示編譯器調用超類方法的特有關鍵字,它不是一個對象的引用,不能將super賦給另外一個對象變量。

e) super關鍵字通常有兩個用途:一是調用超類的方法(格式:super.方法名()),二是調用超類的構造器(格式:super() )。

f)從一個超類擴展而來的類集合稱爲繼承層次(inheritance hierarchy)。在繼承層次中,某個類到其祖先的路徑被稱爲該類的繼承鏈(inheritance chain)。

 注意:Java不支持多繼承

g)多態性:多態性泛指在程序中同一個符號在不一樣的狀況下具備不一樣解釋的現象。

      java中,對象變量是多態的

h) 不容許繼承的類稱爲final類,在類的定義中用final修飾符加以說明,String類是final類的一個例子。不能擴展該類。

I)抽象類:包含一個或多個抽象方法的類必須聲明爲抽象類

                   抽象方法充當着佔位的角色,它們的具體實如今子類中。

                   抽象類不能被實例化,即不能建立對象,只能產生子類。

                                            繼承小結

1)封裝、繼承、多態是面向對象的主要特徵。

2)繼承能夠提升代碼重用性,用extends關鍵字來實現。除構造方法以外,父類的全部方法和屬性都被子類繼承。

3)繼承創建了類與類間的關係,同時是多態特徵的前提

4)java只支持單繼承,不直接支持多繼承(避免兩個父類出現同名方法的調用選擇困難)

5)abstract修飾的抽象類不能實例化爲對象,只能擴展子類;抽象類中的抽象方法充當着佔位的角色,它們的具體實如今子類中。

6)final類不容許繼承;類中final方法不容許被子類重寫。

5.2 Object:全部類的超類

a)Object類是Java中全部類的祖先——每個類都由它擴展而來。在不給出超類的狀況下,Java會自動把Object 做爲要定義類的超類。

b)可使用類型爲Object的變量指向任意類型的對象。但要對它們進行專門的操做都要進行類型轉換。

c)Object類中的equals方法用於測試某個對象是否同另外一個對象相等。它在Object類中的實現是判斷兩個對象是否具備相同的引用。若是兩個對象具備相同的引用,它們必定是相等的。

d)定義子類的equals方法時,可調用超類的equals方法。

  super.equals(otherObject)

e) Object類中的hashCode方法導出某個對象的散列碼。散列碼是任意整數,表示對象的存儲地址。

兩個相等對象的散列碼相等。

 f)Object類的toString方法返回一個表明該對象域值的字符串。定義子類的toString方法時,可先調用超類的toString方法。如:super.toString()

5.3 泛型數組列表

a) Java中,利用ArrayList類,可容許程序在運行時肯定數組的大小。

b)ArryList是一個採用類型參數的泛型類。爲指定數組列表保存元素的對象類型,須要用一對尖括號將數組元素的對象類名括起來加在後面。

ArryList<Employee> staff=new ArrayList<Employee>();

c)沒有<>的ArrayList將被認爲是一個刪去了類型參數的「原始」類型。

d)a.ArrayList定義

>ArrayList <T> 對象 = new ArrayList<T>();

>API : ArrayList的構造器

ArrayList<T>()構造一個空數組列表

ArrayList<T>(int initialCapacity)構造一個具備指定容量的空數組列表

 5.4 對象包裝器和自動打包

a)全部基本數據類型都有着與之對應的預約義類,它們被稱爲對象包裝器。

b)對象包裝器類是不可變的,即一旦構造了包裝器,就不容許更改包裝在其中的值。且對象包裝器類仍是final,所以不能定義它們的子類。

c)使用對象包裝器的好處:

        - 基本類型轉化爲對象

        - 定義了一些有用的基本方法(static方法)

d)在JavaSE5.0中,能夠自動的將基本數據類型轉換爲包裝器類的對象,將這種變換稱爲自動打包

e)相反的,當對一個包裝器類的對象進行賦值或算法運算時,將會自動地拆包。

f)打包和拆包是編譯器承認的。

5.5 參數變量可變的方法

a)在Java SE 5.0之前的版本中,每一個Java方法都有固定數量的參數。然而,如今的版本提供了能夠用可變的參數數量調用的方法(稱爲「可變參 」方法)。

b   用戶本身能夠定義可變參數的方法,並將參數指定爲任意類型,甚至是基本類型。

5.6 枚舉類

a)聲明枚舉類

 public  enum  Grade{A,B,C,D,E};

它包括一個關鍵字enum,一個新枚舉類型的名字 Grade以及爲Grade定義的一組值,這裏的值既非整型,亦非字符型。

b)枚舉類是一個類,它的隱含超類是java.lang.Enum。

c)枚舉值並非整數或其它類型,是被聲明的枚舉類的自身實例,例如A是Grade的一個實例

d)枚舉類不能有public修飾的構造函數,構造函數都是隱含private,編譯器自動處理。

e)枚舉值隱含都是由public、static、final修飾的,無須本身添加這些修飾符。

f)在比較兩個枚舉類型的值時i,永遠不須要調用equals方法,直接使用「==」進行相等比較。

5.7 繼承設計的技巧

a) 將公共操做和域放在超類。

b)不要使用受保護的域。

c)使用繼承實現「is-a」關係。

d)除非全部繼承的方法都有意義,不然就不要使用繼承。

e)在覆蓋方法時,不要改變預期的行爲。

f)使用多態,而非類型信息。

 第二部分 :實驗部分

一、實驗目的與要求

(1) 理解繼承的定義;

(2) 掌握子類的定義要求

(3) 掌握多態性的概念及用法;

(4) 掌握抽象類的定義及用途。

二、實驗內容和步驟

實驗1: 導入第5章示例程序,測試並進行代碼註釋。

測試程序1:

Ÿ   在elipse IDE中編輯、調試、運行程序5-1 —5-3(教材152頁-153頁) ;

Ÿ   1)掌握子類的定義及用法;

Ÿ   2)結合程序運行結果,理解並總結OO風格程序構造特色,理解Employee和Manager類的關係子類的用途,並在代碼中添加註釋;

Ÿ   3)刪除程序中Manager類、ManagerTest類,背錄刪除類的程序代碼,在代碼錄入中理解父類與子類的關係和使用特色。

程序5-1代碼以下:

package inheritance;

/**
 * This program demonstrates inheritance.
 * @version 1.21 2004-02-21
 * @author Cay Horstmann
 */
public class ManagerTest
{
   public static void main(String[] args)
   {
      // 構建管理者對象
      var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
      boss.setBonus(5000);

      var staff = new Employee[3];

      // 用管理者和僱員對象填充工做人員數組

      staff[0] = boss;
      staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
      staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);

      // 打印全部員工對象的信息
      for (Employee e : staff)
         System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
   }
}

運行結果以下:

 

 程序5-2Employee類代碼以下:

package inheritance;

import java.time.*;

public class Employee
{
    //構建三個私有對象
   private String name;
   private double salary;
   private LocalDate hireDay;

   public Employee(String name, double salary, int year, int month, int day)//構造器
   {
      this.name = name;
      this.salary = salary;
      hireDay = LocalDate.of(year, month, day);
   }

   public String getName()
   {
      return name;
   }

   public double getSalary()
   {
      return salary;
   }

   public LocalDate getHireDay()
   {
      return hireDay;
   }

   public void raiseSalary(double byPercent)
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }
}

程序5-3代碼以下:

package inheritance;

public class Manager extends Employee    //關鍵字extends表示繼承。代表正在構造一個新類派生於一個已經存在的類。
{
   private double bonus;     //Manager的私有域

   /**
    * @param name the employee's name
    * @param salary the salary
    * @param year the hire year
    * @param month the hire month
    * @param day the hire day
    */
   public Manager(String name, double salary, int year, int month, int day)
   {
      //super調用構造器的語句必須是子類構造器的第一條語句。
      super(name, salary, year, month, day);  //調用超類中含有name,salary,year,month,day參數的構造器。
      bonus = 0;
   }

   public double getSalary()
   { 
     //子類要想訪問要想訪問超類中的方法須要使用特定的關鍵字super
      double baseSalary = super.getSalary();
      return baseSalary + bonus;
   }

   public void setBonus(double b)
   {
      bonus = b;
   }
}

背錄刪除類override後的程序代碼以下:

package inheritance;

public class Manager extends Employee    //關鍵字extends表示繼承。代表正在構造一個新類派生於一個已經存在的類。
{

    private int bouns;

    public Manager(String name, double salary, int year, int month, int day) {
        super(name, salary, year, month, day);
        // TODO Auto-generated constructor stub
        bouns=0;
    }

    public int getBouns() {
        return bouns;
    }

    public void setBouns(int bouns) {
        this.bouns = bouns;
    }

    @Override
    public double getSalary() {
        // TODO Auto-generated method stub
        return super.getSalary();
    }
 
}

在構造類時,能夠在聲明的時候用父類,在具體的構造器時,用子類。新生成的對象是父類類型的對象,也就是說這個對象中目前只有父類的屬性和方法,可是,若是子類重寫,就應該用子類重寫的方法。

 對於面向對象程序構造特色,我以爲大致以下:

a)封裝:就是把一部分或所有屬性和部分功能(函數)對外界屏蔽。

  封裝有兩方面的含義:一是將有關數據和操做代碼封裝在對象當中,造成一個基本單位,各個對象之間相對獨立、互不干擾。

                                      二是將對象中某些屬性和操做私有化,以達到數據和操做信息隱蔽,有利於數據安全,防止無關人員修改。

b)繼承:是爲了代碼複用,把重複使用的代碼精簡掉。至於如何精簡,當一個類中已經有了相應的屬性和操做的代碼,而另外一個類當中也須要寫重複的代碼,那麼就用繼承方法,把前面的類當成父類,後面的類當成子類,子類繼承父類。用一個關鍵字extends就完成了代碼的複用。

c)多態:沒有繼承就沒有多態,繼承是多態的前提。雖然繼承自同一父類,可是相應的操做卻各不相同,這叫多態。由繼承而產生的不一樣的派生類,其對象對同一消息會作出不一樣的響應。

實驗1:測試程序2

Ÿ   a)編輯、編譯、調試運行教材PersonTest程序(教材163頁-165頁);

Ÿ   b)掌握超類的定義及其使用要求;

Ÿ   c)掌握利用超類擴展子類的要求;

Ÿ   d)在程序中相關代碼處添加新知識的註釋;

Ÿ  e) 刪除程序中Person類、PersonTest類,背錄刪除類的程序代碼,在代碼錄入中理解抽象類與子類的關係和使用特色。

程序5-2代碼以下:

package abstractClasses;

/**
 * This program demonstrates abstract classes.
 * @version 1.01 2004-02-21
 * @author Cay Horstmann
 */
public class PersonTest
{
   public static void main(String[] args)
   {
      var people = new Person[2]; // 抽象類的聲明,但不能將抽象類實例化 ,實例化的是Person類的子類

      // fill the people array with Student and Employee objects
      people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
      people[1] = new Student("Maria Morris", "computer science");

      // print out names and descriptions of all Person objects
      for (Person p : people)   //for each循環
         System.out.println(p.getName() + ", " + p.getDescription());
   }
}

Person類代碼以下:

package abstractClasses;

public abstract class Person
{       //包含一個或多個抽象方法的類被稱爲抽象類,由abstract關鍵字修飾
        //通用的做用域和方法也放到了這裏,抽象類不能被實例化,但能夠被聲明
   public abstract String getDescription();
   private String name;

   public Person(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return name;
   }
}

Student類的代碼以下:

package abstractClasses;

public class Student extends Person   //子類Student繼承超類Person
{
   private String major;      //Student類的私有域

   /**
    * @param name the student's name
    * @param major the student's major
    */
   public Student(String name, String major)    //子類構造器
   {
      // pass name to superclass constructor
      super(name);
      this.major = major;
   }

   public String getDescription()
   {
      return "a student majoring in " + major;
   }
}

程序5-6Employee類代碼以下:

package abstractClasses;

import java.time.*;

public class Employee extends Person    //子類Employee繼承父類Person
{
   private double salary;     //Employee的私有域
   private LocalDate hireDay;

   public Employee(String name, double salary, int year, int month, int day) //子類構造器
   {
      super(name);    //調用父類構造器
      this.salary = salary;
      hireDay = LocalDate.of(year, month, day);
   }

   public double getSalary()
   {
      return salary;
   }

   public LocalDate getHireDay()
   {
      return hireDay;
   }

   public String getDescription()
   {
      return String.format("an employee with a salary of $%.2f", salary);
   }

   public void raiseSalary(double byPercent)
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }
}

 

程序結果運行以下:

 刪除後override的代碼以下:

package abstractClasses;

import java.time.*;

public class Employee extends Person
{
   public Employee(String name) {
        super(name);
        // TODO Auto-generated constructor stub
    }
private double salary;
   private LocalDate hireDay;
@Override
public String getDescription() {
    // TODO Auto-generated method stub
    return String.format("an employee with a salary of $%.2f", salary);
}
@Override
protected Object clone() throws CloneNotSupportedException {
    // TODO Auto-generated method stub
    return super.clone();
}
@Override
public boolean equals(Object obj) {
    // TODO Auto-generated method stub
    return super.equals(obj);
}
@Override
protected void finalize() throws Throwable {
    // TODO Auto-generated method stub
    super.finalize();
}
@Override
public int hashCode() {
    // TODO Auto-generated method stub
    return super.hashCode();
}
@Override
public String toString() {
    // TODO Auto-generated method stub
    return super.toString();
}
public double getSalary() {
    return salary;
}
public void setSalary(double salary) {
    this.salary = salary;
}
public LocalDate getHireDay() {
    return hireDay;
}
public void setHireDay(LocalDate hireDay) {
    this.hireDay = hireDay;
}
}

抽象類與子類的關係及使用特色:

a)abstract方法只能聲明,不能實現

b)包含一個或多個抽象方法的類自己必須被聲明爲抽象類

c)抽象方法充當着佔位的角色,它們的具體實如今子類中

d)抽象類不能被實例化,即不能建立對象,只能產生子類。

實驗1 測試程序3:

Ÿ   a)編輯、編譯、調試運行教材程序5-八、5-九、5-10,結合程序運行結果理解程序(教材174頁-177頁);

Ÿ   b)掌握Object類的定義及用法;

Ÿ   c)在程序中相關代碼處添加新知識的註釋。

 程序5-8代碼以下:

package equals;

/**
 * This program demonstrates the equals method.
 * @version 1.12 2012-01-26
 * @author Cay Horstmann
 */
public class EqualsTest
{
   public static void main(String[] args)
   {
      var alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);  //建立對象,並初始化
      var alice2 = alice1;
      var alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
      var bob = new Employee("Bob Brandson", 50000, 1989, 10, 1);

      System.out.println("alice1 == alice2: " + (alice1 == alice2));

      System.out.println("alice1 == alice3: " + (alice1 == alice3));

      System.out.println("alice1.equals(alice3): " + alice1.equals(alice3));

      System.out.println("alice1.equals(bob): " + alice1.equals(bob));

      System.out.println("bob.toString(): " + bob);

      var carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
      var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
      boss.setBonus(5000);
      System.out.println("boss.toString(): " + boss);
      System.out.println("carl.equals(boss): " + carl.equals(boss));
      System.out.println("alice1.hashCode(): " + alice1.hashCode());
      System.out.println("alice3.hashCode(): " + alice3.hashCode());
      System.out.println("bob.hashCode(): " + bob.hashCode());
      System.out.println("carl.hashCode(): " + carl.hashCode());
   }
}

Employee類代碼以下:

package equals;

import java.time.*;
import java.util.Objects;

public class Employee
{
   private String name;    //實例域定義
   private double salary;
   private LocalDate hireDay;

   public Employee(String name, double salary, int year, int month, int day)//構造器定義
   {
      this.name = name;
      this.salary = salary;
      hireDay = LocalDate.of(year, month, day);
   }

   public String getName()
   {
      return name;
   }

   public double getSalary()
   {
      return salary;
   }

   public LocalDate getHireDay()
   {
      return hireDay;
   }

   public void raiseSalary(double byPercent)
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }

   public boolean equals(Object otherObject)
   {
      // 快速檢查對象是否相同
      //這裏得到一個對象參數,第一個if語句判斷兩個引用是不是同一個,若是是那麼這兩個對象確定相等
      if (this == otherObject) return true;

      // 若是顯式參數爲空,則必須返回false
      if (otherObject == null) return false;

      //  getClass()方法是獲得對象的類,若是兩個對象的類不同,那麼就不相等
      if (getClass() != otherObject.getClass()) return false;

      //如今咱們知道另外一個對象是非空僱員
      //在以上判斷完成,再將獲得的參數對象強制轉換爲該對象,考慮到父類引用子類的對象的出現,而後再判斷對象的屬性是否相同

      var other = (Employee) otherObject;

      //測試字段是否具備相同的值
      return Objects.equals(name, other.name) 
         && salary == other.salary && Objects.equals(hireDay, other.hireDay);
   }

   public int hashCode()
// 哈希散列
   {
      return Objects.hash(name, salary, hireDay); 
   }
// toString()方法,可自動生成
   public String toString()
   
   {
      return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" 
         + hireDay + "]";
   }
}

Manager類代碼以下:

package equals;

public class Manager extends Employee
{
   private double bonus;

   public Manager(String name, double salary, int year, int month, int day)
   {
      super(name, salary, year, month, day);
      bonus = 0;
   }

   public double getSalary()
   {
      double baseSalary = super.getSalary();
      return baseSalary + bonus;
   }

   public void setBonus(double bonus)
   {
      this.bonus = bonus;
   }

   public boolean equals(Object otherObject)
   {
      if (!super.equals(otherObject)) return false;
      var other = (Manager) otherObject;
      //檢查這個和其餘屬於同一個類
      return bonus == other.bonus;
   }

   public int hashCode()
   {
      return java.util.Objects.hash(super.hashCode(), bonus);
   }

   public String toString()
   {
      return super.toString() + "[bonus=" + bonus + "]";
   }
}

程序運行結果以下:

 

實驗2編程練習

Ÿ定義抽象類Shape:

屬性:不可變常量double PI,值爲3.14;

方法:public double getPerimeter();public double getArea())。

Ÿ   讓Rectangle與Circle繼承自Shape類。

Ÿ   編寫double sumAllArea方法輸出形狀數組中的面積和和double sumAllPerimeter方法輸出形狀數組中的周長和。

Ÿ   main方法中

1)輸入整型值n,而後創建n個不一樣的形狀。若是輸入rect,則再輸入長和寬。若是輸入cir,則再輸入半徑。
2) 而後輸出全部的形狀的周長之和,面積之和。並將全部的形狀信息以樣例的格式輸出。
3) 最後輸出每一個形狀的類型與父類型,使用相似shape.getClass()(得到類型),shape.getClass().getSuperclass()(得到父類型);

思考sumAllArea和sumAllPerimeter方法放在哪一個類中更合適?

輸入樣例爲:

3
rect
1 1
rect
2 2
cir
1

輸出樣例爲:

18.28
8.14
[Rectangle [width=1, length=1], Rectangle [width=2, length=2], Circle [radius=1]]
class Rectangle,class Shape
class Rectangle,class Shape
class Circle,class Shape

程序代碼以下:

ShapeTest代碼以下:

import java.util.Scanner;

public class ShapeTest {



        public static void main(String[] args) 
        {
            Scanner in = new Scanner(System.in);
            String rect = "rect";
            String cir = "cir";
            System.out.print("請輸入形狀個數:");
            int n = in.nextInt();
            Shape[] figure= new Shape[n];
            for(int i=0;i<n;i++)
            {
                System.out.println("請輸入形狀類型 (rect or cir):");
                String input = in.next();
                if(input.equals(rect))
                {
                    double length = in.nextDouble();
                    double width = in.nextDouble();
                    System.out.println("Rectangle["+"length:"+length+"  width:"+width+"]");
                    figure[i] = new Rectangle(width,length);
                }
                if(input.equals(cir)) 
                {
                    double radius = in.nextDouble();
                    System.out.println("Circle["+"radius:"+radius+"]");
                   figure[i] = new Circle(radius);
                }
        
            }
      
            System.out.println(sumAllPerimeter(figure));
            System.out.println(sumAllArea(figure));
            for(Shape s:figure) 
            {

                System.out.println(s.getClass()+",  "+s.getClass().getSuperclass());
            }
        }

        public static double sumAllArea(Shape figure[])
        {
             double sum = 0;
             for(int i = 0;i<figure.length;i++)
                 sum+= figure[i].getArea();
             return sum;
        }
        
        public static double sumAllPerimeter(Shape figure[])
        {
             double sum = 0;
             for(int i = 0;i<figure.length;i++)
                 sum+= figure[i].getPerimeter();
             return sum;
        }
        
    }

Shape類代碼以下:

public abstract class Shape {

        double PI = 3.14;
        public abstract double  getPerimeter();
        public abstract double  getArea();
    }

Rectangle類代碼以下:

public class Rectangle extends Shape
{
    private double width;
    private double length;
    public Rectangle(double w,double l)
    {
        this.width = w;
        this.length = l;
    }
    public double getPerimeter()
    {
        double Perimeter = (width+length)*2;
        return Perimeter;
    }
    public double getArea()
    {
        double Area = width*length;
        return Area;
    }

      public String toString()
      {
          return getClass().getName() + "[ width=" +  width + "]"+ "[length=" + length + "]";
      }
}

Circle類代碼以下:

public class Circle extends Shape
{

    private double radius;
    public Circle(double r)
    {
        radius = r;
    }
    public double getPerimeter()
    {
        double Perimeter = 2*PI*radius;
        return Perimeter;
    }
    public double getArea()
    {
        double Area = PI*radius*radius;
        return Area;
    }
    public String toString()
      {
          return  getClass().getName() + "[radius=" + radius + "]";
   }  
}

程序運行結果以下:

 

 第三部分:實驗總結

在老師上課梳理脈絡的前提下,我經過這周的自主學習,掌握了關於繼承的相關知識和實驗技能。在國慶這個假期我將之前所學知識和第5、六週所學結合起來,將本身的知識加以鞏固,關於課本的實驗並無太大的的問題,可是在完成自主編寫程序這一塊,寫程序的時候思惟邏輯並非很清晰,程序代碼有點凌亂,不過勉強能完成了題目要求的內容。經過這一個多月Java的學習,本身對程序語言又有了更深的理解,可以理解程序,而且看得懂程序,並且經過老師的這種學習模式更好的培養了我最欠缺的自主學習能力,這對於咱們來講確實是一個歷練的過程

相關文章
相關標籤/搜索