對象(四) 一、封裝 面向對象的三大特征:封裝、繼承、多態。 今天呢,我們來談談,其中兩個 封裝和繼承。而多態呢,如果沒有繼承也就沒有多態一說,這個我們後續繼續聊。 隱藏了實現細節,提供公共的訪問方式 提高了代碼復用性 提高安全性 隱藏了實現細節,提供公共的訪問方式 提高了代碼復用性 提高安全性 3 ...
對象(四)
一、封裝
面向對象的三大特征:封裝、繼承、多態。
今天呢,我們來談談,其中兩個 ---- 封裝和繼承。而多態呢,如果沒有繼承也就沒有多態一說,這個我們後續繼續聊。
- 什麼是封裝?
封裝說白了,就是把對象的屬性和行為給隱藏起來,僅僅對外提供公共的訪問方式。如:將電腦的具體實現細節隱藏起來,提供鍵盤和一系列介面供用戶使用,用戶不必關心具體內部的實現細節。 - 封裝的好處
- 隱藏了實現細節,提供公共的訪問方式
- 提高了代碼復用性
- 提高安全性
3. 封裝的原則
-
- 將不需要對外提供的內容都隱藏起來。
- 把屬性隱藏,提供公共方法對其訪問。
4 .說明
把成員變數私有(private),提供相應的set/get方法。private僅僅是封裝的一種體現形式,並不能說封裝就是私有。
二、繼承
繼承,利用繼承我們可以基於已經存在的類構造新的類,繼承已經存在的類就可以復用(繼承)這些類的方法和成員變數。同時,我們也可以添加一些自己的方法和屬性,使得子類更加強大。
如:人可以是父類,而學生是人,是人的子類。而學生又具有他自己的特性。因此,這裡存在著很明顯的is-a的關係,is-a關係是繼承的一個很明顯的特征。
繼承使用 extends 關鍵字。
- 繼承的代碼
1 class Test_Animal { 2 public static void main(String[] args) { 3 Cat c1 = new Cat("花",4); 4 System.out.println(c1.getColor() + "..." + c1.getLeg()); 5 c1.eat(); 6 c1.catchMouse(); 7 8 Dog d1 = new Dog("黑",2); 9 System.out.println(d1.getColor() + "..." + d1.getLeg()); 10 d1.eat(); 11 d1.lookHome(); 12 } 13 } 14 /* 15 * A:貓狗案例分析 16 * B:案例演示 17 * 貓狗案例繼承版 18 * 屬性:毛的顏色,腿的個數 19 * 行為:吃飯 20 * 貓特有行為:抓老鼠catchMouse 21 * 狗特有行為:看家lookHome 22 */ 23 24 class Animal { 25 private String color; //毛的顏色 26 private int leg; //腿的個數 27 28 public Animal(){} 29 30 public Animal(String color,int leg) { 31 this.color = color; 32 this.leg = leg; 33 } 34 35 public void setColor(String color) { //設置顏色 36 this.color = color; 37 } 38 39 public String getColor() { //獲取顏色 40 return color; 41 } 42 43 public void setLeg(int leg) { //設置腿的個數 44 this.leg = leg; 45 } 46 47 public int getLeg() { //獲取腿的個數 48 return leg; 49 } 50 51 public void eat() { //吃飯 52 System.out.println("吃飯"); 53 } 54 } 55 56 class Cat extends Animal { 57 public Cat() {} //空參構造 58 59 public Cat(String color,int leg) { //有參構造 60 super(color,leg); 61 } 62 63 public void eat() { //吃魚 64 System.out.println("貓吃魚"); 65 } 66 67 public void catchMouse() { //抓老鼠 68 System.out.println("抓老鼠"); 69 } 70 } 71 72 class Dog extends Animal { 73 public Dog() {} //空參構造 74 75 public Dog(String color,int leg) { //有參構造 76 super(color,leg); 77 } 78 79 public void eat() { //吃肉 80 System.out.println("狗吃肉"); 81 } 82 83 public void lookHome() { //看家 84 System.out.println("看家"); 85 } 86 }
View Code子類擁有父類的方法,子類更加靈活。抽取共性,動物都有顏色,還有腿的個數。貓和狗繼承動物類,這樣可以直接使用Animal的Color和leg屬性。雖說不能直接訪問父類的私有屬性,但是通過set/get方法依然可以進行賦值和取值。
- 繼承的好處
1)提高了代碼的復用性(不用多些多餘的代碼,做重覆性的工作)
2)提高代碼維護性
3)類和類之間產生了關係,這是多態的前提 - 繼承的弊端
類和類之間的耦合性增加了
開發的原則 高內聚,低耦合 耦合 類與類的關係 內聚 就是自己完成某件事情的能力
- java繼承特點
java不支持多繼承,因為會有安全性問題。
但是支持多層繼承,A extends B,C extends A ……(繼承體系) - 繼承的註意事項=
1)子類只能繼承父類所有非私有的成員(成員方法和成員變數)
2)子類不能繼承父類的構造方法,但是可以通過super關鍵字去訪問父類構造方法。
3)不要為了部分功能而去繼承。而是體現 is-a 的關係時使用繼承。
如果有兩個類A,B。只有他們符合A是B的一種,或者B是A的一種,就可以考慮使用繼承。 - 重寫
當子類出現了和父類同名的方法時,則子類的方法稱為重寫父類的方法。
當子類需要父類的功能,而功能主體子類有自己特有內容時,可以重寫父類中的方法。這樣,即沿襲了父類的功能,又定義了子類特有的內容。
1 public class Iphone17 { 2 //打電話 3 public void call(){ 4 System.out.println("打電話"); 5 } 6 7 } 8 9 class Iphone18 extends Iphone17 { 10 11 //重寫父類的方法,實現更加強大的功能,如果想要調用父類的call(),可以super.call() 12 @Override 13 public void call(){ 14 //super.call(); 15 System.out.println("通過意念打電話"); 16 } 17 }
View Code - 重載和重寫的區別(面試問過)
這個問題感覺很奇葩,很二的一個問題,但沒辦法面試問過,而且對於剛出來的學生,例如我。問到的概率還挺大的,其實他兩的關係和區別是,雷鋒和雷峰塔的關係,java和JavaScript的關係,印度和印度尼西亞的關係。
罷了,簡單說說他兩的區別吧。
(圖2)
三、代碼塊(局部代碼塊,構造代碼塊,靜態代碼塊,同步代碼塊)
1):代碼塊概述
* 在Java中,使用 { } 括起來的代碼被稱為代碼塊。
2):代碼塊分類
* 根據其位置和聲明的不同,可以分為局部代碼塊,構造代碼塊,靜態代碼塊,同步代碼塊(多線程講解)。
3):常見代碼塊的應用
* a:局部代碼塊
* 在方法中出現;限定變數生命周期,及早釋放,提高記憶體利用率
* b:構造代碼塊 (初始化塊)
* 在類中方法外出現;多個構造方法方法中相同的代碼存放到一起,每次調用構造都執行,並且在構造方法前執行
* c:靜態代碼塊
* 在類中方法外出現,並加上static修飾;用於給類進行初始化,在載入的時候就執行,並且只執行一次。
* 一般用於載入驅動
4) 例子:(面試可能會遇到,實際開發沒有任何意義)
① 代碼
1 public class Student { 2 3 static { 4 System.out.println("Student 靜態代碼塊"); 5 } 6 7 { 8 System.out.println("Student 構造代碼塊"); 9 } 10 11 public Student() { 12 System.out.println("Student 構造方法"); 13 } 14 } 15 16 class Demo_Student { 17 static { 18 System.out.println("Demo_Student靜態代碼塊"); 19 } 20 21 public static void main(String[] args) { 22 System.out.println("我是main方法"); 23 24 Student s1 = new Student(); 25 Student s2 = new Student(); 26 } 27 }View Code
執行結果:
首先,編譯後運行,位元組碼文件進入方法區(即Demo_Student.class),首先執行Demo_Student靜態代碼塊,因為靜態代碼塊隨著類的載入而載入。
接著,主方法進棧。執行“我是main方法”。
然後,當載入Student.class時候,執行Student 靜態代碼塊。
接著,再執行構造代碼塊,最後執行構造方法。
②代碼
1 class Fu { 2 static { 3 System.out.println("靜態代碼塊Fu"); 4 } 5 6 { 7 System.out.println("構造代碼塊Fu"); 8 } 9 10 public Fu() { 11 System.out.println("構造方法Fu"); 12 } 13 } 14 15 class Zi extends Fu { 16 static { 17 System.out.println("靜態代碼塊Zi"); 18 } 19 20 { 21 System.out.println("構造代碼塊Zi"); 22 } 23 24 public Zi() { 25 System.out.println("構造方法Zi"); 26 } 27 } 28 29 class Dmeo { 30 public static void main(String[] args){ 31 Zi z = new Zi(); //請執行結果。 32 } 33 }View Code
執行結果:
同理,首先,編譯後運行,位元組碼文件進入方法區,因為有繼承關係,在執行子類之前先得完成父類的初始化載入,靜態代碼塊隨著類的載入而載入,因此先執行父類的靜態代碼塊,然後執行子類的靜態代碼塊。
接著,執行父類的構造代碼塊,再執行父類的構造方法。
最後執行子類的構造代碼塊,執行子類的構造方法。
如有錯誤之處,歡迎指正。