9. Számok

A következő ábra bemutatja a Java platformon rendelkezésre álló szám osztályok hierarchiáját.

Number osztályok

Ezen kívül a platform tartalmazza a Boolean, Void és Character osztályokat, amelyek a szám osztályokkal együtt csomagoló (vagy burkoló) osztályoknak hívunk.
Talán meglepő, hogy miért nélkülözhetetlenek a csomagoló osztályok, de ezt majd látni fogjuk a későbbiekben.

9.1. A csomagoló osztályok néhány használata

  • Tudunk használni olyan tároló objektumokat, amelyekbe különböző típusú számok helyezhetők bele. Pl. az ArrayList osztály Object-eket tud tárolni, így akár az Object különböző leszármazott objektumait (vagyis csomagoló objektumokat) el tudunk helyezni benne.
  • Az előző ponthoz hasonlóan akár más típusú objektumokkal is tudunk együtt tárolni számokat.
  • Az osztályok definiálnak hasznos változókat, mint például MIN_VALUE vagy MAX_VALUE, ezek biztosítják az általános információt az adattípusról. Az osztályok definiálnak hasznos metódusokat, amelyek értéket konvertálnak más típussá, például String-gé, vagy egy másik szám típussá.

Továbbá a BigInteger és BigDecimal kiterjeszti a hagyományos adattípusokat, és tetszőleges pontosság alkalmazását engedi meg. A többi osztály a java.lang csomag része, a BigDecimal és a BigInteger a java.math csomagban található.

Következzen egy példa (NumberDemo), amely létrehoz két Float és egy Double objektumot, és utána használja a compareTo és az equalTo metódusokat összehasonlításra.

public class NumberDemo {
    public static void main(String args[]) {
        Float floatOne = new Float(14.78f - 13.78f);
        Float floatTwo = Float.valueOf("1.0");
        Double doubleOne = new Double(1.0);
        int difference = floatOne.compareTo(floatTwo);
        if (difference == 0) {
            System.out.println("floatOne is equal to floatTwo.");
        } else if (difference < 0) {
             System.out.println("floatOne is less than floatTwo.");
        } else if (difference > 0) {
             System.out.println("floatOne is greater than floatTwo.");
        }
        System.out.println("floatOne is " + ((floatOne.equals(doubleOne)) ? "equal" : "not equal") + " to doubleOne.");
    }
}

Ennek a programnak a kimenete kicsit meglepő lehet:

floatOne is equal to floatTwo.
floatOne is not equal to doubleOne.

A következő táblázat bemutatja azon példánymetódusokat, melyeket a Number osztály összes leszármazott osztálya tartalmaz, beleszámítva az összehasonlító és egyenlőség-vizsgáló metódusokat, melyek az előző példában szerepeltek.

  • byte byteValue() short shortValue() int intValue() long longValue() float floatValue() double doubleValue() Konvertálja a szám objektum értékét egy egyszerű adattípussá (short, long, float, doube)
  • int compareTo(Integer) int compareTo(Object) Összehasonlítja a szám objektumot a paraméterrel. A metódus nullánál kisebb, nulla, vagy nullánál nagyobb értékkel tér vissza attól függően, hogy az objektum értéke kisebb, egyenlő vagy nagyobb a hasonlított objektummal.
  • boolean equals(Object) Meghatározza, hogy a szám objektum egyenlő-e a paraméterrel.

Különböző típusú szám objektumok – a matematikai értéküktől függetlenül – sosem egyenlőek.

A szám csomagoló osztályok több hasznos publikus és statikus konstanst tartalmaznak. Ezekre úgy tudunk hivatkozni kifejezésekben, hogy az osztály nevét és a konstanst egy ponttal választjuk el, pl. Integer.MIN_VALUE.

A következő táblázat felsorol több hasznos konstanst a Float és Double osztályokból

  • Float.NaN Double.NaN Nem szám. Ez az alapértelmezett érték, ha egy kifejezés értéke matematikailag nem értelmezhető.
  • Float.NEGATIVE_INFINITY Double.NEGATIVE_INFINITY Negatív végtelen érték
  • Float.POSITIVE_INFINITY Double.POSITIVE_INFINITY Pozitív végtelen érték

9.2. Szövegből számmá konvertálás

Néha szükség van arra, hogy a Stringként rendelkezésre álló adatot számként kell értelmeznünk, például a felhasználó által bevitt adatot esetén.

A numerikus csomagoló osztályok mindegyike biztosítja a valueOf metódust, amely String-et konvertál adott objektummá.

A következő ValueOfDemo példaprogram kap két String-et parancssorból, konvertálja azokat szám objektummá, majd elvégez két aritmetikai műveletet az értékekkel.

public class ValueOfDemo {
    public static void main(String[] args) {
        if (args.length == 2) {
            float a = Float.valueOf(args[0]).floatValue();
            float b = Float.valueOf(args[1]).floatValue();
            System.out.println("a + b = " + (a + b) );
            System.out.println("a - b = " + (a - b) );
            System.out.println("a * b = " + (a * b) );
            System.out.println("a / b = " + (a / b) );
            System.out.println("a % b = " + (a % b) );
        } else {
            System.out.println("This program requires" +
                "two command-line arguments.");
        }
    }
}

A következőkben a program kimenetét láthatjuk, amely a 4.5 és a 87.2-t használta parancssori paraméternek:

a + b = 91.7
a - b = -82.7
a * b = 392.4
a / b = 0.0516055
a % b = 4.5

9.3. Számból szöveggé konvertálás

Néha szükség van a számból szövegbe konvertálásra, mert szükség van a String formátumban lévő értékkel való műveletekre.

Az összes osztály örökli a toString metódust az Object osztálytól. A csomagoló osztályok felülírják ezt a metódust, hogy biztosítsák az ésszerű konverziót.

A következő ToStringDemo program használja a toString metódust, és számot konvertál szöveggé. Utána a program néhány String metódust mutat be, amivel megszámolja a szám tizedes pont előtti és utáni szemjegyeinek számát.

public class ToStringDemo {
    public static void main(String[] args) {
        double d = 858.48;
        String s = Double.toString(d);
        int dot = s.indexOf('.');
        System.out.println(s.substring(0, dot).length() + " digits before decimal point.");
        System.out.println(s.substring(dot+1).length() + " digits after decimal point.");
    }
}

A program kimenete:

3 digits before decimal point.
2 digits after decimal point.

9.4. Számok formázott konvertálása

A szám csomagoló osztályok ugyan rendelkeznek a toString metódussal, de az így létrejövő szöveges forma nem minden esetben megfelelő. Például pénzösszegek valós számként való tárolásánál általában elegendő a két tizedes jegy használata. Formázott konverzióhoz használható a NumberFormat és leszármazottja, a DecimalFormat osztályok a java.text csomagból.

A következő kódrészlet Double objektumot formáz. A getNumberInstance metódus egy NumberFormat objektumot ad vissza. Ennek format metódusa egy Double értéket vár, és formázott String-et állít elő:

Double amount = new Double(345987.246);
NumberFormat numberFormatter;
String amountOut;
numberFormatter = NumberFormat.getNumberInstance();
amountOut = numberFormatter.format(amount);
System.out.println(amountOut);

A kód utolsó sora a következőt írja ki:

345,987.246

Megjegyzés: Az eredmény függ a helyi beállításoktól.

Pénznem formátum

Gazdasági alkalmazás esetén pénzösszegek kezelésére is szükség van. Ennek alkalmazását mutatja a következő kódrészlet:

Double currency = new Double(9876543.21);
NumberFormat currencyFormatter;
String currencyOut;
currencyFormatter = NumberFormat.getCurrencyInstance();
currencyOut = currencyFormatter.format(currency);
System.out.println(currencyOut);

Az utolsó sor kiírja hogy:

876,543.21

A százalék formátum

Százalék formában való kiíráshoz szintén a helyi beállításokat figyelembe vevő objektumot kapunk a getPercentInstance metódus segítségével:

Double percent = new Double(0.75);
NumberFormat percentFormatter;
String percentOut;
percentFormatter = NumberFormat.getPercentInstance();
percentOut = percentFormatter.format(percent);
System.out.println(percentOut);

A printf metódus lehetőségei

A JDK 5.0 tette lehetővé a java.io.PrintStream printf metódusának alkalmazását, amely a kimenet formázását nagyban leegyszerűsítette. A metódus deklarációja:

public PrintStream printf(String format, Object... args)

Az első paraméter azt határozza meg, hogy a további paraméterek milyen formátumban kerüljenek kiírásra. A sokféle lehetőség szerencsére alaposan dokumentálva van az API specifikációban.

Nézzük a következő példát:

Calendar c =...;
String s =
    String.format("Duke's Birthday: %1$tm %1$te,%1$tY",c);

A formátum meghatározás egyszerű. Három van belőlük: %1$tm, %1$te, és %1$tY, amelyek mindegyike elfogadja a c nevű Calendar objektumot. A formátum meghatározás a következőket jelenti:

  • % jelzi a formátum meghatározás kezdetét
  • 1$ az első további paraméter jelzése
  • tm jelenti a hónapot
  • te jelenti a hónap napját
  • tY jelenti az évet

Sablon készítése

A DecimalFormat osztályban meghatározhatjuk egy String minta segítségével a speciális kiírási formátumot. A minta fogja meghatározni, hogy hogyan nézzen ki a megformázott szám. A következő példa készít egy formázót, ami átadja a String mintát a DecimalFormat konstruktorának. A format metódus átvesz egy Double értéket, és formázott String-gel tér vissza.

DecimalFormat myFormatter = new DecimalFormat(pattern);
String output = myFormatter.format(value);
System.out.println(value + " " + pattern + " " + output);

A fenti kódsorok kimenete a lenti táblázatban látható.

value értéke pattern értéke eredmény magyarázat
123456.789 ###,###.### 123,456.789 A kettős kereszt (#) utal a számjegyre, a vessző a csoportosítás helyét jelzi, és a pont jelzi a tizedespont helyét.
123456.789 ###.## 123456.79 A value-nak három számjegye van a tizedes pont után, de a pattern-nek csak kettő. A format metódus ezt úgy oldja meg, hogy felkerekíti a számot.
123.78 000000.000 000123.780 A pattern itt a kettős kereszt (#) helyett kezdő és soron következő nullákat definiál.
12345.67 $###,###.### $12,345.67 Az első karakter a pattern-ben a dollár jel ($). Ez annyit jelent, hogy rögtön a balról első számjegy elé kerül.
12345.67 \u00A5###,###.### ¥12,345.67 A pattern beilleszti a Japán pénzt, a yen-t (¥) az Unicode értékével : 00A5.

A formázott szimbólumok módosítása

Erre a feladatra a DecimalFormatSymbols osztályt lehet használni. Ezzel az osztállyal meg tudjuk változtatni a format metódus által már formázott szimbólumokat. Ezek a szimbólumok többek között magukba foglalják a tizedesvesszőt, az ezres csoportosítást, a mínuszjelet, és a százalék jelet.

A következő példa demonstrálja a DecimalFormatSymbols osztályt alkalmazva egy szokatlan számalakra. A szokatlan formátum eredménye a következő metódusok hívása: setDecimalSeparator, setGroupingSeparator, setGroupingSize.

DecimalFormatSymbols unusualSymbols = new DecimalFormatSymbols(currentLocale);
unusualSymbols.setDecimalSeparator('|');
unusualSymbols.setGroupingSeparator('^');
String strange = "#,##0.###";
DecimalFormat weirdFormatter = new DecimalFormat(strange, unusualSymbols);
weirdFormatter.setGroupingSize(4);
String bizarre = weirdFormatter.format(12345.678);
System.out.println(bizarre);

Futtatás során, a példa furcsa alakban jeleníti meg a számot:

1^2345|678

9.5. Aritmetika

A Java programozási nyelv támogatja az alapvető aritmetikai számításokat az aritmetikai operátorokkal együtt: +, -, *, /, és %. A java.lang csomagban a Java platform biztosítja a Math osztályt. Ez olyan metódusokat és változókat biztosít, amik segítségével már magasabb rendű matematikai számítások is elvégezhetők, mit például egy szög szinuszának kiszámítása, vagy egy szám bizonyos hatványra emelése.

A Math osztály metódusai osztálymetódusok, tehát közvetlenül az osztály nevével kell őket meghívni, például így:

Math.round(34.87);

A Math osztály metódusai közül elsőként különböző alapvető matematikai függvényeket nézzük meg, mint például egy szám abszolút értékének kiszámítása vagy egy szám kerekítése valamelyik irányban. A következő lista ezeket a metódusokat tartalmazza:

  • double abs(double) float abs(float) int abs(int) long abs(long) A paraméterként kapott paraméter abszolút értékével tér vissza.
  • double ceil(double) A legkisebb double értékkel tér vissza, ami nagyobb vagy egyenlő a paraméterrel, és egyenlő egy egész számmal. (felfelé kerekít)
  • double floor(double) A legnagyobb double értékkel tér vissza, ami kisebb vagy egyenlő a paraméterrel, és azonos egy egész számmal. (lefelé kerekít)
  • double rint(double) A paraméterhez legközelebb álló double értékkel tér vissza, és azonos egy egész számmal. (a legközelebbi egészhez kerekít)
  • long round(double) int round(float) A legközelebbi long vagy int értéket adja vissza, ahogy azt a metódus visszatérési értéke jelzi.

A következő BasicMathDemo program néhány alkalmazási módot illusztrál a fentiekre:

public class BasicMathDemo {
    public static void main(String[] args) {
        double aNumber = -191.635;
        System.out.println("The absolute value of " + aNumber + " is " + Math.abs(aNumber));
        System.out.println("The ceiling of " + aNumber +" is "+ Math.ceil(aNumber));
        System.out.println("The floor of " + aNumber + " is " + Math.floor(aNumber));
        System.out.println("The rint of " + aNumber + " is " + Math.rint(aNumber));
    }
}

A program kimenetei:

The absolute value of -191.635 is 191.635
The ceiling of -191.635 is -191
The floor of -191.635 is -192
The rint of -191.635 is -192

További két alapvető metódus található a Math osztályban. Ezek a min és a max. Az alábbi táblázat mutatja be a min és max metódusok különböző formáit, amik két számot hasonlítanak össze, és a megfelelő típusú értékkel térnek vissza.

  • double min(double, double) float min(float, float) int min(int, int) long min(long, long) A két paraméterből a kisebbel térnek vissza.
  • double max(double, double) float max(float, float) int max(int, int) long max(long, long) A két paraméterből a nagyobbal térnek vissza.

A MinDemo program mutatja be a min alkalmazását két érték közül a kisebb kiszámítására:

public class MinDemo {
    public static void main(String[] args) {
        double enrollmentPrice = 45.875;
        double closingPrice = 54.375;
        System.out.println("A vételár: $" + Math.min(enrollmentPrice, closingPrice));
    }
}

A program valóban az alacsonyabb árat adta vissza:

A vételár: $45.875

A Math osztály következő metódusai a hatványozással kapcsolatosak. Ezen kívül megkaphatjuk az e értékét a Math.E használatával.

  • double exp(double) A szám exponenciális értékével tér vissza.
  • double log(double) A szám természetes alapú logaritmusával tér vissza.
  • double pow(double, double) Az első paramétert a második paraméternek megfelelő hatványra emeli.
  • double sqrt(double) A paraméter négyzetgyökével tér vissza.

A következő ExponentialDemo program kiírja az e értékét, majd meghívja egyenként a fenti táblázatban látható metódusokat egy számra:

public class ExponentialDemo {
    public static void main(String[] args) {
        double x = 11.635;
        double y = 2.76;
        System.out.println("Az e értéke: " + Math.E);
        System.out.println("exp(" + x + ") is " + Math.exp(x));
        System.out.println("log(" + x + ") is " + Math.log(x));
        System.out.println("pow(" + x + ", " + y + ") is " + Math.pow(x, y));
        System.out.println("sqrt(" + x + ") is " + Math.sqrt(x));
    }
}

Az ExponentialDemo kimenete:

Az e értéke: 2.71828
exp(11.635) is 112984
log(11.635) is 2.45402
pow(11.635, 2.76) is 874.008
sqrt(11.635) is 3.41101

A Math osztály egy sor trigonometrikus függvényt is kínál, ezek a következő táblázatban vannak összefoglalva. A metóduson áthaladó szögek radiánban értendők. Radiánból fokká, és onnan visszakonvertálásra két függvény áll rendelkezésünkre: toDegrees és toRadians. Előbbi fokká, utóbbi radiánná konvertál. A Math.PI függvény meghívásával a PI értékét kapjuk meg a lehető legpontosabban.

  • double sin(double) Egy double szám szinuszával tér vissza.
  • double cos(double) Egy double szám koszinuszával tér vissza.
  • double tan(double) Egy double szám tangensével tér vissza.
  • double asin(double) Egy double szám arc szinuszával tér vissza.
  • double acos(double) Egy double szám arc koszinuszával tér vissza.
  • double atan(double) Egy double szám arc tangensével tér vissza.
  • double atan2(double) Derékszögű koordinátákat konvertál (b, a) polárissá (r, théta).
  • double toDegrees(double) double toRadians(double) A paramétert radiánná vagy fokká konvertálják, a függvények adják magukat.

A következő TrigonometricDemo program használja a fenti táblázatban bemutatott összes metódust, hogy különböző trigonometrikus értékeket számoljon ki a 45 fokos szögre:

public class TrigonometricDemo {
    public static void main(String[] args) {
        double degrees = 45.0;
        double radians = Math.toRadians(degrees);
        System.out.println("The value of pi is " + Math.PI);
        System.out.println("The sine of " + degrees + " is " + Math.sin(radians));
        System.out.println("The cosine of " + degrees + " is " + Math.cos(radians));
        System.out.println("The tangent of " + degrees + " is " + Math.tan(radians));
    }
}

A program kimenetei:

The value of pi is 3.141592653589793
The sine of 45.0 is 0.8060754911159176
The cosine of 45.0 is -0.5918127259718502
The tangent of 45.0 is -1.3620448762608377

Megjegyezzük, hogy a NaN akkor jelenik meg, amikor az eredmény matematikailag nem definiált. A Double és a Float osztályok is tartalmazzák a NaN konstanst. Összehasonlítva a visszatérési értéket az egyik ilyen konstanssal, a programunk el tudja dönteni, hogy a visszaadott érték érvényes-e. Ilyen módon a programunk elfogadhatóan tud reagálni, ha egy metódus nem definiált értéket kap.
Az utolsó Math metódus, amiről szót ejtünk, a random. A metódus egy kvázi-véletlen 0.0 és 1.0 közé eső számmal tér vissza. Pontosabban leírva:

0.0 ≤ Math.random() < 1.0

Hogy más intervallumban kapjunk meg számokat, műveleteket hajthatunk végre a függvény által visszaadott értéken. Például, ha egy egész számot szeretnénk kapni 1 és 10 között, akkor a következőt kell begépelnünk:

int number = (int)(Math.random() * 10 + 1);

Megszorozva ezt az értéket 10-el a lehetséges értékek intervalluma megváltozik:

0.0 ≤ szám < 10.0.

1-et hozzáadva az intervallum ismét megváltozik:

1.0 ≤ szám < 11.0.

Végül, az érték egésszé konvertálásával egy konkrét számot (int) kapunk 1 és 10 között.

A Math.random használata tökéletes, ha egy egyszerű számot kell generálni. Ha egy véletlen számsorozatot kell generálni, akkor egy hivatkozást kell létrehozni a java.util.Random–ra, és meghívni ennek az objektumnak a különböző metódusait.

9.6. Ellenőrző kérdések

  • Mi a Number osztály szerepe az osztályhierarchiában?
  • Hogyan tudunk egy begépelt szöveget (String) int értékként megkapni?
  • Hogyan tudunk egy egész számot (int) szöveggé (String) konvertálni?
  • Hogyan tudunk Javában összetett matematikai műveletek (szinusz, exponenciális) végrehajtani?

Mit ír ki a következő kódrészlet?

Integer ten = new Integer(10);
Long nine = new Long (9);
System.out.println(ten + nine);
int i = 1;
System.out.println(i + ten);
  • 19, majd 20
  • 19, majd 11
  • 10, majd 1
  • fordítási hiba miatt nem indul el