Java: Lösningar till tentamen 2008-03-15

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. En del av lösningarna är kanske inte fullständiga, utan hänvisar bara till var man kan läsa svaret.

Uppgift 1 (6 p)

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

class Lok {
    private int nummer;
    private int vikt;
    public Lok(int nummer, int vikt) {
        this.nummer = nummer;
        this.vikt = vikt;
    }
    public int getNummer() {
        return nummer;
    }
}

class Vagn {
    private int tomvikt;
    private int lastvikt;
    public Vagn(int tomvikt, int lastvikt) {
        this.tomvikt = tomvikt;
        this.lastvikt = lastvikt;
    }
}

class Tåg {
    private Lok lok;
    private List<Vagn> vagnar = new ArrayList<Vagn>();
    private Plats plats;
    public Tåg(Lok lok, Plats plats) {
        this.lok = lok;
        this.plats = plats;
    }
    public void åkTill(Plats destination) {
        plats = destination;
    }
    public void kopplaPåEnVagn(Vagn vagnen) {
        vagnar.add(vagnen);
    }
    public int getNummer() {
        return lok.getNummer();
    }
}

abstract class Plats {

}

class Station extends Plats {
    private String namn;
    public Station(String namn) {
        this.namn = namn;
    }
}

class Sträcka extends Plats {
    private Station från;
    private Station till;
    public Sträcka(Station från, Station till) {
        this.från = från;
        this.till = till;
    }
}
Kommentar: Metoderna getNummer i klasserna Lok och Tåg behövs egentligen inte i den här uppgiften, utan är till för att underlätta i uppgift 4.

Uppgift 2 (6 p)

    public static void main(String[] args) {
        Station örebro_central = new Station("Örebro Central");
        Station örebro_södra = new Station("Örebro Södra");
        Sträcka järnvägen = new Sträcka(örebro_central, örebro_södra);
        Lok lok1 = new Lok(1, 100);
        Lok lok2 = new Lok(2, 200);
        List<Vagn> alla_vagnar = new ArrayList<Vagn>();
        for (int i = 0; i < 1000; ++i)
            alla_vagnar.add(new Vagn(10, 0));
        Tåg tåg1 = new Tåg(lok1, örebro_central);
        tåg1.kopplaPåEnVagn(alla_vagnar.get(0));
        tåg1.kopplaPåEnVagn(alla_vagnar.get(1));
        tåg1.kopplaPåEnVagn(alla_vagnar.get(2));
        Tåg tåg2 = new Tåg(lok2, örebro_södra);
        tåg2.kopplaPåEnVagn(alla_vagnar.get(3));
        tåg1.åkTill(järnvägen);
        tåg2.åkTill(järnvägen);
        tåg1.åkTill(örebro_södra);
    }

Uppgift 3 (5 p)

a) (2p)

class Tankvagn extends Vagn {
    private double volym;
    public Tankvagn(int tomvikt, int lastvikt, double volym) {
        super(tomvikt, lastvikt);
        this.volym = volym;
    }
}

b) (1p)

.java-filen innehåller källkoden, dvs den Java-kod som programmeraren skrivit in (eller som, i vissa fall, genererats av något verktyg). .class-filen innehåller den kompilerad koden, som kompilerats av Java-kompilatorn och som kan köras av den virtuella Java-maskinen.

c) (2p)

Ja. Vi skriver en ny .java-fil, som i deluppgift a, och den kan sen kompileras av Java-kompilatorn. Den information om basklassen (Vagn) som den Java-kompilatorn behöver, kan den hämta från .class-filen Vagn.class.

I enlighet med hur polymorfism fungerar i objektorientering, kan sen variabler och metodparametrar av typen Vagn utan problem innehålla en Tankvagn, eftersom en Tankvagn ju är en sorts Vagn:

    tåg2.kopplaPåEnVagn(new Tankvagn(10, 0, 17.2));

Uppgift 4 (15 p)

Här har vi förenklat programmeringen genom att protokollet alltid skriver stationsnamn på en separat rad. Notera att servern behöver vara flertrådad: en tråd för varje klient, plus en tråd som lyssnar efter nya uppkopplingar.
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.Map;
import java.util.HashMap;

class Klienttråd extends Thread {
    private final Socket socket;
    private final BufferedReader in;
    private final PrintWriter out;
    private final Spelet spelet;
    private Tåg tåget;

    public Klienttråd(Socket s, Spelet spelet) throws IOException {
        socket = s;
        this.spelet = spelet;
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
        // true: PrintWriter is line buffered

        System.out.println("Klienttråd skapad.");

        // If any of the above calls throw an 
        // exception, the caller is responsible for
        // closing the socket. Otherwise the thread
        // will close it.
        start(); // Starts the thread, and calls run()
    }

    public void run() {
        try {
            tåget = spelet.skapaNyttTåg();
            out.println("TÅG " + tåget.getNummer());
            out.println("Örebro Central");

            while (true) {
                String kommando = in.readLine();
                System.out.println("Klienttråd tog emot: " + kommando);
                if (kommando == null) {
                    break;
                }
                try {
                    if (kommando.equals("FLYTTA")) {
                        String destinationsnamn = in.readLine();
                        Station destination = spelet.slåUppStation(destinationsnamn);
                        if (destination == null) {
                            out.println("NOK");
                        }
                        else {
                            tåget.åkTill(destination);
                            out.println("OK");
                        }
                    }
                    else {
                        out.println("NOK");
                    }
                }
                catch (NumberFormatException e) {
                    out.println("Error!");
                }
            }
            System.out.println("Klienttråd: Avslutar...");
        }
        catch(IOException e) {
            System.out.println("Klienttråd: I/O-fel");
        }
        finally {
            try {
                socket.close();
            }
            catch(IOException e) {
                System.out.println("Klienttråd: Socketen ej stängd");
            }
        }
    } // run
} // class Klienttråd

class Spelet {
    int antalLok = 0;
    private Map<String, Station> stationer = new HashMap<String, Station>();
    public Spelet() {
        Station örebro_central = new Station("Örebro Central");
        stationer.put("Örebro Central", örebro_central);
        Station örebro_södra = new Station("Örebro Södra");
        stationer.put("Örebro Södra", örebro_södra);
        Station kumla = new Station("Kumla");
        stationer.put("Kumla", kumla);
    }
    public synchronized Tåg skapaNyttTåg() {
        Lok loket = new Lok(++antalLok, 100);
        Tåg tåget = new Tåg(loket, stationer.get("Örebro Central"));
        Vagn vagnen = new Vagn(10, 20);
        tåget.kopplaPåEnVagn(vagnen);
        return tåget;
    }
    public Station slåUppStation(String namnet) {
        return stationer.get(namnet);
    }
} // Spelet

public class Tågserver {
    public static final int PORT = 2000;
    public static void main(String[] args) throws IOException {
        Spelet spelet = new Spelet();
        ServerSocket ss = new ServerSocket(PORT);
        System.out.println("Server-socketen: " + ss);
        System.out.println("Servern lyssnar...");

        try {
            while(true) {
                // Blocks until a connection occurs:
                Socket socket = ss.accept();
                System.out.println("Uppkoppling accepterad.");
                System.out.println("Den nya socketen: " + socket);
                try {
                    Klienttråd t = new Klienttråd(socket, spelet);
                    System.out.println("Ny tråd skapad.");
                    System.out.println("Den nya tråden: " + t);
                }
                catch(IOException e) {
                    // If the constructor fails, close the socket,
                    // otherwise the thread will close it:
                    socket.close();
                }
            }
        }
        finally {
            ss.close();
        }
    } // main
} // Tågserver

Uppgift 5 (10 p)

Notera att klienten inte behöver vara flertrådad.

Först en rent textbaserad klient:

import java.net.InetAddress;

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;

public class Tågklient {
    public static final int PORT = 2000;

    public static void main(String[] args) throws IOException {
        InetAddress addr = InetAddress.getByName("localhost");
        Socket socket = new Socket(addr, 2000);
        System.out.println("Uppkopplad...");
        BufferedReader in = new BufferedReader(
                                new InputStreamReader(
                                    socket.getInputStream()));
        PrintWriter out = new PrintWriter(
                              new BufferedWriter(
                                  new OutputStreamWriter(
                                      socket.getOutputStream())), true);
        BufferedReader kbd_reader = new BufferedReader(
                                        new InputStreamReader(System.in));

        in.readLine(); // Ska vara t. ex. "TÅG 17"
        String aktuellPosition = in.readLine(); // Ska vara t. ex. "Örebro Central"

        while (true) {
            System.out.println("Tåget befinner sig nu här: " + aktuellPosition);
            System.out.println("Ange vart du vill köra tåget (Q för att avsluta): ");
            String destination = kbd_reader.readLine();
            if (destination.equals("Q"))
                break;
            else {
                out.println("FLYTTA");
                out.println(destination);
                String status = in.readLine();
                if (status == null)
                    break;
                else if (status.equals("OK"))
                    aktuellPosition = destination;
                else
                    System.out.println("Det gick inte!");
            }
        }
    }
} // Tågklient

Därefter en grafisk klient:

import java.net.InetAddress;
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.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class GrafiskTågklient extends JFrame {
    public static final String SERVER = "localhost";
    public static final int PORT = 2000;

    JLabel platsetiketten = new JLabel("Tågets plats:");
    JTextField platsfältet = new JTextField(20);
    JLabel destinationsetiketten = new JLabel("Destination:");
    JTextField destinationsfältet = new JTextField(20);
    JLabel resultatlabeln = new JLabel("Status:");
    JTextField resultatfältet = new JTextField(20);
    JButton körknappen = new JButton("Kör dit");
    JButton slutaknappen = new JButton("Sluta");

    Socket socketen;
    BufferedReader frånServern;
    PrintWriter tillServern;
    String aktuellPosition; // Ska vara t. ex. "Örebro Central"

    public GrafiskTågklient() {
        super("Grafisk Tågklient");
        Container cp = getContentPane();
        cp.setLayout(new FlowLayout());
        cp.add(platsetiketten);
        cp.add(platsfältet);
        cp.add(destinationsetiketten);
        cp.add(destinationsfältet);
        cp.add(resultatlabeln);
        cp.add(resultatfältet);
        cp.add(körknappen);
        cp.add(slutaknappen);
        setSize(350, 140);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }

    public void kopplaUpp() {
        try {
            InetAddress adressen = InetAddress.getByName("localhost");
            socketen = new Socket(adressen, 2000);
            frånServern = new BufferedReader(
                              new InputStreamReader(
                                  socketen.getInputStream()));
            tillServern = new PrintWriter(
                              new BufferedWriter(
                                  new OutputStreamWriter(
                                      socketen.getOutputStream())), true);
            String tågnummer = frånServern.readLine();
            aktuellPosition = frånServern.readLine();
            platsfältet.setText(aktuellPosition);
            resultatfältet.setText("Uppkopplad.");
        }
        catch (IOException e) {
            resultatfältet.setText("Fel!");
        }

        körknappen.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    try {
                        String destination = destinationsfältet.getText();
                        tillServern.println("FLYTTA");
                        tillServern.println(destination);
                        String status = frånServern.readLine();
                        if (status == null)
                            resultatfältet.setText("Fel!");
                        else if (status.equals("OK")) {
                            aktuellPosition = destination;
                            platsfältet.setText(aktuellPosition);
                            resultatfältet.setText("OK.");
                        }
                        else
                            resultatfältet.setText("Gick inte.");
                    }
                    catch (IOException e) {
                        resultatfältet.setText("Fel!");
                    }
                }
            });

        slutaknappen.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    System.exit(0);
                }
            });
    }

    public static void main(String[] args) throws IOException {
        GrafiskTågklient klienten = new GrafiskTågklient();
        klienten.kopplaUpp();
    }
} // GrafiskTågklient


Thomas Padron-McCarthy (Thomas.Padron-McCarthy@tech.oru.se) 26 mars 2008