封裝
封裝(Encapsulation)是面向對象的三大特征之一(另外兩個是繼承和多態),它指的是將對象的狀態信息隱藏在對象內部,不允許外部程序直接訪問對象內部信息,而是通過該類所提供的方法來實現對內部信息的操作和訪問。
訪問控制符
Java 提供了 3 個訪問控制符:private、 protected 和 public ,代表 3 種不同的訪問級別,再加上一個默認的訪問控制級別(不使用任何訪問控制符),共有 4 個訪問控制級別。

-
private(當前類訪問權限):類中的一個的成員被 private 修飾,它只能在當前類的內部被訪問;
-
default(包訪問權限):類中的一個成員或者一個外部類不使用任何訪問控制符修飾,它能被當前包下其他類訪問;
-
protected(子類訪問權限):類中的一個的成員被 protected 修飾,它既可以被當前包下的其他類訪問,又可以被不同包的子類訪問;
-
public(公共訪問權限):類中的一個成員或者一個外部類使用 public 修飾,它能被所有類訪問。
private | default | protected | public | |
---|---|---|---|---|
同一個類中 | ✔ | ✔ | ✔ | ✔ |
同一個包中 | ✔ | ✔ | ✔ | |
子類中 | ✔ | ✔ | ||
全局范圍內 | ✔ |
繼承
- Java 使用 extends 作為繼承的關鍵字,子類擴展了父類,獲得父類的全部成員變量和方法。
- Java 只能單繼承,只有一個直接父類,但可以有無限多個間接父類。當一個 Java 類並未顯式指定直接父類時,默認繼承
java.lang.Object
,因此java.lang.Object
是所有類的直接或間接父類。
重寫父類方法
重寫父類方法應遵循 “兩同兩小一大“ 規則:
- “兩同” 指方法名相同、形參列表相同;
- “兩小” 指子類方法返回值類型和拋出的異常類型應比父類方法的更小或相等;
- “一大” 指的是子類方法的訪問權限應比父類方法的訪問權限更大或相等。
class B {
public void show() {
System.out.println("B");
}
}
public class A extends B{
@Override
public void show() {
System.out.println("A"); //重寫父類方法
}
}
重載(Overload)和重寫(Override)區別:
- 重載指的是同一類中多個同名方法;
- 重寫指的是子類和父類的同名方法。
super關鍵字
- 訪問父類的構造函數:可以使用 super() 函數訪問父類的構造函數,從而委托父類完成一些初始化的工作;
- 訪問父類的成員:如果子類重寫了父類的某個方法,可以通過使用 super 關鍵字來引用父類的方法實現。
class B {
private int x;
public B(int x) {
this.x = x;
}
public void show() {
System.out.println("x:" + x);
}
}
public class A extends B{
private int x;
public A(int x, int x1) {
super(x1);
this.x = x;
}
@Override
public void show() {
super.show(); //調用被覆蓋的父類方法
}
public static void main(String[] args) {
A a = new A(1, 2);
a.show(); //x:2
}
}
父類構造器
子類繼承了父類的全部變量和方法,所以實例化子類時,必須先將其父類實例化。調用父類構造器的方式是 super(),參數為父類構造器所需參數。使用 super 調用父類構造器必須出現放在子類構造器的第一行,而 this 調用同一個類中重載的構造器也要放在第一行,所以 super() 和 this() 不能同時出現。
不管是否使用 super 顯式調用父類構造器,子類構造器總會調用父類構造器一次,總共會出現三種情況:
- 子類構造器第一行使用 super 顯式調用父類構造器;
- 子類構造器第一行使用 this 調用重載的子類構造器,在本類重載的構造器中調用父類構造器;
- 子類構造器第一行既沒有 super 調用,也沒有 this 調用,系統在第一行隱私調用父類無參構造器。
多態
多態:相同類型的變量調用同一個方法時呈現出多種不同的行為特征。
產生原因:Java 允許把一個子類對象直接賦給一個父類引用變量,無須任何類型轉換。當把一個子類對象賦給父類引用變量時,會出現編譯類型和運行類型不一致的情況,此時調用子類和父類的同名方法時(這里的同名指的是子類重寫了父類方法),總是表現出子類方法的行為特征。例如:B b = new A()
編譯類型看左邊,運行類型看右邊,因此編譯類型為 B,運行類型為 A,當 b 調用 A 和 B 的同名的方法時,運行的總是 A 中的方法。
class B {
public String book = "B";
public void base() {
System.out.println("父類普通方法");
}
public void test() {
System.out.println("父類被覆蓋的方法");
}
public String getBook() {
return book;
}
}
public class A extends B{
public String book = "A";
@Override
public void test() {
System.out.println("子類覆蓋父類方法");
}
public void sub() {
System.out.println("子類普通方法");
}
@Override
public String getBook() {
return book;
}
public static void main(String[] args) {
B b = new B();
System.out.println(b.book); //B
b.base(); //父類普通方法
b.test(); //父類被覆蓋的方法
System.out.println(b.getBook()); //B
A a = new A();
System.out.println(a.book); //A
a.base(); //父類普通方法
a.test(); //子類覆蓋父類方法
System.out.println(a.getBook()); //A
//編譯看左邊,運行看右邊,編譯和運行不一致
B b1 = new A();
//訪問的是父類的屬性,與方法不同,實例變量不具有多態性
System.out.println(b1.book); //B
//訪問父類繼承的方法
b1.base(); //父類普通方法
//訪問的是子類的同名方法
b1.test(); //子類覆蓋父類方法
//B沒有提供sub()方法,就算A有,也無法通過編譯
//b1.sub(); //錯誤
System.out.println(a.getBook()); //A
}
}
當代碼運行 B b1 = new A()
時,編譯類型為 B,運行類型為 A。
當調用 b1.test()
方法時(B 中有 test() 方法,A 中將其覆蓋了),實際運行的是 A 的 test() 方法,方法行為總是表現出子類方法的行為特征,而不是父類方法的行為特征,這就是多態。
當執行 b1.base()
方法時,因為子類繼承了父類的該方法,並且沒有重寫,所以運行一致。
當執行 b1.sub()
方法時,由於父類沒有 sub() 方法,而編譯時類型為父類,所以無法通過編譯。
當執行 b1.book
獲取同名實例變量時,返回的是父類的實例變量,與方法不同,對象的實例變量則不具備多態性,返回的數據看編譯時的類型。
當執行 b1.getBook()
方法時,實際運行的是 A 的 getBook() 方法 ,內部訪問的實例變量是 A 的,方法是表現出多態特性。
注意:只有調用子類重寫父類的方法才表現出多態性,直接訪問公開的同名實例變量時不表現多態性,返回的數據看左邊類型(編譯類型)。