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!

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.
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
Typ | Reichweite | Standardwert | Größe | Wörtliche Beispiele |
---|
Boolescher Wert | wahr oder falsch | falsch | 1 Bit | wahr falsch falsch |
Byte | -128 ... 127 | 0 | 8 Bit | 1, -90, -128 |
char | Unicode-Zeichen oder 0 bis 65 536 | \ u0000 | 16 Bit | 'a', '\ u0031', '\ 201', '\ n', 4 |
kurz | -32.768 ... 32.767 | 0 | 16 Bit | 1, 3, 720, 22.000 |
int | -2 147 483 648 ... 2 147 483 647 | 0 | 32 Bit | -2, -1, 0, 1, 9 |
lang | -9,223,372,036,854,775,808 bis 9,223,372,036,854,775,807 | 0 | 64 Bit | -4000L, -900L, 10L, 700L |
float | 3,40282347 x 1038, 1,40239846 x 10-45 | 0.0 | 32 Bit | 1,67e200f, -1,57e-207f, 0,9f, 10,4F |
doppelt | 1,7976931348623157 x 10308, 4,9406564584124654 x 10-324 | 0.0 | 64 Bit | 1.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?
- befe
- bfce
- efce
- 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:
- Verbreiterung
- Verpackung (Autoboxing und Unboxing)
- 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:

( 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) {
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) {
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.