Java面向程序的三大特性之封裝、繼承、多態(tài)詳解(最新整理)
1. 封裝
1.1 什么是封裝呢?
我們生活中使用的很多電子產品都是封裝好的,有的部分讓我們看到,有的部分不讓我們看到。比如機械手表,它的指針指向的時間讓我們看到,而它的工作原理用表盤遮擋起來不讓我們看,這也是一種合理的設計,我們只需要看時機就行了。


其Java的實封裝也是這樣設計的,把類封裝起來,有些成員變量和成員方法讓我們看到和使用,有的成員變量和成員方法不讓我們使用。
面向對象程序三大特性:封裝、繼承、多態(tài)。而類和對象階段,主要研究的就是封裝特性。何為封裝呢?簡單來說就是套殼屏蔽細節(jié)。
封裝:將成員變量和成員方法進行有機結合,隱藏對象的屬性和實現(xiàn)細節(jié),僅對外公開接口來和對象進行、交互。
實現(xiàn)這些功能就需要訪問限定符
1.2 訪問限定符
| 范圍 | private | default | protected | public | |
|---|---|---|---|---|---|
| 1 | 同一包中的同一類 | ? | ? | ? | ? |
| 2 | 同一包中的不同類 | ? | ? | ? | |
| 3 | 不同包中的子類 | ? | ? | ||
| 4 | 不同包中的非子類 | ? |
說明:public:只看單詞的意思就可以明白,是公共的,誰都可以看到和使用。protected:主要是用在繼承中,子類可以使用。default: 對于自己家族中(同一個包中)不是什么秘密,對于其他人來說就是隱私了。什么都不寫時的默認權限private:單詞的意思是私密的,只有自己使用,其他人不能使用
訪問權限除了可以限定類中成員的可見性,也可以控制類的可見性
1.3 使用封裝
在cn.nyist.watch包中創(chuàng)建一個手表類Watch:
package cn.nyist.watch;
public class Watch {
private String machine; // 機械手表的動力來源
public String time; // 時間
String brand; // 品牌,default
// 構造器
public Watch(String machine,String time, String brand){
this.machine = machine;
this.time = time;
this.brand = brand;
}
// 手表的功能 看時間
public void See_time(){
System.out.println("時間");
}
//調試零件
private void debugging(){
System.out.println("調試零件");
}
}在Watch類中使用main函數,相當于是創(chuàng)造表的人:
可以任意查看和使用
// 程序的入口,在Watch類中
public static void main(String[] args){
// 在類中創(chuàng)建一個手表
// 此場景相當于創(chuàng)造表的人
Watch w = new Watch("機械","12:00","勞力士");
//開始使用手表
//查看表的機械部件
System.out.println(w.machine);
// 查看時間
System.out.println(w.time);
//查看品牌
System.out.println(w.brand);
w.See_time();
w.debugging();
}在cn.nyist.watch包中創(chuàng)建一個Main類,作為相當于買手表的人:
不可以訪問Watch類定義的私有成員和私有方法
package cn.nyist.watch;
public class Buy {
// 程序的入口,在類中
public static void main(String[] args){
// 此場景相當于手表的買者即擁有者
Watch w = new Watch("機械","12:00","勞力士");
//開始使用手表
//查看表的機械部件
//System.out.println(w.machine); 不可以查看private屬性
// 查看時間
System.out.println(w.time); // time是public屬性
//查看品牌
System.out.println(w.brand); // brand 是default屬性
w.See_time(); // See_time是public屬性
//w.debugging(); // 不能調用private方法
}
}在其他包中的非子類,相當于一個陌生人去看你的表:
只能訪問public屬性的成員變量和成員方法
public class People {
// 程序的入口
public static void main(String[] args){
// 此場景相當于一個陌生人去看你的表
Watch w = new Watch("機械","12:00","勞力士");
//查看表的機械部件
//System.out.println(w.machine); 不可以查看private屬性
// 查看時間
System.out.println(w.time); // time是public屬性
//查看品牌
//System.out.println(w.brand); // 不可以看default屬性
w.See_time(); // See_time是public屬性
//w.debugging(); // 不能調用private方法
}
}2. 繼承
2.1 為什么要有繼承?
Java中的程序都是類與類進行交互,如果多個類中有很多一樣的成員方法或成員變量就會出現(xiàn)代碼冗余的現(xiàn)象,比如:
在cn.nyist.animal包中創(chuàng)建一個Dog類:
package cn.nyist.animal;
public class Dog{
public String name;
public String color;
protected int age;
// 構造器
public Dog(String name,String color, int age){
this.name = name;
this.color = color;
this.age = age;
}
public void eat(){
System.out.println("吃飯~");
}
public void sleep(){
System.out.println("睡覺~");
}
// 叫
public void bark(){
System.out.println("汪汪~");
}
}在cn.nyist.animal包中創(chuàng)建一個Cat類:
package cn.nyist.animal;
public class Cat{
public String name;
public String color;
protected int age;
//構造器
public Cat(String name,String color, int age){
this.name = name;
this.color = color;
this.age = age;
}
public void eat(){
System.out.println("吃飯~");
}
public void sleep(){
System.out.println("睡覺~");
}
//叫
public void mm() {
System.out.println("喵喵~");
}
}
這兩個類是不是除了bar()函數,其余的內容都是一樣,這就出現(xiàn)了代碼冗余,我們怎么解決呢?
面向對象思想中提出了繼承的概念,專門用來進行共性抽取,實現(xiàn)代碼復用。
2.2 繼承的概念
繼承(inheritance)機制:是面向對象程序設計使代碼可以復用的最重要的手段,它允許程序員在保持原有類特性的基礎上進行擴展,增加新功能,這樣產生新的類,稱派生類(子類)。繼承呈現(xiàn)了面向對象程序設計的層次結構, 體現(xiàn)了由簡單到復雜的認知過程。繼承主要解決的問題是:共性的抽取,實現(xiàn)代碼復用。
例如:狗和貓都是動物,那么我們就可以將共性的內容進行抽取,然后采用繼承的思想來達到共用。

上述圖示中,Dog和Cat都繼承了Animal類,其中:Animal類稱為父類/基類或超類,Dog和Cat可以稱為Animal的子類/派生類,繼承之后,子類可以復用父類中成員,子類在實現(xiàn)時只需關心自己新增加的成員即可。從繼承概念中可以看出繼承最大的作用就是:實現(xiàn)代碼復用,還有就是來實現(xiàn)多態(tài)。
2.3 繼承的語法
在Java中如果要表示類之間的繼承關系,需要借助extends關鍵字,語法如下:
修飾符 class 子類名 extends 父類名 {
// ...
}對Dog和Cat函數的重新實現(xiàn):
錯誤示例:
在cn.nyist.animal包中創(chuàng)建一個Dog和Cat的父類Animal類:
package cn.nyist.animal;
// 基類(父類)
public class Animal{
public String name;
public String color;
protected int age;
//構造器
public Animal(String name,String color, int age){
this.name = name;
this.color = color;
this.age = age;
}
public void eat(){
System.out.println(name + "吃飯~");
}
public void sleep(){
System.out.println(name + "睡覺~");
}
}在cn.nyist.animal包中創(chuàng)建一個Dog,Dog繼承Animal
package cn.nyist.animal;
public class Dog extends Animal{
// 只需要再添加一個bark()方法
public void bark(){
System.out.println("汪汪~");
}
}當寫完上述兩步的時候就運行會發(fā)生報錯,這是為什么呢?不是已經按要求寫了嗎?
因為 Dog 類的沒有顯示的創(chuàng)建構造器,當創(chuàng)建Dog類對象的時候會調用默認構造器,也會調用默認構造器中的super()(super()是用來調用父類的構造器,并可以傳入參數),調用的super()是無參的,但是父類Animal沒有創(chuàng)建無參的構造器,這會導致發(fā)生報錯。
正確示例:
在cn.nyist.animal包中創(chuàng)建一個Dog和Cat的父類Animal類:
package cn.nyist.animal;
public class Animal{
public String name;
public String color;
protected int age; // 子類可以使用protected的變量
//構造器
public Animal(String name,String color, int age){
this.name = name;
this.color = color;
this.age = age;
}
public void eat(){
System.out.println(name + "吃飯~");
}
public void sleep(){
System.out.println(name + "睡覺~");
}
}在cn.nyist.animal包中創(chuàng)建一個Dog,Dog繼承Animal
package cn.nyist.animal;
public class Dog extends Animal{
public Dog(String name, String color, int age){
super(name,color,age);
}
// 只需要再添加一個bark()方法
public void bark(){
System.out.println("汪汪~");
}
}在cn.nyist.animal包中創(chuàng)建一個Cat,Dog繼承Cat
package cn.nyist.animal;
public class Cat extends Animal{
// 構造器
public Cat(String name, String color, int age){
super(name,color,age);
}
public void mm() {
System.out.println("喵喵~");
}
}測試
package cn.nyist.animal;
public class Cat extends Animal{
// 構造器
public Cat(String name, String color, int age){
super(name,color,age);
}
public void mm() {
System.out.println("喵喵~");
}
}運行結果:

java文件的結構

2.4 訪問父類成員
2.4.1 子類中訪問父類成員的變量
在繼承體系中,子類將父類中的方法和字段繼承下來了,那在子類中能否直接訪問父類中繼承下來的成員呢?
在cn.nyist.study包中創(chuàng)建基類Base
package cn.nyist.study;
public class Base {
protected int a;
protected int b;
}子類和父類不存在同名成員變量
package cn.nyist.study;
public class Son extends Base {
int c;
public void Fun(){
a = 10; //訪問父類的成員
b = 20; // 訪問父類的成員
c = 30; // 訪問自己的成員
}
}子類和父類存在同名成員變量
package cn.nyist.study;
public class Son extends Base {
int b;
int c;
public void Fun(){
a = 10; //訪問父類的成員
b = 20; // 訪問自己的成員
c = 30; // 訪問自己的成員
}
}在子類方法中 或者 通過子類對象訪問成員時:
- 如果訪問的成員變量子類中有,優(yōu)先訪問自己的成員變量。
- 如果訪問的成員變量子類中無,則訪問父類繼承下來的,如果父類也沒有定義,則編譯報錯。
- 如果訪問的成員變量與父類中成員變量同名,則優(yōu)先訪問自己的。
成員變量訪問遵循就近原則,自己有優(yōu)先自己的,如果沒有則向父類中找。
2.4.2 訪問父類的成員方法
在包cn.nyist.study中創(chuàng)建一個類Base
package cn.nyist.study;
public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}成員方法不同名
package cn.nyist.study;
public class Son extends Base {
public void methodC() {
System.out.println("Son中的method(int)方法");
}
public void methodD(){
methodA(); // 訪問父類中的methodA()
methodB(); // 訪問父類中的methodA()
methodC(); // 訪問子類中的methodC()
}
}總結:成員方法沒有同名時,在子類方法中或者通過子類對象訪問方法時,則優(yōu)先訪問自己的,自己沒有時再到父類中找,如果父類中也沒有則報錯。
成員方法同名
package cn.nyist.study;
public class Son extends Base {
public void methodA(int a) {
System.out.println("Son中的method(int)方法");
}
public void methodB(){
System.out.println("Son中的methodB()方法");
}
public void methodD(){
methodA(); // 沒有傳參,訪問父類中的methodA()
methodA(20); // 傳遞int參數,訪問子類中的methodA(int)
methodB(); // 直接訪問,則永遠訪問到的都是子類中的methodB(),基類的無法訪問到
}
}【說明】
- 通過子類對象訪問父類與子類中不同名方法時,優(yōu)先在子類中找,找到則訪問,否則在父類中找,找到則訪問,否則編譯報錯。
- 通過派生類對象訪問父類與子類同名方法時,如果父類和子類同名方法的參數列表不同(重載),根據調用方法適傳遞的參數選擇合適的方法訪問,如果沒有則報錯;
2.5 super關鍵字
在有些場景中,子類和父類中可能會存在相同名稱的成員,如果要在子類方法中訪問父類同名成員時,該如何操作?
直接訪問是無法做到的,Java提供了super關鍵字,該關鍵字主要作用:在子類方法中訪問父類的成員。
在包cn.nyist.study中創(chuàng)建一個類Base
package cn.nyist.study;
public class Base {
int a;
int b;
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}在包cn.nyist.study中創(chuàng)建類Base的子類Son
package cn.nyist.study;
public class Son extends Base {
int a; // 與父類中成員變量同名且類型相同
char b; // 與父類中成員變量同名但類型不同
// 與父類中methodA()構成重載
public void methodA(int a) {
System.out.println("Son中的method()方法");
}
// 與基類中methodB()構成重寫(即原型一致,重寫后序詳細介紹)
public void methodB() {
System.out.println("Son中的methodB()方法");
}
public void methodC() {
// 對于同名的成員變量,直接訪問時,訪問的都是子類的
a = 100; // 等價于: this.a = 100;
b = 101; // 等價于: this.b = 101;
// 注意:this是當前對象的引用
// 訪問父類的成員變量時,需要借助super關鍵字
// super是獲取到子類對象中從基類繼承下來的部分
super.a = 200;
super.b = 201;
// 父類和子類中構成重載的方法,直接可以通過參數列表區(qū)分清訪問父類還是子類方法
methodA(); // 沒有傳參,訪問父類中的methodA()
methodA(20); // 傳遞int參數,訪問子類中的methodA(int)
// 如果在子類中要訪問重寫的基類方法,則需要借助super關鍵字
methodB(); // 直接訪問,則永遠訪問到的都是子類中的methodA(),基類的無法訪問到
super.methodB(); // 訪問基類的methodB()
}
}【注意事項】
- 只能在非靜態(tài)方法中使用
- 在子類方法中,訪問父類的成員變量和方法。
2.6 super 和 this
super和this都可以在成員方法中用來訪問:成員變量和調用其他的成員函數,都可以作為構造方法的第一條語句,那他們之間有什么區(qū)別呢?
【相同點】
- 都是Java中的關鍵字
- 只能在類的非靜態(tài)方法中使用,用來訪問非靜態(tài)成員方法和字段
- 在構造方法中調用時,必須是構造方法中的第一條語句,并且不能同時存在
【不同點】
this是當前對象的引用,當前對象即調用實例方法的對象,super相當于是子類對象中從父類繼承下來部分成員的引用

在非靜態(tài)成員方法中,this用來訪問本類的方法和屬性,super用來訪問父類繼承下來的方法和屬性在構造方法中:this(…)用于調用本類構造方法,super(…)用于調用父類構造方法,兩種調用不能同時在構造
方法中出現(xiàn)構造方法中一定會存在super(…)的調用,用戶沒有寫編譯器也會增加,但是this(…)用戶不寫則沒有
2.7 子類的構造方法使用規(guī)則
父子父子,先有父再有子,即:創(chuàng)建子類對象時,需要先調用基類構造方法,然后執(zhí)行子類的構造方法。
在包cn.nyist.study中創(chuàng)建基類Base
package cn.nyist.study;
public class Base {
public Base() {
System.out.println("Base()");
}
}在包cn.nyist.study中創(chuàng)建類Base的子類
package cn.nyist.study;
public class Son extends Base {
public Son(){
// super(); // 注意子類構造方法中默認會調用基類的無參構造方法:super(),
// 用戶沒有寫時,編譯器會自動添加,而且super()必須是子類構造方法中第一條語句,
// 并且只能出現(xiàn)一次
System.out.println("Son()構造方法");
}
}在包cn.nyist.study中創(chuàng)建類Main,運行程序
package cn.nyist.study;
public class Main {
public static void main(String[] args) {
Son d = new Son();
}
}
在子類構造方法中,并沒有寫任何關于基類構造的代碼,但是在構造子類對象時,先執(zhí)行基類的構造方法,然后執(zhí)行子類的構造方法,因為:子類對象中成員是有兩部分組成的,基類繼承下來的以及子類新增加的部分 。父子父子
肯定是先有父再有子,所以在構造子類對象時候 ,先要調用基類的構造方法,將從基類繼承下來的成員構造完整,然后再調用子類自己的構造方法,將子類自己新增加的成員初始化完整。
注意:
若父類顯式定義無參或者默認的構造方法,在子類構造方法第一行默認有隱含的super()調用,即調用基類構造方法
class Father{
public String name;
public int age;
//不寫構造方法時,會有一個默認的無參構造方法
//public Father(){}
}
public class Son extends Father {
public String color;
// // 不寫構造方法時,會有一個默認的無參構造方法
// public Son(){
// // 默認會調用父類的無參構造方法
// super();
// }
}如果父類構造方法是帶有參數的,此時需要用戶為子類顯式定義構造方法,并在子類構造方法中選擇合適的父類構造方法調用,否則編譯失敗。
class Father{
public String name;
public int age;
//定義有參構造方法
public Father(String name, int age){
this.name = name;
this.age = age;
}
}
public class Son extends Father {
public String color;
// 父類只有有參構造方法,子類必須顯示的調用父類的構造方法
public Son(String name, int age, String color){
// 子類必須顯示的調用父類的構造方法,否則會報錯
super(name, age);
this.color = color;
}
}在子類構造方法中,super(…)調用父類構造時,必須是子類構造函數中第一條語句。
class Father{
public String name;
public int age;
//定義有參構造方法
public Father(String name, int age){
this.name = name;
this.age = age;
}
}
public class Son extends Father {
public String color;
// 父類只有有參構造方法,子類必須顯示的調用父類的構造方法
public Son(String name, int age, String color){
this.color = color;
// super必須子在第一行,這種寫法是錯誤的
super(name, age);
}
}super(...)只能在子類構造方法中出現(xiàn)一次,并且不能和this(…)同時出現(xiàn)。
class Father{
public String name;
public int age;
//定義有參構造方法
public Father(String name, int age){
this.name = name;
this.age = age;
}
}
public class Son extends Father {
public String color;
public Son(String color){
super("張三",18);
this.color = color;
}
// 父類只有有參構造方法,子類必須顯示的調用父類的構造方法
public Son(String name, int age, String color){
// 子類必須顯示的調用父類的構造方法,否則會報錯
super(name, age);
// super()和this()不能同時使用
this(color);
}
}3. 多態(tài)
3.1 多態(tài)的概念
多態(tài)允許你使用一個接口來表示不同的類型,從而實現(xiàn)更靈活和可擴展的代碼設計。
多態(tài)的概念:通俗來說,就是多種形態(tài),具體點就是去完成某個行為,當不同的對象去完成時會產生出不同 的狀態(tài)。
比如,有一個基類Animal,有一個吃飯的方法;子類Dog,重寫方法吃飯變?yōu)槌怨穷^;子類Cat重寫方法變?yōu)槌贼~。使用這兩個子類的時候,都調用吃飯的方法,但是打印出不同的內容。
3.2 多態(tài)的實現(xiàn)
在java中要實現(xiàn)多態(tài),必須要滿足如下幾個條件,缺一不可:
- 必須在繼承體系下。
- 子類必須要對父類中方法進行重寫。
- 通過父類的引用調用重寫的方法。
多態(tài)體現(xiàn):在代碼運行時,當傳遞不同類對象時,會調用對應類中的方法
示例:
在cn.nyist.study包中創(chuàng)建一個基類Animal
package cn.nyist.animal;
public class Animal{
public String name;
public String color;
protected int age;
//構造器
public Animal(String name,String color, int age){
this.name = name;
this.color = color;
this.age = age;
}
public void eat(){
System.out.println(name + "吃飯~");
}
public void sleep(){
System.out.println(name + "睡覺~");
}
}在cn.nyist.study包中創(chuàng)建子類Dog
package cn.nyist.animal;
public class Dog extends Animal{
public Dog(String name, String color, int age){
super(name,color,age);
}
// 重寫父類方法
public void eat(){
System.out.println(name+"吃骨頭");
}
}在cn.nyist.study包中創(chuàng)建子類Cat
package cn.nyist.animal;
public class Cat extends Animal{
// 構造器
public Cat(String name, String color, int age){
super(name,color,age);
}
public void eat(){
System.out.println(name + "吃魚");
}
}在cn.nyist.study包類Main,運行程序
package cn.nyist.animal;
public class Main {
public static void main(String[] args) {
// 創(chuàng)建狗類的對象
Dog dog = new Dog("小狗","黑色",1);
Fun_eat(dog);
// 創(chuàng)建貓類的對象
Cat cat = new Cat("小貓","花色",3);
Fun_eat(cat);
}
// 編譯器在編譯代碼時,并不知道要調用Dog 還是 Cat 中eat的方法
// 等程序運行起來后,形參a引用的具體對象確定后,才知道調用那個方法
// 注意:此處的形參類型必須時父類類型才可以
public static void Fun_eat(Animal a){
a.eat();
a.sleep();
}
}
3.3 重寫
重寫(override):也稱為覆蓋。重寫是子類對父類非靜態(tài)、非private修飾,非final修飾,非構造方法等的實現(xiàn)過程進行重新編寫,返回值和形參都不能改變。即外殼不變,核心重寫!重寫的好處在于子類可以根據需要,定義特定于自己的行為。 也就是說子類能夠根據需要實現(xiàn)父類的方法。
【方法重寫的規(guī)則】
- 子類在重寫父類的方法時,一般必須與父類方法原型一致: 返回值類型 方法名 (參數列表) 要完全一致
- 被重寫的方法返回值類型可以不同,但是必須是具有父子關系
- 訪問權限不能比父類中被重寫的方法的訪問權限更低。例如:如果父類方法被public修飾,則子類中重寫該方法就不能聲明為 protected
- 父類被static、private修飾的方法、構造方法都不能被重寫。
- 重寫的方法, 可以使用 @Override 注解來顯式指定. 有了這個注解能幫我們進行一些合法性校驗. 例如不小心將方法名字拼寫錯了 (比如寫成 aet), 那么此時編譯器就會發(fā)現(xiàn)父類中沒有 aet 方法, 就會編譯報錯, 提示無法構成重寫.
【重寫和重載的區(qū)別】
| 區(qū)別點 | 重寫(override) | 重載(overload) |
|---|---|---|
| 參數列表 | 一定不能修改 | 必須修改 |
| 返回類型 | 一定不能修改【除非可以構成父子類關系】 | 可以修改 |
| 訪問限定符 | 一定不能做更嚴格的限制(可以降低限制) | 可以修改 |
到此這篇關于Java面向程序的三大特性之封裝、繼承、多態(tài)的文章就介紹到這了,更多相關Java封裝、繼承、多態(tài)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
關于Java Spring三級緩存和循環(huán)依賴的深入理解
對于循環(huán)依賴,我相信讀者無論只是聽過也好,還是有過了解也好,至少都有所接觸。但是我發(fā)現(xiàn)目前許多博客對于循環(huán)依賴的講解并不清楚,都提到了Spring的循環(huán)依賴解決方案是三級緩存,但是三級緩存每一級的作用是什么,很多博客都沒有提到,本篇文章帶你深入了解2021-09-09
spring schedule配置多任務動態(tài)cron(增刪啟停)
這篇文章主要介紹了spring schedule配置多任務動態(tài)cron(增刪啟停),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-03-03
SpringBoot數據庫初始化datasource配置方式
這篇文章主要為大家介紹了SpringBoot數據庫初始化datasource配置方式,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12

