1. 程式人生 > >深入理解Java的方法過載機制

深入理解Java的方法過載機制

Java的“過載機制”為開發者提供了便利的同時也規定了一系列的規範和限制,本文就是為了深入理解在“過載”表像之後隱藏的物理本質

*宣告,本文的所有程式碼均在Java語言環境下測試

什麼是方法過載:過載的概念

在搞明白“過載”之前要先弄清楚另一個概念“方法簽名”
方法簽名 = 方法名 + 引數列表 (方法簽名不包括返回值)
我們用方法簽名區分不同的方法,而所謂“過載”,指的就是“兩個方法間的方法簽名中方法名相同而引數列表不同的情況”,比如:

void sum(int a, int b){
    System.out.println("a + b = "+(a+b));
}
void sum(double a, double b){
    System.out.println("a + b = "+(a+b));
}

呼叫時如果傳入的引數是sum(1, 2),就呼叫上面的方法;如果傳入的引數是sum(1.1, 2.2),就呼叫下面的方法;如果傳入的引數是sum(1, 3.14),還是呼叫下面的方法,因為要有一個信念就是編譯器總是有能力找出“最適合的對應呼叫方法”,這裡“最適合”指的是“最匹配”、“精確度最高”,值得注意的是第三個呼叫的第一個引數在編譯時先要被轉換為1.0,然後才能呼叫下面的方法(但是如果有方法三sum(int a, double b)那麼就呼叫方法三)

方法過載,對於像Java、C、C++這樣的靜態語言來說其作用就是“最大限度的增強程式碼複用性,減少程式設計師的體力勞動”(為什麼我不說Python這樣的動態型別語言?因為人家的型別是不確定的呀!好吧扯遠了……)

任何方法都可以“過載”的,包括“建構函式(構造方法)”

繼承來的方法也可以過載嗎:當然可以

對照著我們的定義再看一遍,“方法名相同而引數列表不同……”,再把繼承即概念摳出來,“……子類會繼承父類的的所有方法(父類物件作為子類物件的核心)”——這也就意味著,我們的子類方法可以過載繼承得來的方法

這裡最容易混淆的兩個概念就是“過載繼承來的方法”和“覆寫繼承來的方法”

public class Father{
    ...
public void func(int a, int b){ System.out.println("a + b = "+(a+b)); } ... }
public class Son extends Father{
    ...
    public void func(double a, double b){ //這叫“過載”
        System.out.println("a + b = "+(a+b));
    } //“過載”並不關心方法體或返回值是否相同
    publcic void func(int a, int b){ //這叫“覆寫”
        System.out.println("a - b "+(a-b));
    } //“覆寫”只能改變方法的主體部分,而返回值和方法簽名必須和父類一致
    ...

竊以為這一個例子就夠了,保證以後不會出錯就行

當過載遇到範型

背景知識:“範型”是JDK1.5引入的一個很棒的特性
在有範型之前是這樣的:

ArrayList al = new ArrayList();
al.add("Hello");
al.add(1.5);
String element1 = (String)al.get(0); /*得到的是Object型別
int element2=(Integer)al.get(1);     必須強制型別轉換*/

自從有了範型之後:

ArrayList<String> al = new ArrayList<>();
al.add("Hello");
al.add("1.5"); //1.5的話編譯器報錯!
String element1 = al.get(0);
String element2 = al.get(1);

範型的引入,實際上是在編譯期增加了一次額外的型別檢查以防止體制外的型別的介入,保證了型別安全

關於範型只要注意一下幾點就可以無敵了:
1. 範型分為範型類和範型方法,確定範型種類的是所謂的“型別引數”
2. 範型的“型別引數”中只可以傳遞指向實際物件的變數,不允許填入字面量
3. 範型的工作原理是“型別擦除”後再“型別填充”
4. 範型的存活只限於“編譯期”
5. Java不支援“範型陣列”
其中第三、四點,具體來說:“Java語言的泛型基本上完全是在編譯器中實現的,由編譯器執行型別檢測和型別推斷,然後生成普通的非泛型的位元組碼;JVM是完全意識不到範型的存在的,這稱為型別擦除;編譯器使用泛型型別資訊保證型別安全,然後在生成位元組碼之前將其清除”

好啦!背景知識介紹完了,下面進入正題——當過載遇到範型,會碰撞出怎樣的火花?話不多少,直接上程式碼:

public static<N extends Number> double sum(N a, N b){
    double sum = a.doubleValue() + b.doubleValue();
    return sum;
}

範型原來還可以這麼玩——只用一個方法就表示若干個本來需要過載的方法

然,正如古訓說的:“世間萬物都有兩面性”——範型也不是個例外:

public void func(List<String> ls){
    ...
}
public void func(List<Integer> ls){
    ...
}

這會讓編譯器報錯:
Method test(List<String>) has the same erasure test(List<E>) as another method in type TR

這時候除非改變方法的返回值:

public String func(List<String> ls){
    ...
}
public int func(List<Integer> ls){
    ...
}

這樣就可以通過編譯了(但是這不可是一般意義上的“過載”啊)

為什麼“範型引數”不可以作為過載的判據?為什麼返回值在有範型的方法中卻可以像“方法簽名”一樣用來區分兩個不同的方法?這涉及到簡單的編譯原理——
Java函式的方法簽名包括方法名稱、引數型別以及引數順序;但在位元組碼中,特徵簽名還包括了方法的返回值以及受查異常表,這就是為什麼在class檔案中,其他都相同僅僅返回值不同的兩個方法能共存的原因(不懂請回頭看那個小結範型的第4條機下面的註解)

結論:1. 使用範型可以代替多個方法的過載;2. 過載同樣不關心範型的型別引數,因為在編譯期被型別擦出根本看不出來;3. 有範型參與的方法可以用返回值來區分,類似於方法簽名
(請將這三點作為補充,加到之前關於範型小結的那部分

相關推薦

深入理解 Java 垃圾回收機制

nbsp 循環引用 方式 不同的 整理 一個 復制 垃圾回收機制 提高 垃圾回收機制中的算法: 1.引用計數法:無法檢測出循環引用。如父對象有一個對子對象的引用,子對象反過來引用父對象。這樣,他們的引用計數永遠不可能為0. 2 標記-清除算法:采用從根集合進行掃描,對存活

深入理解Java異常處理機制 (籠統篇)

throw 種類型 綜合 IV 算術 其它 wid all 作用 開篇 1.異常處理(Exception Handling):   就是一種解決這一問題的機制,能夠較好地處理程序不能正常運行的情況。 2.異常(Exception):   是程序在運行時可能出現的

深入理解Java類載入機制(一)

1 前言: 在上一篇文章一文讓你明白 Java 位元組碼中, 我們瞭解了java位元組碼的解析過程,那麼在接下來的內容中,我們來了解一下類的載入機制。 2 題外話 Java的核心是什麼?當然是JVM了,所以說了解並熟悉JVM對於我們理解Java語言非常重要,不管你是做Java還是Andr

深入理解java的反射機制(轉載)

今天將從以下4方面來系統的學習一下java的反射機制: java反射是什麼 java反射(Reflection)底層實現原理 java反射的簡單演示 java反射的應用場景 1,java反射是什麼 首先大家應該先了解兩個概念,編譯

深入理解Java垃圾回收機制

                一、垃圾回收機制的意義  Java語言中一個顯著的特點就是引入了垃圾回收機制,使c++程式設計師最頭疼的記憶體管理的問題迎刃而解,它使得Java程式設計師在編寫程式的時候不再需要考慮記憶體管理。由於有個垃圾回收機制,Java中的物件不再有“作用域”的概念,只有物件的引用才有“作

Java基礎:深入理解java異常處理機制的原理和開發應用【轉載】

Java異常處理機制在日常開發中應用頻繁,本篇文章主要在基礎的使用方法上,更進一步的,如何更加合理的使用異常機制,希望可以對各位朋友能有所幫助。   Java異常處理機制其最主要的幾個關鍵字:try、catch、finally、throw、throws,以及各種各樣

Java基礎9——深入理解java回撥機制

Java回撥機制 回撥的引入故事 轉載自Bro_超 // 同步回撥 1 public interface doJob 2 { 3 public void fillBlank(int a, int b, int result); 4 } 1 public class Su

深入理解java鎖的機制

前面我們看到了Lock和synchronized都能正常的保證資料的一致性(上文例子中執行的結果都是20000000),也看到了Lock的優勢,那究竟他們是什麼原理來保障的呢?今天我們就來探討下Java中的鎖機制!Synchronized是基於JVM來保證資料同步的,而Loc

深入理解 Java 動態代理機制

Java 有兩種代理方式,一種是靜態代理,另一種是動態代理。對於靜態代理,其實就是通過依賴注入,對物件進行封裝,不讓外部知道實現的細節。很多 API 就是通過這種形式來封裝的。 代理模式結構圖(圖片來自《大話設計模式》) 下面看下兩者在概念上的解釋: 靜態代理 靜態代理

深入理解Java異常處理機制

final 語句 什麽 ror instance 說明 security 捕獲 分享圖片 一、引子   try…catch…finally恐怕是大家再熟悉不過的語句了,而且感覺用起來也是很簡單,邏輯上似乎也是很容易理解。不過,我親自體驗的&ld

阿里面試題,深入理解Java類載入機制

類的生命週期 包括以下 7 個階段: 載入(Loading) 驗證(Verification) 準備(Pr

深入理解Java方法過載機制

Java的“過載機制”為開發者提供了便利的同時也規定了一系列的規範和限制,本文就是為了深入理解在“過載”表像之後隱藏的物理本質 *宣告,本文的所有程式碼均在Java語言環境下測試 什

深入理解java反射機制中Method類中的invoke()方法

1.先說明Method類中的幾個重要的屬性 1)Method型別的root屬性: 可以理解為每一個 java方法都有唯一的一個Method物件,這個物件就是root,我們可以利用反射建立java方法的眾多的Method類的物件,這些物件指向root,可以理解為root的映象

深入理解Java方法的參數傳遞機制

static 分享 然而 深入理解java code urn 而是 基本數據類型 概念 形參和實參 我們知道,在Java中定義方法時,是可以定義參數的,比如: public static void main(String[] args){ } 這裏的args就是一

深入理解Java虛擬機- 學習筆記 - 虛擬機類加載機制

支持 pub eth 獲取 事件 必須 string 沒有 字節碼 虛擬機把描述類的數據從Class文件加載道內存,並對數據進行校驗,轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。在Java裏,類型的加載、連接和初始化過程都是在程序

深入理解Java:類加載機制及反射

指定 請求 image vm虛擬機 常量池 使用 元素 靜態 創建 一、Java類加載機制 1.概述 Class文件由類裝載器裝載後,在JVM中將形成一份描述Class結構的元信息對象,通過該元信息對象可以獲知Class的結構信息:如構造函數,屬性和方法等,J

深入理解Java類型信息(Class對象)與反射機制

成員變量 字段 機制 () 程序 轉換 默認 數據 統一   深入理解Class對象    RRTI的概念以及Class對象作用    認識Class對象之前,先來了解一個概念,RTTI(Run-Time Type Identification)運行時類型識別,對於這個詞一

深入理解JAVA虛擬機閱讀筆記——虛擬機類加載機制

info 程序 動態 alt 訪問 什麽 hand jdk 靜態方法   虛擬機把描述類的Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型,這就是虛擬機的類加載機制。 在Java語言中,類型的加載、連接

深入理解Java虛擬機—內存管理機制

heap 通信 行程 單元 和數 define 正在 調用方法 滿足 前面說過了類的加載機制,裏面講到了類的初始化中時用到了一部分內存管理的知識,這裏讓我們來看下Java虛擬機是如何管理內存的。 先讓我們來看張圖 有些文章中對線程隔離區還稱之為線程獨占區,其實是一個意思了

深入理解Java虛擬機(類文件結構+類加載機制+字節碼執行引擎)

本地變量 ber 關鍵字 作者 看書 講解 個數 寫入 class類 周誌明的《深入理解Java虛擬機》很好很強大,閱讀起來頗有點費勁,尤其是當你跟隨作者的思路一直探究下去,開始會讓你弄不清方向,難免有些你說的啥子的感覺。但知識不得不學,於是天天看,反復看,就慢慢的理解了。