Szukaj…


Wprowadzenie

Obiekty mają stany i zachowania. Przykład: Pies ma stany - kolor, imię, rasę i zachowania - machanie ogonem, szczekanie, jedzenie. Obiekt jest instancją klasy.

Klasa - klasę można zdefiniować jako szablon / plan opisujący zachowanie / stan obsługiwany przez obiekt tego typu.

Składnia

  • class Przykład {} // słowo kluczowe class, name, body

Najprostsza możliwa klasa

class TrivialClass {}

Klasa składa się z co najmniej słowa kluczowego class , nazwy i treści, które mogą być puste.

Inicjujesz klasę za pomocą new operatora.

TrivialClass tc = new TrivialClass();

Element obiektu a element statyczny

Z tą klasą:

class ObjectMemberVsStaticMember {

    static int staticCounter = 0;
    int memberCounter = 0;

    void increment() {
        staticCounter ++;
        memberCounter++;
    }
}

następujący fragment kodu:

final ObjectMemberVsStaticMember o1 = new ObjectMemberVsStaticMember();
final ObjectMemberVsStaticMember o2 = new ObjectMemberVsStaticMember();

o1.increment();

o2.increment();
o2.increment();

System.out.println("o1 static counter " + o1.staticCounter);
System.out.println("o1 member counter " + o1.memberCounter);
System.out.println();

System.out.println("o2 static counter " + o2.staticCounter);
System.out.println("o2 member counter " + o2.memberCounter);
System.out.println();

System.out.println("ObjectMemberVsStaticMember.staticCounter = " + ObjectMemberVsStaticMember.staticCounter);

// the following line does not compile. You need an object
// to access its members
//System.out.println("ObjectMemberVsStaticMember.staticCounter = " + ObjectMemberVsStaticMember.memberCounter);

tworzy ten wynik:

o1 static counter 3
o1 member counter 1

o2 static counter 3
o2 member counter 2

ObjectMemberVsStaticMember.staticCounter = 3

Uwaga: Nie powinieneś wywoływać elementów static na obiektach, ale na klasach. Chociaż nie ma to znaczenia dla JVM, czytelnicy z pewnością to docenią.

elementy static są częścią klasy i istnieją tylko raz na klasę. W instancjach istnieją elementy static , dla każdej instancji istnieje niezależna kopia. Oznacza to również, że potrzebujesz dostępu do obiektu tej klasy, aby uzyskać dostęp do jego członków.

Metody przeciążania

Czasami ta sama funkcjonalność musi być napisana dla różnych rodzajów danych wejściowych. W tym czasie można użyć tej samej nazwy metody z innym zestawem parametrów. Każdy inny zestaw parametrów jest znany jako sygnatura metody. Jak widać na przykładzie, jedna metoda może mieć wiele podpisów.

public class Displayer {

    public void displayName(String firstName) {
        System.out.println("Name is: " + firstName);
    }

    public void displayName(String firstName, String lastName) {
        System.out.println("Name is: " + firstName + " " + lastName);
    }

    public static void main(String[] args) {
        Displayer displayer = new Displayer();
        displayer.displayName("Ram");          //prints "Name is: Ram"
        displayer.displayName("Jon", "Skeet"); //prints "Name is: Jon Skeet"
    }
}

Zaletą jest to, że ta sama funkcjonalność jest wywoływana z dwiema różnymi liczbami wejść. Podczas wywoływania metody zgodnie z przekazywanymi danymi wejściowymi (w tym przypadku albo jedna wartość ciągu, albo dwie wartości ciągu) wykonywana jest odpowiednia metoda.

Metody mogą być przeciążone:

  1. Na podstawie liczby przekazanych parametrów .

    Przykład: method(String s) i method(String s1, String s2) .

  1. Na podstawie kolejności parametrów .

    Przykład: method(int i, float f) i method(float f, int i)) .

Uwaga: Metod nie można przeciążać, zmieniając tylko typ zwracany ( int method() jest uważana za taką samą jak String method() i RuntimeException jeśli zostanie podjęta próba). Jeśli zmienisz typ zwrotu, musisz również zmienić parametry w celu przeciążenia.

Podstawowa budowa i użytkowanie obiektu

Przedmioty są w swojej klasie, więc prostym przykładem byłby samochód (szczegółowe wyjaśnienia poniżej):

public class Car {
    
    //Variables describing the characteristics of an individual car, varies per  object
   private int milesPerGallon;
   private String name;
   private String color;
   public int numGallonsInTank; 
    
    public Car(){
        milesPerGallon = 0;
        name = "";
        color = "";
        numGallonsInTank = 0;
    }
    
    //this is where an individual object is created
    public Car(int mpg, int, gallonsInTank, String carName, String carColor){
        milesPerGallon = mpg;
        name = carName;
        color = carColor;
        numGallonsInTank = gallonsInTank;
    }

    //methods to make the object more usable

    //Cars need to drive
    public void drive(int distanceInMiles){
        //get miles left in car
        int miles = numGallonsInTank * milesPerGallon;
        
        //check that car has enough gas to drive distanceInMiles
        if (miles <= distanceInMiles){
            numGallonsInTank = numGallonsInTank - (distanceInMiles / milesPerGallon)
            System.out.println("Drove " + numGallonsInTank + " miles!");
        } else {
            System.out.println("Could not drive!");
        }
    }

    public void paintCar(String newColor){
        color = newColor;
    }
        //set new Miles Per Gallon
    public void setMPG(int newMPG){
        milesPerGallon = newMPG;
    }

       //set new number of Gallon In Tank
    public void setGallonsInTank(int numGallons){
        numGallonsInTank = numGallons;
    }
    
    public void nameCar(String newName){
        name = newName;
    }

    //Get the Car color
    public String getColor(){
        return color;
    }

    //Get the Car name
    public String getName(){
        return name;
    }

    //Get the number of Gallons
    public String getGallons(){
        return numGallonsInTank;
    }
    
}  

Obiekty są instancjami swojej klasy. Tak więc sposób tworzenia obiektu polega na wywołaniu klasy Car na dwa sposoby w klasie głównej (główna metoda w Javie lub onCreate w Androidzie).

opcja 1

`Car newCar = new Car(30, 10, "Ferrari", "Red");

Opcja 1 polega na tym, że po utworzeniu obiektu zasadniczo mówisz programowi o samochodzie. Zmiana dowolnej właściwości samochodu wymagałaby wywołania jednej z metod, takich jak metoda repaintCar . Przykład:

 newCar.repaintCar("Blue");

Uwaga: Upewnij się, że przekazujesz poprawny typ danych do metody. W powyższym przykładzie możesz również przekazać zmienną do metody repaintCar , o ile typ danych jest poprawny` .

To był przykład zmiany właściwości obiektu, otrzymanie właściwości obiektu wymagałoby użycia metody z klasy Car, która ma wartość zwracaną (co oznacza metodę, która nie jest void ). Przykład:

String myCarName = newCar.getName();  //returns string "Ferrari"

Opcja 1 jest najlepszą opcją, jeśli masz wszystkie dane obiektu w momencie tworzenia.

Opcja 2

`Car newCar = new Car();

Opcja 2 uzyskuje ten sam efekt, ale wymagała więcej pracy, aby poprawnie utworzyć obiekt. Chcę przypomnieć sobie tego Konstruktora w klasie samochodów:

public void Car(){
        milesPerGallon = 0;
        name = "";
        color = "";
        numGallonsInTank = 0;
    }

Zauważ, że tak naprawdę nie musisz przekazywać żadnych parametrów do obiektu, aby go utworzyć. Jest to bardzo przydatne, gdy nie masz wszystkich aspektów obiektu, ale musisz użyć posiadanych części. Powoduje to ustawienie ogólnych danych w każdej zmiennej instancji obiektu, dzięki czemu w przypadku wywołania fragmentu danych, który nie istnieje, nie zostaną zgłoszone żadne błędy.

Uwaga: Nie zapominaj, że musisz ustawić części obiektu później, z którymi go nie zainicjowałeś. Na przykład,

Car myCar = new Car();
String color = Car.getColor(); //returns empty string

Jest to częsty błąd wśród obiektów, które nie zostały zainicjowane wszystkimi danymi. Uniknięto błędów, ponieważ istnieje Konstruktor, który umożliwia tworzenie pustego obiektu Car ze zmiennymi stand-in ( public Car(){} ), ale żadna część myCar nie została faktycznie dostosowana. Prawidłowy przykład tworzenia obiektu samochodu:

Car myCar = new Car();
myCar.nameCar("Ferrari");
myCar.paintCar("Purple");
myCar.setGallonsInTank(10);
myCar.setMPG(30);

I, dla przypomnienia, uzyskaj właściwości obiektu, wywołując metodę w swojej klasie głównej. Przykład:

String myCarName = myCar.getName(); //returns string "Ferrari"

Konstruktory

Konstruktory są specjalnymi metodami nazwanymi na cześć klasy i bez typu zwracanego, i służą do konstruowania obiektów. Konstruktory, podobnie jak metody, mogą przyjmować parametry wejściowe. Do inicjowania obiektów służą konstruktory. Klasy abstrakcyjne mogą mieć także konstruktory.

public class Hello{
    // constructor
    public Hello(String wordToPrint){
        printHello(wordToPrint);
    }
    public void printHello(String word){
        System.out.println(word);
    }
}
// instantiates the object during creating and prints out the content
// of wordToPrint

Ważne jest, aby zrozumieć, że konstruktory różnią się od metod na kilka sposobów:

  1. Konstruktory mogą przyjmować modyfikatory tylko public , private i protected i nie mogą być deklarowane jako abstract , final , static ani synchronized .

  2. Konstruktory nie mają typu powrotu.

  3. Konstruktory MUSZĄ mieć taką samą nazwę jak nazwa klasy. W Hello przykład Hello nazwa konstruktora obiektu jest taka sama jak nazwa klasy.

  4. this słowo kluczowe ma dodatkowego wykorzystania wewnątrz konstruktorów. this.method(...) wywołuje metodę w bieżącej instancji, podczas gdy this(...) odnosi się do innego konstruktora w bieżącej klasie z różnymi podpisami.

Konstruktory można również wywoływać przez dziedziczenie za pomocą słowa kluczowego super .

public class SuperManClass{

    public SuperManClass(){
        // some implementation
    }
    
    // ... methods
}


public class BatmanClass extends SupermanClass{
    public BatmanClass(){
        super();
    }
    //... methods...
}

Zobacz specyfikację języka Java # 8.8 i # 15.9

Inicjowanie statycznych pól końcowych za pomocą inicjatora statycznego

Aby zainicjować static final pola static final które wymagają użycia więcej niż jednego wyrażenia, można użyć static inicjatora do przypisania wartości. Poniższy przykład inicjuje niemodyfikowalny zestaw String s:

public class MyClass {

    public static final Set<String> WORDS;
    
    static {
        Set<String> set = new HashSet<>();
        set.add("Hello");
        set.add("World");
        set.add("foo");
        set.add("bar");
        set.add("42");
        WORDS = Collections.unmodifiableSet(set);
    }
}

Wyjaśnienie, czym jest przeciążanie i zastępowanie metod.

Metoda Przesłanianie i Przeciążanie to dwie formy polimorfizmu obsługiwane przez Javę.

Przeciążenie metody

Przeciążanie metod (znane również jako statyczny polimorfizm) to sposób na posiadanie dwóch (lub więcej) metod (funkcji) o tej samej nazwie w jednej klasie. Tak, to takie proste.

public class Shape{
    //It could be a circle or rectangle or square
    private String type;
    
    //To calculate area of rectangle
    public Double area(Long length, Long breadth){
        return (Double) length * breadth;
    }
    
     //To calculate area of a circle
     public Double area(Long radius){
        return (Double) 3.14 * r * r;
    }
}

W ten sposób użytkownik może wywołać tę samą metodę dla obszaru w zależności od rodzaju kształtu.

Ale prawdziwe pytanie brzmi: w jaki sposób kompilator Java rozróżni, która metoda ma zostać wykonana?

Cóż, Java wyjaśniła, że chociaż nazwy metod ( area() w naszym przypadku) mogą być takie same, ale argumenty pobierane przez metodę powinny być inne.

Przeciążone metody muszą mieć różne listy argumentów (ilość i typy).

Biorąc to pod uwagę, nie możemy dodać innej metody obliczania pola kwadratu w ten sposób: public Double area(Long side) ponieważ w tym przypadku będzie on kolidował z metodą obszarową okręgu i spowoduje niejednoznaczność kompilatora Java.

Dzięki Bogu, istnieją pewne relaksacje podczas pisania przeciążonych metod takich jak

Może mieć różne typy zwrotu.

Może mieć różne modyfikatory dostępu.

Może rzucać różne wyjątki.

Dlaczego nazywa się to polimorfizmem statycznym?

To dlatego, że przeciążone metody, które mają zostać wywołane, są ustalane w czasie kompilacji na podstawie faktycznej liczby argumentów i typów argumentów w czasie kompilacji.

Jednym z typowych powodów używania metody przeciążania metod jest prostota dostarczanego kodu. Na przykład pamiętasz String.valueOf() która przyjmuje prawie każdy typ argumentu? To, co jest napisane za sceną, to prawdopodobnie coś takiego: -

static String valueOf(boolean b) 
static String valueOf(char c) 
static String valueOf(char[] data) 
static String valueOf(char[] data, int offset, int count) 
static String valueOf(double d) 
static String valueOf(float f) 
static String valueOf(int i) 
static String valueOf(long l) 
static String valueOf(Object obj) 

Metoda przesłonięcia

Cóż, przesłonięcie metody (tak, zgadujesz, prawda, jest również znane jako dynamiczny polimorfizm) jest nieco bardziej interesującym i złożonym tematem.

W zastępowaniu metod zastępujemy treść metody dostarczonej przez klasę nadrzędną. Rozumiem? Nie? Przejdźmy przez przykład.

public abstract class Shape{
    
    public abstract Double area(){
        return 0.0;
    }
}

Mamy więc klasę o nazwie Kształt i metodę o nazwie obszar, która prawdopodobnie zwróci obszar kształtu.

Powiedzmy, że teraz mamy dwie klasy o nazwie Okrąg i Prostokąt.

public class Circle extends Shape {
    private Double radius = 5.0;

    // See this annotation @Override, it is telling that this method is from parent
    // class Shape and is overridden here
    @Override
    public Double area(){
        return 3.14 * radius * radius;
    }
}

Podobnie klasa prostokąta:

 public class Rectangle extends Shape {
    private Double length = 5.0;
    private Double breadth= 10.0;


    // See this annotation @Override, it is telling that this method is from parent
    // class Shape and is overridden here
    @Override
    public Double area(){
        return length * breadth;
    }
}

Tak więc teraz obie klasy podrzędne zaktualizowały treść metody dostarczonej przez klasę nadrzędną ( Shape ). Teraz pytanie brzmi, jak zobaczyć wynik? Cóż, zróbmy to w stary sposób psvm .

public class AreaFinder{
    
    public static void main(String[] args){

        //This will create an object of circle class
        Shape circle = new Circle();
        //This will create an object of Rectangle class
        Shape rectangle = new Rectangle();
        
        // Drumbeats ......
        //This should print 78.5
        System.out.println("Shape of circle : "+circle.area());

        //This should print 50.0
        System.out.println("Shape of rectangle: "+rectangle.area());            
        
    }
}

Łał! czy to nie świetne? Dwa obiekty tego samego typu wywołujące te same metody i zwracające różne wartości. Mój przyjacielu, to jest siła dynamicznego polimorfizmu.

Oto wykres, aby lepiej porównać różnice między nimi: -

Przeciążenie metody Metoda przesłonięcia
Przeciążenie metody służy do zwiększenia czytelności programu. Przesłanianie metod służy do zapewnienia konkretnej implementacji metody, która jest już zapewniona przez jej superklasę.
Przeciążenie metody odbywa się w obrębie klasy. Zastępowanie metod występuje w dwóch klasach, które mają relację IS-A (dziedziczenie).
W przypadku przeciążenia metody parametr musi być inny. W przypadku zastąpienia metody parametr musi być taki sam.
Przeciążenie metody jest przykładem polimorfizmu czasu kompilacji. Zastępowanie metod jest przykładem polimorfizmu w czasie wykonywania.
W java przeciążenia metody nie można wykonać, zmieniając tylko typ zwracanej metody. Typ powrotu może być taki sam lub inny w przypadku przeciążenia metody. Ale musisz zmienić parametr. Typ zwracany musi być taki sam lub zmienny kowariantne w przypadku przesłonięcia metody.


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow