this
, konstruktor, túlterhelésPoint.java
Circle.java
this
pszeudováltozóPointCircleTest.java
)toString
PointCircleTest.java
)Arrays
, String
, StringBuilder
BufferedReader
, Scanner
, PrintWriter
)throws
static
, alapértelmezett konstruktor, fájlfeldolgozás, felsorolási típusObject
osztálysuper
pszeudováltozóequals
és hashCode
, Comparable
, Set
, Map
, absztrakt osztály, kivételekAz egyes témakörökhöz feladatai a feladatok.html fájlban találhatóak meg.
A feladatok.zip tartalmazza az anyag példakódjait, illetve a feladatokhoz kapcsolódó fájlokat.
Történet, jellemzők
Object
ősosztály leszármazottja)HelloWorld.java
:
class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
HelloWorld.java
fájlban van egy HelloWorld
osztály)HelloWorld.java
-ból készül egy HelloWorld.class
fájl (javac
paranccsal)main()
metódusa (belépési pontja), akkor végrehajtható (a java
paranccsal)Az osztályok package
-ekbe (csomagokba) szerveződnek
javac
paranccsal történik-d
argumentummal megadható, hogy milyen könyvtárba kerüljenek a class fájlokjavac HelloWorld.java
(abszolút vagy relatív elérési út)java
parancs-cp
argumentummal beállítható, hogy honnan vegye a class fájlokatjava HelloWorld
set PATH=%PATH%;c:\Program Files\Java\jdk1.8.0_144\bin\
gedit /etc/profile
, majd ott export PATH=$PATH:/usr/java/jdk1.8.0_144/bin/
byte
, short
, int
, long
, float
, double
, char
, boolean
boolean b = true;
Kétirányú elágazás
if ( i % 5 == 0 ) {
System.out.println( i+" 5-el osztható" );
}
else { System.out.println( i+" 5-el nem osztható" ); }
Többirányú elágazás (Csak primitív, felsorolási és String típusra működik.)
switch ( i % 4 ){
case 1: System.out.println( i+" 4-es maradéka 1" );
break;
case 2: System.out.println( i+" 4-es maradéka 2" );
break;
case 3: System.out.println( i+" 4-es maradéka 3" );
break;
default: System.out.println( i+" 4-el osztható" );
break;
}
Elöl tesztelő ciklus
int i = 0;
while ( i < 3 ){
System.out.println( i );
}
Hátul tesztelő ciklus
int i = 0;
do {
i++;
} while ( i < 5 );
Léptető ciklus
for (int i = 0; i < 5; i += 2) {
System.out.println( i );
}
Iteráló ciklus
List<String> args = new ArrayList<>();
for (String arg : args){ System.out.println( args ); }
continue
break
return
// Fordítás: javac HelloWorld.java
// Futtatás: java HelloWorld
public class HelloWorld {
public static void main(String[] args){
System.out.println("Hello World!");
// kiírás a sztenderd kimenetre
}
}
ComplexPolar.java
)String
-ként való ábrázolásának megadása.ComplexPolarTest.java
)Készítsük el a létrehozott osztály tesztprogramját.
Angle.java
).ComplexPolarTest.java
) lefordítani. A fordító ennek lefordítása közben ugyanis az összes többi hivatkozott osztályt is megpróbálja lefordítani.
javac ComplexPolarTest.java
java ComplexPolarTest
java.util
java.util.Vector
Másik csomag egy típusára hivatkozás teljes névvel (teljesen minősített név / fully qualified name)
// Tegyük fel, hogy a névtelen csomagban vagyunk.
class A {
void foo() {
java.util.Vector bar = new java.util.Vector();
}
}
Importálás + rövid névvel történő hivatkozás
import java.util.Vector;
class A {
void foo() { Vector bar = new Vector(); }
}
Minden típust importálhatunk egyszerre a java.util
csomagból: import java.util.*;
package
+ azonosító (teljesen minősített név))Könyvtárszerkezet kialakítása + forráskódok elhelyezése:
mkdir compol
mkdir compol\basics
mkdir compol\utils
mkdir main
move Angle.java compol\utils
move ComplexPolar.java compol\basics
move ComplexPolarTest.java main
Csomagazonosítók beillesztése a forrásszöveg elejére + importálás:
Angle.java
esetén: package compol.utils
;ComplexPolar.java
esetén: package compol.basics
;ComplexPolarTest.java
esetén: package main
, ill. import compol.utils.Angle;
és import compol.basics.ComplexPolar;
.public
kulcsszóval kell ellátni.javac main\ComplexPolarTest.java
java main.ComplexPolarTest
javac -g HelloWorld.java
-g
kapcsoló nélkül a szimbólumokat (pl. változóneveket) nem éri el a jdb
jdb
: parancssori Java Debuggerjdb
eszköz legfontosabb utasításai (l. help
utasítással):>jdb
Initializing jdb ...
jdb> help
run [class [args]] -- start execution of application's main class
print <expr> -- print value of expression
eval <expr> -- evaluate expression (same as print)
set <lvalue> = <expr> -- assign new value to field/variable/array element
locals -- print all local variables in current stack frame
stop at <class id>:<line> -- set a breakpoint at a line
step -- execute current line
cont -- continue execution from breakpoint
exit (or quit) -- exit debugger
this
, konstruktor, túlterhelésstatic
módosító: osztályszintű (adattag / metódus)Point.java
Készítsük el a Point
osztályt (Point.java
). - Tulajdonságok: x és y koordináta (mindkettő double típusú) - Művelet: eltolás: dx és dy értékekkel eltolja a pontot (mindkettő double típusú)
// megoldás:
class Point {
double x;
double y;
void move(double dx, double dy) {
x += dx;
y += dy;
}
}
Circle.java
Készítsük el a kört reprezentáló Circle
osztályt (Circle.java
). - Tulajdonságok: középpont (Point
) és sugár (double
) - Műveletek: eltolás és nagyítás - eltolás: dx és dy értékekkel eltolja a középpontot (mindkettő double
típusú) - nagyítás: egy adott számmal szorozza a sugarat (factor
double
típusú)
// megoldás:
class Circle {
Point center;
double radius;
void move(double dx, double dy) {
center.move(dx, dy);
}
void enlarge(double factor) {
radius *= factor;
}
}
Motiváció: - az objektum állapotát kívülről ne tudjuk tetszőleges módon megváltoztatni - az osztályokban a metódusok viselkedése a felhasználó számára mindig lényegében egy fekete doboz
Minden adattagra és minden metódusra pontosan egy láthatósági (hozzáférési) kategória vonatkozhat, ezért az alább megadott módosítószavak közül pontosan egyet lehet használni minden egyes tag és metódus esetében. A láthatósági kategóriák (módosítószavak) a következők:
public
public static void main(String[] args)
private
protected
public
⊇protected
⊇félnyilvános⊇private
Módosító |
osztály |
csomag |
leszármazott |
mindenki |
public |
igen |
igen |
igen |
igen |
protected |
igen |
igen |
igen |
nem |
nincs (package-private) |
igen |
igen |
nem |
nem |
private |
igen |
nem |
nem |
nem |
private
–tá tétele:
immutable
, mint pl. a String
osztály objektumai)immutable
)this
pszeudováltozóthis
névvel hivatkozhatunk az aktuális objektumra.static
metódusokban a this
nem használható.this.xxx
–hez általában nem kell a minősítés, néha azonban szükség lehet rá (pl. ha a metódus paramétere ugyanolyan nevű, mint az adattag)this
paraméterként is átadható.Készítsünk lekérdező és beállító műveleteket (metódusokat), amelyekkel le tudjuk kérdezni / be tudjuk állítani a pontok és a körök attribútumait:
x
és y
koordinátáját, valamintA metódusok legyenek publikusak.
(Az adattagok nem publikusak.)
Használjuk a this
pszeudováltozót.
PointCircleTest.java
)Készítsünk tesztprogramot a pontok és körök osztályához.
Point
és egy Circle
objektumot.new
mellett).void
-ot. static
vagy a final
, nem).return
–t használhatjuk.this
-en keresztül érjük el).this
névvel: az effajta konstruktormeghívás csak az első utasítás lehet. new Osztálynév(...paraméterek...)
Pl. mintha a Point
ba bekerülne egy ilyen:
public Point(){}
A Point
és Circle
osztályokhoz készítsünk konstruktorokat. A Point
osztályhoz csak egyet, amelynek a koordinátákat lehet átadni. A Circle
osztályhoz hármat:
Point
objektumot,toString
Készítsünk a Point
osztályba egy szöveges visszatérési értékű, paraméter nélküli, toString
nevű publikus metódust, amely visszaadja egy pont szöveges reprezentációját.
típus[] tömb;
típus
lehet primitív típus vagy osztály, de akár tömb istömb = new típus[méret];
tömb = $\{elem_1, \ elem_2, \ ..., \ elem_n\
;$}tömb.length
i
. elem elérése: tömb[i]
for( int i = 0; i < tömb.length; ++i ) \{
ahol i
sorban felveszi a tömb
indexeitfor( típus t : tömb ) \{
ahol t
sorban felveszi a tömb
elemeitPointCircleTest.java
)centerOfMass
nevű statikus metódust a PointCircleTest
osztályba. A metódus paraméterül egy pontokból álló tömböt kap. Feladata pedig, hogy kiszámítsa a kapott pontok tömegközéppontját (ami szintén egy pont), majd az eredményt visszaadja.centerOfMassTest
nevű statikus metódust a PointCircleTest
osztályba. A metódus hozzon létre két pontokból álló tömböt és (az előző metódus segítségével) mindkettőnek számítsa ki a középpontját, majd az eredményt írja ki.PointCircleTest
osztály main
metódusából hívjuk meg a centerOfMassTest
metódust.Arrays
, String
, StringBuilder
t[i] = t + i * sizeof(T)
, ahol a t
a tömb, az [i]
az indexelő operátor, amelyben i
az elérendő elem indexe, a sizeof()
operátor egy típus értékeinek méretét adja meg byte-ban, a T
pedig a tömb elemeinek típusaArrayIndexOutOfBoundsException
kivételT
típushoz hozzárendelhető egy T[]
típus, amely a T
elemekből képzett tömböt jelentiint[] intArray;
, char[] charArray;
, String[] stringArray;
, vagy int intArray[];
, char charArray[];
, String stringArray[];
int[] t = new int[10];
, int t[] = new int[10];
(a hossz megadása a tömb létrehozásakor, a hosszt megváltoztatni nem lehet)NullPointerException
kivétel, pl.: int[] s; int x = s[0];
Hibás!boolean[] barr1 = { true, false };
boolean[] barr2 = new boolean[] { true, false }
;boolean[] barr3 = new boolean[2];
barr3[0] = true; barr3[1] = false;
int[][] mx;
int[][] mx = new int[5][];
int mdt[][];
mdt = new int[2][];
mdt[0] = new int[2];
mdt[0][0] = 7;
mdt[0][1] = 2;
mdt[1] = new int[3];
mdt[1][0] = 2;
mdt[1][1] = 4;
mdt[1][3] = 0;
java.util.Arrays
osztálytoString()
), bináris keresésnek (binarySearch()
), vagy a tömb feltöltésének (fill()
) műveletét)equals
, nem pedig az ==
operátorralfor (int item : numbers) { ... }
több referencián keresztül hivatkozunk ugyanarra az objektumra
Rectangle box1 = new Rectangle (0, 0, 100, 200);
Rectangle box2 = box1;
tömbök esetében problémát okozhat, pl. tömbok megfordításánál
void reverse( int[] src, int[] dst ){
for( int i=0, j=src.length-1;
i<src.length; ++i, --j ){
dst[j] = src[i];
}
}
int[] t = {1,2,3,4,5};
int[] t = new int[5]{1,2,3,4,5};
reverse(t,t)
void reverse( int[] src, int[] dst ){
assert src != null;
assert dst != null;
assert src.length == dst.length;
assert src != dst;
// ezt is kossuk ki, hogy jol mukodjon
for( int i=0, j=src.length-1;
i<src.length; ++i, --j ){
dst[j] = src[i];
}
}
public class Point {
private final int x,y;
public Point( int x, int y ){
this.x = x;
this.y = y;
}
public int getX(){ return x; }
public int getY(){ return y; }
}
public class Point {
private final int[] coords;
public Point( int x, int y ){
coords = new int[]{x, y};
}
public int getX(){ return coords[0]; }
public int getY(){ return coords[1]; }
public int[] coords(){ return coords; }
}
Point p = new Point(1,1);
int[] c = p.coords();
coords()
metódus engedi kiszökni a Point
belső állapotát
private
)public class Point {
private final int[] coords;
public Point( int x, int y ){
coords = new int[]{x, y};
}
public int getX(){ return coords[0]; }
public int getY(){ return coords[1]; }
public int[] coords()
{ return new int[]{coords[0],coords[1]}; }
// masolatot adunk vissza
}
String
osztályString s = "Szia!";
, String s = new String("Szia!");
s = "Szia?";
, s = new String("Szia?");
length
, charAt
, compareTo
, concat
, endsWith
, replace
, substring
, trim
, valueOf
, indexOf
, equalsIgnoreCase
, toLowerCase
, …StringBuilder
osztályappend
, insert
, reverse
, setCharAt
, setLength
, …StringBuffer
osztály is, a StringBuilder
hatékonyabbBufferedReader
, Scanner
, PrintWriter
)main
metódusig, ahol szintén nem kerül lekezelésre a kivétel, akkor a futás megszakad.példa: repeater
(Main.java
, Repeater.java
)
$ javac Main.java
$ java Main 10 hello
Exception in thread "main" java.lang.NullPointerException
at Repeater.println(Repeater.java:6)
at Main.print(Main.java:13)
at Main.main(Main.java:5)
repeater
objektum text
mezőjét null
értékűnek hagytuk, az objektumszintű trim
metódus hívása NullPointerException
t váltott ki, hiszen a null
referenciához nem tartozik objektum, nem tudjuk min meghívni a metódust.main
metódusig jutott, onnét pedig továbbszivárogva leállította a program futását.Exception in thread "main" java.lang.NullPointerException
at Repeater.println(Repeater.java:6)
at Main.print(Main.java:13)
at Main.main(Main.java:5)
try {
// ...
} catch (ExceptionType1 e) {
// ...
} catch (ExceptionType2 e) {
// ...
} /*
...
*/ catch (ExceptionTypeN e) {
// ...
} finally {
// ...
}
try
blokkba írjuk a védendő kódot, azt, amelyik kiválthatja a kivételt.catch
ág követi, melyek kódja abban az esetben fut le, ha a megadott kivétel kiváltódik a try
blokkban (vagy egy onnan meghívott metódusban, ahonnét továbbadásra kerül).finally
blokk következik, ezt a Java lefuttatja, utolsóként. Tehát akkor is, ha
try
blokk lefutott, és nem keletkezett kivétel;try
blokkban keletkezett egy kivétel, és az valamelyik catch
lekezelte;try
blokkban keletkezett egy kivétel, de azt nem tudtuk lekezelni, és továbbadjuk.public class Main {
public static void main(String[] args) {
try {
System.out.println("Hello, " + args[0] + "!");
} catch (ArrayIndexOutOfBoundsException e) {
System.err.println("Not enough command line arguments.");
// standard error kimenetre írunk
} finally {
System.out.println("bye");
}
}
}
$ javac Main.java
$ java Main
Not enough command line arguments.
bye
$ java Main World
Hello, World!
bye
java.io.File
objektumra, mely egy (nem feltétlenül létező) fájlt (vagy könyvtárat) reprezentál a fájlrendszeren. Olyan, mint egy hivatkozás vagy link az adott fájlra, nem ellenőrzi, hogy a cél megvan-e. (Ne felejtsünk el importálni!)String filepath = args[0]; // abszolút vagy relatív út
File file = new File(filepath);
java.io.FileReader
objektumot, melynek konstruktora paraméterül kapja a File
objektumunkat, és megnyitja a fájlt szerkesztésre.try
kulcsszó után gömbölyű zárójelek között. Így a try
blokk elhagyásával az erőforrást (fájlt) a virtuális gép automatikusan elengedi.try ( FileReader reader = new FileReader(file) ) {
// itt használhatjuk a reader objektumot
}
FileReader
csak alacsony szintű funkcionalitást biztosít, ezért ezt még becsomagoljuk egy java.io.BufferedReader
objektumba, mellyel már tudunk majd soronként beolvasni.try (FileReader reader = new FileReader(file);
BufferedReader br = new BufferedReader(reader) ){
// ...
}
try (BufferedReader br =
new BufferedReader(new FileReader(file)) ){
// ...
}
java.io.FileNotFoundException
és a nála általánosabb java.io.IOException
.try (BufferedReader br =
new BufferedReader(new FileReader(file)) ){
// ...
} catch (FileNotFoundException e) {
System.err.println("File does not exist.");
} catch (IOException e) {
System.err.println("An IO error occurred.");
}
IOException
egy általánosabb kivétel, valójában elég lenne csak azt lekezelni. Ekkor FileNotFoundException
esetén is az IOException
t kezelő catch
ág futna le.BufferedReader
paraméter nélküli metódusával tudjuk megtenni.null
t ad vissza.try ( BufferedReader br =
new BufferedReader(new FileReader(file)) ) {
String line;
for (line = br.readLine();
line != null;
line = br.readLine()) {
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.err.println("File does not exist.");
} catch (IOException e) {
System.err.println("An IO error occurred.");
}
java
util.Scanner osztály használata.FileReader
létrehozására, és az IOException
t sem kell tudnunk lekezelni, csak a FileNotFoundException
t. ,,Cserébe’’ viszont minden sor beolvasása előtt ellenőriznünk kell, hogy van-e még további sor.try ( Scanner sc = new Scanner(file) ) {
while (sc.hasNextLine()) {
String line = sc.nextLine();
System.out.println(line);
}
} catch (FileNotFoundException e) {
System.err.println("File does not exist.");
}
nextLine
az eddigi readLine
helyett)PrintWriter
.
java.io.File
objektumot kell átadnunk.FileNotFoundException
kivétel váltódik ki.print
és println
metódusokkal tudunk.flush
metódussal írathatjuk ki a sorokat.String[] linesToWrite = // ...
try ( PrintWriter pw = new PrintWriter(file) ) {
for (String line : linesToWrite) {
pw.println(line);
}
} catch (FileNotFoundException e) {
System.err.println("File cannot be opened.");
}
throws
FileCopy.java
throws
kulcsszó után.
FileCopy2.java
static
, alapértelmezett konstruktor, fájlfeldolgozás, felsorolási típusArrayList
: void add( elem )
, void add( elem, index )
System.out.println(int);
System.out.println(double);
System.out.println(String);
public class Point {
private int x, y;
public Point( int x, int y ){
this.x = x;
this.y = y;
}
public Point(){
x = 0; y = 0; // amugy is 0
}
}
public class Point {
private int x, y;
public Point( int x, int y ){
this.x = x;
this.y = y;
}
public Point(){
this(0,0);
}
}
0
, \u0000
, false
null
public class Circle {
private int x, y;
private double radius;
public static void main(String[] args){
Circle c = new Circle(); // x:0; y:0, radius: 0.0
}
}
public class Circle {
private int x = 0, y = 0;
private double radius = 1.0; // inicializáló kifejezés
public static void main(String[] args){
Circle c = new Circle(); // x:0; y:0, radius: 1.0
}
}
public class Circle {
private int x, y;
private double radius;
public Circle(int x, int y, int r){/*...*/}
public static void main(String[] args){
//compile error:
//constructor Circle in class Circle cannot be applied to given types;
Circle c = new Circle();
}
}
public class Circle {
public static int counter = 0;
private int x, y;
private double radius;
public Circle(){ counter++; /*...*/}
public static void main(String[] args){
System.out.println(Circle.counter);
Circle c1 = new Circle();
System.out.println(Circle.counter);
Circle c2= new Circle();
System.out.println(Circle.counter);
}
}
Kimenet:
0
1
2
public class Circle {
public static int counter = 0;
private int x, y; private double radius;
private static void incrementCounter(){counter++;}
public Circle(String sx, String sy, String sradius){
incrementCounter();
x = Integer.parseInt(sx);
}
public static void main(String[] args){
incrementCounter();
System.out.println(Circle.counter);
Circle c1 = new Circle("1","2","3");
System.out.println(Circle.counter);
}
}
Kimenet:
1
2
public class Main{
public static void main(String[] args){
Circle.incrementCounter(); // -> OK
System.out.println(Circle.counter); //->OK
Circle c1 = new Circle("1","2","3");
c1.incrementCounter(); // ->NEM OK !!
System.out.println(c1.counter); // ->NEM OK !!
}
}
class Circle {
public static int counter = 0;
/*...*/
public static void incrementCounter(){counter++;}
}
valueOf(String)
- statikus metódus, a paraméterként kapott névvel egyező enumobjektummal tér vissza (ha nincs: null
)public enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}
//...
Day d1 = Day.MONDAY;
Day d2 = Day.valueOf("MONDAY");
public class EnumTest {
Day day;
public EnumTest(Day day) { this.day = day; }
public void tellItLikeItIs() {
switch (day) {
case MONDAY:
System.out.println("Mondays are bad.");
break;
case FRIDAY:
System.out.println("Fridays are better.");
break;
case SATURDAY: case SUNDAY:
System.out.println("Weekends are best.");
break;
default:
System.out.println("Midweek days are so-so.");
break;
}
}
//....
//....
public static void main(String[] args) {
EnumTest firstDay = new EnumTest(Day.MONDAY);
firstDay.tellItLikeItIs();
EnumTest thirdDay = new EnumTest(Day.WEDNESDAY);
thirdDay.tellItLikeItIs();
EnumTest fifthDay = new EnumTest(Day.FRIDAY);
fifthDay.tellItLikeItIs();
EnumTest sixthDay = new EnumTest(Day.SATURDAY);
sixthDay.tellItLikeItIs();
EnumTest seventhDay = new EnumTest(Day.SUNDAY);
seventhDay.tellItLikeItIs();
}
}
values()
statikus metódus, mely visszaadja az összes enumértéket, vagy ordinal()
amely az adott enum érték az osztálydefinicióban való helyzetét adja megpublic enum Planet {
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
//...
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius)
{ this.mass = mass; this.radius = radius;}
public static final double G = 6.67300E-11;
double surfaceGravity()
{ return G * mass / (radius * radius); }
//...
main
metódus a values()
és ordinal()
használatával //..
double surfaceWeight(double otherMass) {
return otherMass * surfaceGravity();
}
public static void main(String[] args) {
if (args.length != 1) {
System.exit(-1);
}
double earthWeight = Double.parseDouble(args[0]);
double mass = earthWeight/EARTH.surfaceGravity();
for (Planet p : Planet.values())
System.out.printf("Your weight on %s
(the %d th planet) is %f%n",
p, p.ordinal()+1,p.surfaceWeight(mass));
}
Specifikáció (követelmények halmaza) A követelményeket teljesítő (“helyes”) program \
Futtatás során ellenőrizzük a követelmények teljesülését A helyesség (automatikus) levezetése a (formális) specifikációból Helyes program levezetése a specifikációból …
Mit tesztelünk?
Mekkora egységet tesztelünk?
Legkisebb önállóan működő alapegységeit (osztály, metódus) Összetartozó egységek együttműködését (csomag). Rendszer, alkalmazás egészét fejlesztői oldalról. Nem-funkcionális követelmények, használati esetek. Üzembehelyezett rendszer, alkalmazás egészét felhasználói oldalról.
A rendszer legkisebb önállóan működő egységeit teszteli (osztály, metódus).
Célok:
Elvárások:
JUnit
: egységteszt-keretrendszer JAVA-hoz.
SimpleTest.java
)Windows rendszeren :
helyett ;
választja el az útvonalakat
javac -cp .:junit-4.12.jar:hamcrest-core-1.3.jar SimpleTest.java
java -cp .:junit-4.12.jar:hamcrest-core-1.3.jar org.junit.runner.JUnitCore SimpleTest
Előkészítés-Kivitelés-Ellenőrzés (Arrange-Act-Assert, AAA)
@Test
public void testStringIsReversed() {
// arrange
String input="Hello world";
// act
String result = MyLib.reverse(input);
// assert
assertEquals("dlrow olleH", result);
}
// ha nem megy az olvashatóság rovására, szabad rövidíteni
@Test
public void testStringIsReversed() {
assertEquals("dlrow olleH", MyLib.reverse("Hello world"));
}
F
ast (gyors)I
solated (egymástól és külvilágtól elkülönölt, egy sikertelen tesztnek pontosan egy oka lehet)R
epeatable (megismételhető, nincsenek mellékhatások és nem-determinisztikus futás)S
elf-verifying (önellenőrző, minden teszt elbukhat, minden bukásnak pontosan egy oka lehet)T
imely (megfelelő időben rendelkezésre álló)0
, 1
, k
iterációra?extends
, pl. class Circle extends Shape
, is--a
reláció)Object
minden osztály közös őse (univerzális ősosztály) ⇒ az öröklődési gráf egy faextends
–et, akkor implicit extends
vanObject
osztályjava.lang
–ban van definiálvaObject
osztály olyan metódusok definícióit tartalmazza, amelyekkel minden objektumnak rendelkeznie kell, pl.
String toString()
boolean equals(Object obj)
és int hashCode()
Minden adattagra és minden metódusra pontosan egy láthatósági (hozzáférési) kategória vonatkozhat, ezért az alább megadott módosítószavak közül pontosan egyet lehet használni minden egyes tag és metódus esetében. A láthatósági kategóriák (módosítószavak) a következők:
public
public static void main(String[] args)
private
protected
public
⊇protected
⊇félnyilvános⊇private
Módosító |
osztály |
csomag |
leszármazott |
mindenki |
public |
igen |
igen |
igen |
igen |
protected |
igen |
igen |
igen |
nem |
nincs (package-private) |
igen |
igen |
nem |
nem |
private |
igen |
nem |
nem |
nem |
super
pszeudováltozósuper
névvel (a szülőosztálybeli konstruktor a legelső sorban)super();
hívás történiksuper
megelőzi az osztálydefinícióban szereplő példányváltozó inicializálásokatprotected
konstruktort new
–val csak a csomagon belül hívhatunk meg, super
–ként pedig csak a csomagon kívülsuper.xxx()
private
⊆ félnyilvános ⊆ protected
⊆ public
)A példába tartozó fájlok:
Point.java
Circle.java
Rectangle.java
Shape.java
ShapeTest.java
class Person {
private String name; /* String "is part of" Person */
private Integer age; /* Integer "is part of" Person */
public static Person make(String name, Integer age) {
return ((age > 0 && !name.isEmpty()) ?
new Person(name, age) : null);
}
private Person(String name, Integer age) {
this.name = new String(name);
this.age = new Integer(age);
}
...
...
public String getName() {
return new String(name);
}
public Integer getAge() {
return new Integer(age);
}
public String toString() {
return String.format
("Person { name = %s, age = %d }", name, age);
}
}
class Person {
private String name;
/* String "is part of" Person */
private Integer age;
/* Integer "is part of" Person */
private ArrayList<Person> friends;
/* Person "has" Persons */
public static Person make(String name, Integer age) {
return ((age > 0 && !name.isEmpty()) ?
new Person(name, age) : null);
}
private Person(String name, Integer age) {
this.name = new String(name);
this.age = new Integer(age);
this.friends = new ArrayList<Person>();
}
...
...
public String getName() {
return new String(name);
}
public Integer getAge() {
return new Integer(age);
}
public void addFriend(Person friend) {
friends.add(friend);
}
public String toString() {
return String.format
("Person { name = %s, age = %d, friends = %s }",
name, age, friends.toString());
}
}
Comparable
, Runnable
(Futtatható)public final static
adattagokpélda
interface Bicycle {
void changeCadence(int newValue);
void changeGear(int newValue);
void speedUp(int increment);
void applyBrakes(int decrement);
}
példa: az interfészt megvalósító osztály
class BMXBicycle implements Bicycle {
int cadence = 0;
int speed = 0;
int gear = 1;
void changeCadence(int newValue) {
cadence = newValue;
}
void changeGear(int newValue) {
gear = newValue;
}
void speedUp(int increment) {
speed = speed + increment;
}
void applyBrakes(int decrement) {
speed = speed - decrement;
}
void printStates() {
System.out.println("cadence:" +
cadence + " speed:" +
speed + " gear:" + gear);
}
}
implements
kulcsszó, amely után több interfész felsorolható, amelyeket az osztály megvalósítpublic
kell, hogy legyenextends
után azoknak az interfészeknek a listája, amelyektől az adott interfész örökölI
egy interfész, J
az I
egyik őse, az A
osztály megvalósítja I
–t, B
leszármazottja az A
–nak, akkor B
megvalósítja J
–t.I v = new A();
J w = new B();
egy interfész típusú formális paraméter: megadható egy olyan aktuális paraméter, amely egy objektum, és amely osztálya (közvetlenül vagy közvetve) megvalósítja az interfészt
void m(I p){...} m(new A());
void n(J p){...} n(new B());
Típussal paraméterezhető interfészek
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
public class OrderedPair<K, V> implements Pair<K, V> {
private K key;
private V value;
public OrderedPair(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() { return key; }
public V getValue() { return value; }
}
Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String> p2 = new OrderedPair<String, String>("hello", "world");
equals
és hashCode
, Comparable
, Set
, Map
, absztrakt osztály, kivételek==
operátorral. Azt nézzük, hogy két referencia ugyanarra a memóriaterületre mutat-e.equals
metódussal. Azt nézzük, hogy a két objektum a felhasználásuk szempontjából egyformán viselkedik-e, például mikor két String ugyanazt a szöveget reprezentálja.Comparable
interfész megvalósításával. Ilyenkor lényegében a kisebb és nagyobb relációkat értelmezzük azokra a típusokra, ahol ennek van értelme.equals
) metódust az Object
osztály definiálja, így minden típus rendelkezik vele. (Emlékeztető: minden Java osztály őse az Object
).Szignatúrája:
public boolean equals(Object obj)
Object
-ben definiált equals
úgy viselkedik, mint az ==
operátor, azaz azonosságot vizsgál (egy objektumot csak önmagával tekint egyenlőnek). Ha ezt meg szeretnénk változtatni, akkor felül kell definiálnunk ezt a metódust.Figyelem! Az equals
metódus paramétere Object
, azaz két tetszőleges objektumot össze tud hasonlítani (bármilyen Java objektumon meghívhatjuk, és bármilyen Java objektumot kaphat paraméterül](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-). Ezt a felüldefiniálásakor sem szűkíthetjük majd le.
hashCode
metódust szintén az Object
osztály definiálja.Szignatúrája:
public int hashCode()
equals
szerint egyenlő, akkor a hashCode
függvényük garantáltan ugyanazt az egész számot adja vissza. (Megfordítva: ha két objektum különböző hashCode
értéket ad, akkor garantáltan nem egyenlőek, vagyis nincs szükség az összehasonlításukra.)Ez azt jelenti számunkra, hogy amikor az equals
metódust felüldefiniáljuk, a hashCode
-ot is felül kell definiálni.
equals
metódust, az alábbi 5 tulajdonság teljesülését kell biztosítanunk az equals
dokumentációja alapján.
x
és y
nem null
, és x == y
, akkor x.equals(y)
is igazat kell, hogy adjon.x
és y
nem null
, és x.equals(y)
, akkor y.equals(x)
.x
, y
és z
nem null
, valamint x.equals(y)
és y.equals(z)
, akkor x.equals(z)
.x
nem null
, akkor x.equals(null)
hamisat kell, hogy adjon.equals
minél gyorsabban lefusson, és ne szálljon el kivétellel.equals
-t.this
-szel, mert ilyenkor gyors választ tudunk adni a kérdésre. Következő lépésben pedig megvizsgáljuk, hogy nem null
-t kaptunk-e, mikor is szintén gyors választ tudunk adni.Vegyük észre, hogy ez a két vizsgálat még teljesen független attól, hogy mely osztályhoz készül az equals
.
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
...
instanceof
operátort használjuk, mely egy objektumot és egy típust ,,hasonlít össze’’. Ha az objektum dinamikus típusa azonos vagy leszármazottja a megadott típusnak, akkor igazat ad vissza, különben hamisat.null
értékekre persze az adattagoknál is figyelnünk kell.Person
osztály equals
metódusát írjuk felül, mely csak a személy nevét és születési évét tárolja.public class Person {
private String name;
private int birthYear;
public Person(String name, int birthYear) {
this.name = name;
this.birthYear = birthYear;
}
public String getName() {
return name;
}
public int getBirthYear() {
return birthYear;
}
... // equals és hashCode
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if ( !(obj instanceof Person) ) {
return false;
}
Person other = (Person) obj;
...
null instanceof T
bármilyen T
típusra hamisat ad, ezért valójában a második if
elhagyható; anélkül is hamisat kapunk a harmadik miatt null
esetén. // Referenciatípusnál két null-t egyenlőnek kell venni,
// null-t és nem null-t különbözőnek
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
// Primitívnél egyszerű a dolgunk: nem lehet null
if (birthYear != other.birthYear) {
return false;
}
return true; // Minden oké, visszatérhetünk igazzal
}
Egy rövidebb változat, egyben.
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if ( !(obj instanceof Person) ) {
return false;
}
Person other = (Person) obj;
return birthYear == other.birthYear &&
( name == null ? other.name == null :
name.equals(other.name) );
}
hashCode
esetén a legfontosabb követelmény, hogy két olyan objektum, melyek az equals
szerint egyenlőek, mindig ugyanazt az egész értéket adják vissza.Például:
@Override
public int hashCode() {
int hash = (name == null ? 0 : name.hashCode());
return hash * 1009 + birthYear; // Az 1009 egy prímszám
}
hashCode
megírásakor gyakran használunk prímszorzókat, mert azok többnyire hatékony eredményt adnak. De lehetne szorozni, hatványozni, bármit, ami változatos értékeket ad.Egy egyszerű megoldás sok adattag esetén is a java.util.Objects
osztály hash
metódusa. Ennek a metódusnak bármennyi paramétert adhatunk, és ugyanazokat a paramétereket ugyanolyan sorrendben megkapva mindig ugyanazt a hash értéket állítja elő.
@Override
public int hashCode() {
return java.util.Objects.hash(name, birthYear);
}
A Comparable
egy generikus interfész, a típusparaméterének azt a típust kell átadnunk, amelyik épp megvalósítja az interfészt (ezzel fogunk összehasonlítani).
public class Person implements Comparable<Person> {
...
A Comparable
interfész egyetlen metódusa a compareTo
, mely az összehasonlítást végzi. Paramétere a típusparaméternek megfelelő, tehát éppen az a típus, amelyiket összehasonlítjuk.
@Override
public int compareTo(Person other) {
...
}
this
objektum kisebb, negatívval térünk vissza.this
objektum nagyobb, pozitívval térünk vissza.null
(pontosabban a dokumentációja szerint null
paraméter esetén NullPointerException
-t dobhatunk).null
-ok, de a példánkban feltehetjük, hogy a személy neve sosem null
, mikor ezt a metódust meghívjuk.@Override
public int compareTo(Person other) {
int compare = this.name.compareTo(other.name);
if (compare != 0) {
return compare;
}
// Ha már itt eldőlt, hogy valamelyik objektum kisebb,
// térjünk vissza. Különben vizsgálódunk tovább.
if (this.birthYear < other.birthYear) {
compare = -1;
} else if (this.birthYear > other.birthYear) {
compare = 1;
}
return compare;
}
Kicsit egyszerűbben:
@Override
public int compareTo(Person other) {
int compare = this.name.compareTo(other.name);
if (compare != 0) {
return compare;
}
return this.birthYear - other.birthYear;
}
compareTo
metódust, nem tehetjük fel, hogy ezeket a konkrét értékeket használja, mert ez nem dokumentált viselkedés, bárki bármikor megváltoztathatja. (Nem használunk ki rejtett invariánsokat.)Ha meg akarjuk jegyezni, hogy mikor kell negatívat és mikor pozitívat visszaadni, gondoljunk arra, hogy x ? y
helyett x.compareTo(y) ? 0
-t kell írnunk.
x.compareTo(y) < 0 // x kisebb-e, mint y
x.compareTo(y) <= 0 // x kisebb vagy egyenlő-e, mint y
x.compareTo(y) == 0 // x egyenlő-e y-nal
x.compareTo(y) >= 0 // x nagyobb vagy egyenlő-e, mint y
x.compareTo(y](https://docs.oracle.com/javase/8/docs/api/java/lang/Comparable.html#compareTo-T-) > 0 // x nagyobb-e, mint y
java.util.Set
, azaz halmaz típus. Fő jellemzője, hogy egyenlő objektumokból csak egyet tartalmazhat.List
, a Set
is egy generikus interfész. Típusparamétere az elemei típusa, két leggyakoribb megvalósítása a java.util.HashSet
és a [java.util.TreeSet
). Előbbi hash alapú (fontos a helyes hashCode
metódus!), utóbbi pedig csak rendezhető típusokat tud kezelni, azaz meg kell valósítanunk a Comparable
interfészt (van más mód is rendezés megadására, de ez nem a mostani gyakorlat anyaga).Set<Person> set = new HashSet<Person>();
set.add(new Person("Aladar", 2000));
// Egy egyenlő objektum már van bent, nem csinál semmit
set.add(new Person("Aladar", 2000));
System.out.println(set.size()); // 1
// A törlés is equals alapján történik
set.remove(new Person("Aladar", 2000));
System.out.println(set.size()); // 0
java.util.Map
, más néven asszociatív tömb, mely kulcs-érték párokat tárol.Set
-hez hasonlóan a java.util.HashMap
és a [java.util.TreeMap
), melyekre hasonló szabályok is igazak.Map
-ben, hiszen indexek. Két egyenlő érték megengedett.Map<String, Person> map =
new HashMap<String, Person>();
Person aladar = new Person("Aladar", 2000);
Person balazs = new Person("Balazs", 1999);
map.put("a", aladar);
map.put("b", aladar); // Oké
System.out.println(map.size()); // 2
map.put("b", balazs); // Felülírjuk
System.out.println(aladar == map.get("a")); // true
System.out.println(aladar == map.get("b")); // false
System.out.println(balazs == map.get("b")); // true
System.out.println(map.get("c")); // null
// Nem létező indexre null-t ad vissza
abstract
kulcsszóval azonosítunk. Absztrakt metódusnak ezenfelül nincs törzse, a kapcsos zárójelek helyett egy pontosvesszőt teszünk.abstract class Rectangle {
private double a;
public Rectangle(double a) {
this.a = a;
}
public double getA() { return a; }
public abstract double getB();
public double getArea() {
return getA() * getB();
}
}
class GeneralRectangle extends Rectangle {
private double b;
public GeneralRectangle(double a, double b) {
super(a);
this.b = b;
}
@Override
public double getB() { return b; }
}
class Square extends Rectangle {
public Square(double a) {
super(a);
}
@Override
public double getB() { return getA(); }
}
Throwable
osztály áll, de ezzel nem foglalkozunk a gyakorlaton.Exception
osztály és a belőle származó [RuntimeException
) osztály az általunk használt kivételek ősei.Exception
vagy a RuntimeException
osztályból. Metódust, adattagot és konstruktort nem muszáj megadni.String
paraméteres konstruktorukat, ez egy hibaüzenetet jelenít meg a konzolon, ha a kivétel a főprogramot is eléri, és nem kerül lekezelésre.class UnnamedPersonException extends Exception {
}
class EmptyStackException extends RuntimeException {
EmptyStackException() {
super("The accessed stack is empty," +
"no element can be retrieved.");
}
}
A kivételek egymásból is származtathatók. A catch
ágak nem csak a megadott kivételeket kapják el, hanem azok leszármazottait is. Ugyanígy a throws
ágban elég a szülőosztályt megadni, és dobható a leszármazottja is.
try {
...
} catch (Exception e) {
// bármilyen kivételt lekezel. Ha tehetjük, nem használjuk
}
RuntimeException
-ből származó kivételek úgynevezett nem ellenőrzött kivételek. (A nem RuntimeException
-ből származó kivételeket pedig ellenőrzött kivételeknek nevezzük.)Abban speciálisak, hogy nem muszáj őket a throws
részben feltüntetni, ha nem kezeljük le a metódusban (de feltüntethetjük).
public T getNextStackElement() {
if (list.isEmpty()) {
throw new EmptyStackException(); // Oké
}
return list.get(0](https://docs.oracle.com/javase/8/docs/api/java/lang/RuntimeException.html);
}
RuntimeException
-ként definiálni, melyek a kódban szinte bárhol előfordulhatnak, illetve amelyekről gyakran tudhatjuk a kód írásakor, hogy biztosan nem váltódnak ki, mert valamilyen korábbi ellenőrzés ezt biztosítja számunkra.Jól ismert példák ilyen kivételekre: NullPointerException
, ArrayIndexOutOfBoundsException
, IllegalArgumentException
.
public class Box {
private Object object;
public void set(Object object)
{
this.object = object;
}
public Object get() {
return object;
}
}
Problémák:
Megoldás: fordítási idejű ellenőrzés - generikusok
public class Box<T> {
// a T a "Type" rövidítése
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
//használat:
Box<Integer> integerBox = new Box<>();
public class Util {
public static<K,V> boolean compare(Pair<K,V> p1 , Pair<K,V> p2) {
return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue());
}
}
public interface Comparable<T> {
public int compareTo(T o );
}
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray) {
if(e.compareTo(elem) > 0) {
++count;
}
}
return count;
}
<T extends B1 & B2 & B3>
, ekkor a paraméternek az összes paraméter altípusának kell lennie egyszerre.extends
interface Edge {
int getSource();
int getTarget();
}
abstract class Graph<E extends Edge> {...}
class SparseGraph<E extends Edge> extends Graph <E > {...}
private
-nak szánt adattagjaihozPerson
típusú objektumok valamely tulajdonságát vizsgálja, majd a teszten átment objektumokat kiírja.(<inputTípus1 input1>, ..., <inputTípus1 input1>)
a megvalósított interfész egyetlen metódusának paraméterei(Person p)
helyett p
)->
a nyíl tokenp -> p. getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
ha blokkot használunk szükség van a return
használatára
p -> {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
Comparator
Listák rendezése: java.util.Collections.sort
statikus metódussal:
Comparable<T>
interfészt megvalósító T
osztályokat tároló listák:
sort(List<T> list)
class Person implements Comparable < Person >{...}
//...
List < Person > l = new ArrayList<>();
//...
Collections.sort(l );
//..
Listákra általában:
sort(List<T> list, Comparator<? super T> c)
- ahol a Comparator
egy úgynevezett funkcionális interfész, azaz csak egy meghatározott metódus meglétét deklarálja:
int compare(T o1 , T o2 )
metódus megadása: névtelen osztállyal:
class Car {...}
//...
List<Car> l = new ArrayList<>();
//...
Comparator<Car> byName = new Comparator<Car>() {
@Override
public int compare(Car c1 , Car c2) {
return c1.getName().compareTo(c2.getName());
}
};
Collections.sort (l , byName );
//..
metódus megadása: lambda-kifejezéssel:
Collections.sort (l ,
(c1 , c2) -> c1.getName().compareTo(c2.getName())
);
mc.clone() != mc
mc.getClass() == mc.clone().getClass()
mc.clone().equals(mc)
clone()
metódus megvalósításaCloneable
interfészt implementáló osztály
clone()
implementálásátclone()
metódusát meg lehet hívniCloneNotSupportedException
: clone()
metódust írhatunk bármilyen osztályhoz, azonban ha az osztály amit klónozni akarunk nincs Cloneable
-nek definiálni az említett kivétel váltódik kiclone()
metódus az Object
osztályban van definiálva, de protected
-ként, vagyis a clone()
metódus csak olyan osztályokon hívható meg, ahol felül lett definiálvaMi történik mikor klónozunk?
//..
public static void main(String [] x) throws CloneNotSupportedException {
CloneTrial ct = new CloneTrial();
CloneTrial ct1 = ct.clone();
ct.xyz.x = 1;
System.out.println(ct1.xyz.x );
}
// OUTPUT : 1