Pokazywanie postów oznaczonych etykietą J2SE. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą J2SE. Pokaż wszystkie posty

środa, 13 sierpnia 2008

testy java

Zawsze mnie zastanawiało, skąd się biorą podchwytliwe pytania dotyczące programowania w Javie. Czy wymyślają je programiści, na zasadzie "sztuka dla sztuki", czy pochodzą one z prawdziwych projektów. Moja zagadka pochodzi z życia.

Co będzie wynikiem wywołania funkcji:


public static void main(String[] args) {
BigDecimal arg1 = new BigDecimal("10");
BigDecimal arg2 = new BigDecimal("3");
System.out.println(arg1.divide(arg2).toPlainString());
}


a) na konsole zostanie wypisane: 3,333333333
b) na konsole zostanie wypisane: 3,33
c) na konsole zostanie wypisane: 3
d) zostanie rzucony wyjątek w linii System.out.println(arg1.divide(arg2).toPlainString());
e) na konsole zostanie wypisane: null

no i odpowiedź:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
następnym razem dzieląc obiekty klasy BigDecimal będę uważał.

poniedziałek, 9 czerwca 2008

Eclipse Ganymede i kompilowanie javy do kodu natywnego

Czołem!

Zapewne zauważyłeś na kilku blogach wysyp wpisów o nowej wersji środowiska Eclipse Ganymede. Dzieje się tak dla tego, że jesteśmy łasi na koszulki, które Eclipse obiecuje w konkursie Ganymede Around the World Contest. Nie inaczej jest ze mną. Zobaczymy, co z tego wyjdzie.

Tak więc dziś postanowiłem się zmierzyć z tematem, który czasami pojawia się na łamach grupy dyskusyjnej pl.comp.lang.java i który zawsze uważałem za ślepą uliczkę rozwoju tego języka. Chodzi o kompilację kodu źródłowego Javy do kodu natywnego systemu operacyjnego.

Plusy tego rozwiązania:
- nie potrzeba JRE
- wyższa wydajność

Minusy takiego rozwiązania:
- brak przenośności
- niższa wydajność

O ile brak konieczności instalacji JRE jak i tracona przenośność są raczej bezsprzeczne o tyle do rozstrzygnięcia pozostaje kwestia wydajności, którą się dzisiaj zajmę.

Kod źródłowy będę pisał oczywiście przy pomocy najnowszego Eclipse Ganymede. Uruchamiał i kompilował będę javą 1.6. Kompilację do kodu natywnego przeprowadzę z wykorzystaniem gcj 4.2.3.

Najpierw zainstaluję gcj. Mam Ubuntu, więc wystarczy:
mateusz@mateusz-laptop:~$ sudo apt-get install gcj

sprawdzenie co mam zainstalowane:

mateusz@mateusz-laptop:~$ java -version
java version "1.6.0_06"
Java(TM) SE Runtime Environment (build 1.6.0_06-b02)
Java HotSpot(TM) Server VM (build 10.0-b22, mixed mode)
mateusz@mateusz-laptop:~$ gcj --version
gcj (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu6)
Copyright (C) 2007 Free Software Foundation, Inc.
To jest wolne/otwarte oprogramowanie; zobacz kod źródłowy aby zapoznać sie
z warunkami kopiowania. Nie ma ŻADNEJ gwarancji; nawet na MOŻLIWOŚĆ SPRZEDAŻY
ani na ODPOWIEDNIŚĆ DO KONKRETNEGO CELU.

Pobieram też Eclipse Ganymede.
Przed uruchomieniem tworzę skrót w pasku szybkiego uruchamiania i tu niespodzianka... W archiwum eclipse-jee-ganymede-RC2-linux-gtk.tar.gz Nie ma ikonki Eclipse, albo ja nie umiem jej znaleźć. Posłużę się ikonką eclipse.xpm z wersji 3.3 tego środowiska.

Odpalam Eclipse:



Niespodzianka numer 2:



Eclipse chce wysyłać informacje o moich wtyczkach. Przez chwile czuję się jak na Windowsie, po czym zaznaczam opcję, żeby nic nie wysyłał.
Tworzę projekt native i klasę pl.matt.gcj.TestClass


package pl.matt.gcj;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* @author mateusz
*
*/
public class TestClass {

/**
* @param args
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
List list = new ArrayList();
for (byte i = 0; i < 300; i++) {
list.add(list.size());
if (list.size() > 10000000) {
break;
}
}

Collections.shuffle(list);
double avg = 0;

for (Integer integer : list) {
avg += (double)integer/list.size();
}

long stop = System.currentTimeMillis();
System.out.println(stop-start + " ms.");
}

}

Do listy wstawiam 10000001 elementów. Listę mieszam i liczę średnią. Używam automatycznej konwersji int do Integer (autoboxng). GCJ podobno radzi sobie z kodem źródłowym zgodnym z Javą 1.5. Zobaczymy.
Kompiluję kod za pomocą gcj:

mateusz@mateusz-laptop:~/priv/workspace/native/src$ gcj -o /tmp/gcj/a.out pl/matt/gcj/TestClass.java
/usr/lib/gcc/i486-linux-gnu/4.2.3/../../../../lib/crt1.o: In function `_start':
(.text+0x18): undefined reference to `main'
collect2: ld returned 1 exit status

rzut okiem na FAQ GCJ i już wiem, że trzeba podać położenie metody main()

mateusz@mateusz-laptop:~/priv/workspace/native/src$ gcj --main=pl.matt.gcj.TestClass
-o /tmp/gcj/a.out pl/matt/gcj/TestClass.java

poszło... Został utworzony plik /tmp/gcj/a.out
Czas na testy. Ponieważ czas wykonywania programu w Javie nie uwzględnia czasu uruchomienia samej wirtualnej maszyny Java, tworzę 2 proste skrypty w BASHu:
a.sh który 5 razy uruchomi plik zawierający kod natywny a.out

mateusz@mateusz-laptop:/tmp/gcj$ cat a.sh
#!/bin/bash
START=$(date +%s)
./a.out
./a.out
./a.out
./a.out
./a.out
STOP=$(date +%s)
let T=$STOP-$START
echo $T " sekund"

oraz j.sh, który uruchomi TestClass za pomocą VM Java.

mateusz@mateusz-laptop:/tmp/gcj$ cat j.sh
#!/bin/bash
START=$(date +%s)
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
java pl.matt.gcj.TestClass
STOP=$(date +%s)
let T=$STOP-$START
echo $T " sekund"

Testy nie pozostawiają wątpliwości:

mateusz@mateusz-laptop:/tmp/gcj$ ./a.sh
8630 ms.
8299 ms.
8290 ms.
8380 ms.
8321 ms.
43 sekund
mateusz@mateusz-laptop:/tmp/gcj$ ./j.sh
5671 ms.
5824 ms.
5913 ms.
5906 ms.
5823 ms.
29 sekund

Co ciekawe czas uruchomienia wirtualnej maszyny Java jest praktycznie pomijalny.
Reasumując:

Plusy kompilowania aplikacji Java do kodu natywnego
- nie potrzeba JRE

Minusy takiego rozwiązania:
- brak przenośności
- niższa wydajność

A może ty stosujesz na co dzień kompilację programów napisanych w języku Java do kodu natywnego? Jeżeli tak, napisz dlaczego i jakie są tego zalety, których nie widzę.

środa, 4 czerwca 2008

getClass() klasy anonimowej

Zastanawialiście się kiedyś, co zwróci metoda getClass() klasy anonimowej? Ja się zastanawiałem. Nic nie wykombinowałem, więc stworzyłem prostą klasę:


package pl.matt;

import java.util.HashSet;
import java.util.Set;

public class AnnonymousClassTest {
public static void main(String[] args) {
Set<Object> set = new HashSet<Object>() {
/**
*
*/
private static final long serialVersionUID = -2992990530694342435L;

{
add(1);
add(2);
}
};

System.out.println(set.getClass());

}
}


odpalam i co widzę?

class pl.matt.AnnonymousClassTest$1


Dociekliwi znajdą w katalogu z klasami pliki:
AnnonymousClassTest$1.class  AnnonymousClassTest.class

czyli wszystko zgodnie z planem...