Konstruktor jest nieodłączną częścią klasy (abstrakcyjna klasa również posiada swój konstruktor), istnieje on nawet wtedy, kiedy go jawnie nie napiszemy (konstruktor domyślny). Wiąże się to z tym, że nie jest możliwe stworzenie obiektu klasy bez udziału konstruktora.
Ponad to:
- Konstruktor MUSI mieć taką samą nazwę, jak nazwa klasy,
- Konstruktor nie może zwracać żadnego typu (nawet void),
- konstruktor domyślny jest zawsze bezparametrowy,
- konstruktor domyślny można wywołać tylko wtedy, gdy klasa nie posiada żądnego, zaimplementowanego konstruktora,
- Konstruktor może być przeciążany,
- podczas wywołania konstruktora danej klasy, wywoływane są również wszystkie konstruktory klas nadrzędnych,
- Konstruktor, o ile posiada parametry, może ustawiać pola wartościami, które dostał jako argumenty, jeżeli konstruktor nie ma żadnych parametrów, wszystkie wartości pól obiektu, będą zainicjalizowane domyślnymi wartościami.
- Konstruktor może być oznaczony dowolnym specyfikatorem dostępu, nawet private (wykorzystywane np. przy implementacji wzorca projektowego Singleton),
- każdy konstruktor ma jako pierwsze wyrażenie albo odwołanie do przeciążonego konstruktora ( this() ), albo do konstruktora z klasy nadrzędnej ( super() ), pamiętać należy, że wyrażenia te mogą być dodane przez kompilator w trakcie kompilacji, jeżeli nie zostaną napisane (nie znaczy to jednak, że zawsze trzeba je jawnie wpisywać w ciało konstruktora),
- Konstruktor nie może mieć zaimplementowane (używać) jednocześnie operatora super() i this(),
- operator super(), może być wywoływany bez parametrów, oraz z parametrami (dokładnie takimi, jakie ma konstruktor w klasie nadrzędnej),
- nie można wywołać metody, lub odwołać się do pola instancji klasy, przed operatorem super(),
- tylko zmienne lub metody statyczne mogą być użyte jako część wywołania operatora super (np. super(ClassX.PoleStatyczne) ),
- Interfejsy nie posiadają konstruktorów,
- Konstruktor nie może być wywołany bezpośrednio (tak jak metoda), wywołanie takie może mieć miejsce tylko w innym konstruktorze,
- Konstruktory nie są dziedziczone.
Możliwe jest napisanie metody o takiej samej nazwie, jak klasa (aczkolwiek nie jest to zalecane), jednak nie będzie ona konstruktorem z uwagi na zweracany typ.
Przyjrzyjmy się teraz przykładowi:
class MyPoint2D {
private int x;
private int y;
public MyPoint2D(int x, int y) {
this.x = x;
this.y = y;
}
public void getInfo() {
System.out.print( "Punkt:\tx=" + x + "\ty=" + y);
// symbol "\t" - interpretowany jest jako tabulacja
}
}
class MyPoint3D extends MyPoint2D {
private int z;
public MyPoint3D( int x, int y, int z ) {
super(x, y); // wywołanie dwuparametrowego konstruktora klasy nadrzędnej
this.z = z;
}
public void getInfo() { // przesłonięta metoda klasy nadrzędnej
super.getInfo(); // za pomocą operatora super, można
// dostać się do metod klasy nadrzędnej
System.out.print( "\tz=" + z );
}
}
class Main {
public static void main( String[] args ) {
MyPoint2D point2D = new MyPoint2D(-1, -2);
point2D.getInfo();
System.out.println();
MyPoint3D point3D = new MyPoint3D(3, 4, 5);
point3D.getInfo();
}
}
Dzięki temu, że klasa MyPoint3D dziedziczy po klasie MyPoint2D, nie jest konieczne deklarowanie tych samych zmiennyh dwa razy w różnych klasach. Ponad to kalsa MyPoint3D oszczędza sporo na ilości kodu, jaki konieczny jest do zaimplementowana, poprzez wykożystanie konstruktora i metody getInfo() z klasy nadrzędnej (a to jest przecież bardzo prosty przykład). Ale to nie są przecież największe zalety, istotą jest to, że instancja klasy MyPoint3D będzie teraz zarówno obiektem klasy MyPoint3D jak i MyPoint2D, umożliwi to np. trzymanie wszystkich obiektów oby tych klas w jednej tablicy typu MyPoint2D (rzutowanie).
Konstruktor klasy MyPoint3D musi posiadać co najmniej takie parametry, jak ten w klasie nadrzędnej (wynika to tego, że przy wywoływaniu konstruktora tej klasy, wywoływane są też konstruktory wszystkich klas nadrzędnych. Klasa nadrzędna posiada konstruktor który oczekuje dwóch argumentów typu int, dostarczyć je musi konstruktor z klasy MyPoint3D, aby wywołanie podczas kompilacji nie spowodowało błędu). W tym miejscu należy powiedzieć, że nie możemy starać się wywołać konstruktora bezparametrowego z klasy bazowej, gdyż ona go nie posiada. W pamięci mamy jednak to, że każda klasa posiada konstruktor domyślny, co jest oczywiście w 100% prawdą, jednak, gdy jakikolwiek konstruktor, z parametrami, czy bez zostanie zaimplementowany w klasie, kompilator to wykryje i nie będzie tworzył niepotrzebnie kolejnego (konstruktory domyślne tworzone są w momencie kompilacji, ale tylko wtedy, kiedy klasa nie posiada zaimplementowanego konstruktora).
Metoda getInfo() została w klasie MyPoint3D przesłonięta, a w jej ciele, za pomocą operatora “super”, została wywołana metoda klasy nadrzędnej, wyświetlająca na ekranie info na temat dwóch pierwszych współrzędnych, naszym zadaniem pozostaje już tylko dodanie info na temat ostatniej współrzędnej.
Przeciążanie konstruktorów
Przeciążanie konstruktorów jest bardzo często wykożystywaną praktyką, bowiem podając odpowiednie argumenty dla kosntruktora, możemy stworzyć obiekt, który będzie już na samym początku w sposób, jaki jest to wymagane, “skonfigurowany”.
import java.util.Random;
class Planet {
String name;
int size;
Random random = new Random();
public Planet() {
name = "noname";
size = Math.abs( random.nextInt() ) % 10000 + 1;
}
Planet( String name ) {
this.name = name;
size = Math.abs( random.nextInt() ) % 10000 + 1;
}
Planet( String name, int size ) {
this.name = name;
if ( size <= 10000 && size >=1 ) {
this.size = size;
} else if( size > 10000 ) {
this.size = 10000;
} else {
this.size = 1;
}
}
@Override
public String toString() {
return "Planeta " + name + " ma wielkość " + size + ".";
}
}
class Main {
public static void main( String[] args ) {
Planet noName = new Planet();
Planet mars = new Planet( "Mars" );
Planet ziemia = new Planet( "Ziemia", 8456 );
System.out.println( noName.toString() );
System.out.println( mars.toString() );
System.out.println( ziemia.toString() );
}
}
Linia 4: Tworzenie obiektu klasy Random, umożliwia ona generowanie pseudolosowych liczb.
Linia 5: Konstruktor bezparametrowy (domyślny), wykorzystamy go np w przypadku, gdy zajdzie potrzeba stworzenia obiektu typu Planet, a nie będziemy znać, lub nie będzie istotna jego nazwa. Przypisuje on domyślną nazwę i losuje wartość z przedziału 1-10000, która będzie rozmiarem nowej planety.
Linia 9: Konstruktor z parametrem String, przeciąża on konstruktor bezparametrowy, umożliwia podanie nazwy planety podczas tworzenia nowego obiektu, wielkość planety generowana jest losowo.
Linia 13: Konstruktor z parametrami String i int (nazwa, wielkość). Konstrukora tego będzie możńa użyć w wypadku, gdy będziemy znać nazwę i rozmiar planety, jaką chcemy utworzyć.
Linia 24: Przesłonięta metoda toString() z klasy Object, napisana w taki sposób, aby zwracała info na temat obiektu kalsy Planet.
Kompilacja kodu i uruchomienie aplikacji wyświetli na ekranie np.:
Planeta noname ma wielkość 3106. (domyślna nazwa, wielkość generowana losowo)
Planeta Mars ma wielkość 7032. (wielkość generowana losowo)
Planeta Ziemia ma wielkość 8456.