
另一個例子:
public static void main(String[] args) { A test = new B(); System.out.println(test.a); } class A { int a = 1; } class B extends A { int a = 2; }
結果是1,是父類中的屬性.這個時候是否存在父類對象?
這時候將父類A用abstract修飾,按理說abstract累不能實例化吧,肯定不能得到父類中的a屬性,結果還是一樣的,怎么理解?
分析結果:
不會產生父類對象,只是用了父類的構造函數而已,並不是用到構造函數就會產生對象,構造函數只是起對象初始化作用的,而不是起產生對象作用的,如果new A();即只有new語句才會產生父類A的對象。
變量是靜態綁定 ,方法是動態綁定。 這里面變量在編譯期間實現了變量調用語句與變量定義賦值語句的綁定,綁定的自然是父類的,因為調用時類型是父類的,所以值是父類中定義的值
其實你可以這么理解 創建了一個子類對象時,在子類對象內存中,有兩份這個變量,一份繼承自父類,一份子類。
絕對不會產生父類對象,父類中的成員被繼承到子類對象中,用指向子類對象的父類引用調用父類成員,只不過是從 子類對象內存空間中找到那個被繼承來的父類成員,也就是說實質是用子類對象調用變量a,這樣就可以解釋成員必須通過對象調用的規定,只不過這時調用的是子類對象中的繼承自父類的a(子類對象中有兩個a,一個繼承自父類,一個屬於自己)
看另一個例子:
public class FieldDemo { public static void main(String[] args){ Student t = new Student("Jack"); Person p = t;//父類創建的引用指向子類所創建的對象 System.out.println(t.name+","+p.name); System.out.println(t.getName()+","+p.getName()); } } class Person{ String name; int age; public String getName(){ return this.name; } } class Student extends Person{ String name; // 屬性和父類屬性名相同,但在做開發時一般不會和父類屬性名相同!! public Student(String name){ this.name = name; super.name = "Rose"; // 為父類中的屬性賦值 } public String getName(){ return this.name; } }
返回結果是:Jack,Rose
Jack,Jack
原因是:在Java中,屬性綁定到類型,方法綁定到對象!
內存圖如下:
3. java中靜態屬性和和靜態方法的繼承問題 以及多態的實質
首先結論是:java中靜態屬性和和靜態方法可以被繼承,但是沒有被重寫(overwrite)而是被隱藏。
靜態方法和屬性是屬於類的,調用的時候直接通過類名.方法名完成的,不需繼承機制就可以調用如果子類里面定義了靜態方法和屬性,那么這時候父類的靜態方法 或屬性稱之為“隱藏”,你如果想要調用父類的靜態方法和屬性,直接通過父類名.方法名或變量名完成,至於是否繼承一說,子類是有繼承靜態方法和屬性,但是 跟實例方法和屬性不太一樣,存在“隱藏”的這種情況。
多態之所以能夠實現是依賴於 繼承 接口和 重寫 、重載(繼承和重寫最為關鍵)。有了繼承和重寫就可以 實現父類的引用可以指向不同子類的對象。重寫的功能是:“重寫”后子類的優先級要高於父類的優先級,但是“隱藏”是沒有這個優先級之分的。
靜態屬性、靜態方法和非靜態的屬性都可以被 繼承 和 隱藏 而不能夠被重寫,因此不能實現多態,不能實現父類的引用可以指向不同子類的對象。 非靜態的方法可以被繼承和重寫,因此可以實現多態。
##接口中的實現和類中的繼承是兩個不同的概念,因此不可以說實現接口的子類從接口那里繼承了常量和方法
例證如下:
public class Test3 { public static void main(String[] args) { C c = new C(); System.out.println(c.name); System.out.println(c.str); c.sing();//輸出的結果都是父類中的非靜態屬性、靜態屬性和靜態方法,推出靜態屬性和靜態方法可以被繼承 System.out.println("-----------"); A c1 = new C(); System.out.println(c1.name); System.out.println(c1.str); c1.sing();//結果同上,輸出的結果都是父類中的非靜態屬性、靜態屬性和靜態方法,推出靜態屬性和靜態方法可以被繼承 System.out.println("-----------"); B b = new B(); System.out.println(b.name); System.out.println(b.str); b.sing();//結果都是子類的非靜態屬性,靜態屬性和靜態方法,這里和非靜態屬性和非靜態類的繼承相同 System.out.println("-----------"); A b1 = new B(); System.out.println(b1.str);//結果是父類的靜態屬性,說明靜態屬性不可以被重寫,不能實現多態 System.out.println(b1.name);//結果是父類的非靜態屬性,說明非靜態屬性不可以被重寫,不能實現多態 b1.sing();//結果都是父類的靜態方法,說明靜態方法不可以被重寫,不能實現多態 } } class A//父類 { public static String str = "父類靜態屬性"; public String name = "父類非靜態屬性"; public static void sing() { System.out.println("父類靜態方法"); } public void run() { System.out.println("父類非靜態方法"); } } class B extends A //子類B { public static String str = "B該改寫后的靜態屬性"; public String name ="B改寫后的非靜態屬性"; public static void sing() { System.out.println("B改寫后的靜態方法"); } } class C extends A //子類C繼承A中的所有屬性和方法 { }
結果:
Java 父子類構造方法中的this變量
代碼如下
public class Father { private Integer a = 10; // 為了便於展示初始化的過程 private String name; public Father(String name) { this.name = name; } Father() { System.out.println(this instanceof Father); // true System.out.println(this instanceof Son); // true System.out.println(this.a); // 10 變量是靜態綁定的 所以在父類中,this.(變量名)輸出的只能是父類中的變量 System.out.println(((Son)this).a); // null 把this強轉成了Son,此時Son中的a還沒進行初始化,使用的是默認值null System.out.println(this.getClass().getName()); // com.gdut.test1.Son 當前的this實例對象是子類Son } public String getName() { return name; } public void setName(String name) { this.name = name; } public static void main(String[] args) { Father s = new Son("sonName"); System.out.println(s.getName()); // sonName } } class Son extends Father { public Integer a = 100; private String name; public Son() { //顯示調用super() 寫不寫都一樣 super(); } public Son(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
思考另一個問題:父類變量私有化后,在子類的構造方法中,為什么使用父類構造方法super(參數),可以給子類對象賦值?
例如:
public class Test7 { public static void main(String[] args) { Student s = new Student("aa", 12, 90); System.out.println(s.getName()); System.out.println(s.getAge()); } } class Person1{ private String name; private int age; // 無參構造方法 public Person1(){ } public Person1(String name, int age){ this.name = name; this.age = age; } // 公有的set get 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; } } class Student extends Person1{ //屬性 private int score; public Student(){} public Student(String name, int age, int score){ super(name, age); this.score = score; } //生成get/set方法 public int getScore(){ return score; } public void setScore(int score){ this.score = score; } }
輸出結果:
aa
12
為什么子類中的構造器調用父類的構造器給變量賦值的時候,可以給子類繼承的變量也賦值了?
public Student(String name, int age, int score){ super(name, age); this.score = score; } // super到下面 public Person1(String name, int age){ this.name = name; this.age = age; }
這時候可以測試一下就知道了
public class Test7 { public static void main(String[] args) { Student s = new Student("aa", 12, 90); System.out.println(s.getName()); // bb System.out.println(s.getAge()); // 12 } } class Person1{ private String name; private int age; // 無參構造方法 public Person1(){ } public Person1(String name, int age){ this.name = name; this.age = age; System.out.println(name); // aa System.out.println(this.name); // aa System.out.println(this instanceof Student); // true this.name = "bb"; System.out.println(this.name); // bb } // 公有的set get 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; } } class Student extends Person1{ //屬性 private int score; public Student(){} public Student(String name, int age, int score){ super(name, age); this.score = score; } //生成get/set方法 public int getScore(){ return score; } public void setScore(int score){ this.score = score; } }
輸出結果:
這時候可以得出的結論是,子類Student繼承了父類的Person1的兩個變量屬性:name、age,當Student中的構造器調用super(name, age)的時候,這需要去初始化父類Person1,在Person1的構造器public Person1(String name, int age){......} 中,this.name = name; this.age = age; 這個this對象其實就是Student的實例對象,這里是初始化子類Student的name、age屬性變量。
Java中子類繼承了父類的私有屬性及方法嗎?
結論:子類對象確實擁有父類對象中所有的屬性和方法,但是父類對象中的私有屬性和方法,子類是無法訪問到的,只是擁有,但不能使用。就像有些東西你可能擁有,但是你並不能使用。所以子類對象是絕對大於父類對象的,所謂的子類對象只能繼承父類非私有的屬性及方法的說法是錯誤的。可以繼承,只是無法訪問到而已。
子類不能直接訪問父類的私有屬性,子類只能在父類中寫一個public的getXXX的方法來獲取父類中的private屬性,子類就調用父類的getXXX來獲取private屬性
父類中的公有方法和域(屬性),在類繼承中將會被子類繼承,但是私有的將不能被繼承。
那么在繼承中子類如何才能繼承到父類的私有域呢?
答案是:在子類的構造方法中通過super()方法調用父類的構造方法。
也就是,在構造子類的同時,為子類構造出跟父類相同的域。如此就在子類的對象中,也擁有了父類聲明的域了。