Object i klasa, sve je objekat
"Erge, onog dana dok sam u biblioteci ponavljala 'Put ka naprednoj Javi', naišla sam na jednog starijeg studenta koji me je pitao da li imam 'objekat', rekla sam da još uvek nemam. I pogađaš šta je on rekao: 'Da ti napravim jednog sa new?' U tom trenutku sam bila potpuno zbunjena — šta znači new, Erge?" Sanmej me je zbunjeno pitala.
"Haha, Sanmej, tvoj stariji student je prilično duhovit. new je ključna reč u Javi koja pretvara klasu u objekat." rekao sam Sanmej sa osmehom. "Objekat i klasa su dva najosnovnija koncepta u Javi — može se reći da nose objektno orijentisano programiranje (OOP)."
01. Proceduralno i objektno orijentisano
Da li Sanmej želi da pita šta je OOP?
Puni naziv OOP-a na engleskom je Object Oriented Programming; da bismo ga razumeli, prvo moramo razumeti objektnu orijentaciju; a da bismo razumeli objektnu orijentaciju, prvo moramo razumeti proceduralnu orijentaciju, jer u početku nije bilo objektno orijentisanih programskih jezika — svi su bili proceduralno orijentisani.
Da navedem jednostavan primer koji razlikuje proceduralnu i objektnu orijentaciju.
Jednog dana poželiš malu supu, šta radiš? Imaš dve opcije:
Sam kupiš namirnice — kora od tofua, meso, beli luk i tako dalje — i sam je pripremiš.
Odeš u restoran i samo vikneš vlasniku: "Jednu malu supu."
Prvi način je proceduralno orijentisan, drugi je objektno orijentovan.
Koje su mane proceduralne orijentacije? Ako kupiš namirnice za malu supu, a onda na kraju poželiš kung pao piletinu, da li ti opet moraš da kupiš nove namirnice?
Koje su prednosti objektne orijentacije? Ako više ne želiš malu supu, samo kažeš vlasniku: "Ako moja mala supa još nije napravljena, zamenite je kung pao piletinom!"
Proceduralna orijentacija je procesno orijentisana — korak po korak; kada se završi prethodni korak, prelazi se na sledeći.
Objektna orijentacija je modularna — ja radim svoje, ti radiš svoje; ako mi treba da ti nešto radiš, samo ti kažem. Ne moram znati kako tačno to radiš, gleda se rezultat, ne trud.
Međutim, ako se iskopa do dna, donji slojevi objektne orijentacije su zapravo i dalje proceduralno orijentisani; samo je proceduralna orijentacija apstrahovana i enkapsulirana u klase, da bi nam olakšala pozivanje.
02. Klasa
Objekat može biti bilo koji predmet koji se može videti u stvarnosti, na primer jedna osobena svinja; takođe može biti bilo koji virtuelni predmet iz mašte, na primer Sun Wukong koji može da se transformiše na 72 načina.
Java definiše te predmete preko klase (class); koje stanje ti predmeti imaju definiše se preko polja, na primer koja je boja svinje — jednobojna ili šarena; koje ponašanje ti predmeti imaju definiše se preko metoda, na primer svinja zna da jede i da spava.
Hajde, da definišemo jednostavnu klasu da ti pokažem.
/**
* @author Chenmo Wang Er
* @date 2020/11/19
*/
public class Person {
private String name;
private int age;
private int sex;
private void eat() {
}
private void sleep() {
}
private void dadoudou() {
}
}Jedna klasa može sadržati:
- Polja (Field)
- Metode (Method)
- Konstruktor (Constructor)
U klasi Person postoje 3 polja: name, age i sex; ona se takođe nazivaju članske promenljive — unutar klase, ali izvan metoda; ono unutar metoda su lokalne promenljive.
Članske promenljive se ponekad nazivaju i instancne promenljive; ne zauzimaju memorijski prostor pri kompajliranju, dobijaju memoriju pri izvršavanju — drugim rečima, tek nakon instanciranja objekta (new Person()) polja dobijaju memoriju, što je i razlog zašto se zovu "instancne" promenljive.
Metoda ima 3: eat(), sleep() i dadoudou(), koji pokazuju šta objekat Person može da radi — jede, spava i udara Doudoua.
Da li Sanmej onda želi da pita: "Zašto nema konstruktora?"
Zaista, u izvornom fajlu (.java) klase Person ne vidi se, ali u dekompajliranom fajlu bajtkoda (.class) se može videti.
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.itwanger.twentythree;
public class Person {
private String name;
private int age;
private int sex;
public Person() {
}
private void eat() {
}
private void sleep() {
}
private void dadoudou() {
}
}public Person(){} je podrazumevani konstruktor; pošto je to prazan konstruktor (u telu metoda nema sadržaja), može se izostaviti. Java je pametna upravo u tome — neki prilično kruti kod ne mora da bude dodat od strane programera, Java ga potajno odradi.
03. new jednog objekta
Pri kreiranju Java objekta, potrebno je koristiti ključnu reč new.
Person person = new Person();Ovaj red koda je preko klase Person kreirao jedan Person objekat. Svaki objekat pri kreiranju dobija prostor u heap memoriji.
Pri kreiranju objekta, potreban je main() metod kao ulazna tačka; main() metod može biti u trenutnoj klasi, a može biti i u drugoj klasi.
Prvi način: main() metod se stavlja direktno u klasu Person.
public class Person {
private String name;
private int age;
private int sex;
private void eat() {}
private void sleep() {}
private void dadoudou() {}
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);
System.out.println(person.age);
System.out.println(person.sex);
}
}Izlaz je prikazan ispod:
null
0
0Drugi način: main() metod nije u klasi Person, već u drugoj klasi.

U realnom razvoju obično ne kreiramo objekat direktno u trenutnoj klasi i koristimo ga, već ga stavljamo u klasu koja taj objekat koristi, na primer klasu PersonTest na gornjoj slici.
Klase PersonTest i Person mogu se nalaziti u dva fajla, a mogu i u jednom fajlu (pod imenom PersonTest.java), kao u sledećem primeru.
/**
* @author Chenmo Wang Er
*/
public class PersonTest {
public static void main(String[] args) {
Person person = new Person();
}
}
class Person {
private String name;
private int age;
private int sex;
private void eat() {}
private void sleep() {}
private void dadoudou() {}
}04. Inicijalizacija objekta
U prethodnom primeru, izlaz programa bio je:
null
0
0Zašto je takav izlaz? Zato što Person objekat nije inicijalizovan, pa je ispisana podrazumevana vrednost za String — null, i podrazumevana vrednost za int — 0.
Kako onda inicijalizovati Person objekat (dodeliti vrednosti poljima)?
Prvi način: preko referentne promenljive objekta.
public class Person {
private String name;
private int age;
private int sex;
public static void main(String[] args) {
Person person = new Person();
person.name = "Chenmo Wang Er";
person.age = 18;
person.sex = 1;
System.out.println(person.name);
System.out.println(person.age);
System.out.println(person.sex);
}
}person se naziva referentnom promenljivom objekta Person, kao na slici ispod:

Preko referentne promenljive objekta, polja se mogu direktno inicijalizovati (person.name = "Chenmo Wang Er"), pa gornji kod daje sledeći izlaz:
Chenmo Wang Er
18
1Drugi način: inicijalizacija preko metoda.
/**
* @author Chenmo Wang Er, jedan zabavan programer
*/
public class Person {
private String name;
private int age;
private int sex;
public void initialize(String n, int a, int s) {
name = n;
age = a;
sex = s;
}
public static void main(String[] args) {
Person person = new Person();
person.initialize("Chenmo Wang Er",18,1);
System.out.println(person.name);
System.out.println(person.age);
System.out.println(person.sex);
}
}U klasi Person se dodaje novi metod initialize(), a zatim se nakon kreiranja objekta prosleđuju parametri za inicijalizaciju (person.initialize("Chenmo Wang Er", 18, 1)).
Treći način: inicijalizacija preko konstruktora.
/**
* @author Chenmo Wang Er, jedan zabavan programer
*/
public class Person {
private String name;
private int age;
private int sex;
public Person(String name, int age, int sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public static void main(String[] args) {
Person person = new Person("Chenmo Wang Er", 18, 1);
System.out.println(person.name);
System.out.println(person.age);
System.out.println(person.sex);
}
}Ovo je ujedno i najstandardniji pristup, gde se parametri proslede direktno prilikom new.
Da dodam još jedno znanje — anonimni objekat. Anonimni objekat znači da nema referentnu promenljivu i može se koristiti samo jednom, pri kreiranju.
new Person();Možete direktno pozvati metod preko anonimnog objekta:
new Person().initialize("Chenmo Wang Er", 18, 1);05. O klasi Object
U Javi se često pominje izraz 'sve je objekat', pri čemu 'sve' označava sve klase u Javi, a te klase su izvedene klase klase Object.
Klasa Object uglavnom pruža 11 metoda, koji se grubo mogu podeliti u šest kategorija:

Poređenje objekata:
① public native int hashCode(): lokalni metod, služi da vrati heš kod objekta.
public native int hashCode();Po konvenciji, jednaki objekti moraju imati isti heš kod. Ako se prepiše equals metod, trebalo bi prepisati i hashCode metod. Može se koristiti Objects.hash() metod za generisanje heš koda.
public int hashCode() {
return Objects.hash(name, age);
}② public boolean equals(Object obj): služi za poređenje da li su memorijske adrese dva objekta iste.
public boolean equals(Object obj) {
return (this == obj);
}Ako želimo da upoređujemo da li su vrednosti dva objekta iste, moramo prepisati taj metod, na primer String klasa, Integer klasa i druge su prepisale taj metod. Za primer, pretpostavimo da imamo klasu Person i smatramo da su, ako su ime i godine iste, u pitanju ista osoba — onda equals možemo prepisati ovako:
class Person1 {
private String name;
private int age;
// gettter i setter metodi su izostavljeni
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Person1) {
Person1 p = (Person1) obj;
return this.name.equals(p.getName()) && this.age == p.getAge();
}
return false;
}
}Kopiranje objekata:
protected native Object clone() throws CloneNotSupportedException: lokalni metod, vraća jednu kopiju ovog objekta. Podrazumevana implementacija radi samo plitko kopiranje, i klasa mora implementirati Cloneable interfejs.
Sam Object ne implementira Cloneable interfejs, pa bez prepisivanja metoda clone, direktan poziv tog metoda izazvaće CloneNotSupportedException izuzetak.
Pretvaranje objekta u string:
public String toString(): vraća stringovnu reprezentaciju objekta. Podrazumevana implementacija vraća ime klase@heksadecimalni prikaz heš koda, ali se obično prepisuje da vraća smislenije informacije.
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}Na primer, za klasu Person možemo prepisati toString metod da vraća smisleni string:
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}Naravno, ovaj posao se može prepustiti i IDE-u, na primer IntelliJ IDEA — desni klik, izaberi Generate, a zatim izaberi toString metod, i automatski će se generisati toString metod.
Takođe se može prepustiti Lombok-u, koristeći @Data anotaciju, koja automatski generiše toString metod.
Niz je takođe objekat, pa kada štampamo niz obično vidimo stringove poput [I@1b6d3586 — to je heš kod int niza.
Zakazivanje više niti:
Svaki objekat može pozvati wait/notify metode klase Object da implementira mehanizam čekanja/obaveštavanja. Hajde da napišemo primer:
public class WaitNotifyDemo {
public static void main(String[] args) {
Object lock = new Object();
new Thread(() -> {
synchronized (lock) {
System.out.println("Nit 1: želim da čekam");
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Nit 1: probuđen sam");
}
}).start();
new Thread(() -> {
synchronized (lock) {
System.out.println("Nit 2: želim da probudim");
lock.notify();
System.out.println("Nit 2: probudio sam je");
}
}).start();
}
}Da objasnim:
- Nit 1 se izvršava prva, pozvala je
lock.wait()metod, a zatim ušla u stanje čekanja. - Nit 2 se izvršava kasnije, pozvala je
lock.notify()metod, a zatim je nit 1 probuđena.
① public final void wait() throws InterruptedException: poziv ovog metoda će dovesti do toga da trenutna nit čeka, sve dok druga nit ne pozove notify() ili notifyAll() metod ovog objekta.
② public final native void notify(): budi jednu nit koja čeka na monitoru ovog objekta. Ako više niti čeka, bira se jedna nit da bude probuđena.
③ public final native void notifyAll(): budi sve niti koje čekaju na monitoru ovog objekta.
④ public final native void wait(long timeout) throws InterruptedException: čeka timeout milisekundi; ako za timeout milisekundi ne bude probuđena, automatski se budi.
⑥ public final void wait(long timeout, int nanos) throws InterruptedException: još preciznije — čeka timeout milisekundi i nanos nanosekundi; ako za to vreme ne bude probuđena, automatski se budi.
Refleksija:
Preporučeno čitanje: Ergov put ka naprednoj Javi: savladajte Java refleksiju
public final native Class<?> getClass(): služi za dobijanje informacija o klasi objekta, kao što je ime klase. Na primer:
public class GetClassDemo {
public static void main(String[] args) {
Person p = new Person();
Class<? extends Person> aClass = p.getClass();
System.out.println(aClass.getName());
}
}Izlaz:
com.itwanger.PersonSmeće prikupljanje:
protected void finalize() throws Throwable: poziva se kada sakupljač smeća odluči da prikupi memoriju koju objekat zauzima. Koristi se za čišćenje resursa, ali ga Java ne preporučuje jer je nepredvidiv i lako izaziva probleme; od Jave 9 je označen kao zastareo.

06. Neka manja znanja o objektima
1) Proces apstrakcije
Svi programski jezici su jedna vrsta apstrakcije; čak se može reći da složenost problema koje možemo rešiti zavisi od vrste i kvaliteta apstrakcije.
Smalltalk je istorijski prvi uspešan objektno orijentisani jezik i pružio je inspiraciju za Javu. Ima 5 osnovnih karakteristika:
- Sve je objekat.
- Jedan program je zapravo više objekata koji šalju poruke jedni drugima da bi rekli šta da rade.
- Komponovanjem se više objekata može enkapsulirati u druge, osnovnije objekte.
- Objekti se instanciraju preko klasa.
- Objekti istog tipa mogu primati iste poruke.
Ukratko, jedna rečenica:
Stanje + ponašanje + identitet = objekat; svaki objekat u memoriji ima jedinstvenu adresu.
2) Objekti imaju interfejse
Svi objekti se mogu svrstati u jednu klasu, i objekti iste klase imaju zajednička ponašanja i karakteristike. U Javi, ključna reč class služi za definisanje tipa.
Kreiranje apstraktnih tipova podataka je osnovni koncept objektno orijentisanog programiranja. Možete kreirati promenljive određenog tipa — u Javi se nazivaju objekti ili instance — a zatim možete manipulisati tim promenljivama, što se u Javi naziva slanje poruke ili slanje zahteva; konačno, objekat odlučuje šta da radi.
Klasa opisuje niz objekata sa istim karakteristikama i ponašanjem; u širem smislu, klasa je zapravo jedan prilagođeni tip podataka.
Jednom kada se kreira klasa, pomoću nje možete kreirati proizvoljan broj objekata. Jedan od najvećih izazova sa kojima se suočavaju objektno orijentisani programski jezici jeste kako apstrahovati stvarne/virtuelne elemente u Java objekte.
Koje zahteve objekat može primiti određeno je njegovim interfejsom. Kako se to tačno radi određeno je njegovim implementacionim metodima.
3) Modifikatori prava pristupa
Tvorac klase se ponekad naziva i pružalac API-ja, dok se korisnik klase naziva pozivaocem API-ja.
JDK nam pruža osnovnu implementaciju Jave; autori JDK-a su pružaoci osnovnog API-ja (Doug Lea, autor dela Jave o višenitnom radu, je jedan veliki autoritet kojeg Java programeri posebno poštuju); mi koji koristimo Java jezik, da budemo jasni, smo pozivaoci JDK-a.

Naravno, i mi kada pružimo nove klase drugim pozivaocima, postajemo novi tvorci.
Kada API tvorac kreira novu klasu, izlaže samo potrebne interfejse, a krije sve ostale nepotrebne informacije. Razlog za to je što, ako su te informacije nevidljive za pozivaoca, tvorac može slobodno menjati skrivene informacije bez brige o uticaju na pozivaoca.
Ovde se mora spomenuti Java modifikator pristupa.
Prva uloga modifikatora prava pristupa jeste da spreči pozivaoce klase da dođu do internih implementacija do kojih ne bi trebalo da dolaze; druga uloga jeste da omogući tvorcima klase da lako menjaju interne mehanizme bez brige o uticaju na upotrebu od strane pozivaoca.
- public
- private
- protected
Postoji i jedan "podrazumevani" modifikator pristupa koji se izostavlja; njime modifikovana klasa može pristupati drugim klasama u istom paketu.
4) Kompozicija
Možemo iskoristiti već kreiranu klasu kao člansku promenljivu druge klase; formiranje nove klase od postojećih klasa se naziva "ponovna upotreba", a kompozicija predstavlja odnos has-a.
5) Nasleđivanje
Nasleđivanje je veoma važan koncept u Javi: izvedena klasa nasleđuje roditeljsku klasu, čime dobija metode i polja modifikovane sa protected i public u roditeljskoj klasi; istovremeno, izvedena klasa može proširiti neke svoje metode i polja, a takođe može prepisati i nasleđene metode.
Uobičajeni primer je što oblik može imati izvedene klase kao što su krug, kvadrat, trougao — njihovi osnovni interfejsi su isti, na primer svi imaju metod draw(), a izvedene klase mogu naslediti taj metod i implementirati svoj način crtanja.
Ako izvedena klasa samo prepisuje metod roditeljske klase, onda je odnos između njih is-a; ali ako izvedena klasa doda nove metode, onda odnos postaje is-like-a.
6) Polimorfizam
Na primer, neka postoji roditeljska klasa Shape:
public class Shape {
public void draw() {
System.out.println("oblik");
}
}Izvedena klasa Circle:
public class Circle extends Shape{
@Override
public void draw() {
System.out.println("krug");
}
}Izvedena klasa Line:
public class Line extends Shape {
@Override
public void draw() {
System.out.println("linija");
}
}Test klasa:
public class Test {
public static void main(String[] args) {
Shape shape1 = new Line();
shape1.draw();
Shape shape2 = new Circle();
shape2.draw();
}
}Rezultat izvršenja:
linija
krugU test klasi, tip promenljive shape1 je Shape, a tip shape2 je takođe Shape, ali nakon poziva metoda draw(), automatski se pozivaju metodi draw() izvedenih klasa Line i Circle — zašto?
To je zapravo polimorfizam u Javi.
07. Rezime
"Pa, Sanmej, da li sada imaš dublje i jasnije razumevanje Jave?" Konačno sam završio, duboko udahnuo — kako prijatno!
"Da, brate, čini mi se da Java ipak nije nešto preterano." Oh, Sanmej je malo naprasita postala. "Sve je objekat, osim primitivnih tipova podataka."
"O, Sanmej, pa dobro ti ide, sama si sumirala." Osetio sam veliko olakšanje; zaista, kad se potrudiš, pažnja se isplati — kada pažljivo objašnjavaš, slušalac može razumeti i shvatiti, što je i ispunilo nameru.
