Java: Föreläsning 1

Preliminär plan

v46       Java allmänt
          Objektorientering, polymorfism
          Instans- och klassvariabler (static)
          Skräpsamling

v47       Undantag ("exceptions")
          Arv, klasser och gränssnitt ("interfaces"), paket
          Applet, inledande (inför inlämningsuppgift 1)
          Trådar

v48       JDBC, inlämningsuppgift 2
          Java och nätverk

v49       Java och nätverk, forts.
          Genomgång av inlämningsuppgift 2
          Repetition

v50       Grafisk applikation och Javadoc
          Mera Applet
          Grafiska gränssnitt, händelsestyrning

v51       Grafikhantering (omritning)
          Strömmar I/O
          Filhantering
          Säkerhet i Java
          JavaBeans

v52-2     Självstudier

v3        Tenta
Inlämningsuppgifterna ska redovisas före tentamen.

Innehåll i föreläsning 1

Kursen

C, C++ och Java

Filen hello.c:
#include <stdio.h>

int main(int argc, char* argv[]) {
    printf("Hello, world!\n");
    return 0;
} /* main */
Körexempel:
linux> gcc -Wall hello.c -o hello
linux> ./hello
Hello, world!
linux> 
Filen hello.cpp:
#include <iostream>

int main(int argc, char* argv[]) {
    std::cout << "Hello, world!\n";
} // main
Körexempel:
linux> g++ -Wall hello.cpp -o hello
linux> ./hello
Hello, world!
linux> 
Filen Hello.java:
public class Hello {
    public static void main(String[] args) {
	System.out.println("Hello, world!");
    }
} // Hello
Körexempel:
linux> javac Hello.java
linux> java Hello
Hello, world!
linux> 
Java-programmet är inte en vanlig körbar fil ("exe-fil"), utan måste köras av en Java-tolk, den virtuella maskinen (JVM).

Javafiler

Virtuell maskin

C#

C# ("C sharp") från Microsoft påminner mycket om Java:
class HelloWorld { 
    public static void Main() { 
        System.Console.WriteLine("Hello, world!"); 
    } 
}
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.

C (och C++) är svårt, jobbigt och farligt

Exempel på farligt: filen helloname.c, med flera allvarliga fel:
#include <stdio.h>

int main(int argc, char* argv[]) {
    printf("What's your name: ");
    char* s;
    gets(s);
    printf("Hello, %s!\n", s);
    return 0;
} /* main */
Körexempel 1:
linux> ./helloname
What's your name: Bob
Hello, Bob!
linux> 
Körexempel 2:
linux> ./helloname
What's your name: Bob med det långa namnet
Hello, Bob med det långa namnet!
Segmentation fault (core dumped)
linux> 

Java på våra Windows-system

Flera olika versioner av Java finns installerade på våra Windows-maskiner. Dels finns flera olika versioner av Sun:s SDK (Software Development Kit), och sen kan man också köra Eclipse, som är en fönsterbaserad programmeringsomgivning. Sun:s SDK kan laddas hem från Sun, och finns för Windows, Linux och flera andra operativsystem. Eclipse kan också laddas hem gratis, och är skriven i Java, så den fungerar om Java fungerar.

Det finns alltså flera sätt att köra Java. Här är ett av dem, som fungerade 2003, men nu är det fel versioner:

Java i ett kommandofönster

Omgivningsvariabler ("environment"):

Tips: Gör en batch-fil, som till exempel kan heta sp.bat:
set path=%path%C:\Program\Java\j2sdk1.4.2_02\bin
set classpath=M:\Javakursen
Eller en klickbar fil för den late och smarte. Kalla den exempelvis startjava.bat:
@ECHO OFF
set path=%path%;C:\Program\Java\j2sdk1.4.2_02\bin
set classpath=M:\Javakursen
M:
cd M:\Javakursen
%SystemRoot%\SYSTEM32\CMD.EXE
(Kalla inte filen java.bat. Då kommer Windows att starta den i stället för java-tolken när du skriver java.)

Rektangelprogram

Filen Rektangelkalkylator.java:
// 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
Körexempel:
linux> javac Rektangelkalkylator.java
linux> java Rektangelkalkylator
Ge sida a: 5
Ge sida b: 4
Area: 20
Omkrets: 108
linux> 
Fixa felet. Sen:
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> 
Jämför med vad som händer i C++. Programmet rektangelkalkylator.cpp:
#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
Körexempel (C++):
linux> make rektangelkalkylator
g++ rektangelkalkylator.cpp -o rektangelkalkylator
linux> ./rektangelkalkylator
Ge sida a: Bengt
Ge sida b: Area: 0
Omkrets: 0
linux> 
Fångar Java alla fel? Nej:
linux> java Rektangelkalkylator
Ge sida a: 1000000
Ge sida b: 1000000
Area: -727379968
Omkrets: 4000000
linux> 

Filhantering i Java

BufferedReader? InputStreamReader? Vilket krångel! Och jag som bara ville kunna mata ett heltal från tangentbordet.

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.

Härnäst

Samma problem med andra lösningar:

Rektangelklass

Filen Rektangel.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
Filen Main.java:
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
Körexempel:
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> 

Main i rektangelklassen

Onödigt med en särskild klass bara för att ha någonstans att lägga main-metoden. Ett vanligt trick är att lägga main i den klass man jobbar med.

Filen Rektangel.java:

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
Körexempel:
linux> javac Rektangel.java
linux> java Rektangel
Ge sida a: 45
Ge sida b: 34
Area: 1530
Omkrets: 158
linux> 
Man kan ha en main-funktion i varje klass. Bra för testning!

Programtyper

Java-applet

Filen Hello.java:
import java.applet.*;
import java.awt.*;

public class Hello extends Applet {
    public void paint(Graphics gr) {
        gr.drawString("Hello, world!", 100, 100);
    } // paint
} // Hello
Ett misslyckat körexempel, för det finns ingen main-metod:
linux> javac Hello.java
linux> java Hello
Exception in thread "main" java.lang.NoSuchMethodError: main
linux> 
Filen hello.html:
<html>
<head>
<title>Ett appletexempel</title>
</head>

<body>
  <applet code="Hello.class" width="200" height="200"></applet>
</body>
</html>
Appleten i en webbläsare:

Appleten i en webbläsare

Har du en Java-plugin i din webbläsare? I så fall bör du se appleten här, "på riktigt":

Syntaxen påminner starkt om C++

Kanske mest förvånande skillnaden: Man måste inte ha semikolon efter en klassdefinition.

Kommentarer

// Sträcker sig över resten av raden
/* Kan sträcka sig
      över flera
          rader */

Datatyper

Primitiva typer - storlek

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

Variabeldeklarationer

Arrayer

Java har flera olika "behållar"-typer, som kan innehålla andra objekt, till exempel listor, mängder och mappningar.

Det finns flera olika sorters arrayer! Ingen av dem fungerar som i C och C++.

Notera att boken Java Direkt med Swing kallar arrayer för "fält".

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!

Tilldelning och likhetsjämförelse

Som i C och C++: = är tilldelning och == är jämförelse

Räkneoperationer

Som i C och C++: +, -, *, /, ++, --, +=, /= med flera.

Notera att bitskiftningsoperatorerna << och >>, som i C++ överlagrats för in- och utmatning, bara är bitskiftningsoperatorer i Java.

Kontrollflöden

(Nästan) precis som i C++: if, while, do ... while, for, switch, break, continue.

Några skillnader gentemot C++

Objektorientering

Kan ni redan. Repetera annars här: http://www.databasteknik.se/webbkursen/oo-grunder/

Klasser och arv: djur, fiskar och fåglar

Basklassen Djur med subklasserna Fågel och Fisk

Ett djur har ett namn (variabeln name) och en vikt (variabeln weight), och det kan äta (metoden eat):

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
(Jag använder engelska namn, men det hade funkat bra med svenska, inklusive ÅÄÖ!)

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:

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
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 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
Vi provkör klasserna med hjälp av klassen Zoo och dess main-metod:
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
Utmatning:
Tweety flutters its wings.
Tweety eats some corn.
Tweety flies.
Bubbles eats some fish food.
Bubbles swims.
Willy eats some tourists.
Kommentarer:

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.

Polymorfism: figurer

Världen består av figurer, och en figur kan antingen vara en rektangel eller en cirkel:

Basklassen Figur med subklasserna Rektangel och Cirkel

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.

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.
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:
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: Kod:
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

Vad är en klass?

Vad är ett objekt?

Ett objekt är en instans av en klass. Exempel:
Cirkel cirkel; // får värdet null
cirkel = new Cirkel( 10, 10, 10 );
Nu har vi en instans av Cirkel. Variabeln cirkel refererar till den cirkeln. (Ungerfär som pekare i C.)
Cirkel cirkel2; // får värdet null
cirkel2 = cirkel;
Nu refererar både cirkel och cirkel2 till samma objekt!

C++:s delete finns inte i Java, och behövs inte. Java har automatisk skräpsamling (engelska: garbage collection).

Syntax

<modifier> 
class 
<name>
 [ extends <baseclassname> ]
 [ implements <interfacename> ]
  {
    . . .
    <variable definitions>
    . . .
    <function definitions>
    . . .
  }

Anrop av instansmetod

Cirkel cirkel = new Cirkel( 10, 10, 10 );
System.out.println("Area: " + cirkel.area());
Anrop av area-metoden i Cirkel. Inuti metoden refererar variabeln this till objektet, dvs cirkeln cirkel.

Peta på data i ett objekt

Gör radie allmänt åtkomlig (public) i Cirkel:
public int radie;
Nu kan man peta på den utifrån:
Cirkel cirkel = new Cirkel( 10, 10, 10 );
cirkel.radie = 27; // som en struct i C

Överlagring av metoder

Flera funktioner med samma namn, men med olika typer på parametrar, olika antal parametrar, olika returtyper. Exempel från klassen Cirkel:
  public void rita(int skala) { /* ... */ }
  public void rita(int x, int y) { /* ... */ }

Konstruktor

Flera konstruktorer

Överlagring även här. Exempel från klassen Figur:
public Figur(int x, int y) { /* ... */ }
public Figur() { /* ... */ }

Destruktor: metoden finalize

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.

Tre olika sorters Java

  1. Java ME ("Micro Edition"), för "små datorer", som mobiltelefoner (gå en kurs?)
  2. Java SE ("Standard Edition"), för vanliga datorer (skrivbords-, bärbara, även servrar)
  3. Java EE ("Enterprise Edition"), för servertillämpningar, t ex webbservrar


Thomas Padron-McCarthy (thomas.padron-mccarthy@tech.oru.se), 11 november 2007