Von Jamal Hassan, Backend Developer bei Java Fleet Systems Consulting
Mit Einblicken von Elyndra Valen (Senior Dev) und Nova Trent (Junior Dev)

Schwierigkeit: 🔴 Fortgeschritten
Lesezeit: 45 Minuten
Voraussetzungen: Java Grundlagen, Multithreading Basics
Kurs: Java Erweiterte Techniken – Tag 10 von 10


📖 Java Erweiterte Techniken – Alle Tage

TagThemaLevel
1Collections – Listen🟢
2Collections – Sets & Maps🟢
3Generics🟡
4Lambda-Ausdrücke🟡
5Functional Interfaces🟡
6Stream-API🟡
7File I/O🟡
8Annotations & Multithreading Basics🟡
9Multithreading – Synchronisation🔴
→ 10Netzwerkprogrammierung🔴

📍 Du bist hier: Tag 10 – Der Abschluss! 🎉


⚡ Das Wichtigste in 30 Sekunden

Dein Problem: Sockets Du musst mit anderen Systemen kommunizieren – APIs aufrufen, Server bauen, Daten übertragen.

Die Lösung: Java’s Netzwerk-APIs – von Low-Level Sockets bis zum modernen HTTP-Client.

Heute lernst du:

  • ✅ TCP/IP Grundlagen verstehen
  • ✅ Sockets für Client-Server-Kommunikation
  • ✅ Der moderne HttpClient (Java 11+)
  • ✅ REST-APIs aufrufen und JSON verarbeiten
  • ✅ Asynchrone HTTP-Requests
  • ✅ WebSockets für Echtzeit-Kommunikation

👋 Jamal: „Willkommen zum Finale!“

Hi! 👋

Jamal hier für den letzten Tag unserer Reise. Netzwerkprogrammierung ist das, was moderne Anwendungen erst möglich macht – von Microservices über REST-APIs bis zu Chat-Anwendungen.

Was wir heute bauen:

  • Einen einfachen Chat-Server und Client
  • REST-API Calls mit dem HttpClient
  • JSON-Verarbeitung

Los geht’s!


🖼️ Netzwerk-Architektur

Sockets

Abbildung 1: TCP/IP, Sockets und HTTP


🟢 GRUNDLAGEN

TCP/IP in 2 Minuten

SchichtProtokollJava-Klasse
AnwendungHTTP, FTP, SMTPHttpClient, URL
TransportTCP, UDPSocket, DatagramSocket
InternetIPInetAddress
NetzwerkEthernet, WiFi(OS-Ebene)

TCP vs. UDP:

  • TCP: Zuverlässig, verbindungsorientiert (HTTP, E-Mail)
  • UDP: Schnell, verbindungslos (Gaming, Streaming)

Socket-Grundlagen

Ein Socket ist ein Endpunkt für Netzwerkkommunikation.

// Server erstellen
ServerSocket serverSocket = new ServerSocket(8080);
Socket clientSocket = serverSocket.accept();  // Blockiert bis Client verbindet

// Client verbinden
Socket socket = new Socket("localhost", 8080);

// Daten senden/empfangen
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();

Einfacher Echo-Server

public class EchoServer {
    
    public static void main(String[] args) throws IOException {
        int port = 8080;
        
        try (ServerSocket serverSocket = new ServerSocket(port)) {
            System.out.println("Server läuft auf Port " + port);
            
            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("Client verbunden: " + clientSocket.getInetAddress());
                
                // In separatem Thread behandeln
                new Thread(() -> handleClient(clientSocket)).start();
            }
        }
    }
    
    private static void handleClient(Socket socket) {
        try (
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true)
        ) {
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println("Empfangen: " + line);
                out.println("Echo: " + line);  // Zurücksenden
                
                if (line.equalsIgnoreCase("bye")) {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Einfacher Echo-Client

public class EchoClient {
    
    public static void main(String[] args) throws IOException {
        String host = "localhost";
        int port = 8080;
        
        try (
            Socket socket = new Socket(host, port);
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader console = new BufferedReader(
                new InputStreamReader(System.in))
        ) {
            System.out.println("Verbunden mit Server. Tippe Nachrichten:");
            
            String userInput;
            while ((userInput = console.readLine()) != null) {
                out.println(userInput);  // An Server senden
                String response = in.readLine();  // Antwort lesen
                System.out.println("Server: " + response);
                
                if (userInput.equalsIgnoreCase("bye")) {
                    break;
                }
            }
        }
    }
}

🟡 PROFESSIONALS

Der moderne HttpClient (Java 11+)

Vergiss HttpURLConnection! Der neue HttpClient ist elegant und modern:

import java.net.http.*;
import java.net.URI;

// Client erstellen (wiederverwendbar!)
HttpClient client = HttpClient.newHttpClient();

// Request bauen
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.github.com/users/octocat"))
    .header("Accept", "application/json")
    .GET()
    .build();

// Synchron senden
HttpResponse<String> response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());

System.out.println("Status: " + response.statusCode());
System.out.println("Body: " + response.body());

GET, POST, PUT, DELETE

HttpClient client = HttpClient.newHttpClient();

// ===== GET =====
HttpRequest getRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
    .GET()
    .build();

// ===== POST =====
String jsonBody = """
    {
        "title": "Mein Post",
        "body": "Inhalt hier",
        "userId": 1
    }
    """;

HttpRequest postRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://jsonplaceholder.typicode.com/posts"))
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

// ===== PUT =====
HttpRequest putRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
    .header("Content-Type", "application/json")
    .PUT(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();

// ===== DELETE =====
HttpRequest deleteRequest = HttpRequest.newBuilder()
    .uri(URI.create("https://jsonplaceholder.typicode.com/posts/1"))
    .DELETE()
    .build();

Asynchrone Requests

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.github.com/users/octocat"))
    .build();

// Asynchron senden - blockiert NICHT!
CompletableFuture<HttpResponse<String>> future = 
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString());

// Callback wenn fertig
future.thenAccept(response -> {
    System.out.println("Status: " + response.statusCode());
    System.out.println("Body: " + response.body());
});

// Andere Arbeit machen...
System.out.println("Request läuft im Hintergrund...");

// Warten falls nötig
future.join();

Mehrere Requests parallel

HttpClient client = HttpClient.newHttpClient();

List<String> urls = List.of(
    "https://jsonplaceholder.typicode.com/posts/1",
    "https://jsonplaceholder.typicode.com/posts/2",
    "https://jsonplaceholder.typicode.com/posts/3"
);

// Alle parallel starten
List<CompletableFuture<String>> futures = urls.stream()
    .map(url -> HttpRequest.newBuilder()
        .uri(URI.create(url))
        .build())
    .map(request -> client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
        .thenApply(HttpResponse::body))
    .toList();

// Auf alle warten
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
    .join();

// Ergebnisse ausgeben
futures.forEach(f -> System.out.println(f.join().substring(0, 50) + "..."));

HttpClient konfigurieren

HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)          // HTTP/2 bevorzugen
    .followRedirects(HttpClient.Redirect.NORMAL) // Redirects folgen
    .connectTimeout(Duration.ofSeconds(10))      // Timeout
    .executor(Executors.newFixedThreadPool(5))   // Thread-Pool
    .build();

// Mit Timeout pro Request
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://example.com"))
    .timeout(Duration.ofSeconds(30))
    .build();

JSON verarbeiten (ohne externe Library)

Für einfache Fälle geht’s auch ohne Jackson/Gson:

// JSON parsen mit Pattern Matching (einfach)
String json = """
    {"name": "Max", "age": 25}
    """;

// Mit Regex (nur für einfache Fälle!)
Pattern namePattern = Pattern.compile("\"name\"\\s*:\\s*\"([^\"]+)\"");
Matcher matcher = namePattern.matcher(json);
if (matcher.find()) {
    String name = matcher.group(1);
    System.out.println("Name: " + name);
}

// Besser: Records für Typsicherheit + manuelle Parsing
record Person(String name, int age) {
    static Person fromJson(String json) {
        // Vereinfachtes Parsing
        String name = extractString(json, "name");
        int age = extractInt(json, "age");
        return new Person(name, age);
    }
    
    private static String extractString(String json, String key) {
        Pattern p = Pattern.compile("\"" + key + "\"\\s*:\\s*\"([^\"]+)\"");
        Matcher m = p.matcher(json);
        return m.find() ? m.group(1) : "";
    }
    
    private static int extractInt(String json, String key) {
        Pattern p = Pattern.compile("\"" + key + "\"\\s*:\\s*(\\d+)");
        Matcher m = p.matcher(json);
        return m.find() ? Integer.parseInt(m.group(1)) : 0;
    }
}

💡 Tipp: Für echte Projekte nutze Jackson oder Gson!


🔵 BONUS

URL und URI

// URL - für Netzwerk-Ressourcen
URL url = new URL("https://example.com:8080/path?query=value");
System.out.println("Protocol: " + url.getProtocol());  // https
System.out.println("Host: " + url.getHost());          // example.com
System.out.println("Port: " + url.getPort());          // 8080
System.out.println("Path: " + url.getPath());          // /path
System.out.println("Query: " + url.getQuery());        // query=value

// URI - für Identifikation (auch nicht-Netzwerk)
URI uri = URI.create("mailto:test@example.com");
URI uri2 = URI.create("file:///home/user/file.txt");

InetAddress – IP-Adressen

// Hostname auflösen
InetAddress address = InetAddress.getByName("google.com");
System.out.println("IP: " + address.getHostAddress());

// Eigene Adresse
InetAddress localhost = InetAddress.getLocalHost();
System.out.println("Mein Host: " + localhost.getHostName());
System.out.println("Meine IP: " + localhost.getHostAddress());

// Erreichbarkeit prüfen
boolean reachable = address.isReachable(5000);  // 5 Sekunden Timeout
System.out.println("Erreichbar: " + reachable);

UDP mit DatagramSocket

// UDP Server
public class UDPServer {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket(9876);
        byte[] buffer = new byte[1024];
        
        System.out.println("UDP Server läuft...");
        
        while (true) {
            DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
            socket.receive(packet);  // Blockiert
            
            String message = new String(packet.getData(), 0, packet.getLength());
            System.out.println("Empfangen: " + message);
            
            // Antwort senden
            String response = "ACK: " + message;
            byte[] responseData = response.getBytes();
            DatagramPacket responsePacket = new DatagramPacket(
                responseData, responseData.length,
                packet.getAddress(), packet.getPort()
            );
            socket.send(responsePacket);
        }
    }
}

// UDP Client
public class UDPClient {
    public static void main(String[] args) throws Exception {
        DatagramSocket socket = new DatagramSocket();
        InetAddress address = InetAddress.getByName("localhost");
        
        String message = "Hallo UDP!";
        byte[] data = message.getBytes();
        
        DatagramPacket packet = new DatagramPacket(data, data.length, address, 9876);
        socket.send(packet);
        
        // Antwort empfangen
        byte[] buffer = new byte[1024];
        DatagramPacket response = new DatagramPacket(buffer, buffer.length);
        socket.receive(response);
        
        System.out.println("Antwort: " + new String(response.getData(), 0, response.getLength()));
        socket.close();
    }
}

Multi-Client Server mit ExecutorService

public class MultiClientServer {
    private static final int PORT = 8080;
    private static final ExecutorService executor = Executors.newFixedThreadPool(10);
    
    public static void main(String[] args) throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(PORT)) {
            System.out.println("Server läuft auf Port " + PORT);
            
            while (true) {
                Socket clientSocket = serverSocket.accept();
                executor.execute(new ClientHandler(clientSocket));
            }
        }
    }
}

class ClientHandler implements Runnable {
    private final Socket socket;
    
    public ClientHandler(Socket socket) {
        this.socket = socket;
    }
    
    @Override
    public void run() {
        try (
            BufferedReader in = new BufferedReader(
                new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true)
        ) {
            out.println("Willkommen! Deine ID: " + Thread.currentThread().getName());
            
            String line;
            while ((line = in.readLine()) != null) {
                System.out.println("[" + Thread.currentThread().getName() + "] " + line);
                out.println("Echo: " + line);
                
                if ("bye".equalsIgnoreCase(line)) break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

💬 Real Talk: Netzwerk-Fallen

Java Fleet Büro, Freitag 17:00. Letzte Debugging-Session.


Nova: „Mein Server stürzt ab wenn ein Client sich plötzlich trennt!“

Jamal: „Klassiker! Du musst Exceptions behandeln und Ressourcen schließen:“

try (Socket socket = clientSocket) {
    // Kommunikation
} catch (IOException e) {
    System.out.println("Client disconnected: " + e.getMessage());
}
// Socket wird automatisch geschlossen

Nova: „Und mein HttpClient hängt manchmal…“

Elyndra: „Immer Timeouts setzen!“

HttpClient client = HttpClient.newBuilder()
    .connectTimeout(Duration.ofSeconds(10))
    .build();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://slow-api.com"))
    .timeout(Duration.ofSeconds(30))
    .build();

Jamal: „Und vergiss nicht: HttpClient ist wiederverwendbar! Nicht für jeden Request neu erstellen.“

// ❌ FALSCH - für jeden Request neu
for (String url : urls) {
    HttpClient client = HttpClient.newHttpClient();  // Overhead!
    // ...
}

// ✅ RICHTIG - einmal erstellen
HttpClient client = HttpClient.newHttpClient();
for (String url : urls) {
    // client wiederverwenden
}

❓ FAQ

Frage 1: Socket vs. HttpClient – wann was?

SocketHttpClient
Low-LevelHigh-Level
Eigenes ProtokollHTTP/HTTPS
Chat, GamingREST APIs
Volle KontrolleEinfacher

Frage 2: Synchron vs. Asynchron?

Synchron:

  • Blockiert bis Antwort da
  • Einfacher zu verstehen
  • OK für wenige Requests

Asynchron:

  • Blockiert nicht
  • Besser für viele parallele Requests
  • Komplexer (Callbacks, Futures)

Frage 3: Wie handle ich JSON richtig?

Für echte Projekte:

  1. Jackson – Industrie-Standard
  2. Gson – von Google, einfacher
  3. JSON-B – Jakarta EE Standard
// Mit Jackson (Dependency hinzufügen!)
ObjectMapper mapper = new ObjectMapper();
Person person = mapper.readValue(jsonString, Person.class);
String json = mapper.writeValueAsString(person);

Frage 4: Wie sichere ich Verbindungen ab?

// HTTPS ist Standard mit HttpClient
HttpClient client = HttpClient.newHttpClient();
// Automatisch HTTPS wenn URI mit https:// beginnt

// Für Sockets: SSLSocket
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) factory.createSocket("secure.example.com", 443);

Frage 5: Bernd sagt, REST sei „übertrieben“?

seufz REST ist der De-facto-Standard für APIs weil:

  • Einfach und verständlich
  • Stateless = Skalierbar
  • HTTP-Verben (GET, POST, PUT, DELETE)
  • Universell unterstützt

🔍 „behind the code“ oder „in my feels“? Die echten Geschichten findest du, wenn du weißt wo du suchen musst…


🎁 Cheat Sheet

🟢 Sockets

// Server
ServerSocket server = new ServerSocket(port);
Socket client = server.accept();

// Client
Socket socket = new Socket(host, port);

// I/O
BufferedReader in = new BufferedReader(
    new InputStreamReader(socket.getInputStream()));
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);

🟡 HttpClient

HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create(url))
    .header("Content-Type", "application/json")
    .GET() / .POST(BodyPublishers.ofString(json))
    .timeout(Duration.ofSeconds(30))
    .build();

// Sync
HttpResponse<String> response = client.send(request, 
    BodyHandlers.ofString());

// Async
client.sendAsync(request, BodyHandlers.ofString())
    .thenAccept(response -> /* ... */);

🔵 Nützliche Klassen

URI.create("https://example.com")
URL url = new URL("https://example.com")
InetAddress.getByName("hostname")
InetAddress.getLocalHost()

🎨 Challenge für dich!

🟢 Level 1 – Einsteiger

  • [ ] Baue einen Echo-Server der Nachrichten zurücksendet
  • [ ] Rufe eine öffentliche API mit HttpClient auf
  • [ ] Gib den HTTP-Status-Code und Body aus

Geschätzte Zeit: 30-40 Minuten

🟡 Level 2 – Fortgeschritten

  • [ ] Erweitere den Server für mehrere Clients
  • [ ] Implementiere POST-Requests mit JSON-Body
  • [ ] Verarbeite die JSON-Antwort

Geschätzte Zeit: 45-60 Minuten

🔵 Level 3 – Profi

  • [ ] Baue einen einfachen Chat-Server mit Broadcast
  • [ ] Implementiere parallele API-Calls mit CompletableFuture
  • [ ] Füge Timeout-Handling hinzu

Geschätzte Zeit: 60-90 Minuten


📦 Downloads

ProjektFür wen?Download
tag10-netzwerk-starter.zip🟢 Mit TODOs⬇️ Download
tag10-netzwerk-complete.zip🟡 Musterlösung⬇️ Download

🔗 Weiterführende Links

🇩🇪 Deutsch

RessourceBeschreibung
Rheinwerk: NetzwerkUmfassendes Kapitel

🇬🇧 Englisch

RessourceBeschreibungLevel
Oracle: NetworkingOffizielle Doku🟢
Baeldung: HttpClientPraxisbeispiele🟡
Java HttpClient DocsAPI-Referenz🟡

🎉 GESCHAFFT! Der Kurs ist komplett!

Was du in 10 Tagen gelernt hast:

Tag 1-2: Collections – Listen, Sets, Maps
Tag 3: Generics für typsicheren Code
Tag 4-5: Lambdas und Functional Interfaces
Tag 6: Stream-API für elegante Datenverarbeitung
Tag 7: File I/O und Properties
Tag 8: Annotations und Multithreading Basics
Tag 9: Thread-Synchronisation
Tag 10: Netzwerkprogrammierung

Du bist jetzt bereit für:

  • Spring Boot
  • Microservices
  • Enterprise Java
  • Oder was auch immer du bauen willst!

👋 Bis zum nächsten Kurs!

Fragen? jamal.hassan@java-developer.online

Das ganze Team:

  • Elyndra Valen (Senior Dev)
  • Jamal Hassan (Backend)
  • Nova Trent (Junior, jetzt nicht mehr so junior 😉)
  • Und natürlich Bernd (der Skeptiker)

Tags: #Java #Networking #Sockets #HttpClient #REST #API #Tutorial

© 2025 Java Fleet Systems Consulting | java-developer.online

Autoren

  • Jamal Hassan

    ⚙️ Jamal Hassan – Der Zuverlässige

    Backend Developer | 34 Jahre | „Ich schau mir das an.“

    Wenn im Team jemand gebraucht wird, der ruhig bleibt, während alle anderen hektisch diskutieren, dann ist es Jamal.
    Er redet nicht viel – er löst.
    Er plant wie ein Schachspieler: drei Züge im Voraus, jede Entscheidung mit Folgen bedacht.
    Seine Art zu arbeiten ist kein Sprint, sondern eine Strategie.

    Er kam 2021 zur Java Fleet, nachdem sein vorheriges Startup gescheitert war. Statt Frust hat er Gelassenheit mitgebracht – und eine Haltung, die das Team bis heute prägt: Stabilität ist keine Bremse, sondern ein Fundament.
    In einer Welt voller Hypes baut Jamal Systeme, die bleiben.

    💻 Die Tech-Seite

    Jamal ist der Inbegriff von Backend-Handwerk.
    Er liebt Architektur, die logisch ist, Datenmodelle, die Bestand haben, und Services, die einfach laufen.
    Spring Boot, REST, Kafka, Docker, DDD – das sind seine Werkzeuge, aber nicht sein Selbstverständnis.
    Er versteht Systeme als Ökosysteme: Jede Entscheidung hat Auswirkungen, jedes Modul muss sich in das Ganze einfügen.

    Er ist der Typ Entwickler, der eine halbe Stunde in Stille auf den Bildschirm schaut – und dann mit einem Satz alles löst:

    „Das Problem liegt nicht im Code. Es liegt in der Annahme.“

    Sein Code ist wie seine Persönlichkeit: still, präzise, verlässlich.
    Er dokumentiert, was nötig ist, und schreibt Tests, weil er Verantwortung ernst nimmt.
    Er hält nichts von Schnellschüssen – und noch weniger von Ausreden.

    🌿 Die menschliche Seite

    Jamal ist kein Mensch der Bühne.
    Er mag es, wenn andere glänzen – Hauptsache, das System läuft.
    Er trinkt arabischen Kaffee, spielt Schach im Verein und genießt es, wenn Dinge logisch ineinandergreifen – egal ob Code oder Leben.
    In der Kaffeeküche hört man ihn selten, aber wenn er etwas sagt, ist es meist ein Satz, der hängen bleibt.

    Im Team ist er der stille Vertraute, der Probleme anhört, bevor er sie bewertet.
    Nova nennt ihn „den Debugger in Menschengestalt“, Kat sagt: „Wenn Jamal nickt, weißt du, dass du auf der richtigen Spur bist.“
    Und Cassian beschreibt ihn als „Architekt mit Geduld und ohne Ego“.

    🧠 Seine Rolle im Team

    Jamal ist das strukturelle Rückgrat der Crew.
    Er denkt in Systemen, nicht in Features – in Verantwortlichkeiten, nicht in Ruhm.
    Wenn Projekte drohen, aus dem Ruder zu laufen, bringt er sie mit wenigen Worten und einer klaren Architektur wieder auf Kurs.
    Er ist das, was Franz-Martin „den ruhigen Hafen im Sturm“ nennt.

    ⚡ Superkraft

    Stabilität durch Denken.
    Jamal löst nicht nur technische Probleme – er beseitigt deren Ursachen.

    ☕ Motto

    „Ich schau mir das an.“
    (Und wenn er das sagt, ist das Problem so gut wie gelöst.)

  • Elyndra Valen

    28 Jahre alt, wurde kürzlich zur Senior Entwicklerin befördert nach 4 Jahren intensiver Java-Entwicklung. Elyndra kennt die wichtigsten Frameworks und Patterns, beginnt aber gerade erst, die tieferen Zusammenhänge und Architektur-Entscheidungen zu verstehen. Sie ist die Brücke zwischen Junior- und Senior-Welt im Team.

  • Ensign Nova Trent

    24 Jahre alt, frisch von der Universität als Junior Entwicklerin bei Java Fleet Systems Consulting. Nova ist brilliant in Algorithmen und Datenstrukturen, aber neu in der praktischen Java-Enterprise-Entwicklung. Sie brennt darauf, ihre ersten echten Projekte zu bauen und entdeckt dabei die Lücke zwischen Uni-Theorie und Entwickler-Realität. Sie liebt Star Treck das ist der Grund warum alle Sie Ensign Nova nennen und arbeitet daraufhin das sie Ihren ersten Knopf am Kragen bekommt.