在Java中,面向對象編程有三大特性:封裝、繼承、多態。 先來說說封裝。 封裝,顧名思義,就是通過抽象數據類型(即ADT,一種為了將數據類型和可能在該數據類型上進行操作而定義的)將數據以及基於數據的操作封裝在一起,使之成為獨立的“實體”。 首先先來看一個Person類: 對於封裝的思想,我們需要盡可 ...
在Java中,面向對象編程有三大特性:封裝、繼承、多態。
先來說說封裝。
封裝,顧名思義,就是通過抽象數據類型(即ADT,一種為了將數據類型和可能在該數據類型上進行操作而定義的)將數據以及基於數據的操作封裝在一起,使之成為獨立的“實體”。
首先先來看一個Person類:
1 public class Person implements Comparable<Person> { 2 private String firstname; 3 private String lastname; 4 5 public String getFirstname() { 6 return firstname; 7 } 8 9 public void setFirstname(String firstname) { 10 this.firstname = firstname; 11 } 12 13 public String getLastname() { 14 return lastname; 15 } 16 17 public void setLastname(String lastname) { 18 this.lastname = lastname; 19 } 20 21 @Override 22 public int hashCode() { 23 int hash = 7; 24 hash = 83 * hash + Objects.hashCode(this.firstname); 25 hash = 83 * hash + Objects.hashCode(this.lastname); 26 return hash; 27 } 28 29 @Override 30 public boolean equals(Object obj) { 31 //檢查參數是否是這個對象的引用 32 if (this == obj) { 33 return true; 34 } 35 //檢查參數是否是正確的類型 36 if (!(obj instanceof chapter5_reflect.Person)) { 37 return false; 38 } 39 //將參數轉換成正確的類型 40 Person person = (Person) obj; 41 //對實體域進行匹配 42 return Objects.equals(this.firstname, person.firstname) 43 && Objects.equals(this.lastname, person.lastname); 44 } 45 46 @Override 47 public int compareTo(Person o) { 48 if (this == o) { 49 return 0; 50 } 51 if (null == o) { 52 return 1; 53 } 54 //先判斷姓氏是否相同 55 int comparison = this.firstname.compareTo(o.firstname); 56 if (comparison != 0) { 57 return comparison; 58 } 59 //姓氏相同則比較名 60 comparison = this.lastname.compareTo(o.lastname); 61 return comparison; 62 } 63 }
對於封裝的思想,我們需要儘可能的隱藏內部細節,只保留一些對外操作。
例如在Person類中,我簡單的定義了兩個成員變數firstname和lastname,在setter方法里我們可以設置姓和名的一些格式,如首字母大寫,其餘小寫來進行“格式化”,對外開放getter來獲取變數的值。
現來總結一下封裝的優點:
1.能夠更好的把控成員變數,甚至是管理類的內部結構;
2.良好的封裝能夠減少耦合,使得代碼更加健壯;
3.外部程式通過對外介面即可進行訪問,無需關註實現細節。
再談繼承。
繼承描述的是is-a的關係,它是復用代碼的一種方式,思想就在於定義和實現了一個類的時候,可以在一個已存在的類上進行擴展,把已存在的類的內容作為自己的內容,同時可以加入新的內容或者修改原來的方法來適應不同的需求。
下麵來看兩個例子:
1 public class Person { 2 3 private String name; 4 private String sex; 5 private int age; 6 7 Person(String name, String sex, int age) { 8 this.name = name; 9 this.sex = sex; 10 this.age = age; 11 } 12 13 //省略setter和getter方法... 14 37 }
1 public class Yang extends Person { 2 3 Yang(String name, String sex, int age) { 4 super(name, sex, age); 5 } 6 7 public String getName() { 8 return super.getName() + " is " + "genius"; 9 } 10 11 }
1 public static void main(String... argc) { 2 // Yang yang = new Yang("yang", "male", 23); 3 Person person = new Yang("yang", "male", 23); 4 out.print(person.getName()); 5 }
輸出: yang is genius
註意,如果父類沒有預設的構造器,子類構造函數中需要指定父類的構造器,否則編譯將會失敗!
從上面的代碼中不得不引出關於繼承的三大重要的東西,即構造器,protected關鍵字以及向上轉型。
我們知道,構造器是不能被繼承的,只許被調用!需要註意的是,子類是依賴於父類的(這也說明瞭一個弊端,即繼承是一種強耦合關係),子類擁有父類的非private屬性和方法(弊端二:破壞了封裝),所以父類應先於子類被創建。
所以當父類沒有預設構造器時,子類需要指定一個父類的構造器,並且寫於子類構造器的第一行!當然父類有預設構造器,子類就無需super了,Java會自動調用。
再說protected關鍵字。插一句,只有合理使用訪問修飾符,程式才能更好的為我們服務!!
對於子類,為了使用父類的方法,我們可以修改它的訪問修飾符為protected,而不是一股腦兒的寫上public!一勞永逸的做法可能會帶來更大的危害!
而對於類的成員變數,保持它的private!
最後是向上轉型了,它是一個重要的方面。從上面的的代碼中,我寫上了Person person = new Yang("yang", "male", 23); 這樣結果是將Yang向上轉型為Person,帶來的影響可能就是屬性和方法的丟失,但是它將是安全的。
同時它最大的作用是.....子類能夠提供父類更加強大的功能以適用於不同的場合,完成不同的操作。
不太清楚可以看看這兩個: List<String> arrayList = new ArrayList<>(); 和 List<String> linkedList = new LinkedList<>();
我們知道ArrayList是數組實現,查找更快;而LinkedList是鏈表實現,添加元素和刪除元素效率更好!
但是向上轉型有一個弊端,指向子類的父類引用因為向上轉型了,它將只擁有父類的屬性和方法,同時子類擁有而父類沒有的方法,是無法使用了的!
所以,繼承實現了軟體的可重用性和可拓展性。但是Java是單繼承的,並且繼承也有多個弊端(上面有提),其實還有一個弊端是父類一旦改變,子類可能也得進行改變!所以慎用吧。
最後一個特性是多態了。多態性就是不同的子類型可以對同一個請求做出不同的操作。同時多態性分為編譯時多態性和運行時多態性,對應著方法重載overload和方法重寫override!
對於方法重寫,存在在繼承中。它作為運行時多態性的表現,首先需要子類繼承和實現父類的方法,然後向上轉型,父類引用子類對象,對同一件事作出不同的響應。
方法重寫時,需要註意的是子類的訪問修飾符的訪問範圍不能小於父類,如父類的一個方法修飾符為protected,那麼子類繼承時,只能選用public或者protected。除了訪問修飾符,其餘完全相同於父類!
對於方法重載,出現在同一個類中,它是編譯時多態性的表現。定義為:同名的方法如果有不同的參數列表便視為重載。
最後有一道經典的題目作為結尾,我也不知道出自哪....Look and think!
1 public class A { 2 3 public String show(D obj) { 4 return ("Father and D"); 5 } 6 7 public String show(A obj) { 8 return ("Father and Father"); 9 } 10 } 11 12 class B extends A { 13 14 public String show(B obj) { 15 return ("Child and Child"); 16 } 17 18 public String show(A obj) { 19 return ("Child and Father"); 20 } 21 } 22 23 class C extends B { 24 } 25 26 class D extends B { 27 } 28 29 class Test { 30 public static void main(String[] args) { 31 A a1 = new A(); 32 A a2 = new B(); 33 B b = new B(); 34 C c = new C(); 35 D d = new D(); 36 37 System.out.println("1--" + a1.show(b)); 38 System.out.println("2--" + a1.show(c)); 39 System.out.println("3--" + a1.show(d)); 40 System.out.println("4--" + a2.show(b)); 41 System.out.println("5--" + a2.show(c)); 42 System.out.println("6--" + a2.show(d)); 43 System.out.println("7--" + b.show(b)); 44 System.out.println("8--" + b.show(c)); 45 System.out.println("9--" + b.show(d)); 46 } 47 }