Java: Föreläsning 13

Av Thomas Padron-McCarthy (Thomas.Padron-McCarthy@tech.oru.se). Senaste ändring 7 december 2003.

Innehåll i föreläsning 13

Dubbelbuffring

Dubbelbuffring = innehållet i en minnesarea ("buffer") visas på skärmen, samtidigt som man ritar i en annan. När man ritat klart, byter man och visar den nyritade minnesarean. Motverkar flimmer.

Nästan alla Swing-komponenter är dubbelbuffrade som default. Det går att stänga av:

RepaintManager currentManager =
    RepaintManager.currentManager(komponenten);
currentManager.setDoubleBufferingEnabled(false);
Man kan till exempel få det animerade cirkeldiagrammet i programmet Grafikdemo6.java från föreläsning 11 att flimra lite.

I AWT var man tvungen att göra dubbelbuffringen själv.

OO-teori: likhet och arv

Det finns ett och annat teoretiskt problem i objektorientering. Här kommer ett exempel, men först lite repetition om likhet mm.

I Java:

Fem grundregler som bör uppfyllas för likhet (alltså metoden equals i Java):
  1. Reflexivitet: x = x (läs "=" som "lika med")
    I Java: x.equals(x) ska alltid vara sant
  2. Symmetri: x = y <==> y = x
    I Java: x.equals(y) <==> y.equals(x)
  3. Transitivitet: x = y och y = z <==> x = z
    I Java: x.equals(y) och y.equals(z) <==> y.equals(z)
  4. Konsekvens: samma jämförelse ska ge samma resultat varje gång (om inte objekten har ändrats)
    I Java: x.equals(y) ska ge samma resultat varje gång (om inte objekten har ändrats)
  5. null är unik, dvs x != null för alla x utom null
    I Java: x.equals(null) ska alltid vara falskt (om x inte är null)
Ett koordinatsystem med några punkter
Klassen Point har en "normal" equals-metod, som uppfyller kraven. Programmet EqualsExample.java:
class Point {
    private final int x, y;
    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public boolean equals(Object o) { 
        if (!(o instanceof Point))
            return false;
        Point p = (Point)o;
        return this.x == p.x && this.y == p.y;
    }
} // class Point

public class EqualsExample {
    public static void main(String[] args) {

        // Repetition av hur metoden "equals" fungerar

        Point p1 = new Point(1, 2);
        Point p2 = new Point(1, 2);
        Point p3 = new Point(1, 2);
        Point p4 = new Point(3, 3);

        System.out.println("*** Repetition av hur metoden \"equals\" fungerar");

        System.out.println("p1 == p2: " + (p1 == p2));
        System.out.println("p1.equals(p2): " + (p1.equals(p2)));
        System.out.println("p1.equals(p4): " + (p1.equals(p4)));

        // Fem grundregler som bör uppfyllas för likhet ("equals" i Java)

        // 1. Reflexivitet: x = x (läs "=" som matematikens "lika med")
        // I Java motsvaras detta av: x.equals(x)

        System.out.println("*** 1. Reflexivitet: x = x (läs \"=\" som \"lika med\")");

        System.out.println("p1.equals(p1): " + (p1.equals(p1)));

        // 2. Symmetri: om x = y <==> y = x
        // I Java motsvaras detta av: x.equals(y) <==> y.equals(x)

        System.out.println("*** 2. Symmetri: om x = y <==> y = x");

        System.out.println("p1.equals(p2): " + (p1.equals(p2)));
        System.out.println("p2.equals(p1): " + (p2.equals(p1)));

        System.out.println("p1.equals(p4): " + (p1.equals(p4)));
        System.out.println("p4.equals(p1): " + (p4.equals(p1)));

        // 3. Transitivitet: x = y och y = z <==> x = z
        // I Java motsvaras detta av:
        // x.equals(y) och y.equals(z) <==> x.equals(z)

        System.out.println("*** 3. Transitivitet: x = y och y = z <==> x = z");

        System.out.println("p1.equals(p2): " + (p1.equals(p2)));
        System.out.println("p2.equals(p3): " + (p2.equals(p3)));
        System.out.println("p1.equals(p3): " + (p1.equals(p3)));

        // 4. Konsekvens: samma jämförelse ska ge samma resultat varje gång
        // (om inte objekten har ändrats!)
        // I Java motsvaras detta av:
        // x.equals(y) ska ge samma resultat varje gång

        System.out.println("*** 4. Konsekvens: samma jämförelse ska ge samma resultat varje gång");

        System.out.println("p1.equals(p2): " + (p1.equals(p2)));
        System.out.println("p1.equals(p2): " + (p1.equals(p2)));

        // 5. null är unik, dvs x != null för alla x utom null
        // I Java motsvaras detta av:
        // x.equals(null) ska ge false för varje x som inte är null

        System.out.println("*** 5. null är unik, dvs x != null för alla x utom null");

        System.out.println("p1.equals(null): " + (p1.equals(null)));
    }
} // class EqualsExample
Utmatning:
*** Repetition av hur metoden "equals" fungerar
p1 == p2: false
p1.equals(p2): true
p1.equals(p4): false
*** 1. Reflexivitet: x = x (läs "=" som "lika med")
p1.equals(p1): true
*** 2. Symmetri: om x = y <==> y = x
p1.equals(p2): true
p2.equals(p1): true
p1.equals(p4): false
p4.equals(p1): false
*** 3. Transitivitet: x = y och y = z <==> x = z
p1.equals(p2): true
p2.equals(p3): true
p1.equals(p3): true
*** 4. Konsekvens: samma jämförelse ska ge samma resultat varje gång
p1.equals(p2): true
p1.equals(p2): true
*** 5. null är unik, dvs x != null för alla x utom null
p1.equals(null): false
Vi använder den enkla mängdklassen SimpleSet för att visa hur equals kan användas inuti en behållare. (Senare ska vi också se vad som händer i SimpleSet när den innehåller objekt som har en osymmetrisk equals-metod.)

Notera att klassen SimpleSet har en main-metod som testkör klassen! Bra programmeringsmetodik: Testa varje klass för sig!

Programmet SimpleSet.java:

// Must compile with "javac -source 1.4 SimpleSet.java"

class SimpleSetFullException extends Exception { }

public class SimpleSet {
    final int MAX_MEMBERS = 3;
    Object[] content = new Object[MAX_MEMBERS];

    public boolean contains(Object o) {
        for (int i = 0; i < MAX_MEMBERS; ++i)
            if (content[i] != null && content[i].equals(o))
                return true;
        return false;
    }

    boolean add(Object o) throws SimpleSetFullException {
        if (contains(o))
            return false;
        for (int i = 0; i < MAX_MEMBERS; ++i) {
            if (content[i] == null) {
                content[i] = o;
                return true;
            }
        }
        throw new SimpleSetFullException();
    }

    public static void main(String[] args) {
        System.out.println("Testing SimpleSet");

        SimpleSet s = new SimpleSet();
        Point p1 = new Point(1, 2);
        Point p2 = new Point(2, 3);
        Point p3 = new Point(3, 4);
        Point p4 = new Point(4, 5);
        Point p5 = new Point(1, 2);

        assert s.contains(p1) == false;
        assert s.contains(p2) == false;
        assert s.contains(p3) == false;
        assert s.contains(p4) == false;
        assert s.contains(p5) == false;

        try {
            assert s.add(p1) == true;
            assert s.add(p1) == false;
        }
        catch (SimpleSetFullException ssfe) {
            assert false;
        }

        assert s.contains(p1) == true;
        assert s.contains(p2) == false;
        assert s.contains(p3) == false;
        assert s.contains(p4) == false;
        assert s.contains(p5) == true;

        try {
            assert s.add(p2) == true;
            assert s.add(p2) == false;
        }
        catch (SimpleSetFullException ssfe) {
            assert false;
        }

        assert s.contains(p1) == true;
        assert s.contains(p2) == true;
        assert s.contains(p3) == false;
        assert s.contains(p4) == false;
        assert s.contains(p5) == true;

        try {
            assert s.add(p3) == true;
            assert s.add(p3) == false;
        }
        catch (SimpleSetFullException ssfe) {
            assert false;
        }

        assert s.contains(p1) == true;
        assert s.contains(p2) == true;
        assert s.contains(p3) == true;
        assert s.contains(p4) == false;
        assert s.contains(p5) == true;

        try {
            s.add(p4);
            assert false;
        }
        catch (SimpleSetFullException ssfe) {

        }

        assert s.contains(p1) == true;
        assert s.contains(p2) == true;
        assert s.contains(p3) == true;
        assert s.contains(p4) == false;
        assert s.contains(p5) == true;

        System.out.println("SimpleSet ok");
    }
} // class SimpleSet
Utmatning:
Testing SimpleSet
SimpleSet ok

Symmetri

Kan man av misstag råka göra en osymmetrisk equals, och vad händer då?

Klassen CaseInsensitiveString ärver (dvs utökar, extends) klassen String: En CaseInsensitiveString är en String som inte bryr sig om stora och små bokstäver. Exempelvis ska "apa" vara lika med "Apa", om det är två CaseInsensitiveString som jämförs.

Men vad händer när en CaseInsensitiveString jämförs med en vanlig String? Vi försöker göra även den jämförelsen så att den inte bryr sig om stora och små bokstäver.

Programmet SymmetryExample.java:

class CaseInsensitiveString {
    private String content;
    public CaseInsensitiveString(String string) {
        this.content = string;
    }

    public boolean equals(Object o) {
        if (o instanceof CaseInsensitiveString) {
            CaseInsensitiveString cis = (CaseInsensitiveString)o;
            return content.equalsIgnoreCase(cis.content);
        }
        else if (o instanceof String) {  /* Bad part */
            String s = (String)o;
            return content.equalsIgnoreCase(s);
        }
        else
            return false;
    }
} // class CaseInsensitiveString

public class SymmetryExample {
    public static void main(String[] args) throws SimpleSetFullException {
        CaseInsensitiveString s1 = new CaseInsensitiveString("hej");
        String s2 = "Hej";

        System.out.println("s1.equals(s2): " + (s1.equals(s2))); // true
        System.out.println("s2.equals(s1): " + (s2.equals(s1))); // false!

        SimpleSet set1 = new SimpleSet();
        set1.add(s1);
        System.out.println("set1.contains(s1): " + set1.contains(s1)); // true
        System.out.println("set1.contains(s2): " + set1.contains(s2)); // true

        SimpleSet set2 = new SimpleSet();
        set2.add(s2);
        System.out.println("set2.contains(s2): " + set2.contains(s2)); // true
        System.out.println("set2.contains(s1): " + set2.contains(s1)); // false!
    }
} // class SymmetryExample
Utmatning:
s1.equals(s2): true
s2.equals(s1): false
set1.contains(s1): true
set1.contains(s2): true
set2.contains(s2): true
set2.contains(s1): false

Transitivitet

Kan man av misstag råka göra en equals som inte är transitiv, och vad händer då?

Klassen ColorPoint ärver (dvs utökar, extends) klassen Point: En ColorPoint är en Point som har en färg.

Ett koordinatsystem med några färgade och ofärgade punkter
Först låter vi bli att definiera en egen equals-metod i ColorPoint. Då används equals-metoden i Point.
Problem: den vet ju inget om färger, så en röd punkt i (1, 2) anses vara lika med en blå punkt i (1, 2).

Delar av programmet TransitivityExample1.java:

import java.awt.Color;

class ColorPoint extends Point {
    final private Color color;
    public ColorPoint(int x, int y, Color color) {
        super(x, y);
        this.color = color;
    }
} // class ColorPoint

public class TransitivityExample1 {
    public static void main(String[] args) throws SimpleSetFullException {
        ColorPoint cp1 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp2 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp3 = new ColorPoint(1, 2, Color.BLUE);
        ColorPoint cp4 = new ColorPoint(3, 3, Color.GREEN);

        System.out.println("cp1.equals(cp2): " + cp1.equals(cp2)); // true
        System.out.println("cp1.equals(cp3): " + cp1.equals(cp3)); // true!
        System.out.println("cp1.equals(cp4): " + cp1.equals(cp4)); // false
    } // main
} // class TransitivityExample1
Utmatning:
cp1.equals(cp2): true
cp1.equals(cp3): true
cp1.equals(cp4): false
Den tyckte alltså att en röd punkt i (1, 2) var lika med en blå punkt i (1, 2). Vi försöker göra en equals som hanterar jämförelser med ColorPoint-objekt, så två olikfärgade punkter inte blir lika!

Ur programmet TransitivityExample2.java:

class ColorPoint extends Point {
    final private Color color;
    public ColorPoint(int x, int y, Color color) {
        super(x, y);
        this.color = color;
    }
    public boolean equals(Object o) {
        if (!(o instanceof ColorPoint))
            return false;
        ColorPoint cp = (ColorPoint)o;
        return super.equals(o) && this.color == cp.color;
    }
} // class ColorPoint

public class TransitivityExample2 {
    public static void main(String[] args) throws SimpleSetFullException {
        ColorPoint cp1 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp2 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp3 = new ColorPoint(1, 2, Color.BLUE);
        ColorPoint cp4 = new ColorPoint(3, 3, Color.GREEN);

        System.out.println("cp1.equals(cp2): " + cp1.equals(cp2)); // true
        System.out.println("cp1.equals(cp3): " + cp1.equals(cp3)); // false
        System.out.println("cp1.equals(cp4): " + cp1.equals(cp4)); // false

        Point p1 = new Point(1, 2);
        System.out.println("cp1.equals(p1): " + cp1.equals(p1)); // false!
        System.out.println("p1.equals(cp1): " + p1.equals(cp1)); // true!
    } // main
} // class TransitivityExample2
Utmatning:
cp1.equals(cp2): true
cp1.equals(cp3): false
cp1.equals(cp4): false
cp1.equals(p1): false
p1.equals(cp1): true
Ok, nu är en blå punkt inte lika med en röd punkt i samma position.
En röd punkt är inte lika med en ofärgad Point i samma position: cp1.equals(p1) är falskt
Men! Om man jämför åt andra hållet används Point-klassens gamla equals-metod, och eftersom den inte vet något om färger säger den att en ofärgad Point är lika med en röd punkt i samma position: p1.equals(cp1): är sant!

Jämförelserna är alltså osymmetriska:

Vi åstadkommer symmetri genom att ändra ColorPoint-klassens equals-metod så att den, när den gör likadant som Point-klassens equals-metod.

Ur programmet TransitivityExample3.java:

class ColorPoint extends Point {
    final private Color color;
    public ColorPoint(int x, int y, Color color) {
        super(x, y);
        this.color = color;
    }
    public boolean equals(Object o) {
        if (o instanceof ColorPoint) {
            ColorPoint cp = (ColorPoint)o;
            return super.equals(o) && this.color == cp.color;
        }
        else if (o instanceof Point)
            return o.equals(this);
        else
            return false;
    }
} // class ColorPoint

public class TransitivityExample3 {
    public static void main(String[] args) throws SimpleSetFullException {
        ColorPoint cp1 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp2 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp3 = new ColorPoint(1, 2, Color.BLUE);
        ColorPoint cp4 = new ColorPoint(3, 3, Color.GREEN);

        System.out.println("cp1.equals(cp2): " + cp1.equals(cp2)); // true
        System.out.println("cp1.equals(cp3): " + cp1.equals(cp3)); // false
        System.out.println("cp1.equals(cp4): " + cp1.equals(cp4)); // false

        Point p1 = new Point(1, 2);
        System.out.println("cp1.equals(p1): " + cp1.equals(p1)); // true
        System.out.println("p1.equals(cp1): " + p1.equals(cp1)); // true

        System.out.println("cp1.equals(p1): " + cp1.equals(p1)); // true
        System.out.println("p1.equals(cp3): " + p1.equals(cp3)); // true
        System.out.println("cp1.equals(cp3): " + cp1.equals(cp3)); // false!
    } // main
} // class TransitivityExample3
Utmatning:
cp1.equals(cp2): true
cp1.equals(cp3): false
cp1.equals(cp4): false
cp1.equals(p1): true
p1.equals(cp1): true
cp1.equals(p1): true
p1.equals(cp3): true
cp1.equals(cp3): false
Ajaj! Nu är jämförelsen symmetrisk, men inte transitiv!

Allmänt i objektorientering: Det går inte att utöka en klass (som Point) med en aspekt (som färg), och få jämförelser mellan objekt från bas- och subklassen att bli både symmetriska och transitiva.

Lösning: Låt bli att ärva. Använd komposition i stället för arv, och gör en klass ColorPoint som inte ärver utan bara innehåller en Point.

Ur programmet TransitivityExample4.java:

class ColorPoint {
    final private Point point;
    final private Color color;
    public ColorPoint(int x, int y, Color color) {
        point = new Point(x, y);
        this.color = color;
    }
    public Point asPoint() {
        return point;
    }
    public boolean equals(Object o) {
        if (!(o instanceof ColorPoint))
            return false;
        ColorPoint cp = (ColorPoint)o;
        return this.point.equals(cp.point) && this.color == cp.color;
    }
} // class ColorPoint

public class TransitivityExample4 {
    public static void main(String[] args) throws SimpleSetFullException {
        ColorPoint cp1 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp2 = new ColorPoint(1, 2, Color.RED);
        ColorPoint cp3 = new ColorPoint(1, 2, Color.BLUE);
        ColorPoint cp4 = new ColorPoint(3, 3, Color.GREEN);

        System.out.println("cp1.equals(cp2): " + cp1.equals(cp2)); // true
        System.out.println("cp1.equals(cp3): " + cp1.equals(cp3)); // false
        System.out.println("cp1.equals(cp4): " + cp1.equals(cp4)); // false

        Point p1 = new Point(1, 2);
        System.out.println("cp1.equals(p1): " + cp1.equals(p1)); // false!
        System.out.println("p1.equals(cp1): " + p1.equals(cp1)); // false!

        System.out.println("cp1.equals(p1): " + cp1.equals(p1)); // false!
        System.out.println("p1.equals(cp3): " + p1.equals(cp3)); // false!
        System.out.println("cp1.equals(cp3): " + cp1.equals(cp3)); // false!
    } // main
} // class TransitivityExample4
Utmatning:
cp1.equals(cp2): true
cp1.equals(cp3): false
cp1.equals(cp4): false
cp1.equals(p1): false
p1.equals(cp1): false
cp1.equals(p1): false
p1.equals(cp3): false
cp1.equals(cp3): false
Nu är Point och ColorPoint helt olika saker, och om man jämför en Point och en ColorPoint är de aldrig lika.

Lärdomar:

Säkerhet: klassen SecurityManager

Endast översiktligt. Ni behöver inte kunna någon kod för detta. Se även Suns Providing Your Own Security Manager.

Programmet SecurityManagerTest.java:

import java.io.*;

public class SecurityManagerTest {
    public static void main(String[] args) throws Exception {

        BufferedReader stdin =
	    new BufferedReader(
		    new InputStreamReader(System.in));

        try {
            System.setSecurityManager(
	        new PasswordSecurityManager("gazonk", stdin));
        } catch (SecurityException se) {
            System.err.println("SecurityManager already set!");
        }

        BufferedReader in = new BufferedReader(new FileReader("in.txt"));
        PrintWriter out = new PrintWriter(new FileWriter("out.txt"));
        String inputString;
        while ((inputString = in.readLine()) != null)
            out.println(inputString);
        in.close();
        out.close();
    }
}
Körexempel 1:
Password: gazonk
Password: gazonk
Körexempel 2:
Password: gabonk
Exception in thread "main" java.lang.SecurityException: No Way!
        at PasswordSecurityManager.checkRead(PasswordSecurityManager.java:35)
        at java.io.FileInputStream.(FileInputStream.java:100)
        at java.io.FileInputStream.(FileInputStream.java:66)
        at java.io.FileReader.(FileReader.java:41)
        at SecurityManagerTest.main(SecurityManagerTest.java:17)
Programmet PasswordSecurityManager.java:
import java.io.*;

public class PasswordSecurityManager extends SecurityManager {
    private String password;
    private BufferedReader stdin;

    public PasswordSecurityManager(String p, BufferedReader b) {
        super();
        this.password = p;
	this.stdin = b;
    }

    private boolean accessOK() {
        String response;

        System.out.print("Password: ");
        try {
            response = stdin.readLine();
            if (response.equals(password))
                return true;
            else
                return false;
        } catch (IOException e) {
            return false;
        }
    }

    public void checkRead(FileDescriptor filedescriptor) {
        if (!accessOK())
            throw new SecurityException("Not a Chance!");
    }

    public void checkRead(String filename) {
        if (!accessOK())
            throw new SecurityException("No Way!");
    }

    public void checkRead(String filename, Object executionContext) {
        if (!accessOK())
            throw new SecurityException("Forget It!");
    }

    public void checkWrite(FileDescriptor filedescriptor) {
        if (!accessOK())
            throw new SecurityException("Not!");
    }

    public void checkWrite(String filename) {
        if (!accessOK())
            throw new SecurityException("Not Even!");
    }
} // PasswordSecurityManager

Mer om strömmar: gränssnittet Serializable

Programmet SerializationTest.java:
import java.io.*;
import java.util.*;

class Data implements Serializable {
    private int n;
    public Data(int n) { this.n = n; }
    public String toString() { return Integer.toString(n); }
} // class Data

public class SerializationTest {
    public static void main(String[] args)
        throws ClassNotFoundException, IOException {

        Data d1 = new Data(17);
        Data d2 = new Data(4711);
        Data d3 = new Data(7);

        System.out.println("d1 = " + d1);
        System.out.println("d2 = " + d2);
        System.out.println("d3 = " + d3);

        ObjectOutputStream out =
            new ObjectOutputStream(new FileOutputStream("data.out"));
        out.writeObject(d1);
        out.writeObject(d2);
        out.writeObject(d3);
        out.close(); // Also flushes output

        ObjectInputStream in =
            new ObjectInputStream(new FileInputStream("data.out"));

        Data d4 = (Data)in.readObject();
        Data d5 = (Data)in.readObject();
        Data d6 = (Data)in.readObject();

        System.out.println("d4 = " + d4);
        System.out.println("d5 = " + d5);
        System.out.println("d6 = " + d6);
    }
} // class SerializationTest
Utmatning:
d1 = 17
d2 = 4711
d3 = 7
d4 = 17
d5 = 4711
d6 = 7
Filen data.out innehåller 53 bytes. Decimal utskrift:
 172 237   0   5 115 114   0   4  68  97 116  97 187  52  26  19
 141 248 164   9   2   0   1  73   0   1 110 120 112   0   0   0
  17 115 113   0 126   0   0   0   0  18 103 115 113   0 126   0
   0   0   0   0   7
Som tecken:
  ¬   í  \0 005   s   r  \0 004   D   a   t   a   »   4 032 023
 215   ø   ¤  \t 002  \0 001   I  \0 001   n   x   p  \0  \0  \0
 021   s   q  \0   ~  \0  \0  \0  \0 022   g   s   q  \0   ~  \0
  \0  \0  \0  \0  \a
Programmet WormTest.java (fritt efter Bruce Eckels Thinking in Java, 3rd Ed):
import java.io.*;
import java.util.*;

class Data implements Serializable {
    private int n;
    public Data(int n) { this.n = n; }
    public String toString() { return Integer.toString(n); }
} // class Data

class Worm implements Serializable {
    private static Random rand = new Random();
    private Data[] d = {
        new Data(rand.nextInt(10)),
        new Data(rand.nextInt(10)),
        new Data(rand.nextInt(10))
    };
    private Worm next;
    private char c;
    public Worm(int nr_segments, char x) {
        System.out.println("Worm constructor: " + nr_segments);
        c = x;
        if (--nr_segments > 0)
            next = new Worm(nr_segments, (char)(x + 1));
    }
    public Worm() {
        System.out.println("Default constructor");
    }
    public String toString() {
        String s = ":" + c + "(";
        for (int i = 0; i < d.length; i++)
            s += d[i];
        s += ")";
        if (next != null)
            s += next;
        return s;
    }
} // class Worm

public class WormTest {
    public static void main(String[] args)
        throws ClassNotFoundException, IOException {
        Worm w = new Worm(6, 'a');
        System.out.println("w = " + w);
        ObjectOutputStream out =
            new ObjectOutputStream(new FileOutputStream("worm.out"));
        out.writeObject("Worm storage\n");
        out.writeObject(w);
        out.close(); // Also flushes output
        ObjectInputStream in =
            new ObjectInputStream(new FileInputStream("worm.out"));
        String s = (String)in.readObject();
        Worm w2 = (Worm)in.readObject();
        System.out.println(s + "w2 = " + w2);
        ByteArrayOutputStream bout =
            new ByteArrayOutputStream();
        ObjectOutputStream out2 = new ObjectOutputStream(bout);
        out2.writeObject("Worm storage\n");
        out2.writeObject(w);
        out2.flush();
        ObjectInputStream in2 =
            new ObjectInputStream(new ByteArrayInputStream(bout.toByteArray()));
        s = (String)in2.readObject();
        Worm w3 = (Worm)in2.readObject();
        System.out.println(s + "w3 = " + w3);
    }
} // class WormTest
Utmatning:
Worm constructor: 6
Worm constructor: 5
Worm constructor: 4
Worm constructor: 3
Worm constructor: 2
Worm constructor: 1
w = :a(839):b(652):c(246):d(663):e(468):f(373)
Worm storage
w2 = :a(839):b(652):c(246):d(663):e(468):f(373)
Worm storage
w3 = :a(839):b(652):c(246):d(663):e(468):f(373)

Javabönor

Hinner vi nog inte med idag.