Java基礎-封裝和繼承
一. 封裝
那封裝是什麼呢?
在面向對象程式設計方法中,封裝(英語:Encapsulation)是指一種將抽象性函式接口的實現細節部分包裝、隱藏起來的方法。
1.1 封裝的目的
- 直接通過操控類對象來達到目的,不需要對具體實現十分瞭解,使類屬性和方法的具體實現對外不可見。不但方便還起到瞭保護作用。
- 封裝最主要的功能在於我們能修改自己的實現代碼,而不用修改那些調用我們代碼的程序片段。
- 適當的封裝可以讓程式碼更容易理解與維護,也加強瞭程式碼的安全性。
1.2 封裝的好處
- 良好的封裝能夠減少耦合。
- 類內部的結構可以自由修改。
- 可以對成員變量進行更精確的控制。
- 隱藏信息,實現細節。
1.3 封裝的步驟
修改屬性的可見性來限制對屬性的訪問(一般限制為private),例如:
public class Person { private String name ; // 姓名 private String gender ; // 性別 private int age; // 年齡 }
這段代碼中,將 name 、sex和 age 屬性設置為私有的,隻能本類才能訪問,其他類都訪問不瞭,如此就對信息進行瞭隱藏。
對每個值屬性提供對外的公共方法訪問,也就是創建一對賦取值方法,用於對私有屬性的訪問,例如:
public class Person { private String name ; // 姓名 private String gender ; // 性別 private int age; // 年齡 public void setName(String name) { this.name = name; } public String getName() { return name; } public void setGender(String gender) { this.gender = gender; } public String gender(){ return gender; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } }
采用 this 關鍵字調用本類中的屬性,也就是類中的成員變量。主要為瞭解決實例變量(private String name)和局部變量(setName(String name)
中的name變量)之間發生的同名的沖突。
1.4 封裝的例子
創建一個用戶類User:
- 代碼如下:
package com.nz.pojo; public class User { private String username; // 用戶名 private String password; // 密碼 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
- 編寫測試User的
demo:EncapsulationDemo
代碼如下:
package com.nz; import com.nz.pojo.User; public class EncapsulationDemo { public static void main(String[] args) { User user = new User(); user.setUsername("太子爺哪吒"); user.setPassword("520"); System.out.println("username: " + user.getUsername() + "-----------" + "password: " + user.getPassword()); } }
執行結果如下:
username:太子爺哪吒—————–password520
1.5 小結
封裝實際上是將類的某些信息隱藏在類的內部,不允許外部程序直接訪問,而是通過該類提供的方法來實現對隱藏信息的訪問和操作。就是把我們想提供給外界的一些方法給暴露出來,以便外界能調用到我們。
二. 繼承
2.1 繼承的介紹
繼承是java面向對象編程技術的一塊基石,因為它允許創建分等級層次的類。描述的是事物之間的所屬關系,這種關系是:is-a 的關系。
繼承:就是子類繼承父類的屬性和行為,使得子類對象(實例)可以直接具有與父類相同的屬性、相同的行為。子類可以直接訪問父類中的非私有的屬性和行為。
2.2 生活中的繼承
兔子和長頸鹿屬於食草動物類,老虎和獅子屬於食肉動物類。而食草動物和食肉動物又是屬於動物類。
那是不是兔子、長頸鹿、老虎、獅子都屬於動物類呢?答案是沒錯滴!雖然食草動物和食肉動物都是屬於動物,但是兩者的屬性和行為上有差別,所以子類會具有父類的一般特性也會具有自身的特性。我們就可以再多個類中存在相同屬性和行為時,我們可以將這些內容抽取到單獨一個類中,那麼多個類無需再定義這些屬性和行為,隻要繼承那一個類即可。
2.3 繼承的好處
- 提高代碼的復用性(減少代碼冗餘,相同代碼重復利用)。
- 使類與類之間產生瞭關系。
- 子類擁有父類非 private 的屬性、方法。
- 子類可以擁有自己的屬性和方法,即子類可以對父類進行擴展。
- 子類可以用自己的方式實現父類的方法。
- 提高瞭類之間的耦合性(繼承的缺點,耦合度高就會造成代碼之間的聯系越緊密,代碼獨立性越差)。
- Java 的繼承是單繼承,但是可以多重繼承,單繼承就是一個子類隻能繼承一個父類,多重繼承就是,例如 B 類繼承 A 類,C 類繼承 B 類,所以按照關系就是 B 類是 C 類的父類,A 類是 B 類的父類,這是 Java 繼承區別於 C++ 繼承的一個特性。
2.4 繼承的格式
在Java當中會通過extends
關鍵字可以申明一個類是從另外一個類繼承而來的,一般形式如下:
class 父類 { } class 子類 extends 父類 { }
需要註意一點: Java 不支持多繼承,但支持多重繼承。就如下:
class A { } class B extends A { (對的) } class C extends A, B { (錯的) } class C extends B { (對的) }
頂層父類是Object類。所有的類默認繼承Object,作為父類。
2.5 繼承的demo
- 編寫一個父類極其對應的子類信息
結構如下:
代碼如下:
父類Person:
package com.nz.pojo; public class Person { private String name ; private int age ; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
子類Student沒有額外的屬性和方法:
package com.nz.pojo; /** * 繼承瞭Person特有的name, age, * 沒有額外的獨有屬性和方法 */ public class Student extends Person{ }
子類Teacher多瞭一個工資的屬性和獨有的教書方法:
package com.nz.pojo; /** * 繼承瞭Person特有的name, age, * 多瞭自己獨有的工資屬性還有獨有的教書方法 */ public class Teacher extends Person{ // 工資 private double salary ; // 特有方法 public void teach(){ System.out.println("老師在認真教書!"); } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } }
編寫測試代碼:
package com.nz; import com.nz.pojo.Student; import com.nz.pojo.Teacher; public class InheritDemo { public static void main(String[] args) { Teacher teacher = new Teacher(); teacher.setName("太子爺哪吒"); teacher.setAge(18); teacher.setSalary(1999.99); System.out.println(teacher.getName()); System.out.println(teacher.getAge()); System.out.println(teacher.getSalary()); teacher.teach(); Student student = new Student(); student.setName("哪吒"); student.setAge(12); //student.setSalary(1999.99); // student沒有工資屬性,報錯! System.out.println(student.getName()); System.out.println(student.getAge()); } }
結果如下:
太子爺哪吒
18
1999.99
老師在認真教書!
哪吒
12
從結果來看,子類繼承父類,就可以直接得到父類的成員變量和方法。而子類可以編寫一些特有的屬性和方法,但是是否可以繼承所有成分呢?
2.6 子類不能繼承的內容
並不是父類的所有內容都可以給子類繼承的:
2.6.1 super 與 this 關鍵字
這裡先將這兩個關鍵字,super和this在繼承關系中,運用比較頻繁。
- super關鍵字:我們可以通過super關鍵字來實現對父類成員的訪問,用來引用當前對象的父類。
- this關鍵字:指向自己本類的引用。
super和this完整的用法如下:
this.成員變量 -- 本類的 super.成員變量 -- 父類的 this.成員方法名() -- 本類的 super.成員方法名() -- 父類的
具體演示,創建測試InheritDemo2:
package com.nz; public class InheritDemo2 { public static void main(String[] args) { Animal a = new Animal(); a.eat(); Cat cat = new Cat(); cat.eatFish(); } } class Animal { void eat() { System.out.println("animal : eat"); } } class Cat extends Animal { void eat() { System.out.println("cat : eat"); } void eatFish() { this.eat(); // this 調用自己的方法 super.eat(); // super 調用父類方法 } }
調用結果如下:
animal : eat
cat : eat
animal : eat
註意:
子類的每個構造方法中均有默認的super(),調用父類的空參構造。手動調用父類構造會覆蓋默認的super()。
super() 和 this() 都必須是在構造方法的第一行,所以不能同時出現。
2.6.2 構造器不能被繼承
- 子類不能繼承父類的構造器(構造方法或者構造函數),它隻是調用(隱式或顯式)。因為子類有自己的構造器。值得註意的是子類可以繼承父類的私有成員(成員變量,方法),隻是子類無法直接訪問而已,可以通過
getter/setter
方法訪問父類的private成員變量。 - 如果父類的構造器帶有參數,則必須在子類的構造器中顯式地通過
super
關鍵字調用父類的構造器並配以適當的參數列表。 - 如果父類構造器沒有參數,則在子類的構造器中不需要使用
super
關鍵字調用父類構造器,系統會自動調用父類的無參構造器。
演示過程:
package com.nz; public class InheritDemo3 { public static void main(String[] args) { System.out.println("------Teacher 類繼承------"); Teacher teacher = new Teacher(); Teacher teacher2 = new Teacher("張三"); System.out.println("------Student 類繼承------"); Student student = new Student(); Student student2 = new Student("張三三"); } } // 父類 class Person { private String name; Person(){ System.out.println("調用瞭父類的無參構造器: Person()"); } Person(String name) { System.out.println("調用瞭父類的帶參構造器: Person(String name)"); this.name = name; } } // Teacher子類繼承Person class Teacher extends Person{ private String name; Teacher(){ // 自動調用父類的無參數構造器 因為會有默認super(); System.out.println("Teacher"); } public Teacher(String name){ super("太子爺哪吒"); // 調用父類中帶有參數的構造器 System.out.println("Teacher(String name):"+name); this.name = name; } } // Student子類繼承Person class Student extends Person{ private String name; Student(){ super("heihei"); // 調用父類中帶有參數的構造器 System.out.println("SubClass2"); } public Student(String name){ // 自動調用父類的無參數構造器 System.out.println("Student(String name):"+name); this.name = name; } }
結果如下:
——Teacher 類繼承——
調用瞭父類的無參構造器: Person()
Teacher
調用瞭父類的帶參構造器: Person(String name)
Teacher(String name):張三
——Student 類繼承——
調用瞭父類的帶參構造器: Person(String name)
SubClass2
調用瞭父類的無參構造器: Person()
Student(String name):張三三
2.6.3 final修飾的類不能被繼承
final 關鍵字主要用在三個地方:變量、方法、類。
- 修飾類:表示該類不能被繼承;
- 修飾方法:表示方法不能被重寫;
- 修飾變量:表示變量隻能一次賦值以後值不能被修改(常量)。
final 的特點:
- 對於一個 final 變量,如果是基本數據類型的變量,則其數值一旦在初始 化之後便不能更改;如果是引用類型的變量,則在對其初始化之後便不 能再讓其指向另一個對象。
- 當用 final 修飾一個類時,表明這個類不能被繼承。final 類中的所有成員 方法都會被隱式地指定為 final 方法。
- 使用 final 方法的原因有兩個。第一個原因是把方法鎖定,以防任何繼承 類修改它的含義;第二個原因是效率。在早期的 Java 實現版本中,會將 final 方法轉為內嵌調用。但是如果方法過於龐大,可能看不到內嵌調用 帶來的任何性能提升(現在的 Java 版本已經不需要使用 final方法進行這些優化瞭)。類中所有的 private 方法都隱式地指定為 final。
我們測試下修飾類後到底能不能繼承:
package com.nz; public class InheritDemo4 { } // 父類 final class Fu { private String name; } //class Zi extends Fu{ // Cannot inherit from final 'com.nz.Fu' 會顯示沒辦法繼承Fu //}
結果:可以看出來在被final修飾的Fu類沒辦法繼承,而且在編譯期間就會報錯瞭,沒辦法通過運行。
2.7 方法重寫
2.7.1 介紹
子類中出現與父類一模一樣的方法時(返回值類型,方法名和參數列表都相同),會出現覆蓋效果,也稱為重寫或者復寫。聲明不變,重新實現。
2.7.2 使用場景與案例
發生在子父類之間的關系。
子類繼承瞭父類的方法,但是子類覺得父類的這方法不足以滿足自己的需求,子類重新寫瞭一個與父類同名的方法,以便覆蓋父類的該方法。
寫個測試案例:
package com.nz; public class InheritDemo5 { public static void main(String[] args) { // 創建子類對象 Cat lanMao = new Cat(); // 調用父類繼承而來的方法 lanMao.run(); // 調用子類重寫的方法 lanMao.sing(); } } class Animal{ public void sing(){ System.out.println("動物都可以唱歌!"); } public void run(){ System.out.println("動物都可以跑!"); } } class Cat extends Animal { public void sing(){ System.out.println("我們一起學貓叫,一起喵喵喵!讓我們一起撒個嬌"); } }
運行結果:
動物都可以跑!
我們一起學貓叫,一起喵喵喵!讓我們一起撒個嬌
可以看出,藍貓調用瞭重寫後的sing方法。
2.7.2 @Override重寫註解
- @Override:註解,重寫註解校驗!
- 這個註解標記的方法,就說明這個方法必須是重寫父類的方法,否則編譯階段報錯。
- 建議重寫都加上這個註解,一方面可以提高代碼的可讀性,一方面可以防止重寫出錯!
加上後的子類代碼形式如下:
class Cat extends Animal { // 聲明不變,重新實現 // 方法名稱與父類全部一樣,隻是方法體中的功能重寫瞭! @Override public void sing(){ System.out.println("我們一起學貓叫,一起喵喵喵!讓我們一起撒個嬌"); } }
2.7.3 註意事項
- 方法重寫是發生在子父類之間的關系。
- 子類方法覆蓋父類方法,必須要保證權限大於等於父類權限。
- 子類方法覆蓋父類方法,返回值類型、函數名和參數列表都要一模一樣。
總結
這篇文章就到這裡瞭,如果這篇文章對你也有所幫助,希望您能多多關註WalkonNet的更多內容!