14.11.1997
Tiivistelmä: Tämä seminaariesitelmä on lyhyt johdanto
Java-kieleen ja sovellusten tekemiseen Borland JBuilder 1.0:lla.
Esitelmässä esitellään eri Java-komponenttien
käyttöä ja hieman Java-ohjelmien siirtoa eri
järjestelmiin. Loppuosassa esitetään Java-kielen
erityispiirteitä.
Tämä esitelmä on lyhyt johdanto Java-kieleen ja sovellusten
luomiseen Borlandin JBuilder 1.0:lla. Pääpaino esityksessä
on ennenkaikkea käytännönläheisyydessä, jotta lukijalla
olisi mahdollisuus päästä mahdollisimman helposti tutuksi
Java-kielen kanssa. Tavoitteena on herättää lukijan
mielenkiinto asiaa kohtaan ja tarjota perustiedot omia jatkotutkimuksia
varten.
Java on saavuttanut suurta suosioita erityisesti verkko-ohjelmointiin
sopivana ja laitteistoriippumattomana kielenä. Java on hyvin
kunnianhimoinen yritys luoda uusi ohjelmointikieli, joka olisi yhtä
aikaa sekä laitteistoriippumaton, hyvin hajautettava, turvallinen ja
vielä helppokäyttöinen. Varsin monia lupauksia Java
onkin jo lunastanut, mutta kehitystyötä on vielä
tehtävä runsaasti.
Toisen luvun alussa rakennetaan malliohjelma, joka havainnollistaa graafisen
käyttöliittymän eri osatekijöiden suunnittelua
JBuilderillä. Päätarkoituksena on esitellä ohjelman
suunnittelua ja toteutusta helposti ja nopeasti. Malliohjelmaa
jatkokehitetään lisäämällä siihen uusia osia,
kuten omia komponentteja, menuja, sekä valmiita ja itsetehtyjä
dialogeja. Luvun loppuosassa selvitetään hieman, kuinka Java-ohjelmia
voidaan siirtää järjestelmästä toiseen.
Kolmannessa luvussa selvitetään Java-kielen perusominaisuuksia,
kuten parametrien välitystä, silmukoita, taulukoita, luokkia.
Säikeiden käyttöä havainnollistetaan graafisella
sovelluksella.
Tämä esitys on rakenteeltaan samanlainen kuin Vesa Lappalaisen
"Delphi pikakurssi". Tarkoituksena onkin, että tämän esityksen
rinnalla luettaisiin ko. teosta.
Esityksessä käytetyt ohjelmaesimerkit ja tiedostot on saatavilla
osoitteesta
http://www.jyu.fi/~majole/java
Tämän luvussa on hieman yleistä asiaa Jbuilderista. Pääasiana on rakentaa ensimmäisen malliohjelma, joka sisältää graafisen käyttöliittymän peruskomponentit. Lisäksi esitellään Applet-tyyppinen Java-ohjelma ja käsitellään hieman ohjelmien siirtoa eri järjestelmiin.
JBuilder on visuaalinen Java-kielen sovelluskehitin. Se on
käytön kannalta varsin yhdenmukainen Borlandin muiden
visuaalisten sovelluskehittimien, kuten Delphin ja C++Builderin
kanssa. Ohjelman ulkoasun suunnittelu tapahtuu samalla tavalla asettelemalla
komponentteja lomakkeille. Javassa komponentit ovat
JavaBeaneja, joiden käsittely on hyvin pitkälle samanlaista
kuin Delphin komponenttien. Tässä esityksessä
käytetäänkin näitä sanoja lähes
synonyymeinä.
JBuilder 1.0:ssa on vähemmän valmiita komponentteja kuin
esim. Delphissä. Koodin organisointi ja synkronointi on varsin
kehittynyttä, tosin hyvin yksinkertainen leikekirjatoteutus hankaloittaa
kehitystyötä. JBuilder 1.0 käyttää JDK
(Java Developer Kit) 1.1-versiota, joten sillä tuotetut
ohjelmat eivät pyöri ympäristöissä, joissa on
käyttössä JDK 1.0-versio.
Ensimmäisenä malliohjelmana teemme "autolaskurin" (kuva 2.2). Sen kahta nappia painamalla voi lisätä joko henkilöautojen tai kuorma-autojen lukumäärää.
Ensimmäiseksi luodaan perustiedostot sovellusta varten.
Nyt voidaaan alkaa sunnitella soveluksen ulkoasua.
Ohjelma on nyt toimintakuntoinen, mutta se ei vielä voi tehdä
mitään. Voimme kuitenkin kokeilla miltä valmis ohjelma
näyttäisi:
Koko ajan, kun komponentteja on aseteltu lomakkeelle, on JBuilder
lisännyt ohjelmakoodia LaskuriFrame-luokkaan (class)
tiedostoon LaskuriFrame.java. Tästä LaskuriFrame-luokasta
luodaan olio Autolaskuri.java-tiedoston Autolaskuri-luokassa.
Painikkeiden toiminnallisuutta vastaavan koodin lisääminen on
ohjelmoijan tehtävä. Onneksi JBuilder tekee tästäkin
suurimman osan:
void buttonControlHA_actionPerformed(ActionEvent e) { } |
2. Kursori on valmiina paikassa, johon oma koodi kirjoitetaan. Halutaan,
että painiketta painettaessa labelControlHA:ssa olevan lukeman
lisääntyyvän yhdellä. Komponenttien ominaisuuksia voi
muuttaa vain niihin tarkoitetuilla metodeilla, joista tässä
käytetään metodia setText(). Tekstikentän teksti
on ensin muutettava numeeriseksi, koska tekstiä ei voi numeerisesti
lisätä. Siispä kirjoitamme koodin
labelControlHA.setText(""+(Integer.parseInt(labelControlHA.getText())+1)); |
3. Koodi kannattaa saman tien laittaa leikekirjaan, koska se tulee lähes
samanlaisena painikkeeseen buttonControlKA.
4. Lisää vastaava koodi oikeaksi muutettuna painikkeeseen
buttonControlKA. Huom! Jos et edellä huomannut laittaa koodia
leikekirjaan, löytyy edellinen koodi samasta koodi-ikkunasta hieman
ylempää, josta voit sen hakea kuin missä tahansa editorissa.
5. Lisää vielä koodi painikkeeseen
buttonControlNollaa. Nyt koodiksi riittää
labelControlHA.setText("0"); labelControlKA.setText("0"); |
6. Käännä ja aja ohjelma.
Seuraavana vielä listaukset valmiin malliohjelman tiedostoista hakemistossa
autol (autolaskuri.jpr ei ole mukana). Ensimmäisenä on
JBuilderin luoma projektin kuvaus.
<HTML> <HEAD> <h1> Project Notes </h1> <hr> </HEAD> <BODY> <FONT SIZE=+1> <B>Project:</B> Autolaskuri <FONT> <BR> <FONT SIZE=+1> <B>Author:</B> Markku Lehtinen <FONT> <BR> <FONT SIZE=+1> <B>Company:</B> <FONT> <BR> <FONT SIZE=+1> <B>Description</B> <FONT> <BR> <HR> <FONT SIZE=+1> <B>Things to do...</B> <FONT> <BR> <UL> <Edit this section to keep track of your to do items> <LI> Item 1 <LI> Item 2 </UL> </BODY> </HTML> |
Autolaskurin pääluokka sijaitsee autolaskuri.jpr-tiedostossa, jossa
on myös main-metodi.
public class Autolaskuri boolean packFrame = false; //Construct the application public Autolaskuri() { LaskuriFrame frame = new LaskuriFrame(); //Pack frames that have useful preferred size info, e.g. from their layout //Validate frames that have preset sizes if (packFrame) frame.pack(); else frame.validate(); frame.setVisible(true); } //Main method static public void main(String[] args) { new Autolaskuri(); } } |
LaskuriFrame.java-tiedostossa on varsinainen graafinen toteutus. Seuraavassa
lomakkeen listauksessa värilliset osat ovat niitä, joita on muutettu
AppBrowserissa. JBuilderissä ei ole erikseen
resurssitiedostoja, vaan kaikki komponenttien ominaisuudet asetetaan ja luetaan
omilla metodeillaan.
import java.awt.*; import java.awt.event.*; import borland.jbcl.control.*; import borland.jbcl.layout.*; public class LaskuriFrame extends DecoratedFrame { BorderLayout borderLayout1 = new BorderLayout(); XYLayout xYLayout2 = new XYLayout(); BevelPanel bevelPanel1 = new BevelPanel(); ButtonControl buttonControlHA = new ButtonControl(); ButtonControl buttonControlKA = new ButtonControl(); LabelControl labelControlKA = new LabelControl(); LabelControl labelControlHA = new LabelControl(); ButtonControl buttonControlNollaa = new ButtonControl(); //Construct the frame public LaskuriFrame() { try { jbInit(); } catch (Exception e) { e.printStackTrace(); } } //Component initialization public void jbInit() throws Exception{ this.setLayout(borderLayout1); this.setSize(new Dimension(269, 207)); this.setTitle("Autolaskuri"); buttonControlHA.setLabel("Henkilöautoja"); buttonControlHA.addActionListener(new LaskuriFrame_buttonControlHA_actionAdapter(this)); labelControlKA.setBackground(Color.white); labelControlKA.setFont(new Font("Dialog", 1, 18)); labelControlKA.setText("0"); labelControlKA.setAlignment(Label.RIGHT); labelControlKA.setForeground(Color.blue); labelControlHA.setBackground(Color.white); labelControlHA.setFont(new Font("Dialog", 1, 18)); labelControlHA.setAlignment(Label.RIGHT); labelControlHA.setForeground(Color.blue); labelControlHA.setText("0"); buttonControlNollaa.setLabel("Nollaa"); buttonControlNollaa.addActionListener(new LaskuriFrame_buttonControlNollaa_actionAdapter(this)); buttonControlKA.setLabel("Kuorma-autoja"); buttonControlKA.addActionListener(new LaskuriFrame_buttonControlKA_actionAdapter(this)); bevelPanel1.setLayout(xYLayout2); this.add(bevelPanel1, BorderLayout.CENTER); bevelPanel1.add(buttonControlHA, new XYConstraints(22, 31, 100, 23)); bevelPanel1.add(buttonControlKA, new XYConstraints(129, 31, 100, 23)); bevelPanel1.add(labelControlKA, new XYConstraints(129, 78, -1, 24)); bevelPanel1.add(labelControlHA, new XYConstraints(22, 78, -1, 24)); bevelPanel1.add(buttonControlNollaa, new XYConstraints(77, 132, 100, -1)); } void buttonControlHA_actionPerformed(ActionEvent e) { labelControlHA.setText(""+(Integer.parseInt(labelControlHA.getText())+1)); } void buttonControlKA_actionPerformed(ActionEvent e) { labelControlKA.setText(""+(Integer.parseInt(labelControlKA.getText())+1)); } void buttonControlNollaa_actionPerformed(ActionEvent e) { labelControlHA.setText("0"); labelControlKA.setText("0"); } } class LaskuriFrame_buttonControlHA_actionAdapter implements java.awt.event.ActionListener { LaskuriFrame adaptee; LaskuriFrame_buttonControlHA_actionAdapter(LaskuriFrame adaptee) { this.adaptee = adaptee; } public void actionPerformed(ActionEvent e) { adaptee.buttonControlHA_actionPerformed(e); } } class LaskuriFrame_buttonControlKA_actionAdapter implements java.awt.event.ActionListener { LaskuriFrame adaptee; LaskuriFrame_buttonControlKA_actionAdapter(LaskuriFrame adaptee) { this.adaptee = adaptee; } public void actionPerformed(ActionEvent e) { adaptee.buttonControlKA_actionPerformed(e); } } class LaskuriFrame_buttonControlNollaa_actionAdapter implements java.awt.event.ActionListener { LaskuriFrame adaptee; LaskuriFrame_buttonControlNollaa_actionAdapter(LaskuriFrame adaptee) { this.adaptee = adaptee; } public void actionPerformed(ActionEvent e) { adaptee.buttonControlNollaa_actionPerformed(e); } } |
Tehtävä 2.1 Polkupyörät
Lisää ohjelmaan myös polkupyörien laskeminen.
Javassa Delphin ja C++Builderin komponetteja vastaavat JavaBeanit. JBuilderissa ei kuitenkaan ole valmiina niin laajaa komponenttikirjastoa kuin esim. Delphissä. Sen sijaan omia beaneja on varsin helppo luoda JBuilderin tarjoamilla työkaluilla. Seuraavissa esimerkeissä käytetään itsetehtyjä beaneja, koska vastaavia toimintoja ei voi JBuilderissä niin yksinkertaisesti ja havainnollisesti toteutettaa kuin tässä luvussa on tarkoituksena. Seuraavia toimenpiteitä varten tarvitaan omat.jar-niminen tiedosto, joka sisältää nämä beanit. Tämän tiedoston voit hakea esitelmän johdannossa mainitusta paikasta. Lisäksi tarvitset auton kuvaa varten tiedoston hauto.gif. Kopioi auton kuva projektitiedostoon ennen seuraavaa vaihetta.
Lisätään lomakkeelle vaikkapa aluksi auton kuva, joka ajaa
ruudun vasemmasta laidasta oikeaan laitaan.
void timerBeanHA_timerAction(TimerEvent e) { imageBeanHA.setLocation(imageBeanHA.getLocation().x + 1, imageBeanHA.getLocation().y); } |
5. Huom! Java-komponenttien päivitys ei ole ilmeisesti tehty tällaista
varten, joten liikkuva kuva saattaa välkkyä ja
käyttäytyä välillä hieman oudosti.
Tehtävä 2.2 Edestakaisin
Muuta ohjelmaa siten, että auton kuva kulkee ruudussa edestakaisin.
Lisätään ohjelmaan vielä päämenu.
File Options Help Exit Colors About |
(Huomaa, että Javassa ei voi &-merkillä aikaansaada
pikavalintaa)
4. Laita vielä lisäksi Exit-kohtaan pikavalinta Ctrl-X
(shortcut-ominaisuus).
5. Valitse Exit-menuvalinnan Events-sivulta actionPerformed
ja lisää tapahtumaksi koodi
void menuItemExit_actionPerformed(ActionEvent e) { System.exit(0); } |
JBuilderissä on valmiina joukko yleisimpiä dialogeja, kuten
tiedostodialogi, fonttien valinta, värin valinta, sekä
viestidialogi.
Lisäämme seuraavaksi mahdollisuuden taustavärin
vaihtamiseksi.
void menuItemColors_actionPerformed(ActionEvent e) { colorChooserTausta.show(); if (colorChooserTausta.getResult() == ColorChooser.OK) bevelPanel1.setBackground(colorChooserTausta.getValue()); } |
Tehtävä 2.3 Muidenkin komponenttien värin vaihto
Muuta ohjelmaa siten, että voit muuttaa kaikkien muidenkin komponenttien
värin (voit käyttää samaa dialogia kaikille komponenteille).
Oikeassa ohjelmassa on harvoin vain yksi ikkuna. Lisäämme esimerkin
vuoksi vielä ohjelmaamme itse tehdyn About-dialogin
(About-dialogin saisi tehtyä myös automaattisesti):
void buttonControlOK_actionPerformed(ActionEvent e) { this.setVisible(false); } |
8. Dialogi on nyt valmis, mutta siihen ei viitata LaskuriFramesta.
Lisää LaskuriFrame.java-tiedostoon
public class LaskuriFrame extends DecoratedFrame { AboutFrame aboutFrame = new AboutFrame(); AboutDialog aboutDialog = new AboutDialog(aboutFrame, "Tietoja Autolaskurista",false); } |
9. Lisää menunvalintaan About seuraava koodi:
void menuItemAbout_actionPerformed(ActionEvent e) { aboutDialog.show(); } |
10. Kokeile ohjelmaa.
AboutDialog aboutDialog = new AboutDialog(aboutFrame,"Tietoja Autolaskurista",true); |
Mitä eroa on nyt ohjelman toiminnassa?
Tehtävä 2.5 Liikkuva auto myös toisessa dialogissa
Lisää liikkuva auto myös About-dialogiin.
JBuilderissä on varsin kehittynyt ohjelmakoodin
editointijärjestelmä. Seuraavassa muutamia piirteitä:
Tapahtuman käsittelijän koodi on voitu kirjoittaa tuplaklikkaamalla
komponenttia. Näin voidaan kirjoittaa kuitenkin vain komponentin
oletustapahtuman käsittelijä. Kullakin komponentilla on lukuisia
muitakin tapahtumia. Nämä muut tapahtumat löytyvä
Component Inspectorissa Events-sivulta. Kuvassa 2.3 on esimerkki
buttonControlHA:n mahdollisista tapahtumista:
Tapahtuman nimi päästään kirjoittamaan tuplaklikkaamalla
tapahtuman nimen paikkaa (tai siinä jo olevaa nimeä). Itse koodia
päästään lisämään tuplaklikkaamalla toisen
kerran tai painamalla enter. Tapahtumakäsittelijä voidaan asettaa
myös samaksi komponentin jonkin toisen tapahtuman kanssa, jolla on sama
tapahtumatyyppi (esim. molempien tyyppi on mouseEvent).
Tehtävä 2.6 Laskenta tapahtumaan myös laskurista
Muuta ohjelmaa (kirjoittamatta lisää koodia) siten, että
myös laskurikentän LabelHA tai LabelKA painaminen
lisää vastaavaa laskuria. Laita vielä liikkuvan kuvan painaminen
lisäämään henkilöautojen
lukumäärää (joudut hidastamaan ajastinta melko paljon,
että viesti ehtii mennä perille).
Javan luokkakirjastoon on määritelty myös Drag and drop-tiedonvälityksen, mutta sitä ei ole vielä toteutettu JDK 1.1:ssä
Tähän mennessä on saatu aikaiseksi Java-sovellus, joka
toimii vain JDK:n mukana tulevan Java-tulkin alaisuudessa.
Entäpä, jos halutaan siirtää autolaskuri sellaiseen muotoon,
että sitä voidaan ajaa vaikkapa jollain HTML-selaimella.
Tätä varten on olemassa Applet-tyyppiset
Java-ohjelmat. Tarkastellaan seuraavaksi, kuinka autolaskuri voidaan
muuttaa Applet-tyyppiseksi.
public class Autolaskuri { boolean packFrame = false; //Construct the application public Autolaskuri() { LaskuriFrame frame = new LaskuriFrame(); //Pack frames that have useful preferred size info, e.g. from their layout //Validate frames that have preset sizes if (packFrame) frame.pack(); else frame.validate(); frame.setVisible(true); } //Main method static public void main(String[] args) { new Autolaskuri(); } } |
5. Liitä leikepöydältä nämä rivit
LaskuriApplet.java-tiedostoon:
public class Autolaskuri extends Applet implements Runnable { ... public Autolaskuri() { LaskuriFrame frame = new LaskuriFrame(); frame.validate(); frame.setVisible(true); } ... } |
6. Poista Autolaskuri.java projektista miinus-painikkeella.
7. Valitse Project-ikkunasta applaskuri.html ja aja ohjelma (Shift-F9).
8. Ruutuun ilmestyy ensin Appletviewer ja sen jälkeen vasta
Autolaskuri.
9. Minkä takia applaskuri.html täytyi olla valittuna, kun ohjelma
käynnistettiin? Tämä on siksi, että Appletviewer
tarvitsee parametrikseen html-tiedoston, jossa
<APPLET>-tagilla on annettu ajettava Java-ohjelma.
<APPLET CODEBASE = "" CODE = "LaskuriApplet.class" NAME = "AutolaskuriApplet" WIDTH = 400 HEIGHT = 300 HSPACE = 0 VSPACE = 0 ALIGN = Middle > |
Java-kielen eräs tärkeimpiä ominaisuuksia on sen käyttöjärjestelmäriippumattomuus. Periaatteessa samat ohjelmat pyörivät eri käyttöjärjestelmissä sellaisenaan. Hankaluuksia saattaa aiheuttaa kuitenkin erilaiset graafiset ympäristöt. Tämän vuoksi Javan perusmäärittelyssä ei ole koordinaatteihin perustuvaa komponenttien käsittelyä, vaan kaikki komponenttien asettelut annetaan järjestelmän tehtäväksi tietyn layoutin mukaan. Tämän esimerkin autolaskurissa on käytetty JBuilderin oletus-layoutia, XYLayoutia, joka ei kuitenkaan sovellu käytettäväksi ohjelmissa, joita halutaan siirtää käyttöjärjestelmien välillä. Tämän tähden XYLayoutia pitäisi käyttää ainoastaan sovelluksen kehitysvaiheessa. Lisäksi luokkakirjastojen siirtäminen tuottaa erilaisia ongelmia ja niihin on kehittetty erilaisia ratkaisuja. Seuraavassa esitellään muutamia tärkeimpiä siirtämiseen liittyviä asioita.
Java-luokkien käytössä oleellisen tärkeä asia on
ympäristömuuttuja CLASSPATH. Sen avulla Java-tulkki
osaa paikallistaa tarvitsemansa luokkakirjastot järjestelmästä.
Seuraavassa on esimerkki CLASSPATHin käytöstä
Windows NT:ssä:
JBuilderillä helpoin tapa siirtää Java-ohjelmia eri
järjestelmiin on käyttää Deployment Wizardia.
Ominaisuuksia:
... <APPLET CODEBASE = "" CODE = "Autolaskuri.class" ARCHIVE = "laskuri.jar" WIDTH = 400 HEIGHT = 300 HSPACE = 0 VSPACE = 0 ALIGN = Middle > ... |
Käynnistä nyt komentotulkki, siirry projektihakemistoon ja aseta ympäristömuuttuja käskyllä set CLASSPATH=laskuri.jar . Kokeile tämän jälkeen laskurin toimintaa appletviewer applaskuri.html. Autolaskurin pitäisi toimia nyt myös UNIX-koneessa esim. janessa. Kopioi laskuri.jar, applaskuri.html ja kuvatiedostot johonkin hakemistoon, aseta CLASSPATH osoittamaan jar-tiedostoon. Kokeile ohjelman toimintaa käskyllä appletviewer applaskuri.html. (Toistaiseksi tuntemattomasta syystä kuvatiedostot eivät kuitenkaan näyttäisi löytyvän sovellusta ajettaessa.)
Tämän luvun pääaiheena on esitellä Java-kielen perusominaisuuksia. Ensin esitellään Javaa lyhyesti. Kielen syntaksia käymme läpi esimerkkiohjelmien avulla. Käsittelemme mm. parametrien välitystä, silmukoita, taulukoita, luokkia ja säikeitä.
Java on kehitetty C++-kielen pohjalta, joten syntaksiltaan
se on sitä hyvin lähellä. Näin on pyritty luomaan tutun
oloinen kieli, jossa kuitenkin olisi korjattu C++:n ongelmia.
Javalla on mm. seuraavia erityisominaisuuksia:
public class Perus { static public void main(String[] args) { int a,b; System.out.println("Anna kaksi lukua rivinvaihdolla erotettuna"); a = (int) Ali.readValue(); b = (int) Ali.readValue(); System.out.println("Suurempi luvuista on " + Ali.bigger(a,b)); System.out.println("Lukujen keskiarvo on " + Ali.average(a,b)); if (a > b) { int apu = a; a = b; b = apu; System.out.println("Luvut järjestyksessä ovat " + a + " " + b); } else System.out.println("Luvut olivat järjestyksessä"); } } |
Java-ohjelman rakenteen tärkeimpiä ominaisuuksia:
static public void main(String[] args) |
* Java ei ole erillisiä funktioita ts. kaikki funktiot ovat jonkun
luokan metodeja
* Isojen ja pienten kirjainten välille tehdään ero. Myös
eksoottiset merkit kelpaavat koodiin esim. int Åke = 5;
* Vahva tyypitys tarkoittaa sitä, että aina kun tyyppimuunnoksessa
voidaan menettää informaatiota, on käytettävä
eksplisiittistä tyyppimuunnosta. Muutoin saadaan virheilmoitus. Esimerkiksi
int a = 1.234; tulee olla int a = (int)1.234;
* Merkkijonoja (String) voidaan yhdistellä +-operaattorilla.
Huomaa, että char[] ei ole sama kuin String.
* if-lauseeseen käy vain boolean-arvon tuottava ehtolause.
Näin ollen esimerkiksi int-tyypin palauttavaa metodia ei voi
suoraan sijoittaa if-lauseen sisälle.
* Standarditulostusvirta on System.out, jossa on implementoituna
kaksi tulostumetodia print ja println (muiden metodien
lisäksi). Metodit ottavat parametrikseen String-tyyppisen olion.
println-metodi tulostaa rivinvaihdon tulostuksen loppuun.
* Starndardisyöttövirta on System.in. Siitä voi lukea
vain yhden merkin kerrallaan. Monipuolisempiin syöttöihin on
käytettävä kehittyneempiä syöttövirtoja.
* Pääohjelman loppuun kannattaa lisätä seuraava koodin
pätkä, jotta konsooli-ikkuna häviää vasta kun on
painettu enter-näppäintä.
int i = -1; do { try { i = System.in.read(); } catch (Exception e) {} } while (i == -1); |
Edellisen esimerkin aliohjelmat on kirjoitettu omaan tiedostoonsa. Tiedoston tulee sijaita projektihakemistossa, josta Java-kääntäjä osaa etsiä sitä automaattisesti tarpeen vaatiessa. Aliohjelmat voidaan määritellä package-käskyn avulla myös yleiskäyttöisiksi aliohjelmakirjastoiksi. Tällöin aliohjelma pitää ottaa käyttöön import-käskyllä pääohjelmatiedoston alussa. Seuraavassa esimerkissä tällä käskyllä otetaan käyttöön Javan syöttöön ja tulostukseen liittyvä aliohjelmakirjasto.
import java.io.*;
public class Ali { |
Aliohjelmiin ja parametreihin liittyviä piirteteitä:
Luodaan uusi luokka (jompaan kumpaan tiedostoon)
class Apu_int { int luku; Apu_int(int arvo) { luku = arvo; } public void aseta(int arvo) { luku = arvo; } public int arvo() { return luku; } static public void swap(Apu_int a, Apu_int b) { int apu; apu = b.arvo(); b.aseta(a.arvo()); a.aseta(apu); } } |
ja lisätään pääohjelmaan rivit
Apu_int bi = new Apu_int(b); Apu_int ai = new
Apu_int(a); Apu_int.swap(ai,bi); a = ai.arvo(); b = bi.arvo(); |
Se, että onko tällaisessa koodimäärässä yhtä aliohjelman kutsua varten mitään järkeä, on eri kysymys! Koska luokan lisäksi taulukko välitetään call-by-reference-muodossa myös sitä voisi käyttää swap-toteutukseen.
Seuraavassa esimerkissä on ohjelma, joka ensin tekee viisipaikkaisen
kokonaislukutaulukon:
0 | 1 | 2 | 3 | 4 |
0 | 3 | 6 | 9 | 12 |
Ohjelma laskee montako taulukon alkiota voidaan ottaa mukaan, ilman että
summa ylittää vielä arvon 10. Lopuksi tulostetaan ko. alkiot
takaperin tyyliin
3 lukua mahtuu alle 10 näiden summa on 9 Luvut on: 6 3 0 |
public class Silmu { final static int TKOKO = 5,RAJA = 10; static void alusta(int[] luvut, int n, int kasvu) { int luku = 0, iraja = ( n > luvut.length ? luvut.length : n ); for(int i = 0; i < iraja; i++) { luvut[i] = luku; luku += kasvu; } } static int montako_mahtuu(int[] luvut, int n, int raja) { int i = 0, summa = 0, iraja = ( n > luvut.length ? luvut.length : n ); do { summa += luvut[i]; } while ( summa < raja && ++i < iraja); return i; } static int summaa(int[] luvut, int n) { int i = 0,summa = 0; while (i < n) { summa += luvut[i]; i++; } return summa; } static void tulosta(int[] luvut, int n) { for( int i = ( n > luvut.length ? luvut.length : n) - 1; i >= 0; i--) System.out.print(luvut[i]+" "); System.out.println(); } public static void main(String[] args) { int luvut[] = new int[TKOKO]; int n; alusta(luvut,TKOKO,3); n = montako_mahtuu(luvut,TKOKO,RAJA); System.out.println(n+" lukua mahtuuu alle "+RAJA+" näiden summa on "+summaa(luvut,n)); System.out.print("Luvut ovat: "); tulosta(luvut,n); } } |
Aliohjelmista ja silmukoista seuraavia havaintoja:
int luvut[] = new int[]{1,2,3,4,5}; |
* Jos yritetään viitata laittomiin indekseihin, saadaan ajonaikainen
poikkeus.
* break- ja continue-lauseita on parannettu C:hen
ja Pascaliin verrattuna. Niiden perään voi laittaa lohkon
nimen, jonka tasolla toiminta halutaan toteuttaa. Esimerkiksi tiedon etsiminen
kaksiulotteisesta taulukosta voidaan tehdä seuraavasti (i ja j ovat
lopuksi löydetyn paikan indeksit):
int i=0,j=0; eka: for (i = 0; i < luvut.length; i++){ for (j = 0; j < luvut[0].length; j++) { if ( luvut[i][j] == etsitty ) { break eka; } } |
Tehtävä 3.1 swap-funktio taulukon avulla.
Toteuta edellisen esimerkin swap-funktio taulukon avulla (laita
parametrit taulukkoon). Onko tämä yhtään parempi
ratkaisu?
Tehtävä 3.2 Kaksiulotteiset taulukot.
Muuta ohjelmaa Silmu.java siten, että se toimii kaksiulotteisilla
taulukoilla. Toiminta on muuten sama, mutta käytä tiedon varastointiin
kaksiulotteista taulukkoa (eli kun yksi taulukon rivi tulee täyteen,
aletaan täyttämään järjestyksessä seuraavaa
riviä).
Seuraavassa esimerkissä Oliot.java toteutetaan kuvan 3.1 luokkahierarkia (käytämme unkarilaista nimeämistapaa, missä c=class ja a=abstract).
abstract class caGraafinenOlio { private int x,y; private boolean nakyy; private boolean paikka(int x, int y){ this.x = x; this.y = y; return false; } caGraafinenOlio(int x, int y) { paikka(x,y); nakyy = false; } caGraafinenOlio() { this(0,0); } abstract boolean piirra(); {} final boolean nakyvissa() { return nakyy; } final boolean sammuta() { if ( !nakyvissa() ) return true; System.out.print("Sammutettu: "); nakyy = false; return piirra(); } final boolean sytyta() { if ( nakyvissa() ) return true; System.out.print("Sytytetty: "); nakyy = true; return piirra(); } boolean siirra(int x, int y) { if ( !nakyvissa() ) return paikka(x,y); sammuta(); paikka(x,y); return sytyta(); } boolean tulosta(String s) { System.out.print(tasaaOik(getClass().getName(),10)+": "+tasaaOik(s,10)+ " ("+tasaaVas(""+x,2)+","+tasaaVas(""+y,2)+")" ); return false; } public String tasaaOik(String str,int koko) { while(str.length() < koko) str += " "; return str; } public String tasaaVas(String str,int koko) { while(str.length() < koko) str = " " + str; return str; } } abstract class caSateellinenOlio extends caGraafinenOlio { private int r; private boolean koko(int r) { this.r = r; return false; } caSateellinenOlio(int x, int y, int r) { super(x,y); this.r = r; } caSateellinenOlio(int x, int y) { this(x,y,1); } caSateellinenOlio() { this(0,0,1); } boolean tulosta(String s) { super.tulosta(s); System.out.print(" r="+r); return false; } boolean muuta_koko(int r) { if ( !nakyvissa() ) return koko(r); sammuta(); koko(r); return sytyta(); } } class cPiste extends caGraafinenOlio { cPiste(int x, int y) { super(x,y); } cPiste() { this(0,0); } boolean piirra() { tulosta("Piste"); System.out.println(); return false; } } class cYmpyra extends caSateellinenOlio { cYmpyra(int x, int y, int r) { super(x,y,r); } cYmpyra(int x, int y) { this(x,y,1); } cYmpyra() { this(0,0,1); } boolean piirra() { tulosta("Ympyra"); System.out.println(); return false; } } public class Oliot { public static void main(String[] argv) { caGraafinenOlio p; caGraafinenOlio[] kuvat = new caGraafinenOlio[10]; cPiste p1 = new cPiste(),p2 = new cPiste(10,20); cYmpyra y1 = new cYmpyra(1,1,2); p1.sytyta(); p2.sytyta(); p1.siirra(7,8); y1.sytyta(); y1.muuta_koko(5); p = new cYmpyra(9,9,9); p = new cYmpyra(9,9,9); p.sytyta(); p.siirra(8,8); if ( p instanceof caSateellinenOlio ) ((caSateellinenOlio) p).muuta_koko(4); kuvat[0] = new cYmpyra(10,10,100); kuvat[1] = new cPiste(11,11); kuvat[2] = new cYmpyra(12,12,102); kuvat[3] = null; for(int i = 0; kuvat[i] != null; i++ ) kuvat[i].sytyta(); } } |
Ohjelma tulostaa seuraavaa:
Sytytetty: cPiste
: Piste
( 0, 0) Sytytetty: cPiste : Piste (10,20) Sammutettu: cPiste : Piste ( 0, 0) Sytytetty: cPiste : Piste ( 7, 8) Sytytetty: cYmpyra : Ympyra ( 1, 1) r=2 Sammutettu: cYmpyra : Ympyra ( 1, 1) r=2 Sytytetty: cYmpyra : Ympyra ( 1, 1) r=5 Sytytetty: cYmpyra : Ympyra ( 9, 9) r=9 Sammutettu: cYmpyra : Ympyra ( 9, 9) r=9 Sytytetty: cYmpyra : Ympyra ( 8, 8) r=9 Sammutettu: cYmpyra : Ympyra ( 8, 8) r=9 Sytytetty: cYmpyra : Ympyra ( 8, 8) r=4 Sytytetty: cYmpyra : Ympyra (10,10) r=100 Sytytetty: cPiste : Piste (11,11) Sytytetty: cYmpyra : Ympyra (12,12) r=102 |
Javassa luokkien käyttöön liittyy seuraavia
asioita:
Kuva 3.2. SaieFrame.
Javassa määritellään säikeet (threads)
kieleen sisäänrakennettuna ominaisuutena. Toteutus tosin voidaan
virtuaalikoneessa hoitaa käyttöjärjestelmän
säikeillä. Seuraava esimerkki luo DecoratedFrame-ikkunassa
(SaieFrame, kuva 3.2) valmiiksi olevan Käynnistä-painikkeen
lisäksi joukon laskureita (Label). Kun
Käynnistä-painiketta painetaan, käynnistetään kutakin
laskuri-kenttää varten oma prosessi (säie), joka
pyörittää kentässä lukuja 0:sta ylöspäin.
Kenttää painamalla voidaan ko. säie "tappaa". Ohjelmasta on
jätetty listaamatta pääohjelma SaieDemo.java sekä
tapahtumakäsittelijä omat/adapter/ActionAdapter.java (lisää
nämä projektiin ajaaksesi ohjelman). Javan
oma tapahtumakäsittelijöiden toteutus perustuu siihen, että
jokaista komponenttia varten luodaan oma tapahtumakäsittelijäluokka,
ei pelkästään oliota. Tämä tuottaa runsaasti koodia
ja tämä menetelmä ei edes onnistu, kun halutaan luoda dynaamisesti
komponentteja ajon aikana. Seuraavassa onkin käytetty itsetehtyä
tapahtumakäsittelijää. Se implementoi erilaisia tapahtumia
ja kelpaa näin ollen parametriksi eri
addXXXListener-funktioille, jotka liittävät komponenttiin
tapahtuman tarkkailijan. Kun tarkkailija havaitsee sille sopivan tapahtuman,
se lähettää komponentille siitä tapahtumaviestin.
import java.awt.*; import java.awt.event.*; import borland.jbcl.control.*; import borland.jbcl.layout.*; import omat.adapter.*; public class SaieFrame extends DecoratedFrame { BorderLayout borderLayout = new BorderLayout(); XYLayout xYLayout2 = new XYLayout(); BevelPanel bevelPanel = new BevelPanel(); Button button = new Button(); final int BUTTON = 1, LABEL = 2; final int SAIKEITA = 20; final int KIERROKSIA = 1000000; Label[] label = new Label[SAIKEITA]; cLaskuri[] laskuri = new cLaskuri[SAIKEITA]; //Construct the frame public SaieFrame() { try { jbInit(); } catch (Exception e) { e.printStackTrace(); } } //Component initialization public void jbInit() throws Exception{ this.setLayout(borderLayout); this.setSize(new Dimension(160, 500)); this.setTitle("SaieFrame"); button.setFont(new Font("Dialog", 0, 10)); button.setLabel("Käynnistä"); button.addMouseListener(new ActionAdapter(MouseEvent.MOUSE_CLICKED,this,"button_mouseClicked",button)); bevelPanel.setLayout(xYLayout2); this.add(bevelPanel, BorderLayout.CENTER); bevelPanel.add(button, new XYConstraints(70, 21, 71, 26)); for (int i = 0; i < SAIKEITA; i++) { label[i] = new Label("0"); label[i].setFont(new Font("Dialog", 0, 10)); bevelPanel.add(label[i], new XYConstraints(10, 10 + i*20, 50, 20)); } } public void button_mouseClicked(Object o, Object event) { for (int i = 0; i < SAIKEITA; i++) { if (laskuri[i] != null ) if ( laskuri[i].isAlive() ) continue; laskuri[i] = new cLaskuri(label[i],KIERROKSIA); laskuri[i].start(); laskuri[i].setPriority(laskuri[i].MIN_PRIORITY); label[i].addMouseListener(new ActionAdapter(MouseEvent.MOUSE_CLICKED,this, "label_mouseClicked",laskuri[i])); } } public void label_mouseClicked(Object o, Object event) { ((cLaskuri) o).lopeta(); } } class cLaskuri extends Thread { Label Text; final int PAIVITYS = 5000; int n; int raja; boolean loppuu = false; cLaskuri(Label Text, int raja) { this.Text = Text; this.raja = raja; n = 0; } void lopeta() { loppuu = true; } public void run() { String mes = ""; while ( n < raja ) { if ( loppuu ) { mes = "T"; break; } count(); } Text.setText("" + n + mes); } void count() { if ( ++n % PAIVITYS == 0 ) Text.setText("" + n); } } |
Javan säikeitten käytöstä seuraavia
huomioita:
Toisessa luvussa rakensimme JBuilderilla yksinkertaisen graafisen
mallisovelluksen, jossa työtä oli pyritty yksinkertaistamaan
mahdollisimman pitkälle. Todellisessa elämässä tuskin
riittävät näin yksinkertaiset toteutukset. Hieman vaativammilla
sovelluksilla päästään jo tuntemaan, mitä
Java-ohjelmointi on, mitä hyviä puolia on tässä
ohjelmointikielessä ja mitä olisi voitu toteuttaa paremmin.
Vähitellen selviävät myös, kuinka kielen
ominaispiirteitä voi käyttää hyväksi eri tarkoituksiin.
Toisaalta selviää myös, mihin Javaa ei kannata
käyttää. Laaja perusluokkakirjasto helpottaa
ohjelmointityötä, kunhan siihen ensin tutustuu tarpeeksi hyvin.
JBuilder 1.0 jatkaa Borlandin hyvää visuaalisten
sovelluskehittimien sarjaa. Graafinen Java-ohjelmointi on pyritty
tekemään mahdollisimman selkeäksi ja mukaan on otettu
Borlandin varsin kattavan yleiskirjaston lisäksi muutamia muita
yleisiä komponettikirjastoja. JBuilder vaatii valitettavasti
runsaasti laitetehoa sillä se on toteutettu itsekin Javalla.
Kun JBuilderiä vielä kehitetään, siitä saa
hyvän työkalun Java-ohjelmointiin.
Tässä työssä ei perehdytty sen tarkemmin eri graafisten
elementtien ominaisuuksiin ja käyttöön. Niinikään
tietokantaominaisuudet, tietoturva ja verkko-ominaisuudet ohitettiin.
JavaBeaneihin päästiin tutustumaan lähinnä
käytön kannalta. Näihin asioihin on tarkoitus palata hieman
laajemmassa työssä myöhemmin.