Java Challengers # 1: Methodenüberladung in der JVM

Java Challengers # 1: Methodenüberladung in der JVM


Guten Tag an alle.


Wir haben bereits den nächsten Thread des Kurses "Java Developer" gestartet, aber wir haben noch einige Materialien, die wir mit Ihnen teilen möchten.


Willkommen zur Artikelserie Java Challengers ! Diese Artikelserie konzentriert sich auf Java-Programmierfunktionen. Ihre Entwicklung ist Ihr Weg, ein hochqualifizierter Java-Programmierer zu werden.


Das Beherrschen der in dieser Artikelserie beschriebenen Techniken erfordert einige Anstrengungen, wird jedoch einen großen Beitrag zu Ihrer täglichen Erfahrung als Java-Entwickler leisten. Das Vermeiden von Fehlern ist einfacher, wenn Sie wissen, wie die grundlegenden Java-Programmiertechniken korrekt angewendet werden, und das Verfolgen von Fehlern ist viel einfacher, wenn Sie genau wissen, was in Ihrem Java-Code vor sich geht.


Sind Sie bereit, die grundlegenden Konzepte der Programmierung in Java zu beherrschen? Dann fangen wir mit unserem ersten Puzzle an!


Verbreiterung-Boxing-Varargs


Der Begriff "Methodenüberladung"

In Bezug auf den Begriff Überlastung neigen Entwickler dazu zu glauben, dass es sich um einen Neustart des Systems handelt, dies ist jedoch nicht der Fall. Bei der Programmierung bedeutet Methodenüberladung die Verwendung desselben Methodennamens mit unterschiedlichen Parametern.

Was ist Methodenüberladung?


Das Überladen von Methoden ist eine Programmiertechnik, mit der ein Entwickler derselben Klasse denselben Namen für Methoden mit unterschiedlichen Parametern verwenden kann. In diesem Fall sagen wir, dass die Methode überladen ist.


Listing 1 zeigt Methoden mit unterschiedlichen Parametern, die sich in Anzahl, Typ und Reihenfolge unterscheiden.


Listing 1. Drei Optionen zum Überladen von Methoden.


//   public class Calculator { void calculate(int number1, int number2) { } void calculate(int number1, int number2, int number3) { } } //   public class Calculator { void calculate(int number1, int number2) { } void calculate(double number1, double number2) { } } //   public class Calculator { void calculate(double number1, int number2) { } void calculate(int number1, double number2) { } } 

Methodenüberladung und primitive Typen


In Listing 1 haben Sie die primitiven Typen int und double . Lassen Sie uns eine Minute abschweifen und uns an die primitiven Typen in Java erinnern.


Tabelle 1. Primitive Typen in Java


TypReichweiteStandardwertGrößeWörtliche Beispiele
Boolescher Wertwahr oder falschfalsch1 Bitwahr falsch falsch
Byte-128 ... 12708 Bit1, -90, -128
charUnicode-Zeichen oder 0 bis 65 536\ u000016 Bit'a', '\ u0031', '\ 201', '\ n', 4
kurz-32.768 ... 32.767016 Bit1, 3, 720, 22.000
int-2 147 483 648 ... 2 147 483 647032 Bit-2, -1, 0, 1, 9
lang-9,223,372,036,854,775,808 bis 9,223,372,036,854,775,807064 Bit-4000L, -900L, 10L, 700L
float3,40282347 x 1038, 1,40239846 x 10-450.032 Bit1,67e200f, -1,57e-207f, 0,9f, 10,4F
doppelt1,7976931348623157 x 10308, 4,9406564584124654 x 10-3240.064 Bit1.e700d, -123457e, 37e1d

Warum sollte ich Methodenüberladung verwenden?


Durch die Verwendung von Überladung wird Ihr Code übersichtlicher und leichter lesbar und es werden Fehler im Programm vermieden.


Stellen Sie sich im Gegensatz zu Listing 1 ein Programm vor, in dem Sie viele calculate() Methoden mit ähnlichen Namen wie calculate1 , calculate2 , calculate3 ... nicht gut haben, oder? Durch Überladen der Methode calculate() können Sie denselben Namen verwenden und nur die erforderlichen Parameter ändern. Es ist auch sehr einfach, überladene Methoden zu finden, da sie im Code gruppiert sind.


Was Überlastung ist nicht


Denken Sie daran, dass das Ändern eines Variablennamens keine Überlastung darstellt. Der folgende Code wird nicht kompiliert:


 public class Calculator { void calculate(int firstNumber, int secondNumber){} void calculate(int secondNumber, int thirdNumber){} } 

Sie können die Methode auch nicht überladen, indem Sie den Rückgabewert in der Methodensignatur ändern. Dieser Code wird auch nicht kompiliert:


 public class Calculator { double calculate(int number1, int number2){return 0.0;} long calculate(int number1, int number2){return 0;} } 

Konstruktorüberlastung


Sie können den Konstruktor auf dieselbe Weise wie die Methode überladen:


 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; } } 

Lösen Sie das Problem der Methodenüberladung


Bist du bereit für den ersten Test? Lass es uns herausfinden!


Überprüfen Sie zunächst den folgenden Code sorgfältig.


Listing 2. Die Herausforderung der Methodenüberladung


 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"; } } 

Gut. Sie haben den Code studiert. Was wird die Schlussfolgerung sein?


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

Die richtige Antwort finden Sie am Ende des Artikels.


Was ist jetzt passiert? Wie die JVM überladene Methoden kompiliert


Um zu verstehen, was in Listing 2 passiert ist, müssen Sie einige Dinge darüber wissen, wie die JVM überladene Methoden kompiliert.


Erstens ist die JVM ziemlich faul: Sie wird immer den geringsten Aufwand betreiben, um eine Methode auszuführen. Wenn Sie also darüber nachdenken, wie die JVM mit Überladung umgeht, sollten Sie drei wichtige Funktionen des Compilers berücksichtigen:


  1. Verbreiterung
  2. Verpackung (Autoboxing und Unboxing)
  3. Argumente variabler Länge (varargs)

Wenn Sie noch nie auf diese Techniken gestoßen sind, sollten Ihnen einige Beispiele helfen, sie zu verstehen. Beachten Sie, dass die JVM sie in der angegebenen Reihenfolge ausführt.


Hier ist eine Beispielerweiterung:


 int primitiveIntNumber = 5; double primitiveDoubleNumber = primitiveIntNumber ; 

Dies ist die Erweiterungsreihenfolge primitiver Typen:


Erweiterungsreihenfolge primitiver Typen


( Anmerkung des Übersetzers - In JLS wird die Erweiterung von Grundelementen mit großen Variationen beschrieben, z. B. kann long in float oder double erweitert werden. )


Beispiel für eine automatische Verpackung:


 int primitiveIntNumber = 7; Integer wrapperIntegerNumber = primitiveIntNumber; 

Beachten Sie, was beim Kompilieren des Codes hinter den Kulissen passiert:


 Integer wrapperIntegerNumber = Integer.valueOf(primitiveIntNumber); 

Hier ist ein Beispiel zum Auspacken:


 Integer wrapperIntegerNumber = 7; int primitiveIntNumber= wrapperIntegerNumber; 

Folgendes passiert hinter den Kulissen beim Kompilieren dieses Codes:


 int primitiveIntNumber = wrapperIntegerNumber.intValue(); 

Und hier ist ein Beispiel für eine Methode mit Argumenten variabler Länge. Beachten Sie, dass Methoden mit variabler Länge immer die letzten sind, die ausgeführt werden.


 execute(int... numbers){} 

Was sind Argumente mit variabler Länge?


Argumente mit variabler Länge sind nur ein Array von Werten, die durch drei Punkte (...) angegeben werden. Wir können so viele int Zahlen an diese Methode übergeben.


Zum Beispiel:


 execute(1,3,4,6,7,8,8,6,4,6,88...); //  ... 

Argumente variabler Länge (varargs) sind sehr praktisch, da Werte direkt an eine Methode übergeben werden können. Wenn wir Arrays verwenden würden, müssten wir eine Array-Instanz mit Werten erstellen.


Erweiterung: Fallstudie


Wenn wir die Nummer 1 direkt an die Methode executeAction() , interpretiert die JVM sie automatisch als int . Aus diesem Grund wird diese Nummer nicht an die Methode executeAction(short var) .


Wenn wir die Zahl 1.0 erkennt 1.0 JVM automatisch, dass sie doppelt ist.


Natürlich kann die Zahl 1.0 auch ein float , aber die Art solcher Literale ist vordefiniert. Daher wird in Listing 2 die Methode executeAction(double var) .


Wenn wir den Double Wrapper verwenden, gibt es zwei Möglichkeiten: Entweder kann die Nummer in einen primitiven Typ entpackt oder in Object erweitert werden. (Denken Sie daran, dass jede Klasse in Java die Object Klasse erweitert.) In diesem Fall wählt die JVM eine Erweiterung vom Typ Double in Object , da dies weniger Aufwand erfordert als das Entpacken.


Der letzte, den wir übergeben, ist 1L und da wir den Typ angegeben haben, ist er long .


Häufige Überlastungsfehler


Inzwischen haben Sie wahrscheinlich verstanden, dass die Überlastung von Methoden verwirrend sein kann. Schauen wir uns also einige Probleme an, auf die Sie wahrscheinlich stoßen werden.


Autoboxing mit Wrappern


Java ist eine stark typisierte Programmiersprache, und wenn wir Auto-Wrapping mit Wrappern verwenden, müssen wir einige Dinge berücksichtigen. Erstens wird der folgende Code nicht kompiliert:


 int primitiveIntNumber = 7; Double wrapperNumber = primitiveIntNumber; 

Das Autopacking funktioniert nur mit dem double da es beim Kompilieren des Codes dem folgenden entspricht:


 Double number = Double.valueOf(primitiveIntNumber); 

Dieser Code wird kompiliert. Das erste int wird zu double erweitert und dann in Double gepackt. Beim automatischen Double.valueof gibt es jedoch keine Double.valueof und der Konstruktor Double.valueof erwartet ein double und kein int . In diesem Fall funktioniert das automatische Packen, wenn wir eine explizite Typkonvertierung durchführen, zum Beispiel:


 Double wrapperNumber = (double) primitiveIntNumber; 

Denken Sie daran, dass Integer nicht Long und Float und nicht Double . Es gibt keine Vererbung. Jeder dieser Typen ( Integer , Long , Float und Double ) ist Number und Object .


Denken Sie im Zweifelsfall daran, dass Wrapper-Nummern zu Number oder Object . (Über Wrapper kann noch viel mehr gesagt werden, aber lassen wir es für einen anderen Artikel.)


Code-Literale


Wenn wir den Typ einer Literalzahl nicht angeben, berechnet die JVM den Typ für uns. Wenn wir die Nummer 1 direkt im Code verwenden, erstellt die JVM sie als int . Wenn wir versuchen, 1 direkt an eine Methode zu übergeben, die short akzeptiert, wird sie nicht kompiliert.


Zum Beispiel:


 class Calculator { public static void main(String... args) { //      // ,   char, short, byte,  JVM    int calculate(1); } void calculate(short number) {} } 

Die gleiche Regel gilt, wenn die Nummer 1.0 . Obwohl es sich möglicherweise um einen float , wird die JVM dies als double .


 class Calculator { public static void main(String... args) { //      // ,   float,  JVM    double calculate(1.0); } void calculate(float number) {} } 

Ein weiterer häufiger Fehler ist die Annahme, dass Double oder ein anderer Wrapper für eine Methode, die double wird, besser ist.


Tatsache ist, dass die JVM weniger Aufwand benötigt, um den Double Wrapper in ein Object anstatt ihn in einen primitiven double entpacken.


Zusammenfassend ist 1 bei direkter Verwendung in Java-Code int und 1.0 double . Die Erweiterung ist der einfachste Weg, um sie auszuführen. Dann erfolgt das Packen oder Entpacken, und die letzte Operation besteht immer aus Methoden variabler Länge.


Wie eine merkwürdige Tatsache. Wissen Sie, dass der Typ char Zahlen akzeptiert?


 char anyChar = 127; // ,  ,    

Was Sie über Überlastung beachten müssen


Überladen ist eine sehr leistungsfähige Technik für Fälle, in denen Sie denselben Methodennamen mit unterschiedlichen Parametern benötigen. Dies ist eine nützliche Technik, da die Verwendung der richtigen Namen das Lesen des Codes erleichtert. Anstatt den Methodennamen zu duplizieren und Ihrem Code Unordnung hinzuzufügen, können Sie ihn einfach überladen.


Dies hält den Code sauber und leicht lesbar und verringert auch das Risiko, dass doppelte Methoden einen Teil des Systems beschädigen.


Was zu beachten ist: Wenn die Methode überladen wird, macht die JVM den geringstmöglichen Aufwand.


Hier ist die Reihenfolge des faulsten Wegs zur Ausführung:


  • Der erste erweitert sich.
  • Der zweite ist Boxen
  • Drittens Argumente variabler Länge (varargs)

Zu beachtende Dinge: Schwierige Situationen entstehen, wenn Zahlen direkt deklariert werden: 1 ist int und 1.0 ist double .


Denken Sie auch daran, dass Sie diese Typen explizit mit der Syntax 1F oder 1f für float und 1D oder 1d für double deklarieren können.


Damit ist die Rolle der JVM bei der Methodenüberladung abgeschlossen. Es ist wichtig zu verstehen, dass die JVM von Natur aus faul ist und immer dem faulsten Pfad folgt.


Die Antwort


Die Antwort auf Listing 2 lautet Option 3. efce.


Weitere Informationen zum Überladen von Methoden in Java



Eine Einführung in Klassen und Objekte für absolute Anfänger, einschließlich kleiner Abschnitte über Methoden und Methodenüberladung.



Erfahren Sie mehr darüber, warum es wichtig ist, dass Java eine stark typisierte Sprache ist, und erfahren Sie mehr über primitive Java-Typen.



Erfahren Sie, welche Einschränkungen und Nachteile das Überladen von Methoden hat und wie Sie diese mithilfe benutzerdefinierter Typen und Parameterobjekte beheben können.

Source: https://habr.com/ru/post/de428307/


All Articles