- 資訊首頁(yè) > 開(kāi)發(fā)技術(shù) > 編程語(yǔ)言 >
- Java基礎之面向對象機制(多態(tài)、繼承)底層實(shí)現
為什么會(huì )產(chǎn)生Java?Java的特點(diǎn)是什么?
從C語(yǔ)言開(kāi)始講,C語(yǔ)言是一種結構化語(yǔ)言,模塊化編程,便于程序的調試,依靠非常全面的運算符和多樣的數據類(lèi)型,可以輕易完成各種數據結構的構建,通過(guò)指針類(lèi)型更可對內存直接尋址以及對硬件進(jìn)行直接操作,因此既能夠用于開(kāi)發(fā)系統程序,也可用于開(kāi)發(fā)應用軟件。其缺點(diǎn)就是封裝性弱,程序的安全性上不是很好。C語(yǔ)言的異常處理一般使用setjmp()與longjmp(),在捕獲到異常時(shí)進(jìn)行跳轉;或者使用abort()和exit()兩個(gè)函數,強行終止程序的運行。如果要實(shí)現多線(xiàn)程,應該要直接操作底層操作系統,語(yǔ)言本身沒(méi)有封裝該機制。
C語(yǔ)言是一門(mén)面向過(guò)程的語(yǔ)言,所謂面向過(guò)程指的是以“事件過(guò)程”為中心的編程思想,即按照事件的解決步驟,在函數中一步一步實(shí)現。其實(shí),生活中大部分事情都可以用面向過(guò)程的思想來(lái)解決。然而,當問(wèn)題的規模變大,且問(wèn)題中有多個(gè)部分是共同的特征時(shí),我們仍然需要對這件事情建立一步一步操作,此時(shí)面向過(guò)程就顯得繁重冗余,因此產(chǎn)生了面向對象的思想。
面向對象的思想是將事件中的一些共同特征抽象出來(lái)作為一個(gè)對象,一個(gè)對象包括屬性和方法,比如說(shuō)一個(gè)班級中的同學(xué),大家都擁有姓名、成績(jì)、年齡、興趣愛(ài)好,在操作這些數據的時(shí)候,我們只需要將共同的部分抽象出來(lái),然后給每個(gè)同學(xué)一個(gè)對象的實(shí)例。如果是面向過(guò)程的方法,我們需要為每一個(gè)同學(xué)定義屬性變量,執行某個(gè)動(dòng)作需要定義獨立的方法。因此,產(chǎn)生了C++語(yǔ)言。
C++繼承自C語(yǔ)言,可以進(jìn)行面向過(guò)程的程序設計,也可以抽象化出對象進(jìn)行基于對象的程序設計,也可以進(jìn)行繼承、多態(tài)為特點(diǎn)的面向對象的程序設計。在C++的面向對象設計中,將數據和相關(guān)操作封裝在一個(gè)類(lèi)中,類(lèi)的實(shí)例為一個(gè)對象。支持面向對象開(kāi)發(fā)的四個(gè)特性:封裝、抽象、繼承、多態(tài)。在C++語(yǔ)言中,內存分為堆(程序運行時(shí)分配內存)和棧(函數內部聲明的變量)兩部分,往往需要手動(dòng)管理內存,通過(guò)new,delete
動(dòng)態(tài)劃分內存并進(jìn)行內存的回收;類(lèi)中包含構造函數和析構函數,分別為建立對象和刪除對象釋放資源。
Java也是一門(mén)面向對象的語(yǔ)言,不僅吸收了C++的各種優(yōu)點(diǎn),同時(shí)摒棄了C++種難以理解的多繼承、指針等概念,功能更加強大且簡(jiǎn)單易上手。其特點(diǎn):簡(jiǎn)單、OOP、平臺無(wú)關(guān)性(JVM的功勞);相比于面向對象的語(yǔ)言C++而言,Java JVM的動(dòng)態(tài)內存管理非常優(yōu)秀。在發(fā)展的過(guò)程中,逐漸更新了更多強大的功能:XML支持、安全套接字soket支持、全新的I/O API、正則表達式、日志與斷言;泛型、基本類(lèi)型的自動(dòng)裝箱、改進(jìn)的循環(huán)、枚舉類(lèi)型、格式化I/O及可變參數。
在C語(yǔ)言、C++、Java的演化過(guò)程中,并不會(huì )導致新語(yǔ)言取代舊語(yǔ)言,每種語(yǔ)言按照自身的特點(diǎn)有了自己適合的領(lǐng)域。如追求程序的性能和執行效率,如系統底層開(kāi)發(fā),就需要使用C++,甚至C語(yǔ)言;安卓開(kāi)發(fā)、網(wǎng)站、嵌入式、大數據技術(shù)等領(lǐng)域,一般使用Java語(yǔ)言,由于其安全性、便攜性、可移植性、可維護性等,很容易實(shí)現多線(xiàn)程,代碼的可讀性高。
C語(yǔ)言和C++是編譯型的語(yǔ)言,而Java是解釋型的語(yǔ)言:
程序執行效率高,依賴(lài)編譯器,跨平臺性差
效率比較低,依賴(lài)解釋器,跨平臺性好
Java是靜態(tài)-強類(lèi)型語(yǔ)言。
多態(tài)是同一個(gè)行為具有多個(gè)不同表現形式或形態(tài)的能力。多態(tài)就是同一個(gè)接口,使用不同的實(shí)例而執行不同操作。
一般實(shí)現形式:
@Overload
:同一類(lèi)種方法名相同,參數不同;返回類(lèi)型不要求@Override
:子類(lèi)繼承自父類(lèi)的方法重寫(xiě)實(shí)現過(guò)程,返回值、形參不能變、只能重寫(xiě)函數體內的語(yǔ)句,便于子類(lèi)根據自身需要定義自己的行為。Animal b = new Dog();
重寫(xiě)規則:
final
,static
方法不可被重寫(xiě)
參數列表:與被重寫(xiě)方法的參數列表必須完全相同
返回類(lèi)型:與被重寫(xiě)方法的返回類(lèi)型可以不相同,但是必須是父類(lèi)返回值的派生類(lèi)
訪(fǎng)問(wèn)權限:不能比父類(lèi)中被重寫(xiě)的方法的訪(fǎng)問(wèn)權限更低(public > protected > private > )
拋異常:如果父類(lèi)方法拋異常,子類(lèi)不能拋更廣泛的異常
同包:除了private
final
可以重寫(xiě)所有父類(lèi)方法
不同包:只能重寫(xiě)public
或者 protected
的非final
方法
子類(lèi)中調用父類(lèi)被重寫(xiě)方法可以用super.method()
多態(tài)性特征的最基本體現有“重載”和“重寫(xiě)”,其實(shí)這兩個(gè)體現在Java虛擬機中時(shí)分派的作用。分派又分為靜態(tài)分派和動(dòng)態(tài)分派,靜態(tài)分派是指所有依賴(lài)靜態(tài)類(lèi)型來(lái)定位方法執行版本的分派動(dòng)作,動(dòng)態(tài)分派是指在運行期根據實(shí)際類(lèi)型確定方法執行版本的分派過(guò)程。
Animal animal = new Bird();
在上面代碼中Animal是父類(lèi),Bird是繼承Animal的子類(lèi);那么在定義animal對象時(shí)前面的“Animal”稱(chēng)為變量的靜態(tài)類(lèi)型(Static Type),或者叫外觀(guān)類(lèi)型(Apparent Type),后面的“Bird”則稱(chēng)為變量的實(shí)際類(lèi)型(Actual Type),靜態(tài)類(lèi)型和實(shí)際類(lèi)型在程序中都可以發(fā)生一些變化,區別是靜態(tài)類(lèi)型的變化僅僅在使用時(shí)發(fā)生,變量本身的靜態(tài)類(lèi)型不會(huì )被改變,并且最終的靜態(tài)類(lèi)型是在編譯器可知的;而實(shí)際類(lèi)型變化的結果在運行期才可以確定,編譯器在編譯程序的時(shí)候并不知道一個(gè)對象的實(shí)際對象是什么。
//實(shí)際類(lèi)型變化 Animal bird = new Bird(); Animal eagle = new Eagle(); //靜態(tài)類(lèi)型變化 sd.sayHello((Bird)bird); sd.sayHello((Eagle)eagle);
animal
對象的靜態(tài)類(lèi)型是Animal
,實(shí)際類(lèi)型是Bird
。靜態(tài)類(lèi)型在編譯期可知,實(shí)際類(lèi)型在運行時(shí)可知。
同一個(gè)類(lèi)中相同方法名的不同方法。
重載,使用哪個(gè)重載版本,就完全取決于傳入參數的數量和數據類(lèi)型。
方法重載是通過(guò)靜態(tài)分派實(shí)現的,靜態(tài)分派是發(fā)生在編譯階段,因此匹配到靜態(tài)類(lèi)型Animal。
例如下面代碼:
package test; /** * @Description: 方法靜態(tài)分派演示 * @version: v1.0.0 */ public class StaticDispatch { static abstract class Animal{ } static class Bird extends Animal{ } static class Eagle extends Animal{ } public void sayHello(Animal animal) { System.out.println("hello,animal"); } public void sayHello(Bird bird) { System.out.println("hello,I'm bird"); } public void sayHello(Eagle eagle) { System.out.println("hello,I'm eagle"); } public static void main(String[] args){ Animal bird = new Bird(); // 靜態(tài)類(lèi)型Animal(編譯期可知)--實(shí)際類(lèi)型Bird(運行期可知) Animal eagle = new Eagle(); // 靜態(tài)類(lèi)型Animal(編譯期可知)--實(shí)際類(lèi)型Eagle(運行期可知) StaticDispatch sd = new StaticDispatch(); sd.sayHello(bird); sd.sayHello(eagle); } } /* 結果: hello,animal hello,animal */
代碼中刻意地定義了兩個(gè)靜態(tài)類(lèi)型相同Animal,實(shí)際類(lèi)型不同的變量,但虛擬機(準確的是編譯器)在重載時(shí)是通過(guò)參數的靜態(tài)類(lèi)型而不是實(shí)際類(lèi)型來(lái)作為判斷依據的。并且靜態(tài)類(lèi)型是編譯期可知的,因此,在編譯階段,Javac編譯器會(huì )根據參數的靜態(tài)類(lèi)型決定使用哪個(gè)重載版本,因此選擇了sayHello(Animal)作為調用目標。
方法重載是通過(guò)靜態(tài)分派實(shí)現的,并且靜態(tài)分派是發(fā)生在編譯階段,所以確定靜態(tài)分派的動(dòng)作實(shí)際上不是由虛擬機來(lái)執行的;另外,編譯器雖然能確定出方法重載的版本,但在很多情況下這個(gè)版本并不是“唯一的”,往往只能確定一個(gè)“更加適合”的版本。
方法的重寫(xiě):與虛擬機中動(dòng)態(tài)分派的過(guò)程有著(zhù)密切聯(lián)系。
package test; /** * @Description: 方法動(dòng)態(tài)分派演示 * @version: v1.0.0 */ public class DynamicDispatch { static abstract class Animal{ protected abstract void sayHello(); } static class Bird extends Animal{ @Override protected void sayHello() { System.out.println("Bird say hello"); } } static class Eagle extends Animal{ @Override protected void sayHello() { System.out.println("Eagle say hello"); } } public static void main(String[] args){ Animal bird = new Bird(); Animal eagle = new Eagle(); bird.sayHello(); eagle.sayHello(); bird = new Eagle(); bird.sayHello(); } } /* 結果: Bird say hello Eagle say hello Eagle say hello */
通過(guò)javap反編譯命令來(lái)看一段該代碼的字節碼:
>javap -c DynamicDispatch.class Compiled from "DynamicDispatch.java" public class com.carmall.DynamicDispatch { public com.carmall.DynamicDispatch(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class com/carmall/DynamicDispatch$Bird 3: dup 4: invokespecial #3 // Method com/carmall/DynamicDispatch$Bir d."<init>":()V 7: astore_1 8: new #4 // class com/carmall/DynamicDispatch$Eagl e 11: dup 12: invokespecial #5 // Method com/carmall/DynamicDispatch$Eag le."<init>":()V 15: astore_2 16: aload_1 17: invokevirtual #6 // Method com/carmall/DynamicDispatch$Ani mal.sayHello:()V 20: aload_2 21: invokevirtual #6 // Method com/carmall/DynamicDispatch$Animal.sayHello:()V 24: new #4 // class com/carmall/DynamicDispatch$Eagle 27: dup 28: invokespecial #5 // Method com/carmall/DynamicDispatch$Eagle."<init>":()V 31: astore_1 32: aload_1 33: invokevirtual #6 // Method com/carmall/DynamicDispatch$Animal.sayHello:()V 36: return }
上面的指令,invokevirtual
表示運行時(shí)按照對象的類(lèi)來(lái)調用實(shí)例方法;invokespecial
根據編譯時(shí)類(lèi)型來(lái)調用實(shí)例方法,也就是會(huì )調用父類(lèi)。
0~15
行的字節碼是準備動(dòng)作,作用時(shí)建立bird
和eagle
的內存空間、調用Bird
和Eagle
類(lèi)型的實(shí)例構造器,將這兩個(gè)實(shí)例的引用存放在第1、2個(gè)局部變量表Slot之中。
接下來(lái)的16~21行時(shí)關(guān)鍵部分;這部分把剛剛創(chuàng )建的兩個(gè)對象的引用壓到棧頂,這兩個(gè)對象是將要執行的sayHello()
方法的所有者,稱(chēng)為接收者(Receiver);17和21句是方法調用命令,這兩條調用命令單從字節碼角度來(lái)看,無(wú)論是指令(invokevirtual
)還是參數(都是常量池中第6項的常量,注釋顯示了這個(gè)常量是sayHello方法的符號引用)完全一樣的,但是這兩句執行的目標方法并不同,這是因為invokevirtual
指令的多態(tài)查找過(guò)程引起的,該指令運行時(shí)的解析過(guò)程可分為以下幾個(gè)步驟:
java.lang.IllegalAccessError
異常。invokevirtual
指令執行的第一步就是在運行期確定接收者的實(shí)際類(lèi)型,所以?xún)纱握{用的invokevirtual
指令把常量池中的類(lèi)方法符號引用解析到了不同的直接引用上,這個(gè)過(guò)程就是Java語(yǔ)言中重寫(xiě)的本質(zhì)。package java.lang; import java.lang.annotation.*; /** * Indicates that a method declaration is intended to override a * method declaration in a supertype. If a method is annotated with * this annotation type compilers are required to generate an error * message unless at least one of the following conditions hold: * * <ul><li> * The method does override or implement a method declared in a * supertype. * </li><li> * The method has a signature that is override-equivalent to that of * any public method declared in {@linkplain Object}. * </li></ul> * * @author Peter von der Ahé * @author Joshua Bloch * @jls 9.6.1.4 @Override * @since 1.5 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.SOURCE) public @interface Override { }
到此這篇關(guān)于Java基礎之面向對象機制(多態(tài)、繼承)底層實(shí)現的文章就介紹到這了,更多相關(guān)Java面向對象機制底層實(shí)現內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站