David Steinsland – informatikkstudent og webutvikler

Innlegg merket med «java»

Lagre og hente frem objekter i Java (cache)

| Ingen kommentarer »

Javaprat

Noe ganske essensielt med et program er egenskapen å huske innstillinger til neste oppstart. Men hvordan skal vi få til dette? Skal vi bruke en database eller XML? Eller finnes det andre metoder?

I Java har vi en interface som heter «Serializable». Klasse som implementerer den, har muligheten til å bli lagret på disk til senere bruk ved bruk av ObjectOutputStream og ObjectInputStream.

Helt enkelt, kan vi gjøre slik:

MyObject myObj = new MyObject (); // MyObject implementerer Serializable

try
{
    FileOutputStream fos = new FileOutputStream ("myObject.ser");
    ObjectOutputStream oos = new ObjectOutputStream(fos);

    oos.writeObject(myObj);

    oos.flush();
    oos.close();
}
catch (Exception e)
{
    e.printStackTrace();
}

Objektet vårt er nå lagret i filen myObject.ser, og vi kan hente det frem igjen slik:

try
{
    FileInputStream fis = new FileInputStream("myObject.ser");
    ObjectInputStream ois = new ObjectInputStream(fis);

    // myObj er nå av samme instans som i eksempelet ovenfor
    MyObject myObj = (MyObject) ois.readObject();

    ois.close();
}
catch (Exception e)
{
    e.printStackTrace();
}

Som du kanskje ser, så er det ikke så veldig komplisert. Men hva dersom applikasjonen inneholder mange flere objekter? Å lagre alle objektene manuelt er både tidkrevende og unødvendig.

Vi kan nemlig anvende oss av programmeringsparadigmet Registry. Essensielt er det en klasse som inneholder alle andre objekter som brukes i en applikasjon. Registeret anvender også gjerne Singleton-mønsteret slik at objektene kan nås overalt.

Når vi har et register trenger vi bare lagre register-objektet. Alle objekter som måtte være i registeret vil da også bli lagret.

Et eksempel på et slik register:

class Registry implements Serializable
{
	private static final long serialVersionUID = 7422025143560909873L;
	private static final Registry instance = new Registry();

	private HashMap registry = new HashMap();

	private Registry ()
	{ }

	public static Registry getInstance ()
	{
		return instance;
	}

	public void addObject (String k, Object v)
	{
		this.registry.put (k, v);
	}

	public Object getObject (String k)
	{
		return this.registry.get (k);
	}
}

Du har kanskje lagt merke til datafeltet serialVersionUID, og lurer på hva det er?

serialVersionUID er en universell versjonsindikator for en klasse som støtter serial-handling. Deserialiseringa bruker dette ID-nummeret for å sikre at klassen er i samsvar med det serialiserte objektet.

I praksis betyr dette at Java bruker serienummeret til å sjekke om det objektet du henter frem fra filen er i samsvar med det objektet du la inn. Det høres kanskje litt diffus ut fortsatt, men ta følgende scenario:

- Du oppretter et objekt av typen MyObject (som i kode #1), og lagrer dette til en fil
- I mellomtiden endrer du på klassestrukturen til MyObject, og legger til flere datafelt
- Når du henter frem objektet igjen, etter klasseendringene, hva tror du skjer?

Java kjenner nemlig ikke igjen klassene, og tror de er forskjellige. Du vil derfor få en feilmelding om at det er en ugyldig klasse. Dersom du hadde implementert serialVersionUID, hadde Java forstått at objektene likevel var like.

Verdien er unik per klasse, og blir generert av Java selv. For å finne ut hvilket nummer du skal legge inn i hver klasse, må du ta ibruk serialver-programmet (følger med Java-installasjonen).

Du åpner opp kommandolinjen og endrer mappe til hvor klassefilene dine ligger. Deretter utfører du kommandoen:

serialver Registry

Bytt ut Registry med hva klassenavnet er i ditt tilfelle.

Et eksempel på bruk

class Program
{
    public static void main (String[] args)
    {
        Registry reg = Registry.getInstance();

        reg.addObject ("myObj", new MyObject ("Something"));
        reg.addObject ("foo", new MyObject ("bar"));

        // lagre objektene ved avslutning
        try
        {
            FileOutputStream fos = new FileOutputStream ("registry.ser");
            ObjectOutputStream oos = new ObjectOutputStream(fos);

            oos.writeObject(reg);

            oos.flush();
            oos.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }

        // ... så kan vi hente dem frem igjen
        try
        {
            FileInputStream fis = new FileInputStream("registry.ser");
            ObjectInputStream ois = new ObjectInputStream(fis);

            reg = (Registry) ois.readObject();
            ois.close();
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }

        // do something..
        MyObject myObj = (MyObject) reg.getObjet ("myObj");
    }
}

Oppsummering

  • Alle klasser som skal lagres implementere Serializable, og bør inneholdet  serialVersionUID
  • Et felles register med alle objekter vil spare deg for tid og ressurser, for eksempel ved lagring av objekter

Kompilere Java-prosjekter med Apache Ant

| Ingen kommentarer »

Javaprat

Når en jobber med store Java-prosjekter som inneholder mange klasse- og kildefiler, kan det være et mareritt å holde styr på alle disse. For ikke å snakke om å sørge for å kompilere alle, én etter én. Apache Ant ble laget for akkurat dette formålet, og har mange likhetstrekk med Make (for de som er kjent med Unix). Ant er et automatisert kompileringsverktøy som kan brukes for å kompilere hele Java-prosjekter med én kommando.

Programmet tar utgangspunkt i en konfigurasjonsfil som er formatert i XML, og styrer hvilke filer som kan kompileres og hvor klassefilene skal ligge. Du kan også velge om du skal opprette JAR-filer for hele prosjektet.

Installasjon

Gå til nedlastningssiden for Apache Ant og last ned siste versjon. Pakk ut mappen til for eksempel C:\ant\.

Sørg deretter for at du har en miljøvariabel kalt JAVA_HOME med verdien tilsvarende mappen som inneholder JDK. For eksempel slik:
c:\Progra~1\Java\jdk1.7.0\

Du må også huske å inkludere C:\ant\bin\ i miljøvariabelen PATH.

Forberedelse

Opprett en helt enkelt Java-klasse med en main-metode, som du lagrer i en mappe kalt src.

Deretter oppretter du en XML-fil som du kaller build.xml med følgende innhold:

<?xml version="1.0"?>
<project name="MySQL" default="compile">

	<!-- Configuration -->
	<property name="src.dir"     value="src"/>
    <property name="build.dir"   value="build"/>
    <property name="classes.dir" value="${build.dir}/classes"/>
    <property name="jar.dir"     value="${build.dir}/jar"/>
    <property name="main-class"  value="Test"/>
	<!-- End Configuration -->

	<target name="clean">
        <delete dir="${build.dir}"/>
    </target>

   <target name="compile">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
    </target>

	<target name="jar" depends="compile">
        <mkdir dir="${jar.dir}"/>

		<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
            <manifest>
                <attribute name="Main-Class" value="${main-class}"/>
            </manifest>
        </jar>
    </target>

	<target name="run" depends="jar">
		<java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
    </target>
</project>

I starten trenger du ikke tenke på mer enn å endre «TestApp» til prosjektets navn, samt endre «main-class» til den klassen som inneholder main-metoden.

De ulike target-blokkene utgjør kommandoene du kan gjøre med Ant. For eksempel vil vi med denne filen ha følgende kommandoer tilgjengelige:

ant compile
ant jar
ant run
ant clean

Dersom du legger merke til linje nummer to, så står det blant annet default=»compile». Dette gjør at dersom vi kjører kommandoen:

ant

Så vil den kjøre compile-oppgaven.

Konfigurasjonsfilen skal lagres i prosjektmappen, slik at du ender opp med følgende mappestruktur;

TestApp
+ src/
|	Test.java
+ build.xml

Gjennomføring

Nå er det lille prosjektet vårt klar til å kompileres.
Start opp kommandolinjen og endre mappe til prosjektet.

For å kompilere prosjektet, skriver du inn kommandoen:

ant

Denne kommandoen gjør oppgaven som er spesifisert under <target name=»compile»>.
Den oppretter mappen build\classes og lagrer klassefilene der.

For å lage en JAR-fil for videre distribusjon, utfører vi kommandoen:

ant jar

Denne er avhengig av at prosjektet først er kompilert, men dette sørger Apache Ant for å gjøre dersom du ikke har gjort det.
Det vil si at vi faktisk ikke trenger å utføre kommandoen ant compile på forhånd.

For å teste at JAR-filen fungerer, kan vi bruke kommandoen ant run.

Java: Primtallgenerering og -faktorisering

| Ingen kommentarer »

Javaprat

Hvordan kan man finne alle primtall mindre enn k? Hvordan sjekke om et gitt heltall n er primtall? Hvordan kan vi primtallsfaktorisere ethvert tall?

Først av alt må man etablere et par matematiske sannheter:

  1. Et primtall er et helt tall større enn 1 som bare er delelig med seg selv og med 1
  2. Et heltall k er også primtall dersom det ikke er delelig med andre primtall mindre enn eller lik kvadratrota til k.
  3. Et hvert heltall større enn 1 kan skrives som produktet av ett eller flere primtall.

Så, hvordan kan man finne et primtall?

  1.  Gitt et heltall k, begynner vi å sjekke om tallet er delelig med det minste primtallet, 2.
  2. Om det er delelig, kan vi trekke konklusjonen om at tallet ikke er primtall.
  3. Dersom det ikke er delelig, går vi videre til 3. Dette gjentar vi for alle primtall \leq \sqrt{k}.
  4. Om ingen tall er delelige, er tallet et primtall.

Vi kan nå begynne å skrive Java-koden vår, og en kan for eksempel ende opp med noe slikt:


public boolean isPrime (int n)
{
	int q = (int) Math.sqrt (n);

	boolean isPrime = true;

	for (int i = 2; i < q; i++)
 	{
		if (n % i == 0)
		{
			return false;
		}
	}

	return true;
}

Det første vi gjør er å ta kvadratrota til heltallet vårt. Deretter kjører vi gjennom alle heltall 2 \leq i \leq \sqrt{n}. Dersom tallet skulle være delelig med et annet, da er det ikke et primtall. Når løkken er gjennomført, vet vi at ingen tall er delelige og at heltallet vårt er primtall.

Primtallsfaktorisering

Ved aritmetikkens fundamentalteorem vet vi at et heltall større enn 1 er enten et primtall, eller et produkt av ett eller flere primtall.

For eksempel er: 7007 = 7 \cdot 7 \cdot 11 \cdot 13


public Integer[] primeFactorization (int a)
{
	ArrayList factors = new ArrayList();

	// fetch primes less than or equal to sqrt(a).
	// if no primes are returned, the number itself is a prime.
	Integer[] primes = primes ((int)Math.sqrt(a));

	int i = 0;
	while (i < primes.length)
 	{
 		int p = primes[i];
 		if (Arrays.asList (primes).contains(a) || isPrime (a))
 		{
 			// the remaining number is now a prime.
 			break;
 		}
 		if (a % p == 0)
 		{
 			// add the prime to our list, and execute the division
 			factors.add (p);
 			a = a / p;
 		}
 		else
 		{
 			// Continue to next prime, if the current one does not
 			// divide
 			i++;
 		}
 	}
 	// The number than remains should be a prime itself.
 	if (a > 1)
	{
		factors.add (a);
	}

	return (Integer[]) factors.toArray(new Integer[factors.size()]);
}

Her ble det kanskje litt komplisert med det første, men prosessen er igrunn ganske enkel:

  1. primes() til å gi oss et int-array med primtall (bruker funksjonen isPrime())
  2. Deretter går vi gjennom alle primtallene, og sjekker om tallet a er delelig med p. Dersom det er delelig, så legger vi primtallet p i samlingen vår og utfører divisjonen \frac{a}{p}.
  3. I neste runde tester vi enten det samme primtallet som i forrige runde, eller så tester vi det neste primtallet. Det er først når det gjeldende primtallet ikke er delelig, at vi går til neste.
  4. Til slutt skal det resterende tallet selv være et primtall, men vi ønsker ikke å inkludere tallet 1 som faktor.

Eksempel

Om vi bruker tallet 7007 om igjen, så vil vi i Java-funksjonen få følgende primtall:

{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83}. Det er alle primtall som er mindre enn kvadratroten til 7007.

Deretter tester vi først om 2 er delelig med 7007. Det er det ikke, så vi går videre til neste primtall. Det er først når vi kommer til 7 at vi finner et primtall som er delelig med 7007.

Vi legger da til tallet 7 i listen vår, og utfører divisjonen \frac{7007}{7} = 1001. I neste runde tester vi tallet 7 en gang til, og igjen så er det delelig. Vi utfører da \frac{1001}{7} = 143.

Når vi i påfølgende runde tester 7 igjen, finner vi ut at det ikke lengre er delelig. Men det er primtallet 11! Det siste primtallet vil da være 13.

Dette gir oss følgende i resultat: {7, 7, 11, 13} som forteller oss hvilke primtallsfaktorer som gir 7007.

Kompilere og kjøre Java i Notepad++

| Ingen kommentarer »

notepad_plus_plus-banner

Da jeg startet på Informasjonsteknologi ved HiOA, ble mange anbefalt å skrive Java i en IDE som Eclipse eller Netbeans fremfor teksteditor. Dette ble nok godtatt av de fleste, men for de av oss som har sverget troskap til Notepad++ er det vanskeligere å godta.

Etter litt søking rundt om, fant jeg frem til en løsning som lar deg kompilere og kjøre Java-kode direkte i fra Notepad++ med et par tastetrykk!

1. Installer NppExec

NppExec er en plugin til Notepad++ som lar deg kjøre kommandoer, og du vil finne denne i Plugin Manager. Gå til: Plugins → Plugin Manager → Show Plugin Manager.

I denne listen vil du finne NppExec. Huk av i avkryssningsboksen og trykk Install.

2. Opprett batch

For å kunne kjøre Java-applikasjoner måtte jeg opprette en egen batch-fil for dette. Lag en fil som heter RunJava.bat, og lim inn dette:


cd %1
java -cp . %2
pause

3. Lage makroer

Makroer  er små miniprogrammer egnet for å utføre små oppgaver, som du kan lage med NppExec.

Trykk F6, lim inn følgende kommando:

D:\bin\JavaRun.bat "$(CURRENT_DIRECTORY)" $(NAME_PART)

Trykk så Save, og kall makroen for Java Run.

For å kompilere Java-kode behøver du ingen batch-filer, bare en spesiell kommando.

Trykk F6, velg <temporary script> fra nedtrekksmenyen, og lim inn kommandoen:

cd $(CURRENT_DIRECTORY)
javac "$(FILE_NAME)"

Lagre den som Java Compile.

4. Plassere makroene i meny

Gå til Plugins → NppExec → Advanced Options

I listen under «Associated Script» velger du Java Run. I boksen inder «Item name» skriver du: Java Run Application. Trykk så på Add/modify.

Gjenta samme prosedyre én gang til, bare denne gangen velger du Java Compile og skriver inn Java Compile Application under «Item name».

Trykk OK, og finn frem Macro i hovedmenyen. Du vil nå se dine makroer i listen nederst.

5. Tilegne tastatursnarveier

Det er greit å kunne ha en tastatursnarvei for disse makroene, spesielt fordi de vil bli benyttet ofte.

I hovedmenyen velg Run → Modify Shortcut/Delete Command. Deretter velger du «Plugin Commands», og finn frem til Java Run Application og Java Compile Application. Dobbeltklikk på hver av dem, og du vil få mulighet om å tilegne makroen en tastatursnarvei.

OBS! Pass på at det ikke oppstår konflikt med andre snarveier. Notepad++ håndterer slikt dårlig, og du vil derfor måtte fjerne en eksisterende snarvei før du kan bruke den på ny.

Gå til