Java面向?qū)ο缶幊讨^承和多態(tài)以及包的解析與使用范例
1.繼承
為什么要有繼承?
多個(gè)類中存在相同屬性和行為時(shí),將這些內(nèi)容抽取到單獨(dú)一個(gè)類中,
那么多個(gè)類無(wú)需再定義這些屬性和行為,只要繼承那個(gè)類即可。
此處的多個(gè)類稱為子類(派生類),單獨(dú)的這個(gè)類稱為父類(基類 或超類)??梢岳斫鉃?“子類 is a 父類”
1.1繼承的基本使用
類繼承語(yǔ)法規(guī)則:
class 子類 extends 父類{ }
繼承的作用:
- 降低代碼的冗余度,提高代碼復(fù)用率
- 繼承的出現(xiàn),更有利于功能的擴(kuò)展。
- 繼承的出現(xiàn)讓類與類之間產(chǎn)生了關(guān)系,提供了多態(tài)的前提
注意:
- 子類不能直接訪問父類中私有的(private)的成員變量和方法
- Java只支持單繼承和多層繼承,不允許多重繼承
- 一個(gè)子類只能有一個(gè)父類一個(gè)父類可以派生出多個(gè)子類
如下代碼示例:
class Animal { public String name; public Animal(String name) { this.name = name; } public void eat(String food) { System.out.println(this.name + "正在吃" + food); } } class Cat extends Animal { public Cat(String name) { // 使用 super 調(diào)用父類的構(gòu)造方法. super(name); } } class Bird extends Animal { public Bird(String name) { super(name); } public void fly() { System.out.println(this.name + "正在飛 ︿( ̄︶ ̄)︿"); } } public class Test { public static void main(String[] args) { Cat cat = new Cat("小黑"); cat.eat("貓糧"); Bird bird = new Bird("圓圓"); bird.fly(); } }
1.2 protected 關(guān)鍵字
剛才我們發(fā)現(xiàn), 如果把字段設(shè)為 private, 子類不能訪問. 但是設(shè)成 public, 又違背了我們 “封裝” 的初衷.
兩全其美的辦法就是 protected 關(guān)鍵字.
- 對(duì)于類的調(diào)用者來說, protected 修飾的字段和方法是不能訪問的
- 對(duì)于類的 子類 和 同一個(gè)包的其他類 來說, protected 修飾的字段和方法是可以訪問的
四種權(quán)限修飾符:
1.3 final 關(guān)鍵字
曾經(jīng)我們學(xué)習(xí)過 final 關(guān)鍵字, 修飾一個(gè)變量或者字段的時(shí)候, 表示 常量 (不能修改).
final int a = 10; a = 20; // 編譯出錯(cuò)
final 關(guān)鍵字也能修飾類, 此時(shí)表示被修飾的類就不能被繼承
final public class Animal { ... } public class Bird extends Animal { ... } // 編譯出錯(cuò) Error:(3, 27) java: 無(wú)法從最終com.bit.Animal進(jìn)行繼承
final 關(guān)鍵字的功能是 限制 類被繼承。我們平時(shí)使用的String字符串類,就是被final修飾的,不能被繼承。
2.多態(tài)
2.1向上轉(zhuǎn)型
在剛才的例子中, 我們寫了形如下面的代碼:
Bird bird = new Bird("圓圓");
這個(gè)代碼也可以寫成這個(gè)樣子
Bird bird = new Bird("圓圓"); Animal bird2 = bird; // 或者寫成下面的方式 Animal bird2 = new Bird("圓圓");
此時(shí) bird2 是一個(gè)父類 (Animal) 的引用, 指向一個(gè)子類 (Bird) 的實(shí)例. 這種寫法稱為 向上轉(zhuǎn)型.
向上轉(zhuǎn)型是子類對(duì)象轉(zhuǎn)成父類對(duì)象
向上轉(zhuǎn)型發(fā)生的時(shí)機(jī):
- 直接賦值
- 方法傳參
- 方法返回
直接賦值的方式我們已經(jīng)演示了. 另外兩種方式和直接賦值沒有本質(zhì)區(qū)別
方法傳參
代碼示例:
public class Test { public static void main(String[] args) { Bird bird = new Bird("圓圓"); feed(bird); } public static void feed(Animal animal) { animal.eat("谷子"); } } // 執(zhí)行結(jié)果 圓圓正在吃谷子
此時(shí)形參 animal 的類型是 Animal (基類), 實(shí)際上對(duì)應(yīng)到 Bird (父類) 的實(shí)例.
方法返回
代碼示例
public class Test { public static void main(String[] args) { Animal animal = findMyAnimal(); } public static Animal findMyAnimal() { Bird bird = new Bird("圓圓"); return bird; } }
此時(shí)方法 findMyAnimal 返回的是一個(gè) Animal 類型的引用, 但是實(shí)際上對(duì)應(yīng)到 Bird 的實(shí)例
2.2動(dòng)態(tài)綁定
當(dāng)子類和父類中出現(xiàn)同名方法的時(shí)候, 再去調(diào)用會(huì)出現(xiàn)什么情況呢?
對(duì)前面的代碼稍加修改, 給 Bird 類也加上同名的 eat 方法, 并且在兩個(gè) eat 中分別加上不同的日志.
如下:
// Animal.java public class Animal { protected String name; public Animal(String name) { this.name = name; } public void eat(String food) { System.out.println("我是一只小動(dòng)物"); System.out.println(this.name + "正在吃" + food); } } // Bird.java public class Bird extends Animal { public Bird(String name) { super(name); } public void eat(String food) { System.out.println("我是一只小鳥"); System.out.println(this.name + "正在吃" + food); } } // Test.java public class Test { public static void main(String[] args) { Animal animal1 = new Animal("圓圓"); animal1.eat("谷子"); Animal animal2 = new Bird("扁扁"); animal2.eat("谷子"); } }
// 執(zhí)行結(jié)果
我是一只小動(dòng)物
圓圓正在吃谷子
我是一只小鳥
扁扁正在吃谷子
此時(shí), 我們發(fā)現(xiàn):
- animal1 和 animal2 雖然都是 Animal 類型的引用, 但是 animal1 指向 Animal 類型的實(shí)例, animal2 指向Bird 類型的實(shí)例.
- 針對(duì) animal1 和 animal2 分別調(diào)用 eat 方法, 發(fā)現(xiàn) animal1.eat() 實(shí)際調(diào)用了父類的方法, 而animal2.eat() 實(shí)際調(diào)用了子類的方法.
因此, 在 Java 中, 調(diào)用某個(gè)類的方法, 究竟執(zhí)行了哪段代碼 (是父類方法的代碼還是子類方法的代碼) , 要看究竟這個(gè)引
用指向的是父類對(duì)象還是子類對(duì)象. 這個(gè)過程是程序運(yùn)行時(shí)決定的(而不是編譯期), 因此稱為 動(dòng)態(tài)綁定
2.3方法重寫
定義:在子類中可以根據(jù)需要對(duì)從父類中繼承來的方法進(jìn)行改造,也稱
為方法的重置、覆蓋。在程序執(zhí)行時(shí),子類的方法將覆蓋父類的方法。
要求:
- 子類重寫的方法必須和父類被重寫的方法具有相同的方法名稱、參數(shù)列表
- 子類重寫的方法的返回值類型不能大于父類被重寫的方法的返回值類型
- 子類重寫的方法使用的訪問權(quán)限不能小于父類被重寫的方法的訪問權(quán)限,子類不能重寫父類中聲明為private權(quán)限的方法
- 子類方法拋出的異常不能大于父類被重寫方法的異常
注意:
子類與父類中同名同參數(shù)的方法必須同時(shí)聲明為非static的(即為重寫),或者同時(shí)聲明為static的(不是重寫)。因?yàn)閟tatic方法是屬于類的,子類無(wú)法覆蓋父類的方法
方法重寫舉例1:
public class Person { public String name; public int age; public String getInfo() { return "Name: "+ name + "\n" +"age: "+ age; } } public class Student extends Person { public String school; public String getInfo() { //重寫方法 return "Name: "+ name + "\nage: "+ age + "\nschool: "+ school; } public static void main(String args[]){ Student s1=new Student(); s1.name="Bob"; s1.age=20; s1.school="school2"; System.out.println(s1.getInfo()); //Name:Bob age:20 school:school2 } }
方法重寫舉例2:
class Parent { public void method1() {} } class Child extends Parent { //非法,子類中的method1()的訪問權(quán)限private比被覆蓋方法的訪問權(quán)限public小 private void method1() {} } public class UseBoth { public static void main(String[] args) { Parent p1 = new Parent(); Child c1 = new Child(); p1.method1(); c1.method1(); } }
2.4向下轉(zhuǎn)型
向下轉(zhuǎn)型就是父類對(duì)象轉(zhuǎn)成子類對(duì)象. 相比于向上轉(zhuǎn)型來說, 向下轉(zhuǎn)型沒那么常見,
但是也有一定的用途.
- 從子類到父類的類型轉(zhuǎn)換可以自動(dòng)進(jìn)行
- 從父類到子類的類型轉(zhuǎn)換必須通過造型(強(qiáng)制類型轉(zhuǎn)換)實(shí)現(xiàn)
- 無(wú)繼承關(guān)系的引用類型間的轉(zhuǎn)換是非法的
- 在造型前可以使用instanceof操作符測(cè)試一個(gè)對(duì)象的類型
對(duì)于 Animal animal = new Bird(“圓圓”) 這樣的代碼:
編譯器檢查有哪些方法存在, 看的是 Animal 這個(gè)類型
執(zhí)行時(shí)究竟執(zhí)行父類的方法還是子類的方法, 看的是 Bird 這個(gè)類型.
那么想實(shí)現(xiàn)剛才的效果, 就需要向下轉(zhuǎn)型.
// (Bird) 表示強(qiáng)制類型轉(zhuǎn)換 Bird bird = (Bird)animal; bird.fly(); // 執(zhí)行結(jié)果 圓圓正在飛
為了讓向下轉(zhuǎn)型更安全, 我們可以先判定一下看看 animal 本質(zhì)上是不是一個(gè) Bird 實(shí)例, 再來轉(zhuǎn)換
Animal animal = new Cat("小貓"); if (animal instanceof Bird) { Bird bird = (Bird)animal; bird.fly(); }
instanceof 可以判定一個(gè)引用是否是某個(gè)類的實(shí)例. 如果是,則返回 ture。 這時(shí)再進(jìn)行向下轉(zhuǎn)型就比較安全了。
2.5super 關(guān)鍵字
2.5.1 super 關(guān)鍵字的基本用法
在Java類中使用super來調(diào)用父類中的指定操作:
- super可用于訪問父類中定義的屬性
- super可用于調(diào)用父類中定義的成員方法
- super可用于在子類構(gòu)造器中調(diào)用父類的構(gòu)造器
注意:
- 尤其當(dāng)子父類出現(xiàn)同名成員時(shí),可以用super表明調(diào)用的是父類中的成員 super的追溯不僅限于直接父類
- super和this的用法相像,this代表本類對(duì)象的引用,super代表父類的內(nèi)存
空間的標(biāo)識(shí)
示例1使用了 super 來調(diào)用父類的構(gòu)造器(這個(gè)代碼前面已經(jīng)寫過了)
public Bird(String name) { super(name); }
示例2使用 super 來調(diào)用父類的普通方法
public class Bird extends Animal { public Bird(String name) { super(name); } @Override public void eat(String food) { // 修改代碼, 讓子調(diào)用父類的接口. super.eat(food); System.out.println("我是一只小鳥"); System.out.println(this.name + "正在吃" + food); } }
2.5.2 this和super的區(qū)別
3.包的使用
包 (package) 是組織類的一種方式.
使用包的主要目的是保證類的唯一性.
例如, 你在代碼中寫了一個(gè) Test 類. 然后你的同事也可能寫一個(gè) Test 類. 如果出現(xiàn)兩個(gè)同名的類, 就會(huì)沖突, 導(dǎo)致代碼不能編譯通過
3.1導(dǎo)入包中的類
包 (package) 是組織類的一種方式.
使用包的主要目的是保證類的唯一性
代碼示例:
public class Test { public static void main(String[] args) { java.util.Date date = new java.util.Date(); // 得到一個(gè)毫秒級(jí)別的時(shí)間戳 System.out.println(date.getTime()); } }
可以使用 import 語(yǔ)句導(dǎo)入包
import java.util.Date; public class Test { public static void main(String[] args) { Date date = new Date(); // 得到一個(gè)毫秒級(jí)別的時(shí)間戳 System.out.println(date.getTime()); } }
如果需要使用 java.util 中的其他類, 可以使用 import java.util.*
注意:Java是用到包中的那個(gè)類就導(dǎo)入那個(gè)類
但是我們更建議顯式的指定要導(dǎo)入的類名. 否則還是容易出現(xiàn)沖突的情況.
例如:
import java.util.*; import java.sql.*; public class Test { public static void main(String[] args) { // util 和 sql 中都存在一個(gè) Date 這樣的類, 此時(shí)就會(huì)出現(xiàn)歧義, 編譯出錯(cuò) Date date = new Date(); System.out.println(date.getTime()); } } // 編譯出錯(cuò)
在這種情況下我們就需要完整的包名
import static java.lang.Math.*; public class Test { public static void main(String[] args) { double x = 30; double y = 40; // 靜態(tài)導(dǎo)入的方式寫起來更方便一些.但不推薦 // double result = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); double result = sqrt(pow(x, 2) + pow(y, 2)); System.out.println(result); } }
3.2常見系統(tǒng)包
- java.lang:系統(tǒng)常用基礎(chǔ)類(String、Object),此包從JDK1.1后自動(dòng)導(dǎo)入。
- java.lang.reflect:java 反射編程包;
- java.net:進(jìn)行網(wǎng)絡(luò)編程開發(fā)包。
- java.sql:進(jìn)行數(shù)據(jù)庫(kù)開發(fā)的支持包。
- java.util:是java提供的工具程序包。(集合類等) 非常重要
- java.io:I/O編程開發(fā)包
以上就是Java面向?qū)ο缶幊讨^承和多態(tài)以及包的解析與使用范例的詳細(xì)內(nèi)容,更多關(guān)于Java 繼承和多態(tài) 的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot使用yml文件配置多環(huán)境方式(dev、test、prod)
這篇文章主要介紹了springboot使用yml文件配置多環(huán)境方式(dev、test、prod),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09解決nacos升級(jí)spring cloud 2020.0無(wú)法使用bootstrap.yml的問題
這篇文章主要介紹了解決nacos升級(jí)spring cloud 2020.0無(wú)法使用bootstrap.yml的問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06Spring Boot與RabbitMQ結(jié)合實(shí)現(xiàn)延遲隊(duì)列的示例
本篇文章主要介紹了Spring Boot與RabbitMQ結(jié)合實(shí)現(xiàn)延遲隊(duì)列的示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Java并發(fā)J.U.C并發(fā)容器類list set queue
這篇文章主要為大家介紹了Java并發(fā),J.U.C并發(fā)容器類list set queue,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06Java調(diào)用groovy實(shí)現(xiàn)原理代碼實(shí)例
這篇文章主要介紹了Java調(diào)用groovy實(shí)現(xiàn)原理代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12java遞歸設(shè)置層級(jí)菜單的實(shí)現(xiàn)
本文主要介紹了java遞歸設(shè)置層級(jí)菜單的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08mybatis多表查詢的實(shí)現(xiàn)(xml方式)
本文主要介紹了mybatis多表查詢的實(shí)現(xiàn)(xml方式),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03解決idea中javaweb的mysql8.0.15配置問題
這篇文章主要介紹了idea中javaweb的mysql8.0.15配置問題 ,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-05-05通過實(shí)例深入學(xué)習(xí)Java的Struts框架中的OGNL表達(dá)式使用
這篇文章主要通過實(shí)例介紹了Java的Strus框架中的OGNL表達(dá)式使用,Struts框架是Java的SSH三大web開發(fā)框架之一,需要的朋友可以參考下2015-11-11