Java複習筆記整理—面向對象

第一部分:基礎知識【C】

  1. Equals ==
  2. For each

第二部分:面向對象

  1. 封裝(encapsulation,有時也稱爲數據隱藏)

    1. 類:對一類事物抽象所得到的一個概念,“模板”,“藍圖”。

      //Java中,最簡單的類定義形式:
      class ClassName
      {
      	field1
        field2
        field3
          ...
        constructor1//構造器
        constructor2
        constructor3
          ...
        method1
        method2
        method3
          ....
      }
      
      //一個簡單的Employee類。在編寫薪金管理系統時可能會用到
      class Employee
      {
        //instance fields
        private String name;
        private double salary;
        private LocalDate hireDay;
        
        //constructor
        public Employee(String n, double s, int year, int month, int day)
        {
          name = n;
          salary = s;
          hireDay = LocalDate.of(year, month, day);
        }
        
        //a method
        public String getName()
        {
      		return name;
        }
        
        //more method
        ...
      }
      

      類與類之間的關係:

      • 依賴(“uses-a”)
        • 一個類的方法操作另一個類的對象,就認爲一個類依賴另一個類
      • 聚合(“has-a”)
        • 聚合關係意味着類A的對象包含類B的對象
      • 繼承(“is-a”)
        • 如果類A擴展類B,類A不但包含從類B繼承的方法,還會擁有一些額外的功能,具體繼承內容在👇
    2. 對象:一個具體的事物(用關鍵字new出來的,女朋友除外)

      • 三個主要特徵:
        • 對象的行爲(behavior):可以對對象施加哪些操作,或可以對對象施加哪些方法?
        • 對象的狀態(state):當施加那些方法時,對象如何響應?
        • 對象標識(identity):如何辨別具有相同行爲與狀態的不同對象?
    3. 構造函數(構造器):

      1. 沒有返回值

      2. 方法名與類名相同

      3. 可以有多個構造函數

      4. 當沒有顯式寫出構造函數時,默認是無返回值、無參的構造函數,其狀態(成員變量)會被設置成適當的默認值。

        //以下是Employee類的無參數構造函數:
        public Employee()
        {
        	//name = "";  //所有將對象(字符串類型本事就是一個String類對象)變量設置爲null
          //salary = 0; //所有的數值類型設置爲0,如果存在boolean類型,則設置爲false
          //hireDay = LocalData.now();
        }
        
        
      5. 如果自己顯式(即自己手動定義一個構造函數),則編譯器將不再生成默認無參的構造函數

      6. 生成一個類對象時,能且只能調用其中一個構造函數

      7. 構造器總是伴隨着new操作一起調用

      8. 不要在構造器中定義與類的成員變量(實例域)重名的局部變量

         {
           String name = n;  //ERROR
           double salary = s; //ERROR
           ....
            /* 這個構造函數聲明瞭局部變量name和salary。這些變量只能在構造器的內部訪問。
            這些變量屏蔽了同名的成員變量(實例域)。
            */
         }
         
      
      
    4. static:

      1. 屬於類本身的成員變量private static int nextId = 1;或者方法public static void getNextId();

        class Employee
        {  //如果將域定義爲static,每個類只有一個這樣的域(成員變量)。而每一個對象對於所有的實例域(非static成員變量)卻都有自己的一份拷貝,此處假定需要給每一個僱員賦予唯一的標識碼。這裏給Employee類添加一個實例域id和一個靜態域nextId;
         
          ...
          private static int nextId = 1;  //靜態變量
          private int id;
          ...
          public static void getNextId()   //靜態方法,一般很少使用
        }
        --------------------------
        public class Math
        {
          ...
          public static final double PI = 3.14159265358979323846;  //靜態常量,帶關鍵字final
          ...
        }
        
      2. 靜態成員不可以訪問非靜態的成員,因爲靜態成員屬於類本身,不屬於任何獨立的對象。可以在不具有實例(對象)的條件下直接通過類本身調用Employee.getNextId();(此處假設有一個Employee的類),但是非靜態成員可以訪問靜態成員

      3. 通過類名也不一定能夠訪問static修飾的靜態成員,通過類名只能訪問此類中非私有的靜態成員。

      4. 可以使用對象調用靜態方法。不過這種方式很容易造成混淆,其原因是靜態方法的反饋結果與某個對象毫無關係。所以建議使用類名,而不是對象來調用靜態方法。

        在下面兩種情況下使用靜態方法:

        • 一個方法不需要訪問對象狀態,其所需參數都是通過顯式參數提供(例如:Math.pow)。
        • 一個方法只需要訪問類的靜態域(例如:Employee.getNextId。)
    5. this指針:具體this函數詳解👉 JAVA入門—this指針,懂這個就夠用了!.

      1. 引用隱式參數。
      2. 調用該類的其他構造器
      3. 非靜態成員方法默認都含有一個this指針,代表正在調用本方法的對象
    6. 函數重載:

      1. 方法同名,形式參數必須不同
      2. 返回值不能夠作爲是否構成函數重載的依據。
    7. final:

      1. 修飾類:不允許擴展的類被稱爲final類。希望阻止利用此類來定義子類。如果將一個類聲明爲final,只有其中的方法自動地稱爲final,而不包括成員變量(域)。
      2. 修飾方法:該方法可以被繼承,但是不能重寫
      3. 修飾成員變量:該成員變量在構建對象時必須被初始化,也就是說,在每個構造器執行之後,這個變量的值被設置。並且後面的操作中,不能夠再對其進行修改。
    8. 封裝部分具體實例:

      
      import java.time.LocalDate;
      class Employee
      {
          private String name;
          private double salary;
          private LocalDate hireDay;
          public Employee()
          {
              //如果第二個構造器沒有,則編譯器自動生成此無參構造函數,若第二個👇構造函數顯式定義,則編譯器不再自動生成此無參數構造函數
          }
          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;
          }
      }
      class Manager extends Employee
      {
          private double bonus;
          /**
           * 物理結構已經繼承來自Employee類的三個成員變量,只是不可以顯式調用
           */
      
          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 b)
          {
              bonus = b;
          }
      }
      
      
      public class ManagerTest
      {
          public static void main(String[] args)
          {
              Manager boss = new Manager("Carl Creacker",80000,1987,12,15);
              boss.setBonus(5000);
              Employee[] staff = new Employee[3];
              staff[0] = boss;
              staff[1] = new Employee("Harry Hacker",50000, 1989, 10, 1);
              staff[2] = new Employee("Harry Li",40000, 1990, 3, 15);
      
              for(Employee e : staff)
              {
                  System.out.println("name is "+e.getName()+",salary="+e.getSalary()+",hireDate"+e.getHireDay());
              }
          }
      }
      ```
      
      
      
      
  2. 繼承(inheritance):"is-a"關係

    1. 基於已存在的類構造一個新類。繼承已存在的類就是複用(繼承)這些類的方法和成員變量(私有成員變量物理上被繼承,但是實際子類無法使用,故通常認爲,私有成員沒有被繼承)。在此基礎上,還可以添加一些新的方法和成員變量,以滿足新的需求。

      public class Manager extends Employee
      {
        //添加特有的方法和域名(成員變量)
      }
      
    2. extends表明正在構造的新類派生於一個已存在的類。已存在的類稱爲父類(parent class);新類稱爲子類(subclass)。

    3. 覆蓋方法(方法的重寫) 具體方法重寫詳解👉 JAVA-入門 函數重寫.

      1. 如果父類中的有些方法對子類並不一定適用。爲此,需要提供一個新的方法來覆蓋(override)父類中的這個方法。
      2. 重寫方法必須和被重寫方法具有相同的方法名稱、參數列表、返回值且***訪問權限***不能低於父類中被覆蓋的方法(相同方法名)的訪問權限。特別是,如果父類方法是public,子類方法一定要聲明爲public。經常會發生這類錯誤:在聲明子類方法的時候,遺漏了public修飾符。此時,編譯器會將它解釋爲試圖提高更嚴格的訪問權限。
  3. 多態 具體多態詳解👉 JAVA-入門 多態.

    1. 有一個用來判斷是否應該設計爲繼承關係的簡單規則,就是"is-a"規則,它表明子類的每個對象也是父類的對象。"is-a"規則的另一種表述方法是置換法則。它表明程序中出現父類對象的任何地方都可以用子類對象置換。即可以將一個子類的對象賦給父類變量對象

      //此處假設Manager是Employee的子類
      Employee e;
      e = new Employee(....);  //Employee object expected
      e.getName();   //此處調用的是Employee類中的getName()方法
      e = new Manager(...); //OK, Manager can be used well. 
      e.getName();  //此處調用的是Employee類的子類Manager中的getName()方法
      //4、6行同一代碼執行不同的操作,就是多態的表現
      //在Java程序設計語言中,對象變量是多態的。一個Employee變量既可以引用一個Employee類對象,也可以引用一個Employee類的任何一個子類的對象(例如,Manager、Secreatary等)。
      
    2. 同一個代碼可以隨着上下文的不同而執行不同的操作,俗塵多態。

    3. 注意事項:

      1. 如果父類的引用指向子類的對象,則通過父類的引用只能訪問子類從父類繼承過來的成員,即此時不能夠訪問子類特有的成員
      2. 如果想通過父類的引用去訪問子類的特有方法,必須滿足兩個條件:
        1. 父類引用指向子類的對象
        2. 父類被強制轉化爲子類類型的引用
  4. 相關知識:

    1. 抽象類:

      1. 包含一個或多個抽象方法的類本身必須被聲明爲抽象類,

        /**抽象類還可以包含具體數據和具體方法*/
        public abstract class Person
        {
          private String name;
          ....
          public abstract String getDescription();
          ...
            
          public String getName()
          {
            return name;
          }
        }
        

        如果自下而上在類的繼承層次結構中上移,位於上層的類更具有通用性,甚至更加抽象。從某種角度看,祖先類更加通用。我們只將它作爲派生其他類的基類,而不作爲想使用特定的實例類。

      2. 抽象方法充當着佔位的角色,它們的具體實現在子類中。擴展抽象類可以有兩種選擇。一種是在抽象類中定義部分抽象方法或不定義抽象類方法,這樣就必須將子類也標記爲抽象類;另外一種是定義全部的抽象方法,這樣子類就不是抽象的了。

      3. 類即使不含有抽象方法,也可以將類聲明爲抽象類。

        1. 因爲抽象類不能被實例化。也就是說,如果將一個類聲明爲abstract,就不能創建這個類的對象。

          new Person("pan ye chen");  //Error,但可以創建一個已經全部實現父類中抽象方法的具體子類的對象。
          
        2. 需要注意的是,可以定義一個抽象類的對象變量,但是它只能引用非抽象子類的對象。例如

          Person p = new Student("pan ye chen", "nanjinguniversity");
          //這裏的p是一個抽象類Person的變量,Person引用了一個非抽象子類Student的實例。
          
        3. 下面定義一個擴展抽象類Person的具體子類Student

          class Student extends Person
          {
              private String major;
          
              public Student(String name, String major)
              {
                  super(name);
                  this.major = major;
              }
              public String getDescription()
              {
                  return "a student majoring in" + major;
              }
          }
          
          
  5. 具體程序示範:

          /**
           * Time : 2020-06-07
           * 
           */
          import java.time.LocalDate;
          
          abstract class Person
          {
              public abstract String getDescription();  //此處不能省略,否則變量p就不能調用getDescription方法了。
              //編譯器只允許調用在類中聲明的方法。
              private String name;
          
              public Person(String name)
              {
                  this.name = name;
              }
              public String getName()
              {
                  return name;
              }
          }
          
          class Employee extends Person
          {
              private double salary;
              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 od $%.2f",salary);
              }
              public void raiseSalary(double byPercent)
              {
                  double raise = salary * byPercent/100;
                  salary += raise;
              }
          }
          
          class Student extends Person
          {
              private String major;
          
              public Student(String name, String major)
              {
                  super(name);
                  this.major = major;
              }
          
              public String getDescription()
              {
                  return "a student majoring in" + major;
              }
          }
          public class PersonTest
          {
              public static void main(String[] args)
              {
                  Person[] people = new Person[2];
          //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 descriptios of all Person objects
                  for(Person p : people)
                  {
                      System.out.println(p.getName()+","+p.getDescription());
                  }
              }
          }
          
    

    是否可以省略Person父類中的抽象方法,而僅僅在EmployeeStudent子類中定義getDescription()方法呢?答案是否定的,如果這樣的話,就不能通過變量p調用getDescription()方法了。編譯器只允許調用在類中聲明的方法。

    1. 接口:

      1. 在Java程序設計語言中,接口不是類,而是對類的一組需求描述,這些類要蹲從接口描述的統一格式進行定義。

      2. 接口的所有的成員變量(域)自動地屬於public static final,接口中所有的方法自動地屬於public abstract.因此,在接口中聲明成員變量和方法時,不必提供自動的關鍵字。

      3. 每個類只能夠擁有一個父類,但卻可以實現多個接口,從而側面填補了無法多重繼承的缺點。使用逗號將實現的各個接口分隔開。

      class Employee extends Person, implements Cloneable, Comparable //此處繼承extends關鍵字必須寫在實現implements關鍵字之前,否則ERROR

      1. 接口的特性:

        1. 接口不是類,尤其不能使用new運算符實例化一個接口:
          
       interface Comparable
          {
            ......
          }
          public Employee implements Comparable
          x = new Comparable(...);  //error
          
          //然而,儘管不能構造接口的對象,卻能聲明接口的變量;
          Comparable x; //OK
          
          //接口變量必須引用實現了接口的類對象:
          x = new Employee(....);  //OK, provided Employee implements Comparable
          
      
      1. 爲了讓類實現一個接口,通常需要下面兩個步驟:

        1. 將類聲明爲實現給定的接口
      2. 對接口中所有方法進行定義,因爲其方法都是public abstract類型,如果沒有全部實現,則此類爲抽象類

  6. 內容擴展:

    1. 歸納Java中用於可見性的4個訪問修飾符:
      1. 僅對本類可見—private
      2. 對所有類可見—public
      3. 對本包和所有子類可見—protected
      4. 對本包可見—默認(default), 不需要修飾符。
  7. 良好的編程習慣:

    1. 需要獲得或設置實例域的值。應該提供下面三項內容

      1. 一個私有的數據域
      2. 一個公有的域訪問器方法(public 數據類型 Getter)
      3. 一個公有的域更改器方法。(public void Setter)
    2. 類設計技巧:

      1. 識別(定義)類的簡單規則

        1. 在分析問題的過程中尋找名詞

        2. 方法對應着動詞

          舉例:

          在訂單處理系統中,有這樣一些名詞:

          • 商品(Item)
          • 訂單(Order)
          • 送貨地址(Shipping address)
          • 付款(Payment)
          • 賬戶(Account)

          這些名詞很可能成爲類Item、Order等。

          接下來,查看動詞:商品被添加到訂單中,訂單被髮送或取消,訂單貨款被支付。對於每一個動詞如:“添加”、“發送”、“取消"以及"支付”,都要標識出主要負責完成相應動作的對象。例如,當一個新的商品添加到訂單中,那個訂單對象就是被指定的對象,因爲它知道如何存儲商品以及對商品進行排序。也就是說,add應該是Order類的一個方法,而Item對象是一個參數。

          當然,所謂"找名詞和動詞"原則只是一種經驗,在創建類的時候,哪些名詞和動詞是重要的完全取決於個人的開發經驗。

      2. 一定要保證數據私有

        1. 這是最重要的;絕對不要破壞封裝性,當數據保持私有時,它們的表現形式的變化不會對類的使用者產生影響,即使出現bug也易於檢測。
      3. 一定要對數據初始化

        1. Java不對局部變量進行初始化,但是會對對象的實例域(成員變量)進行初始化。最好不要依賴與系統的默認值,而是應該顯式地初始化所有的數據,具體的初始化方式可以式提供默認值,也可以是在所有構造器中設置默認值。
      4. 不要在類中使用過多的基本類型

        1. 就是說,用其他的類代替多個相關的基本類型的使用。這樣會使類更加易於理解且修改。例如,用一個稱爲Address的新的類替換一個Customer類中以下的實例域:

          private String street;
          private String city;
          private String state;
          private int zip;
          

          這樣,可以很容易處理地址的變化,例如,需要增加對國際地址的處理。

      5. 不是所有的域(成員變量)都需要獨立的域訪問器和域更改器

      6. 將指責過多的類進行分解

      7. 優先使用不可變的類

      8. 類名和方法名要能夠體現他們的指責 Java多態筆記代碼.

        1. 與變量應該有一個能夠反映其含義的名字一樣,類也應該如此。命名類名的良好編程習慣是採用一個名次(Order)、前面有形容詞修飾的名詞(Rush Order)或動名詞(有"-ing"後綴)修飾的名詞(例如,BillingAddress)。對於方法來說,習慣是訪問器方法用小寫get開頭(getSalary),更改器方法用小寫的set開頭(setSalary).
        本篇介紹了Java這種面嚮對象語言的有關對象和類的基礎知識。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章