Objektno programiranje v Javi
Java je
objektni programski jezik, zato glavno
vlogo igrajo
objekti. Objekt je skupek podatkov, s
katerim želimo uporavljati kot s celoto. Vsak objekt pripada
nekemu
razredu. Če pripada objekt
x
razredu
C
, potem pravimo tudi da je
x
objekt tipaC
. Nekaj primerov objektov iz
Javine standardne knjižnice:
Objekt tipa BufferedReader
predstavlja
vhodni kanal. Kadar želimo brati s tipkovnice ali
z datoteke, naredimo tak objekt in kličemo njegovo metodo
readLine
za branje ene vrstice.
Objekt System.out
predstavlja standardni
izhod. Kadar želimo kaj izpisati na zaslon, pokličemo
metodo println
v objektu System.out
.
Objekt tipa Color
predstavlja barvo na
zaslonu.
Objekt tipa Applet
je grafična aplikacija, ki jo lahko
vstavimo v spletno stran ali poženemo samostojno.
Seveda pa so objekti uporabni predvsem zato, ker lahko programer
definira nove razrede in objekte.
Osnovni pristop objektnega programiranja bi lahko strnili
takole:
Objektno programiranje |
---|
objekt | podatek + metode za delo s podatkom |
računanje | objektu povemo, katero metodo naj izvede |
Ko želimo s kakim podatkom/objektom kaj narediti, mu
signaliziramo, kaj naj se zgodi. To storimo tako,
da pokličemo ustrezno metodo v objektu. Primerjamo ta princip s
klasičnim programiranjem, kjer velja naslednji pogled:
Klasično programiranje |
---|
program | podatkovne strukture + funkcije |
računanje | izvedemo funkcijo na podatkih |
Ko želimo kak podatek obdelati v klasičnem programiranju, to
storimo tako, da pokličemo ustrezno metodo in ji podamo kot
argument podatke, ki naj jih obdela.
Razlika med obema pristopoma je razvidna že iz tega, kako v
objektnem in klasičnem programiranju pokličemo metodo
f
na podatku
x
:
1
2
3
4
5
| // klasično: izvedi staticno metodo f na podatku x
f(x);
// objektno: podatku/objektu x signaliziraj, da naj izvede metoda f
x.f(); |
Komponente in konstruktorji
Oglejmo si primer objektnega programiranja. Denimo, da bi radi
napisali program, ki vodi evidenco o študentih. Podatki o
študentu obsegajo ime, priimek, letnico vpisa na univerzo in
vpisno številke (seveda je to poenostavljen primer). Torej
objekt, ki predstavlja študenta, vsebuje štiri podatke:
Student.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
| public class Student {
public String ime;
public String priimek;
public int leto_vpisa;
public String vpisna_st;
public Student(String ime, String priimek, int l, String v) {
this.ime = ime;
this.priimek = priimek;
this.leto_vpisa = l;
this.vpisna_st = v;
}
} |
S tem smo povedali, da je vsak
objekt tipa
Student
sestavljen iz štirih
komponente:
ime
,
priimek
,
leto_vpisa
,
vpisna_st
.
Če je a
objekt tipa Student
, potem lahko
dostopamo do njegove komponente ime
tako, da napišemo
a.ime
.
Nov objekt naredimo z ukazom
new
, kot to kaže naslednji
primer:
TestStudent.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| public class TestStudent {
public static void main(String args[]) {
Student a = new Student();
a.ime = "Miha";
a.priimek = "Novak";
a.leto_vpisa = 2000;
a.vpisna_st = "40002304";
Student b = new Student();
b.ime = "Mojca";
b.priimek = "Zver";
b.leto_vpisa = 2001;
b.vpisna_st = "40004377";
Student c = b;
c.ime = "Katarina";
System.out.println("Student a:\n" + a.ime + " " + a.priimek +
" " + a.leto_vpisa + " (" + a.vpisna_st + ")\n");
System.out.println("Student b:\n" + b.ime + " " + b.priimek +
" " + b.leto_vpisa + " (" + b.vpisna_st + ")\n");
System.out.println("Student c:\n" + c.ime + " " + c.priimek +
" " + c.leto_vpisa + " (" + c.vpisna_st + ")\n");
}
} |
> javac Student.java TestStudent.java
> java TestStudent
Student a:
Miha Novak 2000 (40002304)
Student b:
Katarina Zver 2001 (40004377)
Student c:
Katarina Zver 2001 (40004377) |
V programu smo naredili dva nova objekta razreda
Student
, ki sta shranjena v spremenljivkah
a
in
b
. Kot vedno, se nove objekte naredi z ukazom
new
. Spremenljivka
c
pa
je isti
objekt kot b
, se pravi, da se v 17. vrstici
ni naredila nova kopija objekta
b
, ampak
se spremenljivki
b
in
c
sklicujeta na isti objekt.
V vrsticah 4—8 smo naredili objekt
a
in nastavili
vrednosti njegovih komponent. Takole nastavljanje je v praksi
dokaj neprimerno, ker se zlahka zgodi, da kako komponento
pozabimo nastaviti. Zato Java omogoča, da delamo nove objekte na
bolj praktičen način s pomočjo
konstruktorjev.
Konstruktor je posebna metoda, ki ima
enako ime kot je ime
razreda. Konstruktor uporabimo v ukazu
new
. Java
najprej naredi nov objekt, nato pa pokliče konstruktor. Znotraj
konstruktorja se pravkar narejeni objekt imenuje
this
.
Primer:
Student2.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
| public class Student2 {
public String ime;
public String priimek;
public int leto_vpisa;
public String vpisna_st;
public Student2(String i, String p, int l, String v) {
this.ime = i;
this.priimek = p;
this.leto_vpisa = l;
this.vpisna_st = v;
}
} |
TestStudent2.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| public class TestStudent2 {
public static void main(String args[]) {
Student2 a = new Student2("Miha", "Novak", 2000, "40002304");
Student2 b = new Student2("Mojca", "Zver", 2001, "40004377");
Student2 c = b;
c.ime = "Katarina";
System.out.println("Student a:\n" + a.ime + " " + a.priimek +
" " + a.leto_vpisa + " (" + a.vpisna_st + ")\n");
System.out.println("Student b:\n" + b.ime + " " + b.priimek +
" " + b.leto_vpisa + " (" + b.vpisna_st + ")\n");
System.out.println("Student c:\n" + c.ime + " " + c.priimek +
" " + c.leto_vpisa + " (" + c.vpisna_st + ")\n");
}
} |
Zgornji program deluje povsem enako kot program
TestStudent.java
, je pa vsekakor bolj pregleden. Namen
konstruktorja je, da nastavi vrednosti komponent objekta. Ker
konstruktorji (pa tudi druge metode znotraj razreda) zelo
pogosto dostopajo do komponent, nam Java omogoča, da namesto
this.komponenta
pišemo kar
komponenta
:
Student3.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
| public class Student3 {
public String ime;
public String priimek;
public int leto_vpisa;
public String vpisna_st;
public Student3(String i, String p, int l, String v) {
ime = i;
priimek = p;
leto_vpisa = l;
vpisna_st = v;
}
} |
Ponovimo, kaj smo se do sedaj naučili:
Vsak objekt razreda Bla ima svojo kopijo vseh
komponent, ki so naštete v definiciji razreda Bla .
Do komponente foo v objektu a dostopamo
z a.foo .
Konstruktorji so posebne metode, s katerimi
nastavimo komponente novih objektov. V konstruktorju se novo
nastali objekt imenuje this .
Namesto this.foo lahko pišemo foo .
|
Za vajo definirajmo razred
Kompleksno
za delo s
kompleksnimi števili. Ker je kompleksno število podano z realno
in imaginarno komponento, ima razred
Kompleksno
dve
komponenti tipa
double
, ki ju poimenujemo
re
in
im
. Napišemo tudi konstruktor:
1
2
3
4
5
6
7
8
9
| public class Kompleksno {
public double re;
public double im;
public Kompleksno(double x, double y) {
this.re = x;
this.im = y;
}
} |
Konstruktorjev v danem razredu je lahko več. Na primer, napišemo
lahko še konstruktor, ki sprejme samo realni del števila,
imaginarnega pa nastavi na nič:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public class Kompleksno {
public double re;
public double im;
public Kompleksno(double x, double y) {
re = x;
im = y;
}
public Kompleksno(double x) {
re = x;
im = 0.0;
}
} |
Primer uporabe:
TestKompleksno.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public class TestKompleksno {
public static void main(String args[]) {
Kompleksno z = new Kompleksno(2.0, -3.0);
Kompleksno v = new Kompleksno(5.0);
System.out.println("z.re = " + z.re);
System.out.println("z.im = " + z.im);
System.out.println("v.re = " + v.re);
System.out.println("v.im = " + v.im);
}
} |
> java TestKompleksno
z.re = 2.0
z.im = -3.0
v.re = 5.0
v.im = 0.0 |
Objekt
z
smo naredili tako, da smo podali realni in
imaginarni del, objekt
v
pa tako, da smo podali le
realni del, zato se je izvedel drugi konstruktor, ki je nastavil
imaginarni del na
0.0
.
Objektne metode
Poleg definicije komponent in konstruktorjev vsebuje razred tudi
definicije objektnih metod. Metodam pravimo tudi
funkcije in podprogrami. Metoda je
zaporedje ukazov, ki kaj izračunajo in vrnejo rezultat.
Objektna metoda se razlikujejo od običajnih funkcij v ostalih
programskih jezikih in od statičnih metod v Javi v tem, da jo
vedno kličemo na objektu. V metodi se objekt, na
katerem je poklicana, imenuje this
.
Zapomnimo si:
Objektno metodo vedno kličemo na objektu.
V metodi se objekt, na katerem je klicana, imenuje this
.
Za primer dodajmo razredu
Kompleksno
metodo
abs
, ki izračuna absolutno vrednost kompleksnega
števila:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| public class Kompleksno {
public double re;
public double im;
public Kompleksno(double x, double y) {
re = x;
im = y;
}
public Kompleksno(double x) {
re = x;
im = 0.0;
}
public double abs() {
return Math.sqrt(this.re * this.re + this.im * this.im);
}
} |
Metodo
abs()
kličemo takole:
Kompleksno z = new Kompleksno(5.0, -12.0);
double r = z.abs(); |
Lahko bi napisali tudi takole:
double r = (new Kompleksno(5.0, -12.0)).abs(); |
Napišimo še metode, ki računajo osnovne operacije nad
kompleksnimi števili, in metodo, ki kompleksno število pretvori
v niz:
Kompleksno vsota(Kompleksno z)
vrne vsoto kompleksnega števila (this
) in
kompleksnega števila z
.
void pristej(Kompleksno z)
kompleksnemu številu prišteje kompleksno število z
.
Kompleksno produkt(Kompleksno z)
vrne produkt kompleksnega števila (this
) in
kompleksnega števila z
.
String toString()
kompleksno število pretvori v niz. Če tako metodo dodamo
razredu, lahko objekte razreda prištevamo nizom in le-ti se
bodo pretvorili v nize.
Kompleksno.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
| public class Kompleksno {
public double re;
public double im;
public Kompleksno(double x, double y) {
this.re = x;
this.im = y;
}
public Kompleksno(double x) {
this.re = x;
this.im = 0.0;
}
public static double abs(Kompleksno z) {
return Math.sqrt(z.re * z.re + z.im * z.im);
}
public double abs() {
return Math.sqrt(this.re * this.re + this.im * this.im);
}
public static Kompleksno vsota(Kompleksno z, Kompleksno w) {
return new Kompleksno(z.re+w.re, z.im+w.im);
}
public Kompleksno vsota(Kompleksno z) {
return new Kompleksno(this.re + z.re, this.im + z.im);
}
public Kompleksno kvadrat() {
return this.produkt(this);
}
public void pristej(Kompleksno z) {
this.re += z.re;
this.im += z.im;
}
public Kompleksno produkt(Kompleksno z) {
return new Kompleksno(re * z.re - im * z.im, re * z.im + im * z.re);
}
public String toString() {
return this.re + " + " + this.im + " * i";
}
} |
Preizkusimo delovanje metod; izračunajmo vsoto kompleksnih števil
z0 + z1 + ... + zn-1
kjer je
z = cos(2π/n) + i sin(2π/n).
Vsota.java |
---|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| public class Vsota {
public static void main(String args[]) {
final int N = 1000000;
final double phi = 2.0 * Math.PI / N;
Kompleksno z = new Kompleksno(Math.cos(phi), Math.sin(phi));
Kompleksno s = new Kompleksno(0.0);
Kompleksno w = new Kompleksno(1.0);
for (int k = 0; k < N; k++) {
s.pristej(w);
w = w.produkt(z);
}
System.out.println("Vsota je enaka " + s);
}
} |
> javac Vsota.java
> java Vsota
Vsota je enaka -6.228765392357616E-9 + 2.439338273715903E-7 * i |
Naloga 1: Ulomki (racionalna števila)
Kot vemo je ulomek ali racionalno število
p/q podano
s
števcemp in
imenovalcemq, ki sta celi števili, pri čemer velja še
q ≠
0. Definirajmo razred
Ulomek
za delo z
ulomki:
1
2
3
4
| public class Ulomek {
public int st; // stevec
public int im; // imenovalec
} |
Seveda je treba razred opremiti še s konstruktorji in metodami:
Napiši konstruktor, ki sprejme vrednost števca in
imenovalca ter nastavi vrednost komponent st
in
im
.
Napiši konstruktor, ki sprejme celo število k in
naredi ulomek k/1.
Napiši objektno metodo void pokrajsaj()
, ki
pokrajsa ulomek this
. Verjetno se splača
napisati še pomožno metodo, ki vrne največji skupni
delitelj dveh celih števil.
Napiši objektno metodo boolean equals(Ulomek
a)
, ki vrne true
, če sta ulomka
this
in a
enaka, sicer vrne
false
.
Napiši objektno metodo
int compareTo(Ulomek a)
,
ki primerja ulomka
this
in
a
in vrne:
negativno število, če je this
<
a
.
število 0
, če je this
=
a
.
pozitivno število, če je this
>
a
.
Napiši objektno metodo String toString()
, ki
vrne niz znakov, ki predstavlja ulomek. Na primer,
ulomek new Ulomek(-18, -6)
predstavimo z nizom
"-18/-6"
.
Napiši objektni metodi Ulomek vsota(Ulomek a)
in void pristej(Ulomek a)
. Prva metoda vrne nov
ulomek, ki je enak vsoti this
in a
,
druga pa ne vrača ničesar, ampak samo prišteje ulomek
a
ulomku this
.
Napiši objektni metodi za odštevanje Ulomek
razlika(Ulomek a)
in void odstej(Ulomek
a)
.
Napiši objektni metodi za množenje Ulomek
zmnozek(Ulomek a)
in void zmnozi(Ulomek
a)
.
Napiši objektni metodi za delenje Ulomek
kvocient(Ulomek a)
in void deli(Ulomek a)
.
Vse metode tudi preizkusi.
Naloga 2: Polinomi
Definiraj razred
Polinom
za delo s polinomi z
racionalnimi koeficienti. Za predstavitev racionalnih števil
uporabi razred
Ulomek
iz
prejšnje naloge.
Polinom
p(x) = a0 + a1 x + ... +
an xn
predstavimo s tabelo ulomkov
Ulomek[] p =
{a0, a1, ..., an}
.
Ničelni polinom
p(x) = 0 predstavimo s prazno tabelo.
Napiši ustrezne komponente, konstruktorje in naslednje metode:
metoda int stopnja()
, ki vrne stopnjo polinoma.
Po dogovoru je stopnja ničelnega polinoma enaka
−1.
metoda Ulomek vrednost(Ulomek a)
, ki vrne
vrednost polinoma this
v točki x =
a
. Uporabi Hornerjev algoritem!
metoda boolean equals(Polinom q)
, ki vrne
true
, če sta polinoma this
in
q
enaka, sicer vrne false
.
metoda String toString()
, ki vrne niz, ki
predstavlja polinom this
. Na primer, polinom
3/4 + 7/2 x2 + 10 x3 predstavimo z
nizom "3/4 + 7/2 x^2 + 10/1 x^3"
.
metoda Polinom vsota(Polinom q)
, ki vrne vsoto
polinomov this
in q
.
metoda Polinom zmnozek(Polinom q)
, ki vrne zmnozek
polinomov this
in q
.
Vse metode tudi preizkusi.
Vaje iz objektnega programiranja
Na vajah boste sestavili razrede in objekte za predstavitev
podatkov (ime, priimek in datum rojstva) o osebah.
Naloga 3
Sestavi razred Datum
, ki predstavlja datum (dan,
mesec in leto). Definiraj ustrezne komponente in
konstruktorje.
Definiraj metodo public String toString()
, ki vrne
datum predstavljen z nizom znakov.
Definiraj metodo public boolean equals(Datum d)
, ki
vrne True
, če je datum this
enak datumu
d
.
Naloga 4
Sestavi razred
Oseba
, ki vsebuje podatke o osebi,
in sicer:
ime
priimek
datum rojstva
Za predstavitev datuma uporabi razred
Datum
iz
prejšnje naloge.
Definiraj ustrezne konstruktorje in metodo public String
toString()
, ki predstavi osebo z nizom znakov.
Razred preizkusi tako, da narediš osebo, ki predstavlja tebe.
Naloga 5
Razredu Oseba
dodaj metodo public String
polno_ime()
, ki vrne polno ime osebe, se pravi ime in
priimek, ločena s presledkom.
Naloga 6
Razredu
Datum
dodaj metodo
public int
compareTo(Datum d)
, ki primerja
this
in
d
ter vrne:
< 0
, če je this
pred d
0
, če je this
enak d
> 0
, če je this
za d
Naloga 7
Razredu Oseba
dodaj metodi public void
shrani(PrintWriter w)
in konstruktor public
Oseba(BufferedReader r)
za zapisovanje in branje
osebe z datoteke.
Metoda
shrani(w)
zapiše na
w
v eno vrstico
podatke o osebi v obliki, ki je primerna za branje, na
primer nekaj takega kot
Miha:Novak:23:7:1982
Konstruktor Oseba(r)
prebere z r
eno
vrstico, ki je bila predhodno zapisana s shrani
, in
zapiše podatke v komponente objekta this
.
Preveri, ali metoda in konstruktor pravilno delujeta.
Naloga 8
Razredu Oseba
dodaj statični metodi
public static void shraniSpisek(Oseba[] spisek, String
dat)
in public Oseba[] static naloziSpisek(String
dat)
, ki shranita in naložita tabelo oseb na datoteko z
imenom dat
.
Preveri, ali metodi pravilno delujeta. V pomoč naj ti bo
spisek naslednjih oseb:
osebe.dat |
---|
Miha:Novak:23:7:1982
Josip:Broz:7:5:1892
Josip:Plemelj:11:12:1873
Britney:Spears:12:2:1981
Kurt:Goedel:28:4:1906
Alan:Turing:23:6:1912
Leonhard:Euler:15:4:1707
Carl Friedrich:Gauss:30:4:1777 |
Naloga 9
Napiši statično metodo public static void
rojeni_pred(String dat, Datum d)
, ki iz datoteke z
imenom dat
prebere spisek oseb in na zaslon izpiše
tiste osebe, ki so rojene pred datumom d
.
Preveri, ali metoda pravilno deluje.