Java架構-JavaSE(二)之繼承、封裝、多型
閱讀目錄(Content)
一、封裝(資料的隱藏)
1.1、封裝的步驟
1.2、舉例
1.3、封裝的作用
1.4、封裝的意義
二、方法的過載
三、繼承
四、super關鍵字
4.1、super的使用
4.2、super使用的注意的地方
4.3、super 和 this 的區別
五、方法重寫(方法覆蓋)
六、多型
七、instanceof和型別轉換
7.1、instanceof
7.2、型別轉換
學習完類與物件終於認識到什麼是類,什麼是物件了。接下來要看的就是java的三大特徵:繼承、封裝、多型。
一、封裝(資料的隱藏)
在定義一個物件的特性的時候,有必要決定這些特性的可見性,即哪些特性對外部是可見的,哪些特性用於表示內部狀態。
通常,應禁止直接訪問一個物件中資料的實際表示,而應通過操作介面來訪問,這稱為資訊隱藏。
1.1、封裝的步驟
1).使用private 修飾需要封裝的成員變數。
2.)提供一個公開的方法設定或者訪問私有的屬性
設定 通過set方法,命名格式: set屬性名(); 屬性的首字母要大寫
訪問 通過get方法,命名格式: get屬性名(); 屬性的首字母要大寫
1.2、舉例
//物件不僅能再類中方法,還能在類的外部"直接"訪問
public class Student{
public String name;
public void println(){
System.out.println(this.name);
}
}
public class Test{
public static void main(String[] args){
Student s = new Student();
s.name = “tom”;
}
}
在類中一般不會把資料直接暴露在外部的,而使用private(私有)關鍵字把資料隱藏起來
例如:
public class Student{
private String name;
}
public class Test{
public static void main(String[] args){
Student s = new Student();
//編譯報錯,在類的外部不能直接訪問類中的私有成員
s.name = “tom”;
}
}
如果在類的外部需要訪問這些私有屬性,那麼可以在類中提供對於的get和set方法,以便讓使用者在類的外部可以間接的訪問到私有屬性
例如:
//set負責給屬性賦值
//get負責返回屬性的值
public class Student{
private String name;
public void setName(String name){
}
public String getName(){
return this.name;
}
}
public class Test{
public static void main(String[] args){
Student s = new Student();
s.setName(“tom”);
System.out.println(s.getName());
}
}
1.3、封裝的作用
1)框架
2)工具類
1.4、封裝的意義
1)隱藏程式碼的實現細節
2)統一使用者的呼叫介面
3)提高系統的可維護性
二、方法的過載
類中有多個方法,有著相同的方法名,但是方法的引數各不相同,這種情況被稱為方法的過載。
方法的過載可以提供方法呼叫的靈活性。
例如:System.out.println()中的println方法,為什麼可以把不同型別的引數傳給這個方法?
例如:
public class Test{
public void test(String str){
}
public void test(int a){
}
}
方法過載必須滿足一下條件:
1)方法名相同
2)引數列表不同(引數的型別、個數、順序的不同)
public void test(Strig str){}
public void test(int a){}
public void test(Strig str,double d){}
public void test(Strig str){}
public void test(Strig str,double d){}
public void test(double d,Strig str){}
3)方法的返回值可以不同,也可以相同。
注:在java中,判斷一個類中的倆個方法是否相同,主要參考倆個方面:方法名字和引數列表
三、繼承
1)繼承是類和類之間的一種關係
除此之外,類和類之間的關係還有依賴、組合、聚合等。
2)繼承關係的倆個類,一個為子類(派生類),一個為父類(基類)。
子類繼承父類,使用關鍵字extends來表示
例如:
public class student extends Person{
}
3)子類和父類之間,從意義上講應該具有"is a"的關係.
例如:
student is a person
dog is a animal
4)類和類之間的繼承是單繼承
一個子類只能"直接"繼承一個父類,就像是一個人只能有一個親生父親
一個父類可以被多子類繼承,就像一個父親可以有多個孩子
注:java中介面和介面之間,有可以繼承,並且是多繼承。
5)父類中的屬性和方法可以被子類繼承
子類中繼承了父類中的屬性和方法後,在子類中能不能直接使用這些屬性和方法,是和這些屬性和方法原有的修飾符(public protected default private)相關的。
例如:
父類中的屬性和方法使用public修飾,在子類中繼承後"可以直接"使用
父類中的屬性和方法使用private修飾,在子類中繼承後"不可以直接"使用
注:具體細則在修飾符部分詳細說明
父類中的構造器是不能被子類繼承的,但是子類的構造器中,會隱式的呼叫父類中的無參構造器(預設使用super關鍵字)。
注:具體細節在super關鍵字部分詳細說明
6)Object類
java中的每一個類都是"直接" 或者 "間接"的繼承了Object類.所以每一個物件都和Object類有"is a"的關係。從API文件中,可以看到任何一個類最上層的父類都是Object。(Object類本身除外)
AnyClass is a Object
例如:
System.out.println(任何物件 instanceof Object);
//輸出結果:true
注:任何物件也包含陣列物件
例如:
//編譯後,Person類會預設繼承Object
public class Person{}
//Student是間接的繼承了Object
public class Student extends Person{}
在Object類中,提供了一些方法被子類繼承,那麼就意味著,在java中,任何一個物件都可以呼叫這些被繼承過來的方法。(因為Object是所以類的父類)
例如:toString方法、equals方法、getClass方法等
注:Object類中的每一個方法之後都會使用到.
四、super關鍵字
子類繼承父類之後,在子類中可以使用this來表示訪問或呼叫子類中的屬性或方法,使用super就表示訪問或呼叫父類中的屬性和方法。
4.1、super的使用
1)訪問父類中的屬性
例如:
public class Person{
protected String name = “zs”;
}
public class Student extends Person{
private String name = “lisi”;
public void tes(String name)t{
System.out.println(name);
System.out.println(this.name);
System.out.println(super.name);
}
}
2)呼叫父類中的方法
例如:
public class Person{
public void print(){
System.out.println(“Person”);
}
}
public class Student extends Person{
public void print(){
System.out.println(“Student”);
}
public void test(){
print();
this.print();
super.print();
}
}
3)呼叫父類中的構造器
例如:
public class Person{
}
public class Student extends Person{
//編譯通過,子類構造器中會隱式的呼叫父類的無參構造器
//super();
public Student(){
}
}
例如:
public class Person{
protected String name;
public Person(String name){
this.name = name;
}
}
public class Student extends Person{
//編譯報錯,子類構造器中會隱式的呼叫父類的無參構造器,但是父類中沒有無參構造器
//super();
public Student(){
}
}
例如:
public class Person{
protected String name;
public Person(String name){
this.name = name;
}
}
public class Student extends Person{
//編譯通過,子類構造器中顯式的呼叫父類的有參構造器
public Student(){
super("tom");
}
}
注:不管是顯式還是隱式的父類的構造器,super語句一定要出現在子類構造器中第一行程式碼。所以this和super不可能同時使用其呼叫構造器的功能,因為它們都要出現在第一行程式碼位置。
例如:
public class Person{
protected String name;
public Person(String name){
this.name = name;
}
}
public class Student extends Person{
//編譯報錯,super呼叫構造器的語句不是第一行程式碼
public Student(){
System.out.println(“Student”);
super(“tom”);
}
}
例如:
public class Person{
protected String name;
public Person(String name){
this.name = name;
}
}
//編譯通過
public class Student extends Person{
private int age;
public Student(){
this(20);
}
public Student(int age){
super(“tom”);
this.age = age;
}
}
4.2、super使用的注意的地方
1)用super呼叫父類構造方法,必須是構造方法中的第一個語句。
2)super只能出現在子類的方法或者構造方法中。
3)super 和 this 不能夠同時呼叫構造方法。(因為this也是在構造方法的第一個語句)
4.3、super 和 this 的區別
1)代表的事物不一樣:
this:代表所屬方法的呼叫者物件。
super:代表父類物件的引用空間。
2)使用前提不一致:
this:在非繼承的條件下也可以使用。
super:只能在繼承的條件下才能使用。
3)呼叫構造方法:
this:呼叫本類的構造方法。
super:呼叫的父類的構造方法
五、方法重寫(方法覆蓋)
1)方法重寫只存在於子類和父類(包括直接父類和間接父類)之間。在同一個類中方法只能被過載,不能被重寫.
2)靜態方法不能重寫
a. 父類的靜態方法不能被子類重寫為非靜態方法 //編譯出錯
b. 父類的非靜態方法不能被子類重寫為靜態方法;//編譯出錯
c. 子類可以定義與父類的靜態方法同名的靜態方法(但是這個不是覆蓋)
例如:
A類繼承B類 A和B中都一個相同的靜態方法test
B a = new A();
a.test();//呼叫到的是B類中的靜態方法test
A a = new A();
a.test();//呼叫到的是A類中的靜態方法test
可以看出靜態方法的呼叫只和變數宣告的型別相關
這個和非靜態方法的重寫之後的效果完全不同
3)私有方法不能被子類重寫
子類繼承父類後,是不能直接訪問父類中的私有方法的,那麼就更談不上重寫了。
例如:
public class Person{
private void run(){}
}
//編譯通過,但這不是重寫,只是倆個類中分別有自己的私有方法
public class Student extends Person{
private void run(){}
}
4)重寫的語法
1.方法名必須相同
2.引數列表必須相同
3.訪問控制修飾符可以被擴大,但是不能被縮小
public protected default private
4.丟擲異常型別的範圍可以被縮小,但是不能被擴大
ClassNotFoundException ---> Exception
5.返回型別可以相同,也可以不同,如果不同的話,子類重寫後的方法返回型別必須是父類方法返回型別的子型別
例如:父類方法的返回型別是Person,子類重寫後的返回類可以是Person也可以是Person的子型別
注:一般情況下,重寫的方法會和父類中的方法的宣告完全保持一致,只有方法的實現不同。(也就是大括號中程式碼不一樣)
例如:
public class Person{
public void run(){}
protected Object test()throws Exception{
return null;
}
}
//編譯通過,子類繼承父類,重寫了run和test方法.
public class Student extends Person{
public void run(){}
public String test(){
return "";
}
}
5)為什麼要重寫
子類繼承父類,繼承了父類中的方法,但是父類中的方法並不一定能滿足子類中的功能需要,所以子類中需要把方法進行重寫。
6)總結:
方法重寫的時候,必須存在繼承關係。
方法重寫的時候,方法名和形式引數 必須跟父類是一致的。
方法重寫的時候,子類的許可權修飾符必須要大於或者等於父類的許可權修飾符。( private < protected < public,friendly < public )
方法重寫的時候,子類的返回值型別必須小於或者等於父類的返回值型別。( 子類 < 父類 ) 資料型別沒有明確的上下級關係
方法重寫的時候,子類的異常型別要小於或者等於父類的異常型別。
六、多型
允許不同類的物件對同一訊息做出響應。即同一訊息可以根據傳送物件的不同而採用多種不同的行為方式。
相同類域的不同物件,呼叫相同的方法,執行結果是不同的
1)一個物件的實際型別是確定的
例如: new Student(); new Person();等
2)可以指向物件的引用的型別有很多
一個物件的實現型別雖然是確定的,但是這個物件所屬的型別可能有很多種。
例如: Student繼承了Person類
Student s1 = new Student();
Person s2 = new Student();
Object s3 = new Student();
因為Person和Object都是Student的父型別
注:一個物件的實際型別是確定,但是可以指向這個物件的引用的型別,卻是可以是這物件實際型別的任意父型別。
3)一個父類引用可以指向它的任何一個子類物件
例如:
Object o = new AnyClass();
Person p = null;
p = new Student();
p = new Teacher();
p = new Person();
4)多型中的方法呼叫
例如:
public class Person{
public void run(){}
}
public class Student extends Person{
}
//呼叫到的run方法,是Student從Person繼承過來的run方法
main:
Person p = new Student();
p.run();
例如:
public class Person{
public void run(){}
}
public class Student extends Person{
public void run(){
//重寫run方法
}
}
//呼叫到的run方法,是Student中重寫的run方法
main:
Person p = new Student();
p.run();
注:子類繼承父類,呼叫a方法,如果a方法在子類中沒有重寫,那麼就是呼叫的是子類繼承父類的a方法,如果重寫了,那麼呼叫的就是重寫之後的方法。
5)子類中獨有方法的呼叫
例如:
public class Person{
public void run(){}
}
public class Student extends Person{
public void test(){
}
}
main:
Person p = new Student();
//呼叫到繼承的run方法
p.run();
//編譯報錯,因為編譯器檢查變數p的型別是Person,但是在Person類中並沒有發現test方法,所以編譯報錯.
p.test();
注:一個變數x,呼叫一個方法test,編譯器是否能讓其編譯通過,主要是看宣告變數x的型別中有沒有定義test方法,如果有則編譯通過,如果沒有則編譯報錯.而不是看x所指向的物件中有沒有test方法.
原理:編譯看左邊,執行不一定看右邊。
編譯看左邊的意思:java 編譯器在編譯的時候會檢測引用型別中含有指定的成員,如果沒有就會報錯。子類的成員是特有的,父類的沒有的,所以他是找不到的。
6)子類引用和父類引用指向物件的區別
Student s = new Student();
Person p = new Student();
變數s能呼叫的方法是Student中有的方法(包括繼承過來的),變數p能呼叫的方法是Person中有的方法(包括繼承過來的)。
但是變數p是父型別的,p不僅可以指向Student物件,還可以指向Teacher型別物件等,但是變數s只能指向Studnet型別物件,及Student子型別物件。變數p能指向物件的範圍是比變數s大的。
Object型別的變數o,能指向所有物件,它的範圍最大,但是使用變數o能呼叫到的方法也是最少的,只能呼叫到Object中的宣告的方法,因為變數o宣告的型別就是Object.
注:java中的方法呼叫,是執行時動態和物件繫結的,不到執行的時候,是不知道到底哪個方法被呼叫的。
7)重寫、過載和多型的關係
過載是編譯時多型
呼叫過載的方法,在編譯期間就要確定呼叫的方法是誰,如果不能確定則編譯報錯
重寫是執行時多型
呼叫重寫的方法,在執行期間才能確定這個方法到底是哪個物件中的。這個取決於呼叫方法的引用,在執行期間所指向的物件是誰,這個引用指向哪個物件那麼呼叫的就是哪個物件中的方法。(java中的方法呼叫,是執行時動態和物件繫結的)
8)多型的注意事項
多型情況下,父類 和 子類存在同名的成員變數,無論是靜態的還是非靜態的變數,預設訪問的是父類中的成員變數。
多型情況下,父類 和 子類存在同名的非靜態方法,訪問的是子類的非靜態方法。
多型情況下,父類 和子類存在同名的靜態方法,訪問的是父類的靜態方法。
多型情況下,不能訪問子類特有的屬性、方法。
多型滿足的條件:必須要有繼承關係。
多型情況下,子類 和 父類如果存在同名的成員,訪問的都是父類,除了同名的非靜態變數訪問的才是子類。
9)多型存在的條件
1)有繼承關係
2)子類重寫父類方法
3)父類引用指向子類物件
補充一下第二點,既然多型存在必須要有“子類重寫父類方法”這一條件,那麼以下三種類型的方法是沒有辦法表現出多型特性的(因為不能被重寫):
1)static方法,因為被static修飾的方法是屬於類的,而不是屬於例項的
2)final方法,因為被final修飾的方法無法被子類重寫
3)private方法和protected方法,前者是因為被private修飾的方法對子類不可見,後者是因為儘管被protected修飾的方法可以被子類見到,也可以被子類重寫,但是它是無法被外部所引用的,一個不能被外部引用的方法,怎麼能談多型呢
七、instanceof和型別轉換
7.1、instanceof
public class Person{
public void run(){}
}
public class Student extends Person{
}
public class Teacher extends Person{
}
例如:
main:
Object o = new Student();
System.out.println(o instanceof Student);//true
System.out.println(o instanceof Person);//true
System.out.println(o instanceof Object);//true
System.out.println(o instanceof Teacher);//false
System.out.println(o instanceof String);//false
---------------------------
Person o = new Student();
System.out.println(o instanceof Student);//true
System.out.println(o instanceof Person);//true
System.out.println(o instanceof Object);//true
System.out.println(o instanceof Teacher);//false
//編譯報錯
System.out.println(o instanceof String);
---------------------------
Student o = new Student();
System.out.println(o instanceof Student);//true
System.out.println(o instanceof Person);//true
System.out.println(o instanceof Object);//true
//編譯報錯
System.out.println(o instanceof Teacher);
//編譯報錯
System.out.println(o instanceof String);
注1:
System.out.println(x instanceof Y);
該程式碼能否編譯通過,主要是看宣告變數x的型別和Y是否存在子父類的關係.有"子父類關"系就編譯通過,沒有子父類關係就是編譯報錯.
之後學習到的介面型別和這個是有點區別的。
注2:
System.out.println(x instanceof Y);
輸出結果是true還是false,主要是看變數x所指向的物件實際型別是不是Y型別的"子型別".
例如:
main:
Object o = new Person();
System.out.println(o instanceof Student);//false
System.out.println(o instanceof Person);//true
System.out.println(o instanceof Object);//true
System.out.println(o instanceof Teacher);//false
System.out.println(o instanceof String);//false
7.2、型別轉換
public class Person{
public void run(){}
}
public class Student extends Person{
public void go(){}
}
public class Teacher extends Person{
}
1)為什麼要型別轉換
//編譯報錯,因為p宣告的型別Person中沒有go方法
Person p = new Student();
p.go();
//需要把變數p的型別進行轉換
Person p = new Student();
Student s = (Student)p;
s.go();
或者
//注意這種形式前面必須要倆個小括號
((Student)p).go();
2)型別轉換中的問題
//編譯通過 執行沒問題
Object o = new Student();
Person p = (Person)o;
//編譯通過 執行沒問題
Object o = new Student();
Student s = (Student)o;
//編譯通過,執行報錯
Object o = new Teacher();
Student s = (Student)o;
即:
X x = (X)o;
執行是否報錯,主要是變數o所指向的物件實現型別,是不是X型別的子型別,如果不是則執行就會報錯。
為什麼某些人會一直比你優秀,是因為他本身就很優秀還一直在持續努力變得更優秀,而你是不是還在滿足於現狀內心在竊喜!
合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!
還是那句話,希望此文能幫到大家的同時,也聽聽大家的觀點。歡迎留言討論,加關注,分享你的高見!持續更新!
To-陌霖Java架構
分享網際網路最新文章 關注網際網路最新發展