Java: Lösningar till tentamen 2004-12-16

Observera att detta är förslag på lösningar. Det kan finnas andra lösningar som också är korrekta, och det kan hända att en del av lösningarna är mer omfattande än vad som krävs för full poäng på uppgiften.

Uppgift 1 (3p)

a) (1p)

Den virtuella maskinen är inte någon fysisk maskin, det vill säga en maskin som man kan ta på, utan den är ett program som läser Java-maskinkoden och sen gör det som den maskinkoden, enligt Java-specifikationen, ska göra.

b) (2p)

Fördelar är att de kompilerade Java-programmen blir portabla mellan olika hårdvaruarkitekturer, och att man kan göra mer avancerad hantering av programmen under körningen än med en fysisk processor.

En nackdel är att man för det mesta och i genomsnitt får sämre prestanda.

Uppgift 2 (5p)

a) (2p)

class Mask {
    private final int längd;
    private Mask störstaMasken = null;
    public Mask(int längd) {
	this.längd = längd;
	if (störstaMasken == null || störstaMasken.längd > this.längd)
	    störstaMasken = this;
    }
} // Mask

b) (3p)

class Äpple { 
    private Mask mask = null;
    public Äpple() { }
    public Äpple(Mask masken) {
	this.mask = masken;
    }
    public void stoppaInEnMask(Mask masken)
	throws ÄppletRedanFulltException {
	if (this.mask != null)
	    throw new ÄppletRedanFulltException();
	this.mask = masken;
    }
    public void taBortMasken()
	throws ÄppletRedanTomtException {
	if (this.mask == null)
	    throw new ÄppletRedanTomtException();
	this.mask = null;
    }
} // Äpple

Uppgift 3 (4p)

Skapar en X(1).
Skapar en XYZ(1, 2, 3).
Skapar en X(0).
Skapar en X().
Skapar en X(4).
Skapar en XYZ(4, 5, 6).
Skapar en X(0).
Skapar en X().
Skapar en XYZ(17, 19).
Uppgift3.f
X.f
XYZ.f
Uppgift3.g
XYZ.f

Uppgift 4 (15p)

// <applet code="TempApplet" width="250" height="200"></applet>

import javax.swing.JApplet;
import javax.swing.JLabel;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

import java.net.InetAddress;
import java.net.Socket;

import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.Iterator;

public final class TempApplet extends JApplet implements ActionListener {
    private static final int PORT = 3003;
    private JLabel etikett = new JLabel("De senaste temperaturerna:");
    private JTextArea tempfönster = new JTextArea(8, 20);
    private JButton knapp = new JButton("Rensa");

    public void init() {
	try {
	    Container cp = getContentPane();
	    cp.setLayout(new FlowLayout());
	    cp.add(etikett);
	    cp.add(tempfönster);
	    cp.add(knapp);
	    knapp.addActionListener(this);
	    InetAddress addr = InetAddress.getByName("localhost");
	    Socket socket = new Socket(addr, PORT);
	    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
	    ServerListener t = new ServerListener(in, tempfönster);
	    t.start();
	}
	catch (IOException e) {
	    tempfönster.append("Det har uppstått ett fel.");
	}
    }

    public void actionPerformed(ActionEvent e) {
	tempfönster.setText("");
    }

    public static void main(String[] args) {
        TempApplet applet = new TempApplet();
        JFrame frame = new JFrame("TempApplet");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(applet);
        frame.setSize(250, 200);
        applet.init();
        applet.start();
        frame.setVisible(true);
    }
} // class TempApplet

final class ServerListener extends Thread {
    private final BufferedReader fromServer;
    private final JTextArea utfönster;
    private final HashMap tabellen;

    public ServerListener(BufferedReader fromServer, JTextArea utfönster) {
	this.fromServer = fromServer;
	this.utfönster = utfönster;
	this.tabellen = new HashMap();
    }

    public void run() {
	String lineFromServer;	
	try {
	    while ((lineFromServer = fromServer.readLine()) != null) {
		// Warning: This fails for location names that contain spaces.
		StringTokenizer tokenizer = new StringTokenizer(lineFromServer, " ");
		String ort = tokenizer.nextToken();
		String temperatur = tokenizer.nextToken();
		tabellen.put(ort, temperatur);
		utfönster.setText("");
		for (Iterator i = tabellen.keySet().iterator(); i.hasNext(); ) {
                    String o = (String)i.next();
		    utfönster.append(o + " " + tabellen.get(o) + "\n");
                }
	    }
	    utfönster.append("Servern har kopplat ner.\n");
	}
	catch (IOException e) {
	    utfönster.append("Det har uppstått ett fel.");
	}
    }
} // class ServerListener

Uppgift 5 (5p)

a) (2p)

En applet är avsedd att köras inuti en webbläsare, som en del av en webbsida. En applikation är ett fristående program. (Dock kräver applikationen, precis som appleten, att det finns en Java-maskin installerad på datorn.)

När en applet ska startas (av webbläsaren) skapas det först en instans av applet-klassen, och sen anropas metoden init i den instansen. När en applikation ska startas skapas det inte automatiskt någon instans, utan den statiska metoden main anropas.

Eftersom appletar är tänkta att kunna användas för att till exempel göra saker som blinkar oc h tutar på webbsidor, och då laddas ner och startas automatiskt av webbläsaren utan att användaren fattat något beslut om att starta något program, måste de vara säkra. Därför finns det begränsningar på vad appletar får göra. Till exempel får en applikation läsa och skriva lokala filer på datorn, men det får inte appleten göra.

b) (2p)

Genom att lägga till en main-metod, mest praktiskt i själva applet-klassen, som skapar ett fönster att visa appleten i, som skapar en instans av appleten, och som anropar init. Så här kan en main-metod se ut som man kan lägga in i appleten i uppgift 1:

    public static void main(String[] args) {
        PlusMinusApplet applet = new PlusMinusApplet();
        JFrame frame = new JFrame("PlusMinusApplet");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(applet);
        frame.setSize(160, 85);
        applet.init();
        applet.start();
        frame.setVisible(true);
    }

c) (1p)

Man kan underlätta felsökningen genom att skriva ut spårutskrifter, på System.out eller på en loggfil som man skapar för det ändamålet, vilket inte går från en applet. Om appleten ska ansluta till en dator via Internet, behöver man inte ha appleten liggande på just den datorn. (Kom ihåg att en applet bara får ansluta till samma dator som appleten själv hämtades från.) Man slipper också skriva html-koden för att visa appleten. Man slipper använda en webbläsare eller speciell appletviewer.

Uppgift 6 (10p)

Man startar servern som en applikation, och en eller flera appletar kan sen koppla upp sig mot den. Serven läser inmatning från standardinmatningen, det vill säga för det mesta tangentbordet, och skickar vidare varje rad till alla uppkopplade appletar. Genom att skriva in olika kombinationer av temperaturdata i serven, kan man provköra appleten och testa dess funktion.

import java.net.ServerSocket;
import java.net.Socket;

import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Iterator;

class InputThread extends Thread {
    BufferedReader in;

    public InputThread() {
	in = new BufferedReader(new InputStreamReader(System.in));
	start();
    }

    public void run() {
	try {
	    while (true) {
		String inline = in.readLine();
		if (inline == null || inline.equals("quit"))
		    break;
		System.out.println("Du sa '" + inline + "'");
		ClientConnection.sendToAllClients(inline);
	    }
	}
	catch(IOException e) {
	    System.out.println("InputThread: " + e);
	}
    } // run
} // class InputThread

class ClientConnection {
    private static ArrayList allClients = new ArrayList();
    private final Socket socket;
    private final PrintWriter out;

    public ClientConnection(Socket s) throws IOException {
	socket = s;
	out = new PrintWriter(
	          new BufferedWriter(
		      new OutputStreamWriter(
		          socket.getOutputStream())), true);
	System.out.println("En klienttråd har skapats.");
	allClients.add(this);
    }
    public static void sendToAllClients(String line) {
	Iterator i = allClients.iterator();
	while (i.hasNext()) {
	    ClientConnection t = (ClientConnection)i.next();
	    t.out.println(line);
	}
    }
} // class ClientThread

public class Temperaturserver {
    public static final int PORT = 3003;
    public static void main(String[] args) throws IOException {
	ServerSocket s = new ServerSocket(PORT);
	System.out.println("Server-socketen: " + s);
	System.out.println("Servern lyssnar...");

	InputThread it = new InputThread();

	try {
	    while(true) {
		// Blocks until a connection occurs:
		Socket socket = s.accept();
		System.out.println("Uppkoppling accepterad.");
		System.out.println("Den nya socketen: " + socket);
		try {
		    ClientConnection cc = new ClientConnection(socket);
		    System.out.println("Ny klientkoppling skapad.");
		    System.out.println("Den nya klientkopplingen: " + cc);
		}
		catch(IOException e) {
		    // If the constructor fails, close the socket,
		    // otherwise the thread will close it:
		    socket.close();
		}
	    }
	}
	finally {
	    s.close();
	}
    } // main
} // Temperaturserver


Thomas Padron-McCarthy (Thomas.Padron-McCarthy@tech.oru.se) 16 december 2004