-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathStocks.html
More file actions
1457 lines (1428 loc) · 89.6 KB
/
Stocks.html
File metadata and controls
1457 lines (1428 loc) · 89.6 KB
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html><html lang="de"><head>
<title>Variationen zum Thema: Java</title>
<meta name="title" content="Variationen zum Thema: Java">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta charset="UTF-8">
<meta name="description" content="Introduction to Java Programming">
<meta name="keywords" content="java,introduction">
<meta name="author" content="Ralph P. Lano">
<meta name="robots" content="index,follow">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" type="text/css" href="book.css">
</head>
<body><center>
<div id="wrap">
<ul class="sidenav">
<p><a href="index.html">Variationen zum Thema</a><a href="index.html">Java</a></p>
<li><a href="Karel.html">Karel</a></li>
<li><a href="Graphics.html">Graphics</a></li>
<li><a href="Console.html">Console</a></li>
<li><a href="Agrar.html">Agrar</a></li>
<li><a href="MindReader.html">MindReader</a></li>
<li><a href="Swing.html">Swing</a></li>
<li><a href="Asteroids.html">Asteroids</a></li>
<li><a href="Stocks.html">Stocks</a></li>
</ul>
<div class="content"><p>
<img alt="" src="images/c24f32f0-1b68-42ac-a304-5a7e04e7d05b.png" style="display: block; margin-left: auto; margin-right: auto; width: 364px; height: 307px;" /></p>
<h1>
Stocks</h1>
<p>
Das Buch neigt sich dem Ende zu, was normalerweise bedeutet, dass es jetzt richtig interessant wird. Wir werden lernen wie wir mit Dateien umgehen, also lesen und schreiben. Dann schauen wir mal was so alles schief gehen kann und wie man das verhindert. Danach erweitern wir unseren Horizont was Datenstrukturen angeht. Die Objektorientierte Analyse kommt als nächstes, und zum Schluß kommen noch Interfaces und Polymorphie.</p>
<p>
.</p>
<h2>
<img alt="" src="images/readFromFile.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />Files</h2>
<p>
Sobald wir den Computer ausschalten sind alle Daten futsch. Es sei denn man hat sie gespeichert. Gespeichert heißt man hat sie in eine Datei geschrieben. Das ist eigentlich ganz einfach. Wir fangen mit dem Lesen von Dateien an.</p>
<p>
Zum Lesen aus Dateien muß man drei Dinge tun:</p>
<ol>
<li>
die Datei öffnen,</li>
<li>
lesen von der Datei, Zeile für Zeile, und</li>
<li>
die Datei schließen.</li>
</ol>
<p>
Im Code sieht das dann so aus:</p>
<pre>
// open file
<span style="color:#0000ff;">FileReader</span> fr = new FileReader("text.txt");
<span style="color:#0000ff;">BufferedReader</span> rd = new BufferedReader(fr);
// read from file, line by line
while (true) {
String line = <span style="color:#0000ff;">rd.readLine()</span>;
if (<span style="color:#0000ff;">line == null</span>)
break;
println(line);
}
// close file
rd.close();
fr.close();</pre>
<p>
In Java verwenden wir zum Lesen von Textdateien die Klasse <em>FileReader</em>. Der übergibt man einfach den Dateinamen der zu öffnenden Datei. Da der FileReader sehr dumm ist, er kann nämlich nur ein Zeichen nach dem anderen lesen, bittet man immer den <em>BufferedReader</em> um Hilfe: der kann nämlich ganze Zeilen lesen. Man übergibt den FileReader dem BufferedReader als Argument, damit der BufferedReader weiß welche Datei er denn lesen soll.</p>
<p>
Dann benutzen wir einfach einen Loop-And-A-Half um Zeile für Zeile mittels der Methode <em>readLine()</em> aus der Datei zu lesen. Wir wissen, dass wir das Ende der Datei erreicht haben, wenn readLine() den Wert <em>null</em> zurückliefert. D.h. es gibt nix mehr zu lesen. Danach schließen wir die beiden Readers mit der <em>close()</em> Methode.</p>
<p>
Leider funktioniert das Programm noch nicht ganz, weil es zu Fehlern (Exceptions) kommen kann. Dazu gleich mehr, aber vorher möchten wir in eine Datei schreiben.</p>
<p>
.</p>
<h2>
<img alt="" src="images/writeToFile.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />Schreiben</h2>
<p>
Auch beim Schreiben müssen wir drei Dinge tun:</p>
<ol>
<li>
die Datei zum Schreiben öffnen,</li>
<li>
dann in die Datei schreiben und</li>
<li>
die Datei schließen.</li>
</ol>
<p>
Schreiben ist einfacher als Lesen, weil wir keinen BufferedDingens brauchen:</p>
<pre>
// open file
<span style="color:#0000ff;">FileWriter</span> fw = new FileWriter("test.txt", false);
// write to file, one string at a time
println("Enter text to write ('.' to quit): ");
while (true) {
String line = readLine("");
if (line.equals("."))
break;
<span style="color:#0000ff;">fw.write(line + "\n")</span>;
}
// close file
fw.close();</pre>
<p>
<img alt="" src="images/testTxt.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 100px; float: right;" />Wir verwenden die Klasse <em>FileWriter</em> zum Schreiben von Textdateien. Wir übergeben als erstes den Dateinamen in den wir schreiben wollen, und wir müssen sagen ob wir anhängen wollen oder überschreiben wollen, falls die Datei schon existiert. Wenn wir anhängen wollen, sagen wir "true", falls wir überschreiben wollen sagen wir "false", also nicht anhängen.</p>
<p>
Dann benutzen wir wieder den Loop-And-A-Half um Zeile für Zeile mittels der Methode <em>write()</em> zu schreiben. Interessant ist vielleicht noch das '\n': es ist das Zeichen für <em>new line</em>, und sorgt dafür, dass der nächste String in der "test.txt" Datei in eine neue Zeile geschrieben wird.</p>
<p>
Damit wir nicht in einer Endlosschleife hängen bleiben, checken wir ob der Nutzer einen Punkt eingegeben hat, das ist unser Abbruchkriterium. Danach schließen wir unseren Writer mit der <em>close()</em> Methode.</p>
<p>
.</p>
<h2>
Exceptions</h2>
<p>
Was ist wenn was schief geht? Oder erst mal, was kann denn schief gehen? Es könnte z.B. sein, dass es die Datei die wir zum Lesen öffnen möchten gar nicht gibt. Dann kommt es zu einer <em>FileNotFoundException</em>. Eclipse zeigt die sogenannte <em>StackTrace</em> im "Console" Fenster:</p>
<p>
<img alt="" src="images/eception.png" style="margin-left: 10px; margin-right: 10px; width: 800px; height: 200px;" /></p>
<p>
An der Fehlermeldung erkennt man, dass es die Datei "text.txt" nicht gibt. Die StackTrace sagt einem auch wo denn der Fehler im eigenen Programm ausgelöst wurde. Dazu muss man die Klassen der Reihe nach durchgehen bis man seine eigene findet, in dem Fall oben ist das die <em>ReadFromFile</em> Klasse. Und dort steht "ReadFromFile.java:25", und die "25" ist die Zeilennummer in der das Problem auftrat. Wir können aber auch einfach auf den Link klicken, und Eclipse navigiert dann den Cursor an die problematische Stelle.</p>
<p>
Kann noch was schief gehen? Ja. Z.B. könnte es sein, dass die Datei zwar existiert, wir sie aber nicht lesen dürfen, da wir keine Berechtigung dazu haben. Dann kommt es auch zu einer FileNotFoundException, aber dieses mal steht in Klammern "Permission denied". Auch beim Schreiben können Sachen schief gehen, z.B. kann es sein, dass wir keine Schreibberechtigung haben, oder es könnte sein, dass wir keinen freien Platz mehr auf der Festplatte haben.</p>
<p>
Was passiert denn mit unserem Programm, wenn es zu einer Exception kommt? Es stürzt ab. Das kann sehr unangenehm sein, z.B. wenn das Programm ein Flugzeug steuert in dem wir sitzen, denn dann stürzt auch das Flugzeug ab. Nicht gut.</p>
<p>
.</p>
<h2>
Try-Catch</h2>
<p>
Wir müssen also das Flugzeug auffangen, oder besser das Programm. Das machen wir mit <em>catch</em>: wir probieren erst mal (<em>try</em>) ob alles funktioniert, wenn ja gut, wenn nein, dann sagen wir dem Programm was es tun soll. Im Code sieht das dann so aus:</p>
<pre>
try {
// code for file access
...
} catch ( Exception ex ) {
// deal with exception
...
}
...
</pre>
<p>
Wir umgeben also unseren problematischen Code mit einem try-Block. Klappt alles, dann macht das Programm ganz normal weiter (der catch-Block wird nicht ausgeführt). Geht aber was schief, dann bricht das Programm nicht ab, sondern springt in den catch-Block und führt diesen aus. Danach geht das Programm davon aus, dass wir alles unter Kontrolle haben, und führt den Code nach dem catch-Block aus, wie wenn nichts gewesen wäre.</p>
<p>
Exceptions treten nicht nur beim Arbeiten mit Dateien auf, sondern auch in vielen anderen Umständen. Betrachten wir kurz die wichtigsten:</p>
<p>
<strong>NullPointerException:</strong> ist wohl die am häufigsten vorkommende Exception. Sie tritt immer dann auf, wenn ein Objekt nicht existiert, also noch keines mit <em>new</em> angelegt wurde:</p>
<pre>
GRect fritz = null;
fritz.setColor(Color.RED);</pre>
<p>
<strong>ArithmeticException:</strong> tritt auf wenn eine mathematische Berechnung nicht möglich ist, der Klassiker, Division durch null:</p>
<pre>
int x = 5 / 0;</pre>
<p>
<strong>NumberFormatException:</strong> wenn wir versuchen aus einem String eine Zahl zu machen, und der String aber keine Zahl enthält:</p>
<pre>
int x = Integer.parseInt("five");</pre>
<p>
<strong>ArrayIndexOutOfBoundsException:</strong> wenn wir versuchen auf Elemente eines Arrays zuzugreifen, die es gar nicht gibt:</p>
<pre>
int[] eggs = { 0, 1, 2, 3 };
println( eggs[5] );</pre>
<p>
.</p>
<h2>
Data Structures</h2>
<p>
Bisher kennen wir nur eine Datenstruktur, das Array. Arrays sind zwar o.k., aber so richtig toll sind sie nicht. Viel cooler sind da schon die <em>ArrayList</em> und die <em>HashMap</em> mit denen wir uns gleich beschäftigen werden.</p>
<p>
.</p>
<h2>
<img alt="" src="images/uniqueNames.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 200px; float: right;" />ArrayList</h2>
<p>
ArrayListen sind wie Arrays nur schlauer. Fangen wir an eine ArrayList zu deklarieren und zu instantiieren:</p>
<pre>
ArrayList<String> names = new ArrayList<String>();
</pre>
<p>
Wir legen also eine ArrayList mit dem Namen "names" an, und in diese ArrayList dürfen nur <Strings> rein, das ist was die spitzen Klammern sagen. Im Prinzip ist es dasselbe wie wenn wir Arrays verwendet hätten:</p>
<pre>
String[] names = new String[10];
</pre>
<p>
Allerdings einen Unterschied erkennen wir sofort: bei Arrays mussten wir sagen wieviele Strings wir reintun wollen, bei der ArrayList ist das nicht notwendig: die ArrayList kann nämlich wachsen und schrumpfen, je nach Bedarf. Das ist super-praktisch!</p>
<p>
Machen wir weiter. Wir wollen jetzt ein paar Namen in unsere Namensliste einfügen, allerdings nur, falls der Name noch nicht in der Liste ist. Das geht so:</p>
<pre>
while (true) {
String name = readLine("Enter new name: ");
if (!names.<span style="color:#0000ff;">contains</span>(name)) {
names.<span style="color:#0000ff;">add</span>(name);
}
}
</pre>
<p>
Wir fragen mit der Methods <em>contains()</em> ob ein bestimmter Name schon in der Liste ist. Falls nicht, dann fügen wir ihn mit <em>add()</em> hinzu. Wir müssen also auch keinen Index beim Hinzufügen angeben, neue Einträge werden einfach am Ende der Liste eingefügt. Wir können aber auch Einträge irgendwo in der Liste einfügen, dann müssen wir aber den Index angeben.</p>
<p>
Wenn wir jetzt unsere Namensliste ausdrucken wollen, dann müssen wir über die Liste iterieren:</p>
<pre>
for (int i = 0; i < names.<span style="color:#0000ff;">size</span>(); i++) {
println(names.<span style="color:#0000ff;">get</span>(i));
}</pre>
<p>
Mit der Methode <em>size()</em> können wir feststellen wieviele Einträge unsere Liste hat, und mit <em>get()</em> lesen wir den Eintrag an einer bestimmten Position. Zusätzlich hat die ArrayList noch folgende praktische Methoden:</p>
<ul>
<li>
<strong>set():</strong> ersetzt den Eintrag an einer bestimmten Position mit einem neuen</li>
<li>
<strong>indexOf():</strong> sucht nach einem Eintrag und gibt die Position zurück</li>
<li>
<strong>remove():</strong> entfernt einen Eintrag aus der Liste</li>
<li>
<strong>clear():</strong> löscht die komplette Liste.</li>
</ul>
<p>
Also, wie wir sehen sind ArrayLists viel besser als Arrays, und deswegen nach langer Zeit wieder mal ein SEP:</p>
<p>
<strong><strong>SEP: Man sollte immer ArrayList anstelle von Array verwenden (Ausnahme sind Bilder).</strong></strong></p>
<p>
.</p>
<h2>
<img alt="" src="images/phoneBook.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 300px; float: right;" />HashMap</h2>
<p>
HashMaps sind wahrscheinlich die praktischste aller Datenstrukturen, weil man in ihnen super suchen kann. Klassische Beispiele sind Telefonbücher und Wörterbücher. Schauen wir uns das Beispiel PhoneBook mal näher an. Wir beginnen mit der Deklaration:</p>
<pre>
HashMap<String, Integer> phoneBook = new HashMap<String, Integer>();</pre>
<p>
Das sieht etwas ähnlich wie bei einer ArrayList aus, allerdings sind hier in der spitzen Klammern zwei Datentypen, ein String und ein Integer: <String, Integer>. Das erste ist der Schlüssel (key) und das zweite der Wert (value). Eine HashMap assoziiert also mit einem Schlüssel einen Wert, deswegen nennt man sie manchmal auch assoziative Arrays. </p>
<p>
Fügen wir mal ein paar Werte in unser PhoneBook:</p>
<pre>
while (true) {
String name = readLine("Enter name: ");
int number = readInt("Enter number: ");
phoneBook.<span style="color:#0000ff;">put</span>(name, number);
}</pre>
<p>
Einfügen geht mit der Methode <em>put()</em>. Wir übergeben ihr einfach einen Namen und eine Nummer. Exisitiert der Name bereits in der Map, dann wird er überschrieben, falls nicht wird er neu eingefügt. In einer HashMap kann es nie zwei Einträge mit gleichem Namen geben, Schlüssel müssen eindeutig sein.</p>
<p>
Nachdem wir unser PhoneBook gefüllt haben, wollen wir mal darin suchen, denn das ist ja wofür die HashMap gut sein soll:</p>
<pre>
String name = readLine("Enter name to search: ");
if (phoneBook.<span style="color:#0000ff;">containsKey</span>(name)) {
println(phoneBook.<span style="color:#0000ff;">get</span>(name));
} else {
println("no entry for this name");
}</pre>
<p>
Mit der <em>containsKey()</em> Methode können wir testen ob ein Name in der Map existiert, und mit <em>get()</em> können wir uns dann die Nummer zu dem Namen geben lassen. Das sieht zwar sehr ähnlich wie bei der ArrayList aus, ist aber viel cooler: bei der ArrayList müssen wir den Index des Eintrags kennen, bei der Map sagen wir einfach: gib mir mal.</p>
<p>
Zum Schluß wollen wir mal sehen was in unserer Map drinnen ist:</p>
<pre>
for (String name : phoneBook.<span style="color:#0000ff;">keySet</span>()) {
int number = phoneBook.<span style="color:#0000ff;">get</span>(name);
println(name + ": " + number);
}</pre>
<p>
Das ist jetzt komplett neu: die erste Zeile macht überhaupt keinen Sinn. Das Problem mit den Maps ist, dass es da keine Nummern mehr gibt. Wir können nicht sagen gib mir mal das zweite Element, denn eine Map hat keinen Index. Es gibt auch keine wirklich feste Reihenfolge, d.h. die Reihenfolge in der die Einträge ausgegeben werden, kann komplett anders sein als die Reihenfolge in der sie eingegeben wurden (wie man im Beispiel rechts sieht). </p>
<p>
Deswegen der Trick mit der Pseudo-for-Schleife:</p>
<pre>
for (String name : phoneBook.<span style="color:#0000ff;">keySet</span>()) {</pre>
<p>
Die heißt soviel wie, gib mir mal einen Namen aus der Liste der Namen in der Map. Und danach, gibst Du mir den nächsten, usw. bis wir alle durch sind. Wenn man das akzeptiert hat, ist der Rest einfach.</p>
<p>
.</p>
<h2>
Objekt-Orientierte Analyse</h2>
<p>
Wir befinden uns gerade an einem Übergang: und zwar verlassen wir gerade die prozedurale Ein-Klassen-Welt und betreten die objekt-orientierte Mehr-Klassen-Welt. Der Übergang ist nicht ganz schmerzfrei. Und wo uns bisher der Top-Down Ansatz wirklich gute Dienste erwiesen hat, hilft er uns jetzt nur noch im Kleinen, nicht mehr aber im Großen weiter.</p>
<p>
Dafür gibt es jetzt aber die Objekt-Orientierte Analyse, manchmal einfach auch Anforderungsanalyse genannt. Bevor man also anfängt Code zu schreiben, überlegt man sich erst einmal was will man denn überhaupt. Das schreibt man dann am besten in möglichst einfachen Sätzen nieder. Z.B. könnten die Anforderungen für einen Webshop so fomuliert werden:</p>
<p style="margin-left: 40px;">
"Der Azamon Shop hat Artikel und Warenkörbe. Ein Artikel hat einen Namen, einen Typ und einen Preis. Ein Warenkorb hat einen Benutzernamen und eine Liste von Artikel. Wir können alle Artikel des Shops auflisten. Wir können alle Artikel in einem Warenkorb auflisten. Wir können einen Artikel in den Warenkorb legen. Wir können den Preis aller Artikel in einem Warenkorb berechnen."</p>
<p>
Diese Anforderungen sind die Basis für unsere Objekt-Orientierte Analyse. </p>
<p>
<strong>1.Schritt:</strong> In ersten Schritt dieser Analyse nehmen wir einen Farbstift und unterstreichen die Verben grün und die Substantive mit rot:</p>
<p style="margin-left: 40px;">
"Der <span style="color:#ff0000;">Azamon Shop</span> <span style="color:#00ff00;">hat</span> <span style="color:#ff0000;">Artikel</span> und <span style="color:#ff0000;">Warenkörbe</span>. Ein <span style="color:#ff0000;">Artikel</span> <span style="color:#00ff00;">hat</span> einen <span style="color:#ff0000;">Namen</span>, einen <span style="color:#ff0000;">Typ</span> und einen <span style="color:#ff0000;">Preis</span>. Ein <span style="color:#ff0000;">Warenkorb</span> <span style="color:#00ff00;">hat</span> einen <span style="color:#ff0000;">Benutzernamen</span> und eine Liste von <span style="color:#ff0000;">Artikel</span>. Wir können alle <span style="color:#ff0000;">Artikel</span> des <span style="color:#ff0000;">Shops</span> <span style="color:#00ff00;">auflisten</span>. Wir können alle <span style="color:#ff0000;">Artikel</span> in einem <span style="color:#ff0000;">Warenkorb</span> <span style="color:#00ff00;">auflisten</span>. Wir können einen <span style="color:#ff0000;">Artikel</span> in den <span style="color:#ff0000;">Warenkorb</span> <span style="color:#00ff00;">legen</span>. Wir können den <span style="color:#ff0000;">Preis</span> aller <span style="color:#ff0000;">Artikel</span> in einem <span style="color:#ff0000;">Warenkorb</span> <span style="color:#00ff00;">berechnen</span>."</p>
<p>
<strong>2.Schritt:</strong> Im zweiten Schritt machen wir eine Liste für die Verben:</p>
<ul>
<li>
hat</li>
<li>
auflisten</li>
<li>
auflisten</li>
<li>
legen</li>
<li>
berechnen</li>
</ul>
<p>
und die Substantive listen und zählen wir:</p>
<ul>
<li>
Shop: II</li>
<li>
Artikel: IIIIIII</li>
<li>
Warenkorb: IIIII</li>
<li>
Name: I</li>
<li>
Typ: I</li>
<li>
Preis: II</li>
<li>
Benutzername: I</li>
</ul>
<p>
Das Zählen ist nicht unbedingt notwendig, hilft aber ein bischen zu erkennen was wichtig ist. </p>
<p>
<strong>3.Schritt:</strong> Im dritten Schritt betrachten wir die Liste der Substantive. Welche der Substantive können wir mit einem einfachen Datentyp wie einem <em>int</em>, <em>double</em>, <em>String</em> etc. beschreiben?</p>
<ul>
<li>
Shop: ???</li>
<li>
Artikel: ???</li>
<li>
Warenkorb: ???</li>
<li>
Name: String</li>
<li>
Typ: String</li>
<li>
Preis: int</li>
<li>
Benutzername: String</li>
</ul>
<p>
Wenn etwas kompliziert ist, also aus anderen Teilen besteht, wie z.B. Shop, Artikel und Warenkorb, dann handelt es sich um einen komplizierten Datentypen, also eine Klasse. Wir haben also unsere Klassen identifiziert: <em>Shop</em>, <em>Artikel</em> und <em>Warenkorb</em>. </p>
<p>
<strong>4.Schritt:</strong> Im vierten Schritt geht es darum die richtigen Attribute den richtigen Klassen zuzuweisen. Dazu lesen wir einfach noch einmal unsere Anforderungen durch. Dort steht:</p>
<ul>
<li>
"Der Azamon Shop hat Artikel und Warenkörbe.": also offensichtlich gehören <em>Artikel</em> und <em>Warenkorb</em> zum <em>Shop</em>, sind also Attribute des Shop.</li>
<li>
"Ein Artikel hat einen Namen, einen Typ und einen Preis.": also gehören Namen, Typ und Preis zum <em>Artikel</em>.</li>
<li>
"Ein Warenkorb hat einen Benutzernamen und eine Liste von Artikel": also gehören Benutzernamen und Liste von Artikel zum <em>Warenkorb</em>.</li>
</ul>
<p>
An dieser Stelle macht es auch Sinn auf Plural zu achten: jedes mal wenn ein Wort im Plural vorkommt, dann bedeutet das, dass wir eine Liste (oder Map) von dem jeweiligen Attribute benötigen. Z.B. aus "Liste von Artikel" oder "Warenkörbe" wird dann eine ArrayList.</p>
<p>
<img alt="" src="images/5e5039fa-0351-444b-b855-7fc03777b3ab.png" style="width: 221px; height: 170px; float: right;" />Mit der Information können wir schon unsere Klassen mit Attributen hinschreiben:</p>
<pre>
public class Shop {
private ArrayList<Cart> carts;
private ArrayList<Article> articles;
}
public class Article {
private String name;
private String type;
private int price;
}
public class Cart {
private String userName;
private ArrayList<Article> articles;
}</pre>
<p>
<strong>5.Schritt:</strong> Beim letzten Schritt geht es darum die Methoden den richtigen Klassen zuzuweisen. Aus den Verben die wir im zweiten Schritt gesammelt haben, werden die Methoden (wir haben ja schon immer gesagt, dass Methoden Tun-Wörter sind). Wir gehen ein Verb nach dem anderen durch. </p>
<ul>
<li>
hat: "hat" zählt nicht als Verb, da es Zugehörigkeit ausdrückt und schon im Schritt 4 verwendet wurde.</li>
<li>
auflisten: bezieht sich auf "Wir können alle Artikel des Shops auflisten", von daher gehört es zum Shop, und sollte <em>artikelAuflisten()</em> heißen.</li>
<li>
auflisten: bezieht sich auf "Wir können alle Artikel in einem Warenkorb auflisten", von daher gehört es zum Warenkorb, und sollte <em>artikelAuflisten()</em> heißen.</li>
<li>
legen: bezieht sich auf "Wir können einen Artikel in den Warenkorb legen", von daher gehört es zum Warenkorb, und sollte <em>artikelInWarenkorbLegen()</em> heißen.</li>
<li>
berechnen bezieht sich auf "Wir können den Preis aller Artikel in einem Warenkorb berechnen", von daher gehört es zum Warenkorb, und sollte <em>preisAllerArtikelImWarenkorbBerechnen()</em> heißen.</li>
</ul>
<p>
Und damit stehen unsere Klassen, ihre Attribute und Methoden fest:</p>
<pre>
public class Shop {
private ArrayList<Cart> carts;
private ArrayList<Article> articles;
public void listArticles() {
}
}
public class Article {
private String name;
private String type;
private int price;
}
public class Cart {
private String userName;
private ArrayList<Article> articles;
public void listArticles() {
}
public void addArticleToCart() {
}
public void calculatePriceOfArticlesInCart() {
}
}</pre>
<p>
Was noch bleibt ist sich zu überlegen welche Parameter die Methoden benötigen und was sie als Rückgabewert liefern. Es schadet auch nie, jeder Klasse einen Constructor zu geben. Und natürlich fehlt noch der Code.</p>
<p>
.</p>
<h2>
<img alt="" src="images/8aff1c4e-4764-43d9-9651-6f407d47f9fe.png" style="width: 171px; height: 154px; float: right;" />Interfaces</h2>
<p>
Wir kommen jetzt zum letzten Thema dieses Kapitels: <em>Interfaces</em>. Wir haben bereits gehört, dass es in Java keine Mehrfachvererbung gibt, also eine Klasse kann keine zwei Eltern haben. Bisher war das kein Problem, aber jetzt wo wir anfangen größere Programme zu schreiben, stellt sich das schon als ärgerlich heraus. Die Lösung ist, wer hätte es vermutet: <em>Interfaces</em>.</p>
<p>
Interfaces erlauben uns zwei Dinge zu tun:</p>
<ul>
<li>
Konstanten zwischen verschiedenen Klassen zu teilen und</li>
<li>
das Definieren einer Schnittstelle zwischen zwei Klassen, daher der Name.</li>
</ul>
<p>
<img alt="" src="images/da024ab5-e8a1-4c12-a38a-39b446de513d.png" style="width: 271px; height: 137px; float: right;" />Beginnen wir mit dem ersten. In dem Projekt MinesClone hatten wir ein Interface namens <em>MinesConstant</em>:</p>
<pre>
public <span style="color:#0000ff;">interface</span> MinesConstant {
/** Playing field should be 8x8 fields and 50 pixel per field */
public static final int FIELD_SIZE = 8;
public static final int PIXEL_PER_TILE = 50;
...
}</pre>
<p>
Wir hatten also alle Konstanten an einer Stelle. Damit diese Konstanten in anderen Klassen verwenden konnten, mussten wir in den Klassen <em>MinesClone</em> und <em>MinesHelper</em> dieses Interface implementieren:</p>
<pre>
public class MinesClone extends GraphicsProgram <span style="color:#0000ff;">implements MinesConstant</span> {
...
}
public class MinesHelper <span style="color:#0000ff;">implements MinesConstant</span> {
...
}</pre>
<p>
Ganz einfach.</p>
<p>
<img alt="" src="images/bd4667e8-8f4d-4567-8360-74950dbd6590.png" style="width: 300px; height: 132px; float: right;" />Kommen wir zum Definieren einer Schnittstelle zwischen zwei Klassen. Betrachten wir die ACM Grafikklassen: wir haben ja schon gehört, dass GLabel, GRect und GOval Kinderklassen von GObject sind. Zusätzlich gibt es aber noch ein Interface <em>GFillable</em>, und die Klassen GRect und GOval implementieren dieses Interface. Für GLabel macht das keinen Sinn, denn was soll denn bitte ein ausgefüllter GLabel sein?</p>
<p>
Warum ist jetzt GFillable eine Schnittstelle zwischen zwei Klassen? Betrachten wir unser aller erstes Grafikprogramm BlueRect: Wir wollten ein ausgefülltes Rechteck zeichnen. Da GRect das GFillable Interface implementiert, wissen wir, dass GRect die Methoden <em>setFilled()</em> und <em>setFillColor()</em> hat. Also die Klasse BlueRect kann sich darauf verlassen, dass es diese beiden Methoden gibt. Umgekehrt verpflichtet sich die Klasse GRect, dass es diese beiden Methoden liefert, da sie ja das Interface GFillable implementiert. Es ist also ein Vertrag zwischen den beiden Klassen BlueRect und GRect. Manchmal wird das auch als "Design by contract (DbC)" [1] bezeichnet.</p>
<p>
Wie muss jetzt das GFillable Interface aussehen?</p>
<pre>
public interface GFillable {
public void setFilled(boolean flag);
public boolean isFilled();
public void setFillColor(Color c);
public Color getFillColor();
}
</pre>
<p>
Wir sehen also, in der Definition des Interfaces werden Methoden nur angedeutet, aber nicht implementiert. Die Implementierung muss in der jeweiligen Klasse erfolgen, Interfaces dürfen keinen Implementierungscode enthalten. </p>
<p>
In der ACM Bibliothek gibt es noch die Interfaces <em>GResizable</em>, welches die Klassen GImage, GOval and GRect implementieren, sowie <em>GScalable</em>, welches die Klassen GArc, GCompound, GLine, GImage, GOval, GPolygon and GRect implementieren. Wir sehen also, obwohl es in Java nur Einfachvererbung zwischen Klassen gibt, so dürfen Klassen durchaus mehrere Interfaces implementieren.</p>
<p>
.</p>
<h2>
<img alt="" src="images/polymorphism.png" style="margin-left: 10px; margin-right: 10px; width: 200px; height: 133px; float: right;" />Polymorphism</h2>
<p>
Polymorphismus bedeutet Vielgestaltigkeit. Und was hat das bitte mit Java zu tun? Schauen wir uns ein Beispiel an:</p>
<pre>
public class Polymorphism extends GraphicsProgram {
public void run() {
GRect frankWalter = new GRect(50, 30);
add(frankWalter, 50, 50);
fillWithRedColor(frankWalter);
for (int i = 0; i < 10; i++) {
moveByTenPixelToLeft(frankWalter);
pause(1000);
}
}
public void <span style="color:#0000ff;">fillWithRedColor(GFillable rect)</span> {
rect.setFilled(true);
rect.setFillColor(Color.RED);
}
public void <span style="color:#0000ff;">moveByTenPixelToLeft(GObject rect)</span> {
rect.move(10, 0);
}
}</pre>
<p>
Das GRect <em>frankWalter</em> ist vielgestaltig. Denn gegenüber der Methode <em>fillWithRedColor()</em> gibt es sich als GFillable aus, während es sich gegenüber der Methode <em>moveByTenPixelToLeft()</em> als GObject ausgibt. Es hat also zwei Gestalten, GFillable und GObject. Mehr ist da nicht.</p>
<p>
So und damit sind wir am Ende. Wir kennen jetzt nämlich alle drei Säulen der objektorientierten Programmierung:</p>
<ul>
<li>
Datenkapselung und Information Hiding</li>
<li>
Vererbung und Komposition</li>
<li>
Polymorphismus</li>
</ul>
<p>
<img alt="" src="images/e5c22719-1927-42b8-86c6-2c21661acdaf.png" style="width: 273px; height: 229px;" /></p>
<p>
.</p>
<hr />
<h1>
Review</h1>
<p>
Es wurde wirklich Zeit, dass wir gelernt haben wie man mit Dateien arbeitet. Auch die Fehlerbehandlung hätte eigentlich schon viel früher kommen sollen, aber macht natürlich viel mehr Sinn wenn man mit Dateien arbeitet. Was wirklich neu und sich für die Zukunft als sehr nützlich erweisen wird, sind die Datenstrukturen ArrayList and HashMap. Die werden uns noch so manches Mal das Leben erleichtern. Und wir haben die Themen Interfaces und Polymorphie abgehackt. </p>
<p>
Das wichtigste in diesem Kapitel war aber die Objekt-Orientierte Analyse, sozusagen Top-Down 2.0.</p>
<p>
.</p>
<hr />
<h1>
Projekte</h1>
<p>
Hat sich die Arbeit gelohnt? Ich denke die Projekte in diesem Kapitel geben eine klare Antwort: Ja!</p>
<p>
.</p>
<h2>
<img alt="" src="images/copyFile.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 100px;" />CopyFile</h2>
<p>
Als kleine Aufwärmübung wollen wir eine Text-Datei kopieren. Der Nutzer soll angeben welche Datei kopiert werden soll. Die Zieldatei soll einfach "copy.txt" heißen. </p>
<p>
Im Prinzip gibt es zwei Möglichkeiten dies zu tun: man könnte die gesamten Daten aus der ersten Datei in einen String lesen, und dann in einem Schwung in die Datei"copy.txt" schreiben. Der Nachteil dieses Verfahrens, es ist ungeeignet für sehr große Dateien. Deswegen wollen wir die zweite Möglichkeit umsetzen: Wir öffenen beide Dateien, die erste mit dem FileReader zum Lesen, die zweite mit dem FileWriter zum schreiben. Dann lesen wir Zeile für Zeile, und schreiben jede Zeile sofort in den FileWriter. Wenn wir dann fertig sind müssen wir natürlich beide Dateien wieder schließen.</p>
<p>
.</p>
<h2>
<img alt="" src="images/minutes.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 100px;" />Minutes</h2>
<p>
Manchmal sind wir in den Team-Meetings fürs Protokoll zuständig. Dann wäre es ganz praktisch ein Programm fürs Protokoll zu haben. Die Idee ist folgende: man tippt einfach einen Satz, drückt auf <em>Enter</em> und die Zeile wird in eine Datei namens "minutes.txt" gespeichert. Man öffnet also eine Datei mittels FileWriter, liest dann kontinuierlich (Loop-and-a-Half) Zeile für Zeile von der Console mittels <em>readLine()</em>, und schreibt jede Zeile sofort in die Datei. Natürlich benötigen wir noch ein Abbruchkriterium (Sentinel), das könnte einfach eine leere Zeile sein. Danach dürfen wir natürlich nicht vergessen die Datei zu schließen. Eine nützliche Erweiterung wäre wenn man an jede Zeile die Uhrzeit anfügt, wann der Text eingegeben wurde. Dann würde unser Programm wirklich seinen Namen verdienen.</p>
<p>
.</p>
<h2>
<img alt="" src="images/wordCount.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 100px;" />WordCount</h2>
<p>
WordCount ist ein ganz einfaches Programm: liest eine bestimmte Datei ein und zählt die Zeilen, die Wörter und die Zeichen die diese Datei enthält. Wir verwenden den FileReader zusammen mit dem BufferedReader um Zeile für Zeile zu lesen. Wir benötigen drei Variablen zum zählen</p>
<pre>
int counterLines = 0;
int counterWords = 0;
int counterChars = 0;</pre>
<p>
(das dürfen lokale Variablen sein). Die Zeilen zu zählen ist ganz einfach, die Zeichen, auch denn mittels <em>line.length()</em> wissen wir wieviel Zeichen in einer Zeile sind. Für das Zählen der Wörter könnte man entweder den StringTokenizer nehmen, oder die Methode <em>split()</em> der String Klasse verwenden:</p>
<pre>
private int countWords(String line) {
String[] words = line.split(" ");
return words.length;
}</pre>
<p>
Die Methode <em>split()</em> ist etwas mit Vorsicht zu geniesen. Denn augenscheinlich sieht es so aus, wie wenn sie einfach einen String in seine Einzelteile zerschneiden würde, und diese Einzelteile in einem String-Array speichert. Das tut sie auch, allerdings stellt sich die Frage, nach welchem Kriterium schneidet sie denn den String auseinander? Im Gegensatz zum StringTokenizer, der einfach eine Liste von Trennzeichen nimmt um einen String zu zerteilen, verwendet die <em>split()</em> Methode <em>Reguläre Ausdrücke</em>. Solange wir also nicht wissen was <em>Reguläre Ausdrücke</em> sind (kommt nächstes Semester) sollten wir die <em>split()</em> Methode eigentlich nicht verwenden.</p>
<p>
.</p>
<h2>
<img alt="" src="images/createRandomNumbers.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 100px;" />CreateFileWithRandomNumbers</h2>
<p>
Ab und zu (z.B. im nächsten Projekt) benötigen wir eine Datei mit ein paar Zufallszahlen. Das könnten aber auch Zufallsnamen oder Adressen oder was auch immer sein. Der Nutzer soll uns angeben wie die Datei heißen soll in die wir die Zufallszahlen schreiben sollen, dann benötigen wir den Bereich in dem die Zahlen sein sollen, und wir müssten noch wissen wieviele Zahlen wir generieren sollen. Das Programm soll dann mittels FileWriter und RandomGenerator eine solche Datei erzeugen. Für das nächste Projekt benötigen wir Zufallszahlen zwischen 0 und 100, und zwar 43 an der Zahl.</p>
<p>
.</p>
<h2>
<img alt="" src="images/histogram.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 175px;" />Histogram</h2>
<p>
Um sich z.B. mal ganz schnell einen Überblick über die Notenverteilung in einer Prüfung zu schaffen, kann man sich ein Histogramm ausgeben lassen. Man kann das natürlich mit einer tollen Grafik machen (kommt später), aber viel einfacher zu programmieren ist das mit einem Konsolenprogramm. </p>
<p>
Wir wollen Punkte kummulieren, also zählen wieviele Klausuren Punkte zwischen 0 und 9, zwischen 10 und 19, usw hatten. Wir könnten z.B. ein Array für elf Zahlen definieren:</p>
<pre>
private int[] histogramData = new int[11];</pre>
<p>
und dann diese mit der Hilfe unseres Freunds Ganzzahldivision befüllen:</p>
<pre>
private void putScoreInHistogram(int score) {
histogramData[score / 10]++;
}</pre>
<p>
Die Zeile hat es in sich. Nach genügend langer Bewunderung können wir jetzt aber weiter machen. </p>
<p>
Wir lesen also Zeile für Zeile aus unserer Scores.txt Datei, konvertieren die Strings mittels</p>
<pre>
int score = Integer.parseInt(line);</pre>
<p>
in Ganzzahlen und fügen die dann mittels unserer Wundermethode <em>putScoreInHistogram()</em> in unser Array. Wenn wir fertig sind, gehen wir das Array durch und geben es auf der Konsole aus. Wir könnten einfach die Zahlen ausgeben, viel hübscher sind aber kleine Sternchen (Asterisk nicht Asterix!):</p>
<pre>
private String convertToStars(int i) {
String stars = "";
for (int j = 0; j < i; j++) {
stars += "*";
}
return stars;
}</pre>
<p>
.</p>
<h2>
<img alt="" src="images/punctuation.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 175px;" />Punctuation</h2>
<p>
Wenn wir so im Internet unterwegs sind, dann gibt es Programme (oder besser Algorithmen) die unsere Sprache automatisch erkennen können. Wir tippen ein paar Wörter und Satzzeichen ein, und schon kann uns das Programm sagen welche Sprache das ist. Wir können so etwas ähnliches auch ganz einfach bewerkstelligen.</p>
<p>
Die Idee ist es die Satzzeichen, also ";:'",!?.", zu zählen. Das machen wir genauso wie im letzten Projekt (Histogram) und zeigen dann die Häufigkeit der Satzzeichen in einem Histogramm. Es stellt sich heraus, das jede Sprache ihre Lieblingssatzzeichen hat, und man an der Verteilung der Satzzeichen eine Sprache erkennen kann, natürlich muss der zu ananlysierende Text lang genug sein, und sollte typisch für die Sprache sein (Ulysses wäre also eher ungeeignet [2]).</p>
<p>
Der Clou bei diesem Programm ist die Instanzvariable <em>punctuation</em>:</p>
<pre>
private String punctuation = ";:'<span style="color:#0000ff;">\"</span>,!?.";</pre>
<p>
</p>
<p>
also ein String der alle möglichen Satzzeichen einthält. Wir haben hier das Zeichen '\"' speziell markiert, denn es ist die einzige Möglichkeit wie man das Anführungszeichen in einen String bekommt.</p>
<p>
Wir lesen dann wieder Zeile für Zeile aus einer Datei die uns der Nutzer genannt hat, und analysieren jede Zeile</p>
<pre>
private void analyzeForPunctuation(String line) {
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (punctuation.contains("" + c)) {
int index = punctuation.indexOf(c);
histogramData[index]++;
totalNrOfPunctuations++;
}
}
}</pre>
<p>
Das ist ziemlich schwerer Tabak, aber eigentlich auch wieder nicht. Wir gehen ein Zeichen nach dem anderen durch. Sehen nach ob es ein Satzzeichen ist, und falls ja, erhöhen wir den Zähler für dieses Satzzeichen um eins. Außerdem haben wir noch einen Zähler für die Gesamtzahl der Satzzeichen, das erlaubt uns dann beim Anzeigen der Sternchen die Zahlen zu normieren.</p>
<p>
.</p>
<h2>
<img alt="" src="images/worldMap.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 100px;" />WorldMap</h2>
<p>
Die Wikipedia hat eine Liste mit den Längen- und Breitengraden vieler Städte weltweit [3]. Wenn wir diese Liste, "Cities.txt", durchgehen und für jede Stadt ein GOval malen, dann können wir eine Weltkarte zeichnen. Wir verwenden also wieder die FileReader/BufferedReader Kombo um Zeile für Zeile aus der Datei zu lesen. Die Daten in einer Zeile</p>
<pre>
Germany, Berlin, <span style="color:#0000ff;">52</span>, 32, N, <span style="color:#0000ff;">13</span>, 25, E</pre>
<p>
müssen wir <em>parsen</em>, d.h. die Information so umformen, dass sie für uns nützlich ist. Für unsere Zwecke sind das Land und der Name der Stadt nutzlos. Mit der Genauigkeit nehmen wir es auch nicht so genau, deswegen genügt uns die Grad Angabe, also die <em>52</em>, z.B.. Was wir allerdings noch benötigen ist ob sich die Stadt im Norden oder im Süden der Erdkugel befindet:</p>
<pre>
String[] data = line.split(",");
String lat1 = data[2].trim();
String lat3 = data[4].trim();
int lat = Integer.parseInt(lat1);
if (lat3.endsWith("S")) {
lat = -lat;
}</pre>
<p>
und das Gleiche für die Länge (longitude). Das Ganze skalieren wir noch, damit es in unseren Screen passt, also</p>
<pre>
double x = (0.5 - lon / 360.0) * WIDTH;
double y = (0.5 - lat / 180.0) * HEIGHT;
GOval city = new GOval(CITY_SIZE, CITY_SIZE);
add(city, x, y);</pre>
<p>
Sieht doch sehr hübsch aus, oder?</p>
<p>
.</p>
<h2>
<img alt="" src="images/editor.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 150px;" />Editor</h2>
<p>
Im Swing Kapitel haben wir ja schon eine UI für einen Editor geschrieben. Endlich können wir auch Dateien lesen und schreiben. In der Methode <em>saveFile()</em> holen wir uns einfach den Text aus der JTextArea <em>display</em> mittels</p>
<pre>
String text = display.getText()</pre>
<p>
und schreiben den Text in einem Stück mittels eines FileWriters in eine Datei. In der Methode <em>openFile()</em> lesen wir Zeile für Zeile aus der Datei und zeigen sie dann in der JTextArea <em>display</em> mittels <em>display.setText(text)</em> an.</p>
<p>
.</p>
<h2>
<img alt="" src="images/quiz2.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 133px;" />Quiz</h2>
<p>
Auch für unser Quiz Programm steht die UI bereits. Allerdings ist ein Quiz mit nur einer Frage nicht besonders nützlich. Außerdem sollte man verschiedene Quizzes mit unserem Programm halten können. </p>
<p>
Wir beginnen mit den Fragen. Die sollten aus einer Text Datei kommen, dann kann man nämliche beliebige Quizzes halten. Für unser Beispiel ist das einfach eine Textdatei namens "Quiz.txt":</p>
<pre>
Correct: 1 + 1 = 2 ?; Yes; No; Maybe
What is 2 ^ 2 ?; 2; 4; 8
A zebra has stripes?; Yes; No</pre>
<p>
Jede Zeile entspricht einer Frage mit den möglichen Antworten. Die Frage ist an erster Stelle, gefolgt von den möglichen Antworten. Frage und Antworten durch Strichpunkte getrennt. Diese Datei können wir Zeile für Zeile einlesen, und mittels der <em>split()</em> Methode kommen wir an Frage und Antworten:</p>
<p>
String[] words = line.split(";");</p>
<p>
Aus jeder Zeile machen wir ein Frage:</p>
<pre>
class Question {
String question;
String[] answers;
public Question(String[] words) {
question = words[0];
answers = words;
}
}</pre>
<p>
indem wir einfach dieses Array an den Konstruktor übergeben. Da die Klasse <em>Question</em> nur in unserer Quiz Klasse verwendet wird, kann es eine lokale Klasse sein, muss es aber nicht.</p>
<p>
Nun ist die Frage, was machen wir mit den Fragen? Wir stecken sie alle in eine ArrayList:</p>
<pre>
private ArrayList<Question> questions;</pre>
<p>
Man könnte auch ein Array verwenden, aber dann müsste man im Voraus wissen wieviele Fragen ein Quiz hat, das wissen wir aber nicht. Zusammengefasst sieht das dann so aus:</p>
<pre>
while (true) {
String line = rd.readLine();
if (line == null)
break;
String[] words = line.split(";");
Question q = new Question(words);
questions.add(q);
}</pre>
<p>
Kommen wir zum Darstellungsteil unseres Programms. Wir benötigen natürlich eine <em>actionPerformed()</em> Methode für den <em>Previous</em> und <em>Next</em> JButton:</p>
<pre>
public void actionPerformed(ActionEvent e) {
if (e.getSource() == btnNext) {
currentQuestion++;
currentQuestion = currentQuestion % questions.size();
setQuestion(currentQuestion);
} else {
currentQuestion--;
...
}
}</pre>
<p>
Wir haben also einen Zähler, <em>currentQuestion</em>, der auf die momentan aktuelle Frage zeigt, also der Index in der ArrayList <em>questions</em>. Was noch bleibt ist die <em>setQuestion()</em> Methode. </p>
<p>
private void setQuestion(int index) {<br />
Question q = questions.get(index);<br />
lbl.setText(q.question);<br />
buildMultipleChoiceAnswers(q.answers);<br />
}</p>
<p>
Die holt einfach die momentan aktuelle Frage aus der ArrayList, und setzt unser Label mit der Frage und ruft unsere alte <em>buildMultipleChoiceAnswers()</em> Methode mit den Antworten auf.</p>
<p>
Erweiterungen: Was noch fehlt ist dass die Antworten des Nutzers auch irgendwo gespeichert werden, und am Ende ausgewertet werden. Das ist nicht weiter schwer, braucht aber seine Zeit.</p>
<p>
.</p>
<h2>
<img alt="" src="images/dictionary.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 100px;" />Dictionary</h2>
<p>
Wörterbücher sind eine typische Anwendung für eine HashMap. Als Beispiel wollen wir ein deutsch-englisches Wörterbuch implementieren. Das Wörterbuch halten wir als Instanzvariable:</p>
<pre>
private HashMap<String, String> dictionary = new HashMap<String, String>();</pre>
<p>
und im ersten Schritt müssen wir das Wörterbuch initialisieren:</p>
<pre>
private void initialzeDictionary() {
dictionary.put("hund", "dog");
dictionary.put("katze", "cat");
dictionary.put("fisch", "fish");
}</pre>
<p>
Wir fragen dann den Nutzer nach einem deutschen Wort mittels readLine() von der Konsole und der <em>get()</em> Methode</p>
<pre>
String english = dictionary.get( german.toLowerCase() );</pre>
<p>
erhalten wir dann das englische Wort. Ganz wichtig, die Übersetzung geht nur in eine Richtung.</p>
<p>
.</p>
<h2>
<img alt="" src="images/stateLookup.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 100px;" />StateLookup</h2>
<p>
Eine andere typische Anwendung für HashMaps ist wenn wir etwas suchen. Z.B. wir haben den Kürzel eines US-Bundesstaates (z.B. "NY") und würden gerne wissen um welchen Bundesstaat es sich denn handelt. Im Internet findet man Tabellen mit der Liste aller Bundesstaaten, z.B. in der Form:</p>
<pre>
AL,Alabama
AK,Alaska
AZ,Arizona
...</pre>
<p>
Wir lesen also Zeile für Zeile, und fügen diese in unsere HashMap ein:</p>
<pre>
private void readStateEntry(String line) {
int comma = line.indexOf(",");
String stateInitial = line.substring(0, comma).trim();
String stateName = line.substring(comma + 1).trim();
states.put(stateInitial, stateName);
}</pre>
<p>
(das kann man natürlich auch mit der split() Methode oder dem StringTokenizer machen). Der Rest funktioniert dann genauso wie im Projekt Dictionary.</p>
<p>
.</p>
<h2>
<img alt="" src="images/VocabularyTrainer.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 200px;" />VocabularyTrainer</h2>
<p>
Das Pendant zu einem Dictionary ist ein Vokabel-Trainer. Allerdings soll unser Vokabel-Trainer für beliebige Sprachen funktionieren, deswegen können wir nicht einfach die Vokabeln hardcoden, sondern sollten diese aus einer Datei lesen. In der Datei "Vocabulary.txt" sind die Vokabeln in folgender Form gespeichert:</p>
<pre>
dog,Hund
cat,Katze
fish,Fisch
...</pre>
<p>
Wir lesen also Zeile für Zeile und speichern die Vokabeln in einer HashMap namens <em>vocabulary</em>:</p>
<pre>
private HashMap<String, String> vocabulary = new HashMap<String, String>();</pre>
<p>
Was den Vokabel-Trainer jetzt aber vom Dictionary unterscheidet ist, dass er den Nutzer nach der Übersetzung eines englischen Wortes fragt. Ist die Antwort des Nutzers richtig, dann bekommt ein verbales Klopfen auf die Schulter. Wusste der Nutzer allerdings das Wort nicht, so müssen wir es in einer Liste von <em>unknownWords</em> speichern:</p>
<pre>
private ArrayList<String> unknownWords = new ArrayList<String>(); </pre>
<p>
Am Ende sollten wir dann die Liste der Wörter die der Nutzer nicht wusste auflisten, damit er sie noch mal in Ruhe lernen kann.</p>
<p>
.</p>
<h2>
<img alt="" src="images/vocabularyTrainerSwing.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 160px;" />VocabularyTrainerSwing</h2>
<p>
Konsolenanwendung sind zwar einfach, aber häßlich. Da wir aber die schwere Arbeit schon im letzten Projekt geschafft haben, machen wir daraus einfach eine Swing Applikation. Die besteht aus einem JLabel im Norden und einem JTextField im Süden. Wir zeigen dem Nutzer das zu übersetzende Wort im JLabel, und der Nutzer soll dann die Übersetzung im JTextField eingeben. </p>
<p>
Für das Feedback an den Nutzer würden wir gerne eine Dialogfenster verwenden. In Swing geht das mit der <em>JOptionPane</em>:</p>
<pre>
public void actionPerformed(ActionEvent e) {
String english = englishLbl.getText();
String guess = germanTf.getText();
if (guess.toLowerCase().equals(dictionary.get(english))) {
<span style="color:#0000ff;">JOptionPane.showMessageDialog(this, "Great job!",
"Check", JOptionPane.INFORMATION_MESSAGE);</span>
} else {
JOptionPane.showMessageDialog(this, "Try again!",
"Check", JOptionPane.INFORMATION_MESSAGE);
}
setRandomWord();
}</pre>
<p>
Ansonsten, können wir den Code vom Projekt VocabularyTrainer wiederverwenden.</p>
<p>
.</p>
<h2>
<img alt="" src="images/trains.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 200px;" />Trains</h2>
<p>
Eine sehr schöne Anwengung für HashMaps sind Fahrpläne. Nehmen wir an wir wollen von München nach Berlin und es gibt aber keine direkte Verbindung. Dann sehen wir im Fahrplan nach und sehen, dass man von München nach Nürnberg fahren kann, und von Nürnberg gibt es einen Zug nach Berlin. So eine einfacher Fahrplan könnte einfach eine Textdatei sein, die alle Verbindung enthält:</p>
<pre>
Nuremberg > Berlin
Nuremberg > Frankfurt
Nuremberg > Munich
Munich > Nuremberg
Hamburg > Berlin</pre>
<p>
Wichtig ist hier, dass die Verbindungen zwischen Städten eine Richtung haben, also der Zug geht von Nürnberg nach Berlin, muss aber nicht zurück gehen. </p>
<p>
Der nächste Schritt ist sich zu überlegen wie man so einen Fahrplan in einer HashMap unterbringt. Es ist klar, dass der Key der Ausgangsbahnhof sein muss. Da es aber mehrere Zielbahnhöfe geben kann, müssen wir hier eine Liste verwenden:</p>
<pre>
private HashMap<String, ArrayList<String>> connections;</pre>
<p>
Außerdem macht es auch noch Sinn eine Liste von allen Bahnhöfen irgendwo zu haben:</p>
<pre>
private ArrayList<String> cities;</pre>
<p>
Im <em>setup()</em> lesen wir also den Fahrplan und befüllen unsere beiden Datenstrukturen. Mit dem StringTokenizer trennen wir source von destination:</p>
<pre>
StringTokenizer st = new StringTokenizer(line, ">");
String source = st.nextToken().trim();
String destination = st.nextToken().trim();</pre>
<p>
Dann sollten wir checken, ob es den Ausgangsbahnhof schon gibt</p>
<pre>
if (!cities.contains(source)) {
cities.add(source);
connections.put(source, new ArrayList<String>());
}</pre>
<p>
und schließlich müssen wir die neue Verbindung hinzufügen:</p>
<pre>
ArrayList<String> cits = connections.get(source);
cits.add(destination);
</pre>
<p>
Nachdem die Daten jetzt geladen sind, können wir mit dem eigentlichen Programm fortfahren. Als erstes sollten wir dem Nutzer eine Liste aller Ausgangsbahnhöfe auflisten. Daraus sollte er seinen Ausgangbahnhof wählen. Im nächsten Schritt listen wir die möglichen Zielbahnhöfe auf, und lassen den Nutzer wieder wählen. Das machen wir so lange, bis der Nutzer seinen Zielbahnhof erreicht hat, also den leeren String eingibt.</p>
<p>
Erweiterungen: Was natürlich cool wäre, wenn der Nutzer einfach nur seinen Ausgangsbahnhof und Zielbahnhof eingeben könnte, und das Programm dann automatisch eine Route vorschlägt. Gegen Ende des nächsten Semesters können wir das auch.</p>
<p>
.</p>
<h2>
<img alt="" src="images/adventure.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 200px;" />Adventure</h2>
<p>
Auch eine schöne Anwendung für HashMaps sind Abenteuer Spiele. In vielen von diesen text-basierten Spielen geht es darum eine Welt zu erkunden, und Gegenstände einzusammeln. Wir konzentrieren uns hier auf den Erkunden Teil, aber der Einsammel Teil ist auch nicht so schwer.</p>
<p>
Ähnlich wie beim <em>Trains</em> Projekt benötigen wir eine Beschreibung der Umgebung. Am einfachsten ist da eine Beschreibung unserer Wohnung. Also bei uns zu hause sieht das so aus:</p>
<pre>
hallway > kitchen
hallway > living room
hallway > bath room
kitchen > hallway
living room > hallway
bath room > hallway
bath room > kitchen</pre>
<p>
Auch hier verwenden wir wieder eine HashMap die den Plan unserer Wohnung wiedergibt:</p>
<pre>
private HashMap<String, ArrayList<String>> roomMap;</pre>
<p>
Da es sich um ein Erkundungsspiel handelt, benötigen wir keine Liste aller Räume, anstelle lassen wir den Spieler einfach in der Küche mit seiner Erkundung beginnen. Wir listen dann die Räume auf die von der Küche aus zu erreichen sind, und bitten den Spieler eine Wahl zu treffen. Auf diese Art und Weise kann der Spieler nach und nach unsere ganz Wohnung erkunden. Mit der Eingabe des leeren Strings endet das Spiel.</p>
<p>
Erweiterungen: Für diesen Spieletyp gibt es zahllose Erweiterungen. Man könnte zum Beispiel die Welt aus StarWars oder Herr der Ringe auf diese Art abbilden. In den verschiedenen Räumen könnte man magische Gegenstände verstecken. Und manche Räume kann man nur betreten wenn man einen bestimmten Gegenstand hat, usw...</p>
<p>
.</p>
<h2>
<img alt="" src="images/buildIndex.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 100px;" />BuildIndex</h2>
<p>
Bücher aus Papier kann man nicht so leicht durchsuchen wie elektronische Bücher. Deswegen haben die meisten Bücher hinten einen Index, auch Stichwortverzeichnis genannt. Als Beispiel wollen wir eine Liste von Stichwörtern für das Buch "TomSawyer.txt" erstellen. </p>
<p>
Wie üblich gehen wir Zeile für Zeile durch das Buch und benutzen den StringTokenizer</p>
<pre>
StringTokenizer st = new StringTokenizer(line, "[]\"',;:.!?()-/ \t\n\r\f");</pre>
<p>
um die Wörter aus eine Zeile zu extrahieren. Das ist eines der wenigen Male wo die <em>split()</em> Methode der String Klasse nicht funktionieren würde (es sei denn man beherrscht Reguläre Ausdrücke).</p>
<p>
Wir gehen also alle Zeilen und alle Wörter (Tokens) durch und speichern diese in einer HashMap</p>
<pre>
private Map<String, Integer> words = new HashMap<String, Integer>();</pre>
<p>
Diese HashMap befüllen wir dann mit der folgenden Methode:</p>
<pre>
private void addWordToHashMap(String word) {
if (word != null) {
if (words.containsKey(word)) {
int count = words.get(word);
words.put(word, ++count);
} else {
words.put(word, 1);
}
}
}</pre>
<p>
denn wir wollen zählen, wie häufig ein bestimmtes Wort vorkommt. Jetzt müssen wir nur noch die Map auf der Konsole ausgeben.</p>
<p>
Erweiterungen:</p>
<ul>
<li>
Wenn wir die Liste betrachten, stellen wir fest, dass die meisten Wörter mit weniger als acht Buchstaben eigentlich nichts in einem Index verloren haben. Also sollten wir sie gar nicht in die Liste mit aufnehmen.</li>
<li>
Man könnte noch Wörter die im Plural enden herausfiltern (das ist im Englischen relativ einfach)</li>
<li>
Man könnte Wörter mit nutzlosen Endungen (im Englischen "ly", "ial", "ive", "ous", "ed") herausfiltern.</li>
<li>
Man kann auch eine Liste von Stoppwörtern haben und diese dann herausfiltern [6].</li>
<li>
Sortieren: wenn wir anstelle von HashMap eine TreeMap verwenden, dann ist der Index auf einmal sortiert. Warum das so ist, lernen wir nächstes Semester.</li>
</ul>
<p>
.</p>
<h2>
<img alt="" src="images/languages.png" style="margin-left: 10px; margin-right: 10px; width: 200px; float: right; height: 200px;" />Languages</h2>
<p>
Was ist schon ein Wörterbuch, das von einer Sprache in eine andere übersetzt? Wir wollen ein Wörterbuch, das von einer Sprache in zehn übersetzt! Dazu muss man aber erst mal irgendwo die nötigen Daten finden. Glücklicherweise gibt es auf dem Website zu dem Buch "Introduction to Programming in Java" von Robert Sedgewick und Kevin Wayne [4] (übrigens ein Super-Buch) eine Datei die die Übersetzungen von über 800 englischen Wörter in zehn andere Sprachen enthält [5].</p>
<p>
Die Datei "Languages.csv" enthält diese Daten:</p>
<pre>
"cat","kat","kattekop","kissa",<span style="color:#0000ff;">"chat, matou, rosse"</span>,"Katze",...</pre>
<p>
Das erste Wort in jeder Zeile ist das englische Wort, gefolgt von der dänischen, der holländischen, usw., Übersetzung. Welche Sprache an welcher Stelle kommt steht in der ersten Zeile der Datei. Die Daten zu parsen wird nicht ganz einfach, wenn wir uns die französische Übersetzung für Katze ansehen, denn es gibt anscheinend mindestens drei Worte für Katze. Aber wenn wir nach Anführungsstrichen mit der <em>indexOf()</em> Methode suchen,</p>
<pre>
private ArrayList<String> parseLine(String line) {
ArrayList<String> translations = new ArrayList<String>();
while (true) {
int begin = line.indexOf("\"");
if (begin < 0)
break;
int end = line.indexOf("\"", begin + 1);
String s = line.substring(begin + 1, end);
line = line.substring(end + 1);
translations.add(s);
}
return translations;
}</pre>
<p>
dann ist das durchaus machbar. Die Methode <em>parseLine()</em> zerlegt also eine Zeile aus unserer Datei, und wandelt sie in eine ArrayList von Strings um. Diese ArrayList enthält also das englische Wort mit all seinen Übersetzungen. Deutsch ist an sechster Stelle, d.h. mit</p>
<pre>
ArrayList<String> translations = parseLine(line);
String german = translations.get(5);</pre>
<p>
erhalten wir die deutsche Übersetzung des Wortes. So parsen ist erledigt.</p>
<p>
Ähnlich wie bei unserem einfach Dictionary Projekt, wollen wir ja nach Wörtern suchen, und dafür verwenden wir die HashMap: </p>
<pre>
private Map<String, List<String>> dictionary;</pre>
<p>
Allerdings speichern wir jetzt eben nicht nur ein Wort pro englischem, sondern zehn, und deswegen steht da auch <em>List<String></em>. Die Liste zu befüllen ist ganz einfach:</p>
<pre>
ArrayList<String> translations = parseLine(line);
dictionary.put(translations.get(0), translations);</pre>
<p>
Das Übersetzen ist jetzt ganz einfach. Wir müssen nur wissen welche Sprache gewünscht ist (also z.B. 5 für Deutsch) und welches Wort übersetzt werden soll:</p>
<pre>
private String translate(String english, int lang) {
List<String> words = dictionary.get(english);
if (words != null) {
return words.get(lang);
}
return null;
}</pre>
<p>
Das <em>if</em> ist nötig um zu verhindern, dass unser Programm abstürzt falls wir nach einem Wort suchen, das nicht in unserer Datenbank ist. </p>
<p>
Erweiterungen:</p>
<ul>
<li>
wie könnte man von jeder der zehn Sprachen in jede andere der zehn Sprachen übersetzen?</li>
<li>
könnte man nicht nur Wörter, sondern ganze Sätze übersetzen?</li>
<li>
man könnte natürlich auch eine hübsche UI Anwendung dafür schreiben.</li>
</ul>
<p>
.</p>
<h2>