Körexempel:public class Hello { public static void main(String[] args) { System.out.println("Hello, world!"); } } // Hello
Java-programmet är inte en vanlig körbar fil ("exe-fil"), utan måste köras av en Java-tolk, den virtuella maskinen (JVM).linux> javac Hello.java linux> java Hello Hello, world! linux>
Jämför med Microsofts C# ("sii shaarp"), som påminner mycket om Java:
En åsikt är att Microsofts C#, och hela .NET-arkitekturen, är en kopia av Sun:s Java, "as close as legally possible", dvs en så exakt kopia som det går att göra utan att bli stämd.class HelloWorld { public static void Main() { System.Console.WriteLine("Hello, world!"); } }
Vi jämför med C. Filen hello.c:
Körexempel:#include <stdio.h> int main(int argc, char* argv[]) { printf("Hello, world!\n"); return 0; } /* main */
Vi jämför med C++. Filen hello.cpp:linux> gcc -Wall hello.c -o hello linux> ./hello Hello, world! linux>
Körexempel:#include <iostream> int main(int argc, char* argv[]) { std::cout << "Hello, world!\n"; } // main
linux> g++ -Wall hello.cpp -o hello linux> ./hello Hello, world! linux>
Körexempel:// Det finns minst ett fel i det här programmet. import java.io.*; class Rektangelkalkylator { public static void main(String args[]) { int a = 0, b = 0; String buf; BufferedReader kbd_reader = new BufferedReader(new InputStreamReader(System.in)); try { System.out.print("Ge sida a: "); buf = kbd_reader.readLine(); a = Integer.parseInt(buf); System.out.print("Ge sida b: "); buf = kbd_reader.readLine(); b = Integer.parseInt(buf); System.out.println("Area: " + a * b); System.out.println("Omkrets: " + 2*a + 2*b); } catch (IOException e) { System.out.println("Kunde inte läsa!"); } } // main } // Rektangelkalkylator
Fixa felet. Sen:linux> javac Rektangelkalkylator.java linux> java Rektangelkalkylator Ge sida a: 5 Ge sida b: 4 Area: 20 Omkrets: 108 linux>
Jämför med vad som händer i C++. Programmet rektangelkalkylator.cpp:linux> java Rektangelkalkylator Ge sida a: Bengt Exception in thread "main" java.lang.NumberFormatException: For input string: "Bengt" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Integer.parseInt(Integer.java:447) at java.lang.Integer.parseInt(Integer.java:497) at Rektangelkalkylator.main(Rektangelkalkylator.java:15) linux>
Körexempel (C++):#include <iostream> using namespace std; int main(int argc, char* argv[]) { int a = 0, b = 0; cout << "Ge sida a: "; cin >> a; cout << "Ge sida b: "; cin >> b; cout << "Area: " << a * b << "\n"; cout << "Omkrets: " << 2*a + 2*b << "\n"; } // main
Fångar Java alla fel? Nej:linux> make rektangelkalkylator g++ rektangelkalkylator.cpp -o rektangelkalkylator linux> ./rektangelkalkylator Ge sida a: Bengt Ge sida b: Area: 0 Omkrets: 0 linux>
linux> java Rektangelkalkylator Ge sida a: 1000000 Ge sida b: 1000000 Area: -727379968 Omkrets: 4000000 linux>
Tips: IOStreamDemo.java ur Bruce Eckels Thinking in Java, 3d Ed, visar upp typiska användningar av Java-I/O. Det finns förklaringar i online-boken.
Filen Main.java:public class Rektangel { public Rektangel(int a, int b) { this.a = a; this.b = b; } public int omkrets() { return 2 * this.a + 2 * this.b; } public int area() { return a * b; } private int a = 0, b = 0; } // Rektangel
Körexempel:import java.io.*; public class Main { public static void main(String args[]) { BufferedReader kbd_reader = new BufferedReader(new InputStreamReader(System.in)); int a, b; String buf; try { System.out.print("Ge sida a: "); buf = kbd_reader.readLine(); a = Integer.parseInt(buf); System.out.print("Ge sida b: "); buf = kbd_reader.readLine(); b = Integer.parseInt(buf); Rektangel rektangel = new Rektangel(a, b); System.out.println("Area: " + rektangel.area()); System.out.println("Omkrets: " + rektangel.omkrets()); } catch (IOException e) { System.out.println("Kunde inte läsa!"); } } // main } // Main
linux> javac Rektangel.java linux> javac Main.java linux> java Rektangel Exception in thread "main" java.lang.NoSuchMethodError: main linux> java Main Ge sida a: 5 Ge sida b: 4 Area: 20 Omkrets: 18 linux>
Filen Rektangel.java:
Körexempel:import java.io.*; class Rektangel { public Rektangel(int a, int b) { this.a = a; this.b = b; } public int omkrets() { return 2 * this.a + 2 * this.b; } public int area() { return a * b; } private int a = 0, b = 0; public static void main(String args[]) { BufferedReader kbd_reader = new BufferedReader(new InputStreamReader(System.in)); int a, b; String buf; try { System.out.print("Ge sida a: "); buf = kbd_reader.readLine(); a = Integer.parseInt(buf); System.out.print("Ge sida b: "); buf = kbd_reader.readLine(); b = Integer.parseInt(buf); Rektangel rektangel = new Rektangel(a, b); System.out.println("Area: " + rektangel.area()); System.out.println("Omkrets: " + rektangel.omkrets()); } catch (IOException e) { System.out.println("Kunde inte läsa!"); } } // main } // Rektangel
Man kan ha en main-funktion i varje klass. Bra för testning!linux> javac Rektangel.java linux> java Rektangel Ge sida a: 45 Ge sida b: 34 Area: 1530 Omkrets: 158 linux>
// Sträcker sig över resten av raden /* Kan sträcka sig över flera rader */
boolean 1 bit false eller true char 16 bitar Unicode-tecken byte 8 bitar -128 .. 127 short 16 bitar -32768 .. 32767 int 32 bitar -2147483648 .. 2147483647 long 64 bitar -9223372036854775808 .. 9223372036854775807 float 32 bitar 3.40282347E+38 .. 1.40239846E-45 double 64 bitar 1.79769313486231570E+308 .. 4.94065645841246544E-324
Det finns flera olika sorters arrayer! Ingen av dem fungerar som i C och C++.
De inbyggda arrayerna kan lagra både primitiva typer och objekt. Man måste (oftast) göra new.
Exempel:
int[] intarray = new int[10]; int intarray[] = new int[10]; String[] fruits = { "Äpplen", "Päron", "Apelsiner" }; int intarray[10]; // Ger kompileringsfel!
Notera att bitskiftningsoperatorerna << och >>, som i C++ överlagrats för in- och utmatning, bara är bitskiftningsoperatorer i Java.
Ett djur har ett namn (variabeln name) och en vikt (variabeln weight), och det kan äta (metoden eat):
(Jag använder engelska namn, men det hade funkat bra med svenska, inklusive ÅÄÖ!)class Animal { protected String name; private int weight; public Animal(String n, int w) { name = n; weight = w; } public void eat(String food) { System.out.println(name + " eats some " + food + "."); weight += 0.5; } } // class Animal
En fisk är ett djur. Den har alla egenskaper som ett djur har (nämligen namn och vikt) och den kan allt som ett djur kan (nämligen äta). Men dessutom har den en största djup som den kan simma på, och den kan simma:
En fågel är också ett djur. Den har alla egenskaper som ett djur har (nämligen namn och vikt) och den kan allt som ett djur kan (nämligen äta). Men dessutom har den en högsta flyghöjd, och den kan flyga. Dessutom beter den sig lite speciellt när den äter: först fladdrar den med vingarna av lycka över att ha fått matm och därefter äter den precis som ett vanligt djur.class Fish extends Animal { private int maxDepth; public Fish(String n, int w, int d) { super(n, w); maxDepth = d; } public void swim() { System.out.println(name + " swims."); } } // class Fish
Vi provkör klasserna med hjälp av klassen Zoo och dess main-metod:class Bird extends Animal { private int maxAltitude; public Bird(String n, int w, int a) { super(n, w); maxAltitude = a; } public void fly() { System.out.println(name + " flies."); } public void eat(String food) { System.out.println(name + " flutters its wings."); super.eat(food); } } // class Bird
Utmatning:class Zoo { public static void main(String[] args) { Bird tweety = new Bird("Tweety", 3, 1000); tweety.eat("corn"); tweety.fly(); Fish b = new Fish("Bubbles", 13, 100); b.eat("fish food"); b.swim(); Animal w = new Fish("Willy", 4000, 1000); w.eat("tourists"); // w.swim(); -- Doesn't work. See below. } } // class Zoo
Kommentarer:Tweety flutters its wings. Tweety eats some corn. Tweety flies. Bubbles eats some fish food. Bubbles swims. Willy eats some tourists.
Lägg märke till att klassen Bird har en egen metod som heter eat. Det finns en i Animal också, men den nya ersätter eller "döljer" (engelska: overrides) den gamla, och kommer att användas i alla Bird-objekt. Om man har en metod i en subklass med samma namn (och argument) som en metod i superklassen, så döljer metoden i subklassen metoden i superklassen. Om man trots det vill anropa metoden i superklassen kan man använda super, som är objektet men "tolkat som om det enbart hörde till superklassen". Det är vad vi gör i metoden eat: efter att ha fladdrat med vingarna så ska fågeln faktiskt äta, och det görs med ett anrop till super.eat.
Angående w.swim(): Variabelb w är av typen Animal, och Animal har ingen metod som heter swim. Variabeln w innehåller Willy, som är en Fish, och Fish har ju en swim-metod, så den skulle ha kunnat anropas när programmet körs. Men kompilatorn måste vara säker på att det aldrig kan uppstå ett typfel! Därför kontrollerar den vid kompileringstillfället att anropet aldrig kan misslyckas. Det betyder att varje objekt som kan lagras i variabeln w måste vara av en klass som har en swim-metod.
Programmet finns som filen Zoo.java.
En Figur har en mittpunkt, som anges med koordinaterna x_center och y_center. Man kan räkna ut dess area och omkrets, och den högsta x-koordinat och y-koordinat som täcks av figuren.
Men! Hur figuren ser ut, och hur man räknar ut area och omkrets, beror ju helt på vilken figur det är! Därför kan de metoderna inte implementeras i klassen Figur, utan "lämnas" till subklasserna. Det kallas att metoderna är abstrakta. Eftersom det finns abstrakta metoder i klassen, kan man inte skapa objekt av den typen, så hela klassen blir abstrakt.
Se filen Figur.java.public abstract class Figur { public Figur(int x, int y) { this.x_center = x; this.y_center = y; } // Figur public abstract int max_x(); public abstract int max_y(); public abstract int area(); public abstract int omkrets(); protected int x_center, y_center; } // class Figur
En Rektangel är en sorts Figur, och den innehåller förstås ett sätt att räkna ut omkretsen och ett sätt att räkna ut arean. Klassen Rektangel är inte abstrakt, utan man kan skapa Rektangel-objekt. En Rektangel har inte bara en mittpunkt, utan dessutom två sidor. Vi skriver en konstruktor med de extra parametrarna.
Se filen Rektangel.java.public class Rektangel extends Figur { public Rektangel(int x, int y, int sida, int hojd) { super(x, y); this.sida = sida; this.hojd = hojd; } // Rektangel public int max_x() { return super.x_center + this.sida / 2; } // max_x public int max_y() { return super.x_center + this.hojd / 2; } // max_y public int area() { return this.sida * this.hojd; } // area public int omkrets() { return 2*this.sida + 2*this.hojd; } // omkrets private int sida, hojd; } // class Rektangel
En Cirkel är en annan sorts Figur:
Se filen Cirkel.java.public class Cirkel extends Figur { public Cirkel(int x, int y, int radie) { super( x, y ); this.radie = radie; } // Cirkel public int max_x() { return super.x_center + this.radie; } // max_x public int max_y() { return super.y_center + this.radie; } // max_y public int area() { return (int)(Math.PI * this.radie * this.radie); } // area public int omkrets() { return (int)(2*Math.PI * this.radie); } // omkrets public int diameter() { return this.radie + this.radie; } // diameter private int radie; } // class Cirkel
Klassen FigurMain innehåller metoden main:
Se filen FigurMain.java.public class FigurMain { public static void main(String args[]) { Figur[] figurer = new Figur[4]; figurer[0] = new Rektangel( 10, // x 10, // y 10, // sida 10 ); // hojd figurer[1] = new Cirkel( 10, // x 10, // y 10 ); // radie figurer[2] = new Rektangel( 20, // x 20, // y 20, // sida 20 ); // hojd figurer[3] = new Cirkel( 20, // x 20, // y 20 ); // radie for (int index = 0; index < figurer.length; index++) { if (figurer[index] instanceof Cirkel) { System.out.println("Cirkel:"); System.out.println(" Diameter: " + ((Cirkel)figurer[index]).diameter() ); } else { System.out.println("Rektangel:"); } // if System.out.println(" Högsta x: " + figurer[index].max_x()); System.out.println(" Högsta y: " + figurer[index].max_y()); System.out.println(" Area: " + figurer[index].area() ); System.out.println(" Omkrets: " + figurer[index].omkrets()); } // for } // main } // class FigurMain
Körexempel:
Rektangel: Högsta x: 15 Högsta y: 15 Area: 100 Omkrets: 40 Cirkel: Diameter: 20 Högsta x: 20 Högsta y: 20 Area: 314 Omkrets: 62 Rektangel: Högsta x: 30 Högsta y: 30 Area: 400 Omkrets: 80 Cirkel: Diameter: 40 Högsta x: 40 Högsta y: 40 Area: 1256 Omkrets: 125
Nu har vi en instans av Cirkel. Variabeln cirkel refererar till den cirkeln. (Ungerfär som pekare i C.)Cirkel cirkel; // får värdet null cirkel = new Cirkel( 10, 10, 10 );
Nu refererar både cirkel och cirkel2 till samma objekt!Cirkel cirkel2; // får värdet null cirkel2 = cirkel;
C++:s delete finns inte i Java, och behövs inte. Java har automatisk skräpsamling (engelska: garbage collection).
<modifier> class <name> [ extends <baseclassname> ] [ implements <interfacename> ] { . . . <variable definitions> . . . <function definitions> . . . }
Anrop av area-metoden i Cirkel. Inuti metoden refererar variabeln this till objektet, dvs cirkeln cirkel.Cirkel cirkel = new Cirkel( 10, 10, 10 ); System.out.println("Area: " + cirkel.area());
Nu kan man peta på den utifrån:public int radie;
Cirkel cirkel = new Cirkel( 10, 10, 10 ); cirkel.radie = 27; // som en struct i C
public void rita(int skala) { /* ... */ } public void rita(int x, int y) { /* ... */ }
public Figur(int x, int y) { /* ... */ } public Figur() { /* ... */ }
Men Java har automatisk skräpsamling! Inte som i C++, där destruktorn måste städa upp allt som ett objekt allokerat.
Lita inte på finalize, till exempel för att stänga filer och ta bort fönster. Man har ingen aning om när den körs.
Filen TwoButtons.java:
Utseende:import javax.swing.*; import java.awt.event.*; import java.awt.*; class TwoButtonWindow extends JFrame { private JButton button1 = new JButton("Bing"); private JButton button2 = new JButton("Bong"); private JTextField textfield = new JTextField(10); private class ButtonPressListener implements ActionListener { public void actionPerformed(ActionEvent e) { String name = ((JButton)e.getSource()).getText(); textfield.setText(name); } } // ButtonPressListener private ButtonPressListener listener = new ButtonPressListener(); public TwoButtonWindow() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(button1); cp.add(button2); cp.add(textfield);; button1.addActionListener(listener); button2.addActionListener(listener); setSize(400, 100); setVisible(true); } } // TwoButtonWindow public class TwoButtons { public static void main(String[] args) { TwoButtonWindow window = new TwoButtonWindow(); } // main } // class Buttons
Vad kan gå fel i följande C-program, unsafe-hello.c?
#include <stdio.h> int main(int argc, char* argv[]) { printf("Hello, world!\n"); return 0; } /* main */
En säkrare version, safe-hello.c:
#include <stdlib.h> #include <stdio.h> int main(int argc, char* argv[]) { if (printf("Hello, world!\n") != 14) { fprintf(stderr, "Kunde inte skriva hela texten!\n"); return EXIT_FAILURE; } if (fclose(stdout) != 0) { fprintf(stderr, "Kunde inte skriva till filen!\n"); return EXIT_FAILURE; } return 0; } /* main */
Liknelse: När man använder sig av ett subsystem (till exempel att man anropar metoden tvätta i objektet t av klassen Tvättmaskin) ska man inte behöva stå och kontrollera att den verkligen tvättar. I stället låter man den tvätta bäst den vill, och om något går fel kommer det ett undantagsobjekt flygande och träffar en i huvudet.
En bra sak med undantag är att de kan kastas "genom" en metod. Även om den metoden inte gör någon felhantering alls, kan det kastade undantaget skickas vidare till anroparen. Studera detta utdrag ur ett Java-program som inte innehåller någon felhantering (dinner-1/Dinner.java):
Vad händer om det uppstår ett fel någonstans, till exempel i drinkCoffee? Vi måste lägga till felhantering. Vi kan till exempel låta metoderna returnera true om allt gick bra, och false om något gick fel. Utdrag ur dinner-2/Dinner.java:private void drinkCoffee() { // Drick kaffe } private void eatDinner() { eatAppetizer(); eatMainCourse(); eatDessert(); drinkCoffee(); } public static void main(String[] args) { Dinner d = new Dinner(); d.eatDinner(); System.out.println("Ätandet klart."); System.out.println("(Kanske i alla fall.)"); } // main
Notera hur mycket jobb det blev i eatDinner, och hur det programmet egentligen ska göra nästan försvinner bland all felhanteringen.private boolean drinkCoffee() { if (coffeePot == null) return false; else { // Drick kaffe. Det gick bra: return true; } } private boolean eatDinner() { if (eatAppetizer() == false) return false; if (eatMainCourse() == false) return false; if (eatDessert() == false) return false; if (drinkCoffee() == false) return false; return true; } public static void main(String[] args) { Dinner d = new Dinner(); if (d.eatDinner() == false) System.out.println("Ätandet misslyckades!"); } // main
Bättre med undantag! Utdrag ur dinner-3/Dinner.java:
Så här ser NoCoffeePotException ut:private void drinkCoffee() throws NoCoffeePotException { if (coffeePot == null) throw new NoCoffeePotException(); else { // Drick kaffe } } private void eatDinner() throws NoCoffeePotException { eatAppetizer(); eatMainCourse(); eatDessert(); drinkCoffee(); } public static void main(String[] args) { Dinner d = new Dinner(); try { d.eatDinner(); } catch (NoCoffeePotException e) { System.out.println("Ingen kaffepanna!"); } catch (Exception e) { System.out.println("Något annat gick snett."); } } // main
class NoCoffeePotException extends Exception { } // class NoCoffeePotException
Checked exceptions ("kontrollerade undantag") måste fångas med try och catch, eller deklareras med throws. Unchecked exceptions ("okontrollerade undantag") behöver inte.
Körexempel:import java.io.BufferedReader; import java.io.InputStreamReader; // Att kastas vid division med noll class DivZeroException extends Exception { } // class DivZeroException class Number { public Number(int tal) { this.num = tal; } // Number public void addera(int tal) { this.num = this.num + tal; } // addera public void dividera(int tal) throws DivZeroException { if (tal == 0) throw new DivZeroException(); this.num = this.num / tal; } // dividera public int varde() { return this.num; } private int num = 0; public static void main(String[] args) { BufferedReader kbd_reader = new BufferedReader( new InputStreamReader(System.in)); int tal = 1; String buf; Number num = new Number(14); System.out.println("Startar med: " + num.varde()); System.out.println("Adderar udda tal."); System.out.println("Dividerar jämna tal." ); try { while (tal != 99) { System.out.print("Ge ett tal (99 avslutar): "); buf = kbd_reader.readLine(); tal = Integer.parseInt(buf); if (tal % 2 == 1) num.addera(tal); else try { num.dividera(tal); } catch (DivZeroException e) { System.out.println("Ojdå! Division med 0."); } System.out.println("Nu är talet: " + num.varde()); } // while } // try catch (Exception e) { System.out.println("Fel inmatning / tangentbordsfel"); } // catch } // main } // class Number
pc105-246.oru.se DivZeroException > javac Number.java pc105-246.oru.se DivZeroException > java Number Startar med: 14 Adderar udda tal. Dividerar jämna tal. Ge ett tal (99 avslutar): 13 Nu är talet: 27 Ge ett tal (99 avslutar): 4 Nu är talet: 6 Ge ett tal (99 avslutar): Bengt Fel inmatning / tangentbordsfel pc105-246.oru.se DivZeroException >