Ungefär motsvarande föreläsningsanteckningar från förra året: Delar av java013.pdf
Filen Client2.java: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 ClientThread extends Thread { private static int numberOfClients = 0; private static ArrayList allClients = new ArrayList(); private final int clientNumber = ++numberOfClients; private final Socket socket; private final BufferedReader in; private final PrintWriter out; public ClientThread(Socket s) throws IOException { socket = s; 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 " + clientNumber + " skapad."); out.println("Välkommen. Du är klient nummer " + clientNumber + "."); allClients.add(this); // 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 { while (true) { String inline = in.readLine(); System.out.println("Klienttråd " + clientNumber + " tog emot: " + inline); // Not: inline == "quit" if (inline == null || inline.equals("quit")) break; out.println("Du sa '" + inline + "'"); Iterator i = allClients.iterator(); while (i.hasNext()) { ClientThread t = (ClientThread)i.next(); if (t != this) t.out.println("Från klient " + clientNumber + ": " + inline); } } System.out.println("Klienttråd " + clientNumber + ": Avslutar..."); } catch(IOException e) { System.out.println("Klienttråd " + clientNumber + ": I/O-fel"); } finally { try { socket.close(); } catch(IOException e) { System.out.println("Klienttråd " + clientNumber + ": Socketen ej stängd"); } allClients.remove(allClients.indexOf(this)); } } // run } // class ClientThread public class MultiServer { public static final int PORT = 2000; public static void main(String[] args) throws IOException { ServerSocket s = new ServerSocket(PORT); System.out.println("Server-socketen: " + s); System.out.println("Servern lyssnar..."); 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 { ClientThread t = new ClientThread(socket); 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 { s.close(); } } // main } // MultiServer
import java.net.Socket; import java.net.InetAddress; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.PrintWriter; import java.io.IOException; final class ServerListener extends Thread { final private BufferedReader fromServer; public ServerListener(BufferedReader fromServer) { this.fromServer = fromServer; } public void run() { String lineFromServer; try { while ((lineFromServer = fromServer.readLine()) != null && !lineFromServer.equals("quit")) { System.out.println("Från servern: " + lineFromServer); } } catch (IOException e) { System.out.println("Undantag fångat: " + e); } } } // class ServerListener public class Client2 { public static final int PORT = 2000; public static void main(String[] args) throws IOException { InetAddress addr; if (args.length >= 1) addr = InetAddress.getByName(args[0]); else addr = InetAddress.getByName(null); Socket socket = new Socket(addr, PORT); System.out.println("Den nya socketen: " + socket); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter( socket.getOutputStream())), true); // true: PrintWriter is line buffered BufferedReader kbd_reader = new BufferedReader( new InputStreamReader(System.in)); ServerListener t = new ServerListener(in); t.start(); String buf; while (true) { buf = kbd_reader.readLine(); System.out.println("Från tangentbordet: " + buf); System.out.println("Till servern: " + buf); out.println(buf); } } // main } // class Client2
Som övning kan du flytta även utmatningen och inmatningen till det fönstret, så har du en grafisk chatklient!
Filen Client3.java:
import java.net.*; import java.io.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; final class ServerListener extends Thread { private final BufferedReader fromServer; public ServerListener(BufferedReader fromServer) { this.fromServer = fromServer; } public void run() { String lineFromServer; try { while ((lineFromServer = fromServer.readLine()) != null && !lineFromServer.equals("quit")) { System.out.println("Från servern: " + lineFromServer); } } catch (IOException e) { System.out.println("Undantag fångat: " + e); } } } // class ServerListener class ChatButtonWindow extends JFrame { private final PrintWriter toServer; public ChatButtonWindow(PrintWriter to) { super("Extra chat buttons"); this.toServer = to; Container cp = getContentPane(); cp.setLayout(new FlowLayout()); JButton button1 = new JButton("Skicka förolämpning"); cp.add(button1); button1.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { toServer.println("Ni, min herre, är en apa."); System.out.println("Skickade en förolämpning."); } }); JButton button2 = new JButton("Skicka beröm"); cp.add(button2); button2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { toServer.println("Du är bäst!"); System.out.println("Skickade beröm."); } }); JButton button3 = new JButton("Avsluta"); cp.add(button3); button3.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.out.println("Avslutar."); System.exit(0); } }); setSize(200, 200); setVisible(true); } } // class ChatButtonWindow public class Client3 { public static final int PORT = 2000; public static void main(String[] args) throws IOException { InetAddress addr; if (args.length >= 1) addr = InetAddress.getByName(args[0]); else addr = InetAddress.getByName(null); Socket socket = new Socket(addr, PORT); System.out.println("Den nya socketen: " + socket); BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter( new BufferedWriter( new OutputStreamWriter( socket.getOutputStream())), true); // true: PrintWriter is line buffered BufferedReader kbd_reader = new BufferedReader( new InputStreamReader(System.in)); ServerListener t = new ServerListener(in); t.start(); ChatButtonWindow w = new ChatButtonWindow(out); String buf; while (true) { buf = kbd_reader.readLine(); System.out.println("Från tangentbordet: " + buf); System.out.println("Till servern: " + buf); out.println(buf); } } // main } // class Client3
Exempel 1:
Java 2 har flera nya typer av behållare, som inte tas upp i Erikssons bok Programutveckling med Java. Läs mer i Kapitiel 11 i Bruce Eckels bok Thinking in Java, 3d Ed.
Klassdiagram med med de viktigaste typerna av behållare (ur Bruce Eckels Thinking in Java, 3d Ed):
Behållarna finns i paketet java.util:
import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.HashSet; import java.util.TreeSet; import java.util.HashMap; import java.util.TreeMap;
Alla behållare (utom de inbyggda arrayerna) innehåller enbart Object:
Ett testprogram med en massa behållare av olika slag: Datatest.java
Man måste skapa själva arrayen med new:public static void main(String[] args) { for (int i = 0; i < args.length; ++i) System.out.println("Argument " + i + ": '" + args[i] + "'"); } }
Paketet java.util.Arrays innehåller några nyttiga funktioner, till exempel sort som sorterar en array:int[] intarray1 = new int[10]; int intarray2[] = new int[10]; String[] fruits = { "Äpple", "Päron", "Apelsin" }; int intarray[10]; // Ger kompileringsfel: ']' expected fruits = new String[] { "Ananas", "Banan" };
Man kan förstås också ha instanser av sina egna klasser i arrayerna. Antag att vi har en klass som heter Hamster:java.util.Arrays.sort(fruits); java.util.Arrays.fill(fruits, "Citron");
Utmatning, om vi har definierat en lämplig toString-metod i klassen Hamster:Hamster[] ha1 = { new Hamster("Adam"), new Hamster("Bertil"), new Hamster("Cecar") }; for (int i = 0; i < ha1.length; ++i) System.out.println("ha1[" + i + "]: '" + ha1[i] + "'");
Man kan också allokera arrayen först, och sätta elementen efteråt:ha1[0]: 'Hamster-Adam' ha1[1]: 'Hamster-Bertil' ha1[2]: 'Hamster-Cecar'
Om man försöker adressera utanför arrayen, kastas ett ArrayIndexOutOfBoundsException:Hamster[] ha2 = new Hamster[4]; ha2[0] = new Hamster("Anna"); ha2[1] = new Hamster("Beata"); ha2[2] = new Hamster("Cecilia"); ha2[3] = new Hamster("Dora");
Arrayer är typsäkra. Det går inte att stoppa in fel sorts objekt:try { ha2[4] = new Hamster("Eva"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("Undantag som fångades: '" + e + "'"); }
Arrayerna har fix storlek, men man kan ändå "ändra storlek" på en array genom att skapa en ny:ha2[2] = new Båt("Titanic"); // Ger kompileringsfel: "incompatible types"
System.out.println("ha2.length = " + ha2.length); ha2 = new Hamster[7]; System.out.println("ha2.length = " + ha2.length);
Utmatning, om vi har definierat en lämplig toString-metod i klassen Båt:ArrayList båtar = new ArrayList(); Båt b1 = new Båt("Titanic"); Båt b2 = new Båt("Exxon Valdez"); Båt b3 = new Båt("Torrey Canyon"); båtar.add(b1); båtar.add(b2); båtar.add(b3); båtar.add(b1); båtar.add(b1); System.out.println("Båtarna:"); for (int i = 0; i < båtar.size(); ++i) System.out.println(" båtar[" + i + "]: " + båtar.get(i));
Vi la ju till båten Titanic flera gånger, så den förekommer förstås flera gånger i listan!Båtarna: båtar[0]: Båten Titanic båtar[1]: Båten Exxon Valdez båtar[2]: Båten Torrey Canyon båtar[3]: Båten Titanic båtar[4]: Båten Titanic
I stället för att stega fram ett heltal som anger vilken position vi vill titta på, och sen hämta elementet på den positionen med get, kan vi använda en Iterator. Metoden iterator returnerar en ny iterator, och i den finns metoderna hasNext och next.
Oftast är det mer praktiskt att byta while-loopen mot en for-loop. Då kan man definiera iterator-variabeln så att den bara finns inuti själva loopen.System.out.println("Båtarna:"); Iterator i1 = båtar.iterator(); while (i1.hasNext()) System.out.println(" En båt: " + i1.next());
Såvitt Java-kompilatorn känner till, innehåller en ArrayList objekt av typen Object. Därför kommer get och next, såvitt kompilatorn vet, bara att returnera Object, och inte något mer specifikt:System.out.println("Båtarna:"); for (Iterator i = båtar.iterator(); i1.hasNext(); ) System.out.println(" En båt: " + i.next());
Man måste göra en explicit typkonvertering:System.out.println("Båtarna:"); for (Iterator i = båtar.iterator(); i.hasNext(); ) { Båt b; b = i.next(); // Ger kompileringsfel: "incompatible types" System.out.println(" En båt: " + b); }
Observera att det är inget objekt som konverteras på något sätt. Vi bara talar om för Java-kompilatorn att "hördudu, det där Object som returneras av metoden next, det är faktiskt en Båt, så du kan lugnt lägga det i variabeln b".System.out.println("Båtarna:"); for (Iterator i = båtar.iterator(); i.hasNext(); ) { Båt b; b = (Båt)i.next(); System.out.println(" En båt: " + b); }
Men dessutom kan man kan stoppa in objekt av helt fel sorts typ:
Ojoj! Nu finns det en hamster bland båtarna.Hamster hasse = new Hamster("Hasse"); båtar.add(hasse); // Inget fel -- "båtar" innehåller Object, inte Båt!
Jämför med C++, och dess templates: vector<Boat>
När man försöker konvertera till den agivna typen, kan det kastas ett ClassCastException. Ett Object som verkligen är av typen Båt kan konverteras till Båt (jag talar ju bara om att den där saken där, det är faktiskt en båt), men en Hamster kan omöjligt konverteras till en Båt. (På vetenskapens nuvarande stadium.)
Metoden indexOf returnerar positionen för (den första förekomsten av) ett objekt i listan, eller -1 om det inte fanns i listan.try { System.out.println("Båtarna:"); for (Iterator i = båtar.iterator(); i.hasNext(); ) { Båt b; b = (Båt)i.next(); // Kommer att ge ClassCastException för hamstern Hasse System.out.println(" En båt: " + b); } } catch (ClassCastException e) { System.out.println("Undantag som fångades: '" + e + "'"); }
Tänk på att den gör en likhetsjämförelse. Är en ny hamster som heter Hasse lika med den gamla hamstern som heter Hasse? Eller räknas två hamstrar bara som lika om de verkligen är samma hamster, dvs om de två variablerna refererar till samma objekt? Det beror på hur vi definierat klassen Hamster, och dess equals-metod. Om vi inte skrivit någon egen equals-metod, räknas två objekt som lika bara om de är samma objekt.
Vi kan ta bort element ur listan med hjälp av metoden remove, både genom att ange en position och genom att ange vilket objekt vi vill ta bort:System.out.println("Hasses position (1): " + båtar.indexOf(hasse)); System.out.println("Hasses position (2): " + båtar.indexOf(new Hamster("Hasse")));
båtar.remove(4); båtar.remove(b1);
LinkedList båtlista = new LinkedList(); for (Iterator i = båtar.iterator(); i.hasNext(); ) båtlista.add(i.next()); båtlista.addAll(båtar); // Alla på en gång! System.out.println("Båtarna i båtlistan:"); for (Iterator i = båtlista.iterator(); i.hasNext(); ) System.out.println(" En båt: " + i.next());
Både HashSet och TreeSet implementerar gränssnittet Set, så de kan göra precis samma saker, men de har olika prestanda.
Utmatning:HashSet båtmängd = new HashSet(); for (Iterator i = båtar.iterator(); i.hasNext(); ) båtmängd.add(i.next()); båtmängd.addAll(båtar); // Alla på en gång! System.out.println("Båtarna i båtmängden:"); for (Iterator i = båtmängd.iterator(); i.hasNext(); ) System.out.println(" En båt: " + i.next()); båtmängd.remove(hasse); System.out.println("Båtarna i båtmängden, nu utan hamstern Hasse:"); for (Iterator i = båtmängd.iterator(); i.hasNext(); ) System.out.println(" En båt: " + i.next());
Eftersom elementen lagras i en hashtabell, kan de komma i vilken ordning som helst.Båtarna i båtmängden: En båt: Båten Exxon Valdez En båt: Båten Titanic En båt: Hamster-Hasse En båt: Båten Torrey Canyon Båtarna i båtmängden, nu utan hamstern Hasse: En båt: Båten Exxon Valdez En båt: Båten Titanic En båt: Båten Torrey Canyon
TreeSet lagrar internt sina data i form av ett träd, och den kräver därför att objekten implementerar gränssnittet Comparable, som säger att det ska finnas en compareTo-metod.
Utmatning:// TreeSet kräver att objekten implementerar Comparable TreeSet telefonnummermängd2 = new TreeSet(); telefonnummermängd2.add(new Telefonnummer(116090)); telefonnummermängd2.add(new Telefonnummer(271010)); telefonnummermängd2.add(new Telefonnummer(271011)); telefonnummermängd2.add(new Telefonnummer(271012)); telefonnummermängd2.add(new Telefonnummer(111111)); telefonnummermängd2.add(new Telefonnummer(111111)); telefonnummermängd2.add(new Telefonnummer(111111)); System.out.println("Telefonnumren i telefonnummermängd 2:"); for (Iterator i = telefonnummermängd2.iterator(); i.hasNext(); ) System.out.println(" Ett telefonnummer: " + i.next());
Eftersom det är ett träd, kommer nycklarna i ordning.Telefonnumren i telefonnummermängd 2: Ett telefonnummer: +46-(0)19-111111 Ett telefonnummer: +46-(0)19-116090 Ett telefonnummer: +46-(0)19-271010 Ett telefonnummer: +46-(0)19-271011 Ett telefonnummer: +46-(0)19-271012
En HashMap och TreeMap implementerar gränssnittet Map, så de kan göra precis samma saker.
Utmatning:HashMap telefonbok1 = new HashMap(); telefonbok1.put("Anna", new Telefonnummer(116090)); telefonbok1.put("Bengt", new Telefonnummer(224000)); telefonbok1.put("Conny", new Telefonnummer(125566)); telefonbok1.put("Doris", new Telefonnummer(171045)); telefonbok1.put("Eberhart", new Telefonnummer(111111)); telefonbok1.put("Eberhart", new Telefonnummer(222222)); telefonbok1.put("Eberhart", new Telefonnummer(333333)); System.out.println("Conny har nummer " + telefonbok1.get("Conny")); System.out.println("Telefonbok 1:"); for (Iterator i = telefonbok1.keySet().iterator(); i.hasNext(); ) { String namn = (String)i.next(); System.out.println(" " + namn + " har nummer " + telefonbok1.get(namn)); }
Eftersom det är en hashtabell, kan nycklarna komma i vilken ordning som helst.Conny har nummer +46-(0)19-125566 Telefonbok 1: Conny har nummer +46-(0)19-125566 Doris har nummer +46-(0)19-171045 Eberhart har nummer +46-(0)19-333333 Bengt har nummer +46-(0)19-224000 Anna har nummer +46-(0)19-116090
TreeMap lagrar internt sina nycklar i form av ett träd, och den kräver därför att nyckelobjekten implementerar gränssnittet Comparable, som säger att det ska finnas en compareTo-metod.
Utmatning:// TreeMap kräver att nycklarna implementerar Comparable (och det gör String) TreeMap telefonbok2 = new TreeMap(); telefonbok2.put("Anna", new Telefonnummer(116090)); telefonbok2.put("Bengt", new Telefonnummer(224000)); telefonbok2.put("Conny", new Telefonnummer(125566)); telefonbok2.put("Doris", new Telefonnummer(171045)); telefonbok2.put("Eberhart", new Telefonnummer(111111)); telefonbok2.put("Eberhart", new Telefonnummer(222222)); telefonbok2.put("Eberhart", new Telefonnummer(333333)); System.out.println("Telefonbok 2:"); for (Iterator i = telefonbok2.keySet().iterator(); i.hasNext(); ) { String namn = (String)i.next(); System.out.println(" " + namn + " har nummer " + telefonbok2.get(namn)); }
Eftersom det är ett träd, kommer nycklarna i ordning.Telefonbok 2: Anna har nummer +46-(0)19-116090 Bengt har nummer +46-(0)19-224000 Conny har nummer +46-(0)19-125566 Doris har nummer +46-(0)19-171045 Eberhart har nummer +46-(0)19-333333