Omówię tutaj styl programowania w Javie, czyli obiektowość.
Programowanie w tym języku jest programowaniem obiektowym (Object Oriented Programming – OOP).
W programowaniu tym skupiamy się głównie na danych – obiektach, a dopiero potem na algorytmach/metodach, które je przetwarzają.
Klasa jest określeniem sposobu tworzenia obiektów. Tworząc nowy obiekt tworzymy nową instancję danej klasy.
Z obiektowością związane jest pojęcie hermetyzacji, czyli zamknięcie danych przed dostępem z zewnątrz. Dane zawarte w obiekcie nazywane są danymi składowymi, a funkcje operujące na tych danych metodami. Określają one łącznie stan obiektu.
Obiekt posiada trzy podstawowe cechy:
1. Zachowanie – czyli co można zrobić z obiektem i jakie metody zawiera
2. Stan – reakcja obiektu na wywołane na nim metody
3. Tożsamość – cechy, które odróżniają obiekt od innych o tym samym zachowaniu i stanie
Relacje między klasami:
–zależność (jedna klasa używa innej, która nie jest z nią zbytnio powiązana)
–agregacja (zawieranie)
–dziedziczenie (że klasa jest jakby daną klasą – rozszerza ją)
Możemy używać już klas predefiniowanych z biblioteki Javy.
Nowe obiekty tworzymy za pomocą słowa kluczowego „new” i tworzone są one przy pomocy konstruktorów.
Przykładowy listing kodu:
package net.javainfo.objectivity;
import java.time.DayOfWeek;
import java.time.LocalDate;
public class Calendar {
public static void main(String[] args){
LocalDate date = LocalDate.now();
int month = date.getMonthValue();
int today = date.getDayOfMonth();
date = date.minusDays(today - 1);
DayOfWeek weekday = date.getDayOfWeek();
int value = weekday.getValue();
System.out.println(" Pn Wt Sr Cz Pt Sb Nd");
for(int i = 1; i < value; ++i)
System.out.print(" ");
while(date.getMonthValue() == month){
System.out.printf("%3d",date.getDayOfMonth());
if(date.getDayOfMonth() == today)
System.out.print("*");
else
System.out.print(" ");
date = date.plusDays(1);
if(date.getDayOfWeek().getValue() == 1)
System.out.println();
}
if(date.getDayOfWeek().getValue() != 1)
System.out.println();
}
}
Jak definiujemy własne klasy?
Otóż przyjmujemy regułę, że klasa zawiera własne pola i metody. Oraz konstruktory, wykorzystywane przy tworzeniu instancji obiektów.
W jednym pliku może być wiele klas, jedna tylko może być publiczna i mieć nazwę pliku.
Ogólne zasady tworzenia konstruktorów:
-musi mieć taką samą nazwę jak klasa
-klasa może mieć więcej niż jeden konstruktor
-konstruktor może przyjmować zero lub więcej parametrów
-nie zwracają one wartości jak metody
-wywoływany zawsze przy użyciu słowa kluczowego „new”
Od Javy 10 zmienne lokalne można deklarować przy pomocy słowa kluczowego „var„:
//zamiast:
Object obiekt = new Object();
//można napisać:
var obiekt = new Object();
Brak obiektu oznaczany jest przez specjalną wartość „null”. Często będzie się można spotkać z wyjątkiem „NullPointerException„, czyli odwołaniem się do wartości null.
Możemy spotkać się z parametrami jawnymi i niejawnymi.
Niejawne to te w metodzie korzystające z pól klasy, a jawne to te podane w definicji metody w nawiasach: „()”.
Hermetyzacja opiera się głównie na tym, że pola składowe z reguły tworzymy prywatne, metody dostępu i mutatory(zmieniające te pola) publiczne.
Stałe tworzymy przy użyciu słowa kluczowego „final„.
Pola i metody statyczne, to takie, przy których nie musimy tworzyć nowych obiektów.
Są one inicjalizowane raz przy kompilacji programu.
Przykład zastosowania:
package net.javainfo.objectivity;
//Przykład zastosowania pola i metody statycznej
public class StaticExample {
public static void main(String[] args){
var customers = new Customer[3];
customers[0] = new Customer("Jan Kowalski", 2300);
customers[1] = new Customer("Piotr Nowakowski", 400);
customers[2] = new Customer("Anna Nowicka", 1540);
for(Customer c : customers){
System.out.println(c.toString());
}
}
}
class Customer{
private static int nextId = 1; //raz zainicjalizowane pole składowe jako statyczne
private String name;
private double amount;
private int id;
Customer(String name, double amount){
this.name = name;
this.amount = amount;
id = setId();
}
public static int setId(){ //demonstracja działania metody statycznej
int tmp = nextId;
nextId++;
return tmp;
}
public int getId(){
return this.id;
}
public String getName(){
return this.name;
}
public double getAmount(){
return this.amount;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", amount=" + amount +
", id=" + id +
'}';
}
}
Parametry są przekazywane do metod przez wartość.
Zasady dotyczące używania parametrów metod:
-metoda nie może modyfikować parametru typu podstawowego
-metoda może zmienić stan obiektu przekazanego jako parametr
-metoda, nie może sprawić, aby parametr zaczął się odwoływać do nowego obiektu
Przykład:
package net.javainfo.objectivity;
//Przykład sposobów przekazywania parametrów do metod
public class Parameter {
public static void main(String[] args){
double x = 200;
//metoda nie może zmodyfikować parametru typu podstawowego
System.out.println("Wartość x przed metodą: " + x);
raiseValue(x);
System.out.println("Wartość x po metodzie: " + x);
//metoda może zmienić stan obiektu przekazanego jako parametr
Customer2 customer = new Customer2("Asia", 200);
System.out.println("Customer przed zmianą: " + customer);
raiseObjectValue(customer);
System.out.println("Customer po zmianie " + customer);
//metoda nie może sprawić, aby parametr obiektowy zaczął się odwoływać do nowego obiektu
Customer2 customer2 = new Customer2("Carol", 300);
System.out.println("Przed zamianą: " + "Customer1: " + customer + "\nCustomer2: " + customer2);
swapObject(customer,customer2);
System.out.println("Po zamianie :" + "Customer1: " + customer + "\nCustomer2: " + customer2);
}
public static void raiseValue(double value){
value *= 2;
}
public static void raiseObjectValue(Customer2 customer){
customer.setAmount(customer.getAmount()*2);
}
public static void swapObject(Customer2 customer1, Customer2 customer2){
Customer2 temp = customer1;
customer1 = customer2;
customer2 = temp;
}
}
class Customer2{
private String name;
private double amount;
Customer2(String name, double amount){
this.name = name;
this.amount = amount;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public void raiseAmount(){
this.amount *= 2;
}
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", amount=" + amount +
'}';
}
}
Nowe obiekty tworzymy poprzez słowo kluczowe „new„. Możemy je tworzyć na wiele sposobów, dzięki przeciążaniu, czyli utworzeniu kilku metod/konstruktorów o tej samej nazwie. Domyślnie pola składowe klasy są inicjalizowane w sposób domyślny, pola typów liczbowych na 0, boolean na false, a referencje obiektów na null.
Konstruktorem domyślnym jest konstruktor bezargumentowy. Jest on domyślnie wywoływany, nawet jak go nie ma w kodzie. Natomiast jeżeli przeciążymy go innym, musimy ręcznie go wprowadzić.
Przyjmuje się, że w konstruktorze odnosimy się do zmiennej składowej poprzez słowo kluczowe „this”:
public Note(int id, String content){
this.id = id;
this.content = content;
}
Przez this w jednym konstruktorze możemy też wywołać inny,np.:
public Note(String content){
this();
}
Pamiętajmy, że wtedy konstruktor musi być pierwszą instrukcją w bloku.
Poprzez blok inicjujący można również zainicjalizować pola egzemplarza:
class Note{
private static int nextId;
int id;
String content;
{
id = nextId;
nextId++;
}
Są one wykonywane przy każdym konstruowaniu nowego obiektu.
Istnieje jeszcze w Javie coś takiego jak rekordy.
Jest to specjalny rodzaj klasy o niezmiennym stanie, którego zawartość jest ogólnie dostępna.
record Point(double x, double y){}
W ten sposób mamy klasę zawierającą:
private final double x;
private final double y;
//konstruktor:
Point(double x, double y);
//metody dostępowe:
public double x();
public double y();
Możemy dodawać do rekordu własne metody, oraz pola statyczne/metody.
Nie można natomiast tworzyć samemu pól egzemplarza.
Domyślny konstruktor zwany jest konstruktorem kanonicznym. Konstruktor niestandardowy może być tworzony przez programistę dodatkowo, ale w pierwszej instrukcji bloku musi wywołać inny konstruktor.
Możemy korzystać w konstruktorze kanonicznym formę kompaktową, w której nie podajemy listy parametrów:
record Point(double x, double y){
public Point{
double tmp = x;
x = y;
y = tmp;
}
}
Pakiety tworzymy poprzez użycie nazwy odwróconej domeny, tak jak w internecie adres strony. Żeby nie było konfliktu nazw.
Np. net.javainfo
Potem stosujemy nazwę projektu czy inną dowolną.
Klasy zewnętrzne importujemy po słowie kluczowym „package„, które musi być wywołane pierwsze w kodzie, dopiero potem stosujemy importy poprzez słowo „import” i pełna nazwa pakietu, np. java.util.*;
„*” oznacza że importujemy wszystkie klasy z pakietu java.util.
JDK zawiera javadoc, czyli narzędzie do tworzenia komentarzy dokumentacyjnych.
Tworzymy je poprzez /** */.
Niektóre ze znaczników to:
@author
@version
Generujemy je poprzez komendę:
javadoc -d docDirectory nazwaPakietu
Wywołujemy ją w katalogu z plikami źródłowymi.
Dodaj komentarz