Kod i "vanliga" programspråk brukar kompileras till maskinkod, som kan exekveras av en fysisk processor, till exempel en Pentium eller en Sparc-processor. Olika processortyper har olika maskinkod. Java-kod kompileras i stället till standardiserad bytekod, som kan köras av en virtuell javamaskin, det vill säga ett program som tolkar bytekoden.
b (2p)
Felhantering med undantag:
c (2p)
Abstrakta klasser och gränssnitt:
abstract class AbstraktKlass { public void f() { } abstract public void g(); } interface Gränssnitt { public void g(); } class Klass1 extends AbstraktKlass { public void g() { } public void h() { } } class Klass2 implements Gränssnitt { public void g() { } public void h() { } }Och sen, till exempel i main:AbstraktKlass ak = new Klass1(); Gränssnitt g = new Klass2();
Korrekt version av programmet:public class UndersökArgument { int summan = 0; public static int Main(Strings[] args) { private int antalHeltal = 0; for (int i = 0; i < args.length(); ++i); { try { /* Om argumentet inte int talet = * är ett heltal kastas Int.parseInt(args[i]); * NumberFormatException, antalHeltal = antalHeltal++; * och antalHeltal } * räknas inte upp. catch (NumberFormatException e) { */ } summan += talet; } // for System.out.println(antalHeltal + " tillåtna heltal."); System.out.println("Summan är " + summan + "."); } // main } // class UndersökArgument
Poängmall: 0 rätt = 0 poäng; 1-2 = 0.5; 3 = 1; 4-5 = 1.5; 6 = 2; 7-8 = 2.5; 9 = 3; 10 = 3.5; 11 = 4; 12-13 = 4.5; 14-15 = 5public class UndersökArgument { public static void main(String[] args) { int antalHeltal = 0; int summan = 0; for (int i = 0; i < args.length; ++i) { /* Om argumentet inte är ett heltal kastas * NumberFormatException, och antalHeltal * räknas inte upp. */ try { int talet = Integer.parseInt(args[i]); antalHeltal++; summan += talet; } catch (NumberFormatException e) { } } // for System.out.println(antalHeltal + " tillåtna heltal."); System.out.println("Summan är " + summan + "."); } // main } // class UndersökArgument
Här är en annan och lite mer rättfram lösning på actionPerformed:import javax.swing.*; import java.awt.event.*; import java.awt.*; public class CalcApplet extends JApplet implements ActionListener { private JTextField text1 = new JTextField(10); private JTextField text2 = new JTextField(10); private JButton plusButton = new JButton("+"); private JButton minusButton = new JButton("-"); private JButton timesButton = new JButton("*"); private JButton divideButton = new JButton("/"); private JTextField svarstext = new JTextField(10); public void actionPerformed(ActionEvent event) { String s1 = text1.getText(); String s2 = text2.getText(); try { double num1 = Double.parseDouble(s1); double num2 = Double.parseDouble(s2); String op = ((JButton)event.getSource()).getText(); double result = 0; switch (op.charAt(0)) { case '+': result = num1 + num2; break; case '-': result = num1 - num2; break; case '*': result = num1 * num2; break; case '/': result = num1 / num2; break; } svarstext.setText(result + ""); } catch (NumberFormatException exc) { svarstext.setText("ERROR"); } } // actionPerformed public void init() { plusButton.addActionListener(this); minusButton.addActionListener(this); timesButton.addActionListener(this); divideButton.addActionListener(this); Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(text1); cp.add(text2); cp.add(plusButton); cp.add(minusButton); cp.add(timesButton); cp.add(divideButton); cp.add(svarstext); } } // class CalcApplet
Man kan också ha en actionPerformed-metod per knapp, men då måste man skapa fyra nya ActionListener-objekt, av fyra olika klasser, för varje klass kan bara ha en enda actionPerformed-metod.public void actionPerformed(ActionEvent event) { String s1 = text1.getText(); String s2 = text2.getText(); try { double num1 = Double.parseDouble(s1); double num2 = Double.parseDouble(s2); JButton button = (JButton)event.getSource(); double result = 0; if (button == plusButton) result = num1 + num2; else if (button == minusButton) result = num1 - num2; else if (button == timesButton) result = num1 * num2; else if (button == divideButton) result = num1 / num2; svarstext.setText(result + ""); } catch (NumberFormatException exc) { svarstext.setText("ERROR"); } } // actionPerformed
class UFOobservation { private final double tid, x, y; public UFOobservation(double tid, double x, double y) { this.tid = tid; this.x = x; this.y = y; } public boolean equals(Object o) { if (!(o instanceof UFOobservation)) return false; UFOobservation u = (UFOobservation)o; return this.tid == u.tid && this.x == u.x && this.y == u.y; } // equals } // class UFOobservation
En kommentar: När man programmerar bör man tänka på att programkod
inte bara är något som datorn ska läsa och förstå,
utan det är ännu viktigare att de människor som kommer att läsa koden
förstår den. Därför bör man tänka på att skriva begriplig och vacker kod.
Ett program är inte bra bara för att det är korrekt.
Många har, såväl på den här uppgiften som på andra,
skrivit onödigt omständlig kod. Till exempel kanske man ersätter
en enkel return-sats som
medreturn tid == u.tid && x == u.x && y == u.y; Just den omskrivningen är kanske inte så dålig, och det är inte omöjligt att det till och med är tydligare vad koden gör när man har två return-satser. Men se på det här:if (tid == u.tid && x == u.x && y == u.y) return true; else return false; Här har man komplicerat koden ganska mycket, med tre onödiga if-satser, och det tar mycket längre tid att förstå vad som händer. Kanske beror det hela på att den som skrev koden inte kom ihåg &&-operatorn?if (tid != u.tid) { if (x != u.x) { if (y != u.y) { return false; } } } return true; Man ska förstås inte skriva kod som är så kompakt att den är kryptisk, men man ska inte heller låta enkla saker svälla ut och bli oöverskådliga. Det finns en orsak till att matematikerna uppfunnit multiplikation, så att de kan skriva 12 * 5 i stället för 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5 + 5. (Övning: Hitta felet. Vad illustrerar det?) På den här uppgiften har jag inte dragit poäng för svårlästa eller onödigt komplicerade lösningar, men tänk på det i fortsättningen. |
b (2p)
c (2p)static boolean liknande(UFOobservation o1, UFOobservation o2) { return Math.abs(o1.tid - o2.tid) < 1 && Math.abs(o1.x - o2.x) < 1 && Math.abs(o1.y - o2.y) < 1; }
d (2p)public static void main(String[] arg) { UFOobservation o1 = new UFOobservation(1, 2, 3); UFOobservation o2 = new UFOobservation(1, 2, 3); UFOobservation o3 = new UFOobservation(1.5, 2.9, 2.1); System.out.println("o1.equals(o2): " + o1.equals(o2)); System.out.println("liknande(o1, o2)): " + UFOobservation.liknande(o1, o2)); } // main
...class OmöjligUFOobservationException extends Exception { }
e (1p)public UFOobservation(double tid, double x, double y) throws OmöjligUFOobservationException { if (tid < 0 || x < 0 || y < 0) throw new OmöjligUFOobservationException(); this.tid = tid; this.x = x; this.y = y; }
try { UFOobservation o4 = new UFOobservation(1, 2, 3); } catch (OmöjligUFOobservationException e) { System.out.println("Fångat: " + e); }
Följande equals-metod är sämre, eftersom den är osymmetrisk, men den ger inget poängavdrag.class AvanceradUFOobservation extends UFOobservation { private final double höjd; public AvanceradUFOobservation(double tid, double x, double y, double höjd) throws OmöjligUFOobservationException { super(tid, x, y); this.höjd = höjd; } public boolean equals(Object o) { if (o instanceof AvanceradUFOobservation) { AvanceradUFOobservation u = (AvanceradUFOobservation)o; return super.equals(o) && this.höjd == u.höjd; } else if (o instanceof UFOobservation) return super.equals(o); else return false; } // equals } // class AvanceradUFOobservation
b (3p)public boolean equals(Object o) { if (!(o instanceof AvanceradUFOobservation)) return false; AvanceradUFOobservation u = (AvanceradUFOobservation)o; return super.equals(o) && this.höjd == u.höjd; } // equals
Likhet mellan objekt bör vara symmetrisk och transitiv. Om man ärver från en klass och utvidgar den med ytterliagre en egenskap, går det inte att åstadkomma en likhetsrelation som är både symmetrisk och transitiv.
Antag att vi har en UFO-observation, o1, och två avancerade UFO-observationer med höjd, ao1 och ao2:
Att likheten mellan två objekt är symmetrisk innebär att o1 är lika med ao1 om och endast om ao1 är lika med o1. I Java betyder det att o1.equals(ao1) ska ha samma värde (true eller false) som ao1.equals(o1).UFOobservation o1 = new UFOobservation(1, 2, 3); AvanceradUFOobservation ao1 = new AvanceradUFOobservation(1, 2, 3, 4); AvanceradUFOobservation ao2 = new AvanceradUFOobservation(1, 2, 3, 5);
UFOobservation-klassens equals-metod vet inte något om höjder, utan jämför bara tid och x- och y-koordinater när den jämför med en annan UFOobservation-instans. AvanceradUFOobservation ärver UFOobservation, vilket betyder att en AvanceradUFOobservation-instans är en UFOobservation-instans. Därför kommer o1.equals(ao1) bara att jämföra tid, x och y, och den anser därför att objekten är lika. Den returnerar alltså värdet true.
För att bevara symmetrin måste AvanceradUFOobservation-klassens equals-metod skrivas så, att den, när den jämför med en UFOobservation-instans som inte är en AvanceradUFOobservation-instans, jämför på samma sätt som UFOobservation-klassens equals-metod. Det kan ske genom att anropa den metoden.
Alltså är o1 (symmetriskt) lika med ao1. På samma sätt är o1 (symmetriskt) lika med ao2.
Men samtidigt har ao1 och ao2 olika höjd, så de bör betraktas som olika!
Detta strider mot regeln att likhet mellan objekt bör vara transitiv. För våra tre objekt (ao1, o1 och ao2) gäller att om både ao1 = o1 och o1 = ao2, så ska ao1 = ao2, och tvärtom. I Java betyder det att ao1.equals(o1) and o1.equals(ao2) ska ha samma värde (true eller false) som ao1.equals(ao2).
import java.net.*; import java.io.*; class ClientThread extends Thread { private final Socket socket; private final BufferedReader in; private final PrintWriter out; private final Storage storage; public ClientThread(Socket s, Storage m) throws IOException { socket = s; storage = m; in = new BufferedReader( new InputStreamReader( socket.getInputStream())); out = new PrintWriter( new BufferedWriter( new OutputStreamWriter( socket.getOutputStream())), true); start(); } // ClientThread public void run() { try { while (true) { String inline = in.readLine(); if (inline == null) break; else if (inline.equals("LAGRA")) { String newString = in.readLine(); storage.setString(newString); out.println("OK"); } else if (inline.equals("HÄMTA")) { out.println(storage.getString()); } else out.println("NOK"); } // while } // try catch(IOException e) { } } // run } // class ClientThread class Storage { private String s; public synchronized void setString(String s) { this.s = s; } public synchronized String getString() { return s; } } // class Storage public class MultiServer { public static final int PORT = 2000; public static void main(String[] args) throws IOException { Storage storage = new Storage(); ServerSocket s = new ServerSocket(PORT); while(true) { Socket socket = s.accept(); ClientThread t = new ClientThread(socket, storage); } // while } // main } // MultiServer