java-day21
目錄
四種訪問權限修飾符:
-
public 子類中繼承後一定可以直接訪問
-
protected 子類中繼承後一定可以直接訪問
-
default 子類中繼承後可能可以直接訪問
-
子父類同包,則可以直接訪問
-
子父類不同包,則不可以直接訪問
-
-
private 子類中繼承後一定不可以直接訪問
-
注意,能直接訪問就說明這個繼承過來的方法可以重寫,不能直接訪問則說明不能重寫。
多態
相同類型的不同對象,調用同一個方法,最終執行結果是不同的。
-
java中的對象的實際類型一但確定不會改變。
-
new Student();這個對象的實際類型就是Student,因爲是使用 Student類創建出來的。但是這個對象也屬性object類型
-
//例如 new Student(); new Person();
-
一個變量一旦被聲明出來,那麼這個變量的類型就不會再改變了,雖然可以進行強制類型轉換,只是得到了一個轉換後的結果,轉換結束之後,這個變量還是原來的類型。
-
//例如 Person p; p = new Student(); Student stu = (Student) p;
-
這裏的變量p,聲明爲Person類型的,那麼任何時候訪問到這 個變量p都是Person類型的,不會改變。
-
-
-
指向同一個對象的引用可以是不同的類型
-
一個對象的實際類型雖然是確定的只有一個,但是對象所處的類型卻有很多種(一般是它的父類型或者實現的接口),所以我們可以使用不同的類型去指向一個對象。
-
Student s1 = new Student(); Person s2 = new Student(); Object s3 = new Student(); //Person 和 Object都是Student類的父類型
-
-
一個父類型的引用可以指向它的任何子類對象
-
一個對象可以被他的 直接父類 / 間接父類 / 實現的接口類型 的引用所指向
-
Object o = new 任意類型(); Person p; p = new Student(); p = new Teacher(); p = new Person();
-
-
多態中的方法調用(結合重寫)
-
public class Person{ public void run(){} } public class Student extends Person{ } //調用的run方法是從父類Person中繼承過來的 main: Person p = new Student(); p.run();
public class Person{ public void run(){} } public class Student extends Person{ public void run(){ //子類中重寫這個run方法 } } //調用的run方法是子類重寫的run方法 main: Person p = new Student(); p.run();
-
-
子類中獨有的方法調用
-
子類繼承了父類,然後在子類中又拓展一些屬於自己的方法
-
public class Person{ public void run(){} } public class Student extends Person{ public void say(){} } main: Person p; p = new Student(); //編譯通過 p.run(); //編譯報錯 //因爲變量p的類型是Person,並且這個變量p的類型是不會改變的, //編譯器檢查這個Person發現沒有say方法,所以報錯。 p.say();
-
注意,一個變量,假設名字爲x,當這個變量去調用一個方法的時候,例如x.test(),編譯器能否讓這句代碼編譯通過,主要是看這個變量x聲明時候的類型是什麼,以及這個類型中是否定義了要調用的test方法,如果有test方法則編譯通過,否則編譯報錯。至於這個變量x是指向哪一個具體的對象,編譯器是不關心的。因爲在運行期間,這個變量x是可以隨意指向它的任何一個子類對象的,在編譯的期間根本無法確定將來到底會指向哪一個對象。
-
總結:編譯器只關心引用聲明時的所屬類型,而不關心這個引用指向哪一個具體的對象。
-
-
-
子類引用和父類引用指向對象的區別
-
Student s = new Student(); Person p = new Student();
-
子類類型的變量s,能調用的方法是Student中有的方法,包含Student繼承過來和Student中自己的方法。
-
父類類型的變量p,能調用的方法是Person中的有的方法,包含Person繼承過來和Person中自己的方法。
-
變量s能調用的方法是比變量p能調用的方法要多的。
-
父類類型的變量p,不僅可以指向Student對象,還可以指向Teacher對象,假設Teacher、Student都是Person的子類型
-
子類類型的變量s,只能指向Student類型的對象,以及Student子類類型的對象。
-
變量p能夠指向的對象範圍是要變量s要大的。
-
-
Object o = new Student();
-
Object類型的變量o,能指向的對象訪問是最大的,能夠指向java中的任意的對象,但是變量o能調用到的方式也是最少的,只能調用方法只有Object類中聲明的方法,因爲變量o聲明的類型就是Object。
-
-
java中的方法調用,是運行時動態和對象綁定的,不到最後運行的時候,是無法知道將來哪一個對象中的test方法被調用。
-
-
使用多態的相關示例
-
一個java文件中,可以寫多個類,但只能有一個類是public的
-
//Shape.java: public class Shape{ public void draw(){ System.out.println("正在進行圖形的繪製..."); } public void draw(String title){ System.out.println("正在進行圖形的繪製... 標題爲:"+title); } } class Circle extends Shape{ public void draw(){ System.out.println("繪製一個圓形"); } public void draw(String title){ System.out.println("繪製一個圓形... 標題爲:"+title); } } class Rectangle extends Shape{ public void draw(){ System.out.println("繪製一個正方形"); } public void draw(String title){ System.out.println("繪製一個正方形... 標題爲:"+title); } } class ShapeTest{ public static void main(String[] args){ ShapeTest t = new ShapeTest(); Shape shape; int a = (int)(Math.random()*10); //[0,9] if(a%3==0){ shape = new Shape(); } else if(a%3==1){ shape = new Circle(); } else{ shape = new Rectangle(); } t.draw(shape); } //這裏shape.draw()方法的調用 //將來到底會調用到哪一個對象中的draw方法 //主要是看在運行期間,這個引用shape最終指向的對象是誰,以及這個對象中是否重寫過draw方法 public void draw(Shape shape){ shape.draw(); } public void draw(Shape shape,String title){ shape.draw(title); } }
//Game.java: public class Game{ public void start(Ball ball){ ball.run(); } } class Ball{ public void run(){ System.out.println("啓動遊戲..."); } } class BasketBall extends Ball{ public void run(){ System.out.println("啓動運行籃球遊戲..."); } } class FootBall extends Ball{ public void run(){ System.out.println("啓動運行足球遊戲..."); } } class PingPangBall extends Ball{ public void run(){ System.out.println("啓動運行乒乓球遊戲..."); } } class GameTest{ public static void main(String[] args){ Game g = new Game(); Ball ball; ball = new BasketBall(); ball = new FootBall(); ball = new PingPangBall(); g.start(ball); } }
-
-
instanceof關鍵字
-
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);//編譯報錯
-
注意,System.out.println(x instanceof Y); 編譯的問題
-
這個代碼能不能編譯通過,主要是看變量x的聲明類型,和要判斷的Y類型之間,有沒有子父類關係,如果有就編譯通過,如果沒有則編譯報錯。(這個是針對x的聲明類型和Y類型都是類的前提下)
-
-
注意,System.out.println(x instanceof Y); 運行的問題
-
編譯通過後,運行的結果是true還是false,主要是看引用類型變量x所指向對象的實際類型,是不是要比較的Y類型或者Y類型的子類類型,如果是的話,就返回true,否則返回false
-
-
總結,很多時候,編譯器在編譯期間,關注的是這個引用類型變量聲明的類型是什麼,而JVM在運行的時候,關注的時候這個引用類型變量所指向的對象是誰
-
-
-
類型轉換
-
public class Person{ public void run(){} } public class Student extends Person{ public void say(){} } public class Teacher extends Person{}
-
爲什麼要類型轉換
-
//編譯報錯,變量p聲明的類型Person中,沒有say方法 Person p = new Student(); p.say(); //需要把變量p轉換類型爲Student,就可以調用了 Person p = new Student(); Student stu = (Student)p; stu.say(); //或者 把倆行合成一行代碼 ((Student)p).say();
-
父類引用指向子類對象,這個父類的引用是不能調用到子類中獨有的方法的,如果想調用這個子類獨有的方法,那麼就需要做類型轉換
-
-
類型轉換中的問題
-
//編譯通過,運行也沒有問題 Object o = new Student(); Person p = (Person)o; //假設上面代碼可以轉換成功,將來會出現這個代碼: Person p = new Student();//把中間變量o去掉就是這個代碼 //編譯通過,運行也沒有問題 Object o = new Student(); Student p = (Student)o; //假設上面代碼可以轉換成功,將來會出現這個代碼: Student p = new Student();//把中間變量o去掉就是這個代碼 //編譯通過,運行也沒有問題 Object o = new Teacher(); Person p = (Person)o; //假設上面代碼可以轉換成功,將來會出現這個代碼: Person p = new Teacher();//把中間變量o去掉就是這個代碼 //編譯通過,運行報錯 Object o = new Teacher(); Student p = (Student)o; //假設上面代碼可以轉換成功,將來會出現這個代碼: Student p = new Teacher();//把中間變量o去掉就是這個代碼
-
總結:X x = (X)o;
-
對於這個一個類型轉換的代碼: 運行是否報錯,主要是看變量o在運行時所指向的對象的實際類型,是不是X類型或X類型的子類型,如果是的話,運行成功,否則運行報錯。另外,也可以使用instanceof來判斷這個引用o所指向的對象是否屬於要比較類型X.
-
//如果返回true,那麼這個類型強制轉換就可以的 //如果返回false,那麼這個類型強制轉換代碼如果執行了就會報錯 if(o instanceof X){ X x = (x)o; }
-
-
-