繼承(extends)
-
什麼是繼承?繼承有什麼用?
繼承:在現實世界中也存在,例如:父親有錢,兒子不用努力也很有錢
繼承的作用:
- 基本作用:子類繼承父類,代碼可以得到複用。
- 主要/重要作用:有了繼承關系,才有了後期的方法覆寫和多态機制
- 繼承的相關特性
-
B類繼承A類:A類為超類(superclass)、父類、基類。
B類則稱為子類(subclass)、派生類、擴充類
class A{} class B extends A{} //我們平時聊天說的比較多的是:父類和子類。 suoperclass 父類 subclass 子類
-
java中的繼承隻支援單繼承,不支援多繼承 ,C++中支援多繼承
這也是java展現簡單性的一點,換句話說,java中不允許這樣寫代碼:
class B extends A,C{}//這樣寫是錯誤的.
- 雖然java不支援多繼承,但有的時候會産生間接繼承的效果,例如:
calss C extends B{} class B extends A{} //也就是說:c直接繼承B、其實C還間接繼承A.
public class ExtendsTest02 {public static void main(String[] args){}} class AA{} class BB{} class CC extends AA{} class DD extends BB{} //文法錯誤 //java隻允許單繼承,不允許多繼承。java是簡單的,C++支援多繼承。 //C++更接近現實一些。現實中:兒子同時繼承父母親的基因。 //class EE extends AA,BB{} class XX{} class YY extends XX{} //其實這樣也說明了ZZ是繼承XX和YY的。 //較長的描述:ZZ直接繼承了YY ,間接繼承了XX。 class ZZ extends YY{}
- java中規定:子類繼承父類,除了構造方法不能繼承外,剩下的都可以繼承,但是私有的屬性無法在子類中直接通路。(父類中private修飾的不能在子類中直接通路,可以通過間接的手段通路,如:使用set和get方法)
- java的類沒有顯示的繼承任何類,則預設繼承Object類,Object類是java語言提供的根類(老祖宗類),也就是說:類一旦建立,就有Object類型中所有的特征。
//C繼承B,B繼承A,A繼承Object //C具有所有的Object對象的特征(基因) //Object是所有類的超類、老祖宗,是類體系結構中的根。 //java這麼龐大的一個繼承結構,最頂點:Object
- 繼承也有缺點: 如:CreditAccount類繼承Account類會導緻它們之間的耦合度非常高,Account類發生改變之後馬上影響到CreditAccount類。
/* 使用繼承解決 繼承:除了構造方法其他的都繼承,私有化的屬性也繼承,但是在子類中不能直接通路 繼承也是有缺點的:耦合度高,父類修改,字類受牽連。 */ public class ExtendsTest01 { public static void main(String[] args){ //建立普通賬戶 Account1 act = new Account1(); act.setActNo("123456"); act.setBalance(10000); System.out.println(act.getActNo()+"的餘額:"+act.getBalance()); //建立信用賬戶 CreditAccount ca = new CreditAccount(); ca .setActNo("654321"); ca.setBalance(-20000); ca.setCredit(0.99); System.out.println(ca.getActNo()+"的餘額:"+ca.getBalance()+" \t他的信譽為:"+ca.getCredit()); } } //銀行賬戶類 //賬戶屬性:賬戶、餘額 class Account1{//父類 //屬性 private String actNo; private double balance; //構造方法 public Account1(){} public Account1(String actNo,double balance){ this.actNo = actNo; this.balance = balance; } //set 和 get 方法 public String getActNo(){ return actNo; } public void setActNo(String actNo){ this.actNo = actNo; } public double getBalance(){ return balance; } public void setBalance(double balance){ this.balance = balance; } } //其他類型的賬戶:信用卡賬戶 //賬号、餘額、信譽度 class CreditAccount extends Account1{//子類 private double credit; //構造方法 public CreditAccount(){} public void doSome(){ //錯誤: actNo 在 Account1 中是 private 通路控制 //繼承的私有屬性無法直接通路。 //System.out.println(actNo); //隻能間接通路 System.out.println(getActNo()); } //set 和 get 方法 public double getCredit(){ return credit; } public void setCredit(double credit){ this.credit = credit; } } /*分析以下程式存在什麼問題? 代碼臃腫,代碼沒有得到複用性。 */ /* public class ExtendsTest01 { public static void main(String[] args){ //建立普通賬戶 Account1 act = new Account1(); act.setActNo("123456"); act.setBalance(10000); System.out.println(act.getActNo()+"的餘額:"+act.getBalance()); //建立信用賬戶 CreditAccount ca = new CreditAccount(); ca .setActNo("654321"); ca.setBalance(-20000); ca.setCredit(0.99); System.out.println(ca.getActNo()+"的餘額:"+ca.getBalance()+" \t他的信譽為:"+ca.getCredit()); } } //銀行賬戶類 //賬戶屬性:賬戶、餘額 class Account1{ //屬性 private String actNo; private double balance; //構造方法 public Account1(){} public Account1(String actNo,double balance){ this.actNo = actNo; this.balance = balance; } //set 和 get 方法 public String getActNo(){ return actNo; } public void setActNo(String actNo){ this.actNo = actNo; } public double getBalance(){ return balance; } public void setBalance(double balance){ this.balance = balance; } } //其他類型的賬戶:信用卡賬戶 //賬号、餘額、信譽度 class CreditAccount{ private String actNo; private double balance; private double credit; //構造方法 public CreditAccount(){} public CreditAccount(String actNo,double balance,double credit){ this.actNo = actNo; this.balance = balance; this.credit = credit; } //set 和 get 方法 public String getActNo(){ return actNo; } public void setActNo(String actNo){ this.actNo = actNo; } public double getBalance(){ return balance; } public void setBalance(double balance){ this.balance = balance; } public double getCredit(){ return credit; } public void setCredit(double credit){ this.credit = credit; } } */
-
- 測試: 字類繼承父類之後,能使用字類調用父類方法嗎?
/* 測試: 子類繼承父類之後,能使用子類調用父類方法嗎? 本質上,子類繼承父類之後,父類繼承過來的方法歸自己所有。 繼承:把父類的東西複制一份挪過來。 實際上調用的也不是父類的方法,是子類自己的方法(因為已經繼承過來了,就屬于自己的。) */ public class ExtendsTest03 { public static void main(String[] args){ Cat c = new Cat(); //調用父類中方法 c.move(); //子類可以通路name嗎 //父類屬性如果沒有封裝,子類可以通路。封裝後使用get以及set方法讀改。 System.out.println(c.name);//可以 } } class Animal{//先不封裝 //名字 String name = "xiaohua"; //提供一個動物移動的方法 public void move(){ System.out.println(name +"正在疾跑"); } } //Cat類繼承Animal類,會将Animal中除構造方法外的東西都繼承過來。 class Cat extends Animal{ }
- 在實際開發中,滿足什麼條件時才可以使用繼承?
/* 凡是采用“is a” 能描述的,都可以繼承。 例如:Cat is a Animal :貓是一個動物 例如:Dog is a Animal :狗是一個動物 CreditAccount is a Account :信用卡是一個銀行賬戶 ...... 假設以後開發中有一個A類,有一個B類,A類和B類确實也有重複的代碼,那麼他們兩個之間就可以繼承嗎? 不一定,還是要看他們之間是不是可以使用is a來描述 */ class Customer{ String name;//名字 //set以及get 方法 } class Product{ String name //名字 //set以及get 方法 } //class Product extends Customer{} //這種繼承就是錯誤的,因為Product is a Customer 是不符現實的。 //雖然他們都有名字這個屬性,但一個是人,一個是東西。是以繼承主要還是要兩個類之間存在關系。
-
任何一個類,沒有顯示繼承任何類,預設繼承Object,那麼Object類當中有哪些方法呢?老祖宗為我們提供了哪些方法?
println()方法的解釋
Object中有一個方法toString(),測試後發現:/* out後面沒有小括号,說明out是變量名。System是一個類名,類名.out,說明out是一個靜态變量。 System.out傳回一個對象,然後采用“對象.”的方式通路println()方法。 */ //以下代碼中:System out println都是辨別符 public class Test3 { //靜态變量 static Student6 stu = new Student6(); //入口 public static void main(String[] args){ //拆分為兩行 Student6 s = Test3.stu;//把靜态變量的值指派給一個變量 s.exam(); //通過這個變量調用其中的方法。 //合并 Test3.stu.exam(); System.out.println("Hello World"); } } class Student6{ //執行個體方法 public void exam(){ System.out.println("考試中。。"); } }
Sysetm.out.println(引用); //當我們直接輸出一個“引用”的時候,println()方法會自動調用“引用.toString()”,然後輸出toString()的執行結果。
/* //預設繼承Object,Object中有哪些方法呢? public class Object { //注意:當源碼中一個方法以“;”結尾,并且修飾符清單中有“native”關鍵字時 //表示底層調用C++寫的dll程式(dll動态連結庫檔案) private static native void registerNatives(); //靜态代碼塊 static { //調用registerNatives方法 registerNatives(); } //無參數構造方法 @HotSpotIntrinsicCandidate public Object() {} //底層也是調用C++ @HotSpotIntrinsicCandidate public final native Class<?> getClass(); //底層也是調用C++ @HotSpotIntrinsicCandidate public native int hashCode(); //equals方法應該看懂 //public是公開的、boolean是方法的傳回值類型、equals是一個方法名,意思是:相等。 //(Object obj) 形參 //隻不過目前還不知道這個方法存在的意義。 public boolean equals(java.lang.Object obj) { //方法體 return (this == obj); } //已有對象a,想建立一個和a一模一樣的對象,你可以調用這個克隆方法。 //底層也是調用C++ @HotSpotIntrinsicCandidate protected native java.lang.Object clone() throws CloneNotSupportedException; //一會可以測試一下toString()方法 //public 表示公共的、String是傳回值類型、toString()方法執行結束之後傳回一個字元串。 //toString是方法名,()表示形參個數為0。 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } @HotSpotIntrinsicCandidate public final native void notify(); @HotSpotIntrinsicCandidate public final native void notifyAll(); public final void wait() throws InterruptedException { wait(0L); } public final native void wait(long timeoutMillis) throws InterruptedException; public final void wait(long timeoutMillis, int nanos) throws InterruptedException { if (timeoutMillis < 0) { throw new IllegalArgumentException("timeoutMillis value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeoutMillis++; } wait(timeoutMillis); } @Deprecated(since="9") protected void finalize() throws Throwable { } } */ // public class ExtendsTest04 { // ExtendsTest04中預設繼承Object // ExtendsTest04類當中是有toString()方法 // 不過toString()方法是一個執行個體方法,需要建立對象才能調用。 public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public static void main(String[] args){ //分析這個代碼可以執行嗎? //ExtendsTest04.toStirng(); //報錯 //先new對象 ExtendsTest04 et = new ExtendsTest04(); String retValue = et.toString();//傳回String類型的值,定義一個變量接收 System.out.println(retValue);//[email protected] //1b6d3586 可以“等同”看做對象在堆記憶體中的記憶體位址 //實際上是記憶體位址經過“雜湊演算法”得出的十六進制結果 //建立對象 Product1 p = new Product1(); System.out.println(p.toString());//[email protected] System.out.println(100); System.out.println(true); //如果直接輸出“引用”呢? //直接輸出引用,會自動調用這個引用的toString()方法 System.out.println(p);//[email protected] //println方法會自動調用p的toString()方法 } } class Product1{ /* public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } */ }