Eksamen: REA3049 | Semester: Vår 2025 | Tema: Pseudokode, OOP, Caesar-chiffer, bibliotek, friluftsdatasett, skogbrann-simulering
| i | i % 2 | i % 3 | Hvilken gren? | Lagt til |
|---|---|---|---|---|
| 1 | 1 | 1 | else | "1 " |
| 2 | 0 | 2 | else if i%2==0 | "Tromm " |
| 3 | 1 | 0 | else if i%3==0 | "Lom " |
| 4 | 0 | 1 | else if i%2==0 | "Tromm " |
| 5 | 1 | 2 | else | "5 " |
| 6 | 0 | 0 | første if (begge) | "TrommeLom " |
Komposisjon er en sterk «inneholder»-relasjon der den indre delens livssyklus er bundet til den ytre helheten. I Java uttrykkes dette typisk ved at den indre klassen instansieres i konstruktøren til den ytre — den eies fullstendig av den ytre.
I Java realiseres polymorfisme ved at subklassen overstyrer (@Override) en metode fra superklassen. Når metoden kalles på en superklasse-referanse som peker på et subklasse-objekt, kjøres subklassens versjon (dynamisk binding).
Innkapsling i Java oppnås ved private-felter og public getter/setter-metoder. Dette skiller grensesnittet fra implementasjonen og lar interne detaljer endres uten å bryte kallende kode.
Algoritmen er et Caesar-chiffer tilpasset det norske alfabetet (29 tegn: a–å). Hver bokstav forskyves n plasser framover; modulo 29 sørger for at vi pakker rundt når vi går forbi å. Bokstaver utenfor alfabetet (mellomrom, tall, tegnsetting) beholdes uendret. Med n = 3 blir a → d, og å → c.
// Oppgave5b.java — Caesar-chiffer for norsk alfabet
import java.util.Scanner;
public class Oppgave5b {
private static final String ALFABET_SMA = "abcdefghijklmnopqrstuvwxyzæøå";
private static final String ALFABET_STORE = ALFABET_SMA.toUpperCase();
private static final int ALFABET_LENGDE = 29;
private static char manipulerBokstav(char bokstav, int n) {
int idxSma = ALFABET_SMA.indexOf(bokstav);
if (idxSma != -1) {
int nyPos = ((idxSma + n) % ALFABET_LENGDE + ALFABET_LENGDE) % ALFABET_LENGDE;
return ALFABET_SMA.charAt(nyPos);
}
int idxStor = ALFABET_STORE.indexOf(bokstav);
if (idxStor != -1) {
int nyPos = ((idxStor + n) % ALFABET_LENGDE + ALFABET_LENGDE) % ALFABET_LENGDE;
return ALFABET_STORE.charAt(nyPos);
}
return bokstav;
}
public static String behandleTekst(String tekst, int n) {
StringBuilder sb = new StringBuilder();
for (char b : tekst.toCharArray()) sb.append(manipulerBokstav(b, n));
return sb.toString();
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("Skriv inn tekst: ");
String tekst = sc.nextLine();
System.out.print("Forskyvning n (heltall): ");
int n = Integer.parseInt(sc.nextLine().trim());
String kryptert = behandleTekst(tekst, n);
System.out.println("Original: " + tekst);
System.out.println("Kryptert: " + kryptert);
System.out.println("Verifisering: " + behandleTekst(kryptert, -n));
}
}
Toveis assosiasjon: en Bok er utlånt til 0 eller 1 Låner; en Låner har 0 eller flere Bok-objekter.
// Bok.java
public class Bok {
private String tittel;
private String forfatter;
private Laner utlant;
public Bok(String tittel, String forfatter) {
this.tittel = tittel;
this.forfatter = forfatter;
this.utlant = null;
}
public String getTittel() { return tittel; }
public Laner getUtlant() { return utlant; }
void setUtlant(Laner l) { this.utlant = l; } // pakke-privat for kun Laner
public void visInfo() {
String status = utlant == null
? "tilgjengelig"
: "utlånt til " + utlant.getNavn() + " (ID " + utlant.getLanerId() + ")";
System.out.println("'" + tittel + "' av " + forfatter + " — " + status);
}
}
// Laner.java
import java.util.ArrayList;
import java.util.List;
public class Laner {
private int lanerId;
private String navn;
private List<Bok> lanteBoker = new ArrayList<>();
public Laner(int lanerId, String navn) {
this.lanerId = lanerId;
this.navn = navn;
}
public int getLanerId() { return lanerId; }
public String getNavn() { return navn; }
public List<Bok> getLanteBoker() { return lanteBoker; }
public void lanBok(Bok bok) throws BibliotekFeil {
if (bok.getUtlant() != null) {
throw new BibliotekFeil("'" + bok.getTittel() + "' er allerede utlånt.");
}
bok.setUtlant(this);
lanteBoker.add(bok);
}
public void leverTilbakeBok(Bok bok) throws BibliotekFeil {
if (!lanteBoker.contains(bok)) {
throw new BibliotekFeil(navn + " har ikke lånt '" + bok.getTittel() + "'.");
}
bok.setUtlant(null);
lanteBoker.remove(bok);
}
}
// BibliotekFeil.java — egendefinert checked exception
public class BibliotekFeil extends Exception {
public BibliotekFeil(String melding) { super(melding); }
}
// TestBibliotek.java
public class TestBibliotek {
public static void main(String[] args) {
Bok bok1 = new Bok("Sofies verden", "Jostein Gaarder");
Bok bok3 = new Bok("Beatles", "Lars Saabye Christensen");
Laner emma = new Laner(1, "Emma");
Laner ola = new Laner(2, "Ola");
try {
emma.lanBok(bok1);
bok1.visInfo(); // utlånt til Emma
emma.leverTilbakeBok(bok1);
bok1.visInfo(); // tilgjengelig
ola.lanBok(bok3);
// Forsøk å låne ut bok som er utlånt
try { emma.lanBok(bok3); }
catch (BibliotekFeil e) {
System.out.println("OK – fanget: " + e.getMessage());
}
// Lever tilbake bok som ikke er lånt
try { emma.leverTilbakeBok(bok3); }
catch (BibliotekFeil e) {
System.out.println("OK – fanget: " + e.getMessage());
}
} catch (BibliotekFeil e) {
System.err.println("Uventet feil: " + e.getMessage());
}
System.out.println("Tester ferdig.");
}
}
BibliotekFeil er en checked exception i Java. Det betyr at metoder som kan kaste den, må deklarere throws BibliotekFeil, og kallende kode må enten fange eller videreformidle. Dette gjør feilhåndteringen eksplisitt og synlig på kompileringstidspunktet.
VR-baserte behandlingsverktøy gir nye muligheter for trygg eksponering, men reiser etiske spørsmål. To sentrale dilemmaer:
Dilemma 1: Personvern og biometri. VR-headset registrerer mer enn bilde og lyd: blikkretning, hodebevegelser, hudkonduktans, puls. For en ungdom med sosial angst innebærer dette innsamling av sensitive data om frykt-respons og kroppslige reaksjoner. Etter GDPR (artikkel 9) er dette spesielt beskyttede helseopplysninger. Hvem eier dataene — sykehuset, leverandøren, eller Emma? At Emma er mindreårig (16) skjerper kravet til reelt informert samtykke.
Dilemma 2: Overføringsverdi versus teknologiavhengighet. Casen sier at Emma «føler seg tryggere i virtuelle situasjoner, men er usikker på hvordan hun vil håndtere virkelige presentasjoner». Hvis Emma blir avhengig av VR-rommet, kan teknologien forsterke unngåelsesatferd — det motsatte av målet. Etisk er det viktig at behandlingen designes med tydelig overgang til reell øvelse, og at framgang måles i hverdagslige situasjoner.
Konklusjon: VR er etisk forsvarlig dersom det kombineres med streng datakontroll, reell overføring til ekte situasjoner, og likeverdig tilgang.
// Oppgave8.java — analyse av friluftsaktiviteter (CSV)
import java.io.*;
import java.util.*;
public class Oppgave8 {
private static final String DATAFIL = "friluftsaktiviteter_2024.csv";
private static List<Map<String, String>> lesData(String filsti) throws IOException {
List<Map<String, String>> rader = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filsti))) {
String[] headers = br.readLine().split(";");
String linje;
while ((linje = br.readLine()) != null) {
String[] verdier = linje.split(";", -1);
Map<String, String> rad = new LinkedHashMap<>();
for (int i = 0; i < headers.length; i++) {
rad.put(headers[i], i < verdier.length ? verdier[i] : "");
}
rader.add(rad);
}
}
return rader;
}
private static List<String> aktivitetsnavn(List<Map<String, String>> data) {
List<String> result = new ArrayList<>(data.get(0).keySet());
result.remove("Fylke");
return result;
}
private static void visTotaler(List<Map<String, String>> data) {
List<String> aktiviteter = aktivitetsnavn(data);
Map<String, Integer> totaler = new LinkedHashMap<>();
for (String a : aktiviteter) {
int sum = 0;
for (Map<String, String> rad : data) sum += Integer.parseInt(rad.get(a));
totaler.put(a, sum);
}
System.out.printf("%n%-28s%10s%n", "Aktivitet", "Total");
System.out.println("-".repeat(38));
totaler.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.forEach(e -> System.out.printf("%-28s%10d%n", e.getKey(), e.getValue()));
}
private static void visFylke(List<Map<String, String>> data, String fylke) {
Map<String, String> rad = data.stream()
.filter(r -> r.get("Fylke").equalsIgnoreCase(fylke))
.findFirst().orElse(null);
if (rad == null) { System.out.println("Fylke ikke funnet."); return; }
List<String> aktiviteter = aktivitetsnavn(data);
List<int[]> par = new ArrayList<>();
int total = 0;
for (int i = 0; i < aktiviteter.size(); i++) {
int v = Integer.parseInt(rad.get(aktiviteter.get(i)));
par.add(new int[]{i, v});
total += v;
}
par.sort((a, b) -> Integer.compare(a[1], b[1]));
System.out.printf("%nAktiviteter i %s (sortert stigende):%n", rad.get("Fylke"));
for (int[] p : par) {
System.out.printf("%-28s%6d%9.1f %%%n",
aktiviteter.get(p[0]), p[1], (double) p[1] / total * 100);
}
}
// 8c — Topp 3 SVG-stolpediagram (uten eksterne bibliotek)
private static void toppTreSvg(List<Map<String, String>> data, String fylke) throws IOException {
Map<String, String> rad = data.stream()
.filter(r -> r.get("Fylke").equalsIgnoreCase(fylke)).findFirst().orElse(null);
if (rad == null) return;
List<String> aktiviteter = aktivitetsnavn(data);
List<String[]> topp3 = new ArrayList<>();
for (String a : aktiviteter) topp3.add(new String[]{a, rad.get(a)});
topp3.sort((a, b) -> Integer.compare(Integer.parseInt(b[1]), Integer.parseInt(a[1])));
topp3 = topp3.subList(0, 3);
int bredde = 600, hoyde = 400, padding = 60;
int maks = topp3.stream().mapToInt(t -> Integer.parseInt(t[1])).max().getAsInt();
int stolpebredde = (bredde - padding * 2) / topp3.size() - 10;
String[] farger = {"#2196F3", "#4CAF50", "#FFC107"};
StringBuilder svg = new StringBuilder();
svg.append("<svg xmlns='http://www.w3.org/2000/svg' width='").append(bredde)
.append("' height='").append(hoyde).append("'>");
svg.append("<text x='").append(bredde / 2).append("' y='30' text-anchor='middle' font-weight='bold'>Topp 3 i ")
.append(rad.get("Fylke")).append("</text>");
for (int i = 0; i < topp3.size(); i++) {
int v = Integer.parseInt(topp3.get(i)[1]);
int h = (hoyde - padding * 2) * v / maks;
int x = padding + i * (stolpebredde + 20);
int y = hoyde - padding - h;
svg.append("<rect x='").append(x).append("' y='").append(y)
.append("' width='").append(stolpebredde).append("' height='").append(h)
.append("' fill='").append(farger[i]).append("'/>");
}
svg.append("</svg>");
String filnavn = "topp3_" + fylke.replace(" ", "_") + ".svg";
try (FileWriter fw = new FileWriter(filnavn)) { fw.write(svg.toString()); }
System.out.println("Diagram lagret: " + filnavn);
}
public static void main(String[] args) throws IOException {
List<Map<String, String>> data = lesData(DATAFIL);
visTotaler(data);
Scanner sc = new Scanner(System.in);
System.out.print("\nVelg et fylke (f.eks. Oslo): ");
String fylke = sc.nextLine().trim();
visFylke(data, fylke);
toppTreSvg(data, fylke);
}
}
// Skogbrann.java — objektorientert simulering med Swing
import javax.swing.*;
import java.awt.*;
import java.util.Random;
enum Tilstand { TOM, TRE, BRANN }
public class Skogbrann extends JPanel {
private static final int RUTER = 40;
private static final int CELLE = 14;
private static final double P_VOKS = 0.003;
private static final double P_LYN = 0.0003;
private final Tilstand[][] celler = new Tilstand[RUTER][RUTER];
private final Random rng = new Random();
public Skogbrann() {
for (int r = 0; r < RUTER; r++)
for (int k = 0; k < RUTER; k++) celler[r][k] = Tilstand.TOM;
setPreferredSize(new Dimension(RUTER * CELLE, RUTER * CELLE));
Timer t = new Timer(100, e -> { tick(); repaint(); });
t.start();
}
private boolean brennerNoe() {
for (Tilstand[] rad : celler) for (Tilstand t : rad) if (t == Tilstand.BRANN) return true;
return false;
}
private void tick() {
Tilstand[][] ny = new Tilstand[RUTER][RUTER];
for (int r = 0; r < RUTER; r++) ny[r] = celler[r].clone();
boolean brenner = brennerNoe();
for (int r = 0; r < RUTER; r++) {
for (int k = 0; k < RUTER; k++) {
Tilstand t = celler[r][k];
if (t == Tilstand.BRANN) {
ny[r][k] = Tilstand.TOM;
for (int[] d : new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) {
int nr = r + d[0], nk = k + d[1];
if (nr >= 0 && nr < RUTER && nk >= 0 && nk < RUTER && celler[nr][nk] == Tilstand.TRE) {
ny[nr][nk] = Tilstand.BRANN;
}
}
} else if (t == Tilstand.TRE) {
if (rng.nextDouble() < P_LYN) ny[r][k] = Tilstand.BRANN;
} else if (t == Tilstand.TOM && !brenner) {
if (rng.nextDouble() < P_VOKS) ny[r][k] = Tilstand.TRE;
}
}
}
for (int r = 0; r < RUTER; r++) celler[r] = ny[r];
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int r = 0; r < RUTER; r++) {
for (int k = 0; k < RUTER; k++) {
g.setColor(switch (celler[r][k]) {
case TOM -> new Color(232, 215, 185);
case TRE -> new Color(46, 125, 50);
case BRANN -> new Color(229, 57, 53);
});
g.fillRect(k * CELLE, r * CELLE, CELLE, CELLE);
}
}
}
public static void main(String[] args) {
JFrame f = new JFrame("Skogbrann");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Skogbrann());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
ny-array unngår at brann «løper» tvers over kartet på ett trinn.brennerNoe()-sjekk.123456/
├── oppgave5b/
│ ├── Oppgave5b.java
│ └── Oppgave5b.class
├── oppgave6/
│ ├── Bok.java
│ ├── Laner.java
│ ├── BibliotekFeil.java
│ └── TestBibliotek.java
├── oppgave8/
│ ├── Oppgave8.java
│ ├── friluftsaktiviteter_2024.csv
│ └── topp3_Oslo.svg
├── oppgave9/Skogbrann.java
└── README.md (krev: Java 17+ for switch-expressions)