[轉載] Java Challengers#1:JVM中的方法過載

ey_snail發表於2020-12-21

參考連結: Java中自動裝箱和擴充套件的方法過載

歡迎來到新的Java Challengers部落格!本部落格致力於挑戰Java程式設計中的概念。掌握它們,你將成為一名技術嫻熟的Java程式設計師。 

本部落格中的技術需要付出一些努力才能掌握,但它們會對你作為Java開發人員的日常體驗產生重大影響。當你知道如何正確應用核心Java程式設計技術時,避免錯誤會更容易,並且當你確切知道Java程式碼中發生的情況時,跟蹤錯誤會更容易。 

你準備好開始掌握Java程式設計中的核心概念了嗎?然後讓我們開始我們的第一個Java挑戰! 

術語:方法過載 

由於過載,開發人員傾向於認為這種技術會使系統過載,但事實並非如此。在程式設計中,方法過載意味著使用相同的方法名和不同的引數。 

什麼是方法過載? 

方法過載是一種程式設計技術,允許開發人員在同一個類中多次使用相同的方法名,但具有不同的引數。在這種情況下,我們說該方法是過載的。清單1顯示了一個方法,其引數在數量,型別和順序上有所不同。 清單1.三種型別的方法過載 

Number of parameters:

public class Calculator {

  void calculate(int number1, int number2) { }

  void calculate(int number1, int number2, int number3) { }}

Type of parameters:

public class Calculator {

  void calculate(int number1, int number2) { }

  void calculate(double number1, double number2) { }

}

Order of parameters:

public class Calculator {

  void calculate(double number1, int number2) { }

  void calculate(int number1, double number2) { }

}

 

方法過載和原始型別 在清單1中,你可以看到基本型別int和double。我們將更多地使用這些和其他型別,所以花一點時間來回顧Java中的原始型別。 表1. Java中的原始型別 

 

 

 TypeRangeDefaultSizeExample literals boolean true or false false 1 bit true, false byte -128 .. 127 0 8 bits 1, -90, 128 char Unicode character or 0 to 65,536 \u0000 16 bits 'a', '\u0031', '\201', '\n', 4 short -32,768 .. 32,767 0 16 bits 1, 3, 720, 22,000 int -2,147,483,648 .. 2,147,483,647 0 32 bits -2, -1, 0, 1, 9 long -9,223,372,036,854,775,808 to  9,223,372,036,854,775,807 0 64 bits -4000L, -900L, 10L, 700L float 3.40282347 x 1038, 1.40239846 x 10-45 0.0 32 bits 1.67e200f, -1.57e-207f, .9f, 10.4F double 1.7976931348623157 x 10308,  4.9406564584124654 x 10-324 0.0 64 bits 1.e700d, -123457e, 37e1d

 

 **為什麼我們要使用方法過載?** 過載使你的程式碼更清晰,更易於閱讀,它還可以幫助你避免程式中的錯誤。 與清單1相比,想象一個程式,其中有多個calculate()方法,其名稱為calculate1 calculate2, calculate3. 。。不好,對嗎?過載calculate()方法允許你使用相同的方法名稱,同時僅更改需要更改的內容:引數。找到過載方法也很容易,因為它們在程式碼中組合在一起。 **什麼不是過載?** 請注意,更改變數的名稱不是過載。以下程式碼將無法編譯: 

public class Calculator {

 void calculate(int firstNumber, int secondNumber){}

 void calculate(int secondNumber, int thirdNumber){}

}

 

你也不能通過更改方法簽名中的返回型別來過載方法。以下程式碼不會編譯: 

public class Calculator {

 double calculate(int number1, int number2){return 0.0;}

long calculate(int number1, int number2){return 0;}

}

 

建構函式過載 你可以像對待方法一樣過載建構函式: 

public class Calculator {

 private int number1;

 private int number2;

 

 public Calculator(int number1) {this.number1 = number1;}

 public Calculator(int number1, int number2) {

  this.number1 = number1;

  this.number2 = number2;}

}

 

挑戰方法過載! 

你準備好迎接你的第一個Java挑戰嗎?我們來看看吧! 首先仔細檢視以下程式碼。 清單2.高階方法過載挑戰 

public class AdvancedOverloadingChallenge3 {

 static String x = "";

 public static void main(String... doYourBest) {

     executeAction(1);

     executeAction(1.0);

     executeAction(Double.valueOf("5"));

     executeAction(1L);

 

 System.out.println(x);

 }

 static void executeAction(int ... var) {x += "a"; }

 static void executeAction(Integer var) {x += "b"; }

 static void executeAction(Object var) {x += "c"; }

 static void executeAction(short var) {x += "d"; }

 static void executeAction(float var) {x += "e"; }

 static void executeAction(double var) {x += "f"; }}

 

好的,你已經檢視了程式碼。輸出是什麼? 

 

 1.befe 2.bfce 3.efce 4.aecf 

 

剛剛發生了什麼?JVM如何編譯過載方法 

為了理解清單2中發生的事情,你需要了解有關JVM如何編譯過載方法的一些資訊。 首先,JVM是智慧懶惰的:它總是儘可能少地執行一個方法。因此,當你考慮JVM如何處理過載時,請記住三種重要的編譯器技術: 

 

 1.寬化型別轉換 2.box(自動裝箱和拆箱) 3.可變引數 

 

如果你從未遇到過這三種技術,那麼一些示例應該有助於使它們清晰明瞭。請注意,JVM 按給定的順序執行它們。 這是一個寬化的例子: 

int primitiveIntNumber = 5;

double primitiveDoubleNumber = primitiveIntNumber ;

 

這是寬化時基本型別的順序:  以下是自動裝箱的示例: 

int primitiveIntNumber = 7;

Integer wrapperIntegerNumber = primitiveIntNumber;

 

注意編譯此程式碼時幕後發生的事情: 

Integer wrapperIntegerNumber = Integer.valueOf(primitiveIntNumber);

 

這是一個拆箱的例子 : 

Integer wrapperIntegerNumber = 7;

int primitiveIntNumber= wrapperIntegerNumber;

 

以下是編譯此程式碼時幕後發生的事情: 

int primitiveIntNumber = wrapperIntegerNumber.intValue();

 

這是一個可變引數的例子; 請注意,可變引數它始終是最後執行的: 

execute(int… numbers){}

 

什麼是可變引數? 

用於可變數量引數,可變引數基本上是由三個點指定的值陣列(…)我們可以傳遞許多int我們想要給這個方法的數字。 例如: 

execute(1,3,4,6,7,8,8,6,4,6,88...); // We could continue…

 

可變引數非常方便,因為值可以直接傳遞給方法。如果我們使用陣列,我們必須使用值例項化陣列。 

寬化:一個實際的例子 

當我們將數字1直接傳遞給executeAction方法時,JVM會自動將其視為一個int。這就是為什麼這個數字不適用於該executeAction(short var)方法。 同樣,如果我們傳遞數字1.0,JVM會自動將該數字識別為double。 當然,數字1.0也可以是 float,但型別是預先定義的。這就是executeAction(double var)清單2中呼叫該方法的原因。 當我們使用Double包裝器型別時,有兩種可能性:包裝器數字可以解包為基本型別,也可以寬化為Object。(請記住,Java中的每個類都繼承了Object類。)在這種情況下,JVM選擇將Double型別寬化為一個Object,因為它比取消裝箱所需的工作量少,正如我之前所解釋的那樣。 我們傳遞的最後一個數字是1L,因為我們這次指定了變數型別,所以它是long。 

過載常見錯誤 

到目前為止,你可能已經發現方法過載會讓事情變得棘手,所以讓我們考慮一下你可能遇到的一些挑戰。 使用包裝器進行自動裝箱 Java是一種強型別程式語言,當我們使用包裝器進行自動裝箱時,我們必須記住一些事情。首先,以下程式碼將無法編譯: 

int primitiveIntNumber = 7;Double wrapperNumber = primitiveIntNumber;

 

自動裝箱只適用於該double型別,因為編譯此程式碼時發生的情況與以下內容相同: 

Double number = Double.valueOf(primitiveIntNumber);

 

上面的程式碼將編譯。第一種 int型別將被擴充套件為double,然後它將被包裝成Double。但是當自動裝箱時,沒有型別寬化,函式Double.valueOf將收到一個double,而不是一個int。在這種情況下,自動裝箱僅在我們強制轉換時才有效,如下所示: 

Double wrapperNumber = (double) primitiveIntNumber;

 

請記住, Integer不能轉換為Long和Float不能轉換為Double。沒有繼承關係。這些每個型別–Integer,Long,Float,和Double-- 是一個Number和Object。 如有疑問,請記住包裝數字可以擴充套件為Number或Object。(還有很多關於包裝的探索,但我會將它留給另一篇文章。) 

JVM中的硬編碼數字型別 

當我們沒有為數字指定型別時,JVM將為我們執行此操作。如果我們直接在程式碼中使用數字1,JVM將建立它作為int。如果你嘗試將1直接傳遞給接收short的方法,則無法編譯。 例如: 

class Calculator {

 public static void main(String… args) {

 // This method invocation will not compile

   // Yes, 1 could be char, short, byte but the JVM creates it as an int

              calculate(1);

 } 

 void calculate(short number) {}

 }

 

使用數字1.0時將應用相同的規則; 雖然它可能是 float,但JVM會將此數字視為double: 

class Calculator {

 public static void main(String… args) {

 // This method invocation will not compile

 // Yes, 1 could be float but the JVM creates it as double

               calculate(1.0);

 } 

 void calculate(float number) {} 

 }

 

另一個常見錯誤是認為該Double包裝型別或任何其他包裝型別更適用於接收double的方法。事實上,對於JVM來說,將Double寬化到Object而不是將其拆箱為double型別花費的工作更少。 總而言之,當直接在Java程式碼中使用時,1將是int,1.0將是 double。寬化是最懶的執行路徑,接下來是裝箱或拆箱,最後的操作將始終是可變引數。 作為一個奇怪的事實,你知道這個char型別接受數字嗎? 

char anyChar = 127; // Yes, this is strange but it compiles

 

關於過載要記住什麼 

對於需要使用不同引數的相同方法名稱的情況,過載是一種非常強大的技術。這是一種有用的技術,因為在程式碼中使用正確的名稱會對可讀性產生重大影響。你可以簡單地過載它,而不是複製方法併為你的程式碼新增混亂。這樣做可以使程式碼保持清潔,易於閱讀,並降低了重複方法破壞系統某些部分的風險。 需要記住的是:當過載方法時,JVM將盡可能少地工作; 這是最懶惰的執行路徑的順序: 

 

 l 首先是寬化 l 第二是裝箱 l 第三是可變引數 

 

需要注意的是:直接宣告一個數字會產生棘手的情況:1將是int,而1.0將是 double。 還要記住,你可以使用語法顯式宣告這些型別,1F或1f用於float或者1D或1d用於 double。 這就是我們的第一個Java挑戰,介紹了JVM在方法過載中的作用。重要的是要意識到JVM本質上是懶惰的,並且總是遵循最懶的執行路徑。 

答案 

清單2中的Java Challenger的答案是:選項3. efce。 

有關Java中方法過載的更多資訊 

· Java 101: Classes and objects in Java: A true beginner’s introduction to classes and objects, including short sections on methods and method overloading. · Java 101: Elementary Java language features: Learn more about why it matters that Java is a strongly typed language and get a full introduction to primitive types in Java. · Too many parameters in Java methods, Part 4: Explore the limitations and disadvantages of method overloading, and how they may be remedied by integrating custom types and parameter objects. 點選英文原文連結 更多文章歡迎訪問: http://www.apexyun.com 公眾號:銀河系1號 聯絡郵箱:public@space-explore.com (未經同意,請勿轉載)

相關文章