Programmation Orientee Objets Java

PARTIE A : GENERALITES

Presentation

Le cours de "Programmation Orientee Objets Java" a fourni une formation complete en programmation orientee objet avec Java. En tant que l'un des langages de programmation les plus populaires pour les applications d'entreprise, les services web et le developpement Android, l'independance de plateforme de Java et son ecosysteme etendu en font un langage essentiel pour le developpement logiciel moderne.

Annee Universitaire : 2023-2024
Semestre : 7
Categorie : Programmation


PARTIE B : PARTIE DESCRIPTIVE

Details de l'experience

Environnement et contexte

Le cours couvrait les fondamentaux de Java jusqu'aux sujets avances, incluant les principes orientes objet, la gestion des exceptions, le framework de collections, le multithreading et le developpement d'interfaces graphiques. Nous avons utilise des IDE professionnels comme Eclipse et IntelliJ IDEA pour le developpement et pratique les principes du developpement dirige par les tests (TDD).

Ma fonction

Dans ce cours, j'etais responsable de :

  • Comprendre la syntaxe Java et les concepts orientes objet
  • Implementer des classes, interfaces et hierarchies d'heritage
  • Utiliser efficacement le Java Collections Framework
  • Developper des applications multithreadees
  • Creer des interfaces graphiques avec Swing/JavaFX
  • Gerer les exceptions et les ressources
  • Ecrire des tests unitaires avec JUnit
  • Travailler avec les API d'E/S et de reseau Java

PARTIE C : PARTIE TECHNIQUE

Cette section explore les aspects techniques de la programmation Java et du developpement oriente objet.

Concepts techniques appris

1. Fondamentaux Java et bases de la POO

Structure de classe :

public class BankAccount {
    // Variables d'instance (private pour l'encapsulation)
    private String accountNumber;
    private double balance;
    private String owner;

    // Constructeur
    public BankAccount(String accountNumber, String owner) {
        this.accountNumber = accountNumber;
        this.owner = owner;
        this.balance = 0.0;
    }

    // Methodes
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: " + amount);
        }
    }

    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }

    // Getters
    public double getBalance() {
        return balance;
    }

    public String getAccountNumber() {
        return accountNumber;
    }
}

Principes cles de la POO :

  • Encapsulation : Masquage des donnees avec des champs prives et des methodes publiques
  • Heritage : Reutilisation du code par des hierarchies de classes
  • Polymorphisme : Dispatch dynamique de methodes
  • Abstraction : Masquage des details d'implementation

2. Heritage et interfaces

Exemple d'heritage :

public abstract class Animal {
    protected String name;
    protected int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Methode abstraite (doit etre implementee par les sous-classes)
    public abstract void makeSound();

    // Methode concrete
    public void sleep() {
        System.out.println(name + " is sleeping");
    }
}

public class Dog extends Animal {
    private String breed;

    public Dog(String name, int age, String breed) {
        super(name, age);  // Appel au constructeur parent
        this.breed = breed;
    }

    @Override
    public void makeSound() {
        System.out.println("Woof! Woof!");
    }

    public void fetch() {
        System.out.println(name + " is fetching");
    }
}

Interfaces :

public interface Drawable {
    void draw();
    default void display() {  // Methode par defaut (Java 8+)
        System.out.println("Displaying drawable object");
    }
}

public interface Resizable {
    void resize(double factor);
}

public class Circle implements Drawable, Resizable {
    private double radius;

    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }

    @Override
    public void resize(double factor) {
        radius *= factor;
    }
}

Interface vs Classe abstraite :

  • Interfaces : Heritage multiple, definition de contrat
  • Classes abstraites : Implementation partielle, etat partage

3. Gestion des exceptions

Gestion robuste des erreurs en Java :

public class FileProcessor {
    public String readFile(String filename) throws IOException {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(filename));
            StringBuilder content = new StringBuilder();
            String line;

            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }

            return content.toString();

        } catch (FileNotFoundException e) {
            System.err.println("File not found: " + filename);
            throw e;
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
            throw e;
        } finally {
            // Toujours execute, meme si une exception survient
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    System.err.println("Error closing file");
                }
            }
        }
    }

    // Try-with-resources (Java 7+)
    public String readFileModern(String filename) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
            StringBuilder content = new StringBuilder();
            String line;

            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }

            return content.toString();
        }
        // reader.close() appele automatiquement
    }
}

// Exception personnalisee
public class InsufficientFundsException extends Exception {
    private double amount;

    public InsufficientFundsException(double amount) {
        super("Insufficient funds: " + amount);
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}

4. Framework de collections

Structures de donnees et algorithmes puissants :

import java.util.*;

public class CollectionsDemo {
    public void demonstrateCollections() {
        // List (ordonnee, autorise les doublons)
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Apple");
        arrayList.add("Banana");
        arrayList.add("Apple");  // Doublon autorise

        List<String> linkedList = new LinkedList<>();

        // Set (pas de doublons)
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Apple");
        hashSet.add("Banana");
        hashSet.add("Apple");  // Ignore
        // La taille sera 2

        Set<String> treeSet = new TreeSet<>();  // Trie

        // Map (paires cle-valeur)
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("Alice", 25);
        hashMap.put("Bob", 30);
        hashMap.put("Charlie", 28);

        // Iteration
        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // Queue (file d'attente)
        Queue<String> queue = new LinkedList<>();
        queue.offer("First");
        queue.offer("Second");
        String first = queue.poll();  // Retire et retourne "First"

        // File de priorite
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        pq.offer(5);
        pq.offer(2);
        pq.offer(8);
        System.out.println(pq.poll());  // Retourne 2 (le plus petit)
    }
}

Comparateurs et tri :

public class Student {
    private String name;
    private double gpa;

    // Constructeur, getters, setters...
}

// Tri avec Comparator
List<Student> students = new ArrayList<>();
// Ajouter des etudiants...

// Trier par GPA (decroissant)
students.sort((s1, s2) -> Double.compare(s2.getGpa(), s1.getGpa()));

// Ou en utilisant Comparator
students.sort(Comparator.comparingDouble(Student::getGpa).reversed());

5. Generiques

Programmation type-safe :

public class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }
}

// Utilisation
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get();  // Pas de cast necessaire

// Methode generique
public <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.print(element + " ");
    }
    System.out.println();
}

// Parametres de type bornes
public <T extends Comparable<T>> T findMax(T[] array) {
    T max = array[0];
    for (T element : array) {
        if (element.compareTo(max) > 0) {
            max = element;
        }
    }
    return max;
}

6. Multithreading

Programmation concurrente en Java :

// Extension de la classe Thread
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// Implementation de l'interface Runnable
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // Code de la tache
    }
}

// Utilisation d'ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
    executor.submit(new MyRunnable());
}
executor.shutdown();

Synchronisation :

public class Counter {
    private int count = 0;

    // Methode synchronisee
    public synchronized void increment() {
        count++;
    }

    // Bloc synchronise
    public void incrementBlock() {
        synchronized(this) {
            count++;
        }
    }

    public int getCount() {
        return count;
    }
}

7. Expressions lambda et Streams (Java 8+)

Fonctionnalites de programmation fonctionnelle :

// Expressions lambda
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// Ancienne methode
for (String name : names) {
    System.out.println(name);
}

// Avec lambda
names.forEach(name -> System.out.println(name));
// Ou reference de methode
names.forEach(System.out::println);

// Streams
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// Filtrer, transformer et collecter
List<Integer> evenSquares = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .collect(Collectors.toList());
// Resultat : [4, 16, 36, 64, 100]

// Somme avec reduce
int sum = numbers.stream()
    .reduce(0, (a, b) -> a + b);

// Grouper par
Map<Integer, List<String>> groupedByLength = names.stream()
    .collect(Collectors.groupingBy(String::length));

8. E/S de fichiers et serialisation

Travail avec les fichiers et la persistance des donnees :

import java.io.*;
import java.nio.file.*;

// Lecture depuis un fichier
public List<String> readLines(String filename) throws IOException {
    return Files.readAllLines(Paths.get(filename));
}

// Ecriture dans un fichier
public void writeLines(String filename, List<String> lines) throws IOException {
    Files.write(Paths.get(filename), lines);
}

// Serialisation
public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    // Constructeur, getters, setters...
}

// Sauvegarder un objet
public void saveObject(Person person, String filename) throws IOException {
    try (ObjectOutputStream oos = new ObjectOutputStream(
            new FileOutputStream(filename))) {
        oos.writeObject(person);
    }
}

// Charger un objet
public Person loadObject(String filename) throws IOException, ClassNotFoundException {
    try (ObjectInputStream ois = new ObjectInputStream(
            new FileInputStream(filename))) {
        return (Person) ois.readObject();
    }
}

9. Developpement d'interfaces graphiques avec Swing

Creation d'interfaces utilisateur graphiques :

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class SimpleCalculator extends JFrame {
    private JTextField display;
    private double result = 0;
    private String operator = "=";
    private boolean startNewNumber = true;

    public SimpleCalculator() {
        setTitle("Calculator");
        setSize(300, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Creer l'affichage
        display = new JTextField("0");
        display.setEditable(false);
        display.setHorizontalAlignment(JTextField.RIGHT);
        add(display, BorderLayout.NORTH);

        // Creer le panneau de boutons
        JPanel buttonPanel = new JPanel(new GridLayout(4, 4));
        String[] buttons = {
            "7", "8", "9", "/",
            "4", "5", "6", "*",
            "1", "2", "3", "-",
            "0", ".", "=", "+"
        };

        for (String text : buttons) {
            JButton button = new JButton(text);
            button.addActionListener(new ButtonClickListener());
            buttonPanel.add(button);
        }

        add(buttonPanel, BorderLayout.CENTER);
    }

    private class ButtonClickListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            String command = e.getActionCommand();
            // Logique de gestion du clic...
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            SimpleCalculator calc = new SimpleCalculator();
            calc.setVisible(true);
        });
    }
}

10. Tests unitaires avec JUnit

Developpement dirige par les tests :

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

public class CalculatorTest {
    private Calculator calculator;

    @BeforeEach
    public void setUp() {
        calculator = new Calculator();
    }

    @Test
    public void testAddition() {
        assertEquals(5, calculator.add(2, 3));
        assertEquals(-1, calculator.add(2, -3));
    }

    @Test
    public void testDivision() {
        assertEquals(2.0, calculator.divide(6, 3), 0.001);
    }

    @Test
    public void testDivisionByZero() {
        assertThrows(ArithmeticException.class, () -> {
            calculator.divide(5, 0);
        });
    }

    @AfterEach
    public void tearDown() {
        calculator = null;
    }
}

PARTIE D : PARTIE ANALYTIQUE

Connaissances et competences mobilisees

  • Comprehension de la syntaxe Java et des principes de la POO
  • Implementation de hierarchies de classes et d'interfaces
  • Utilisation efficace du Collections Framework
  • Ecriture de code resistant aux exceptions
  • Developpement d'applications multithreadees
  • Creation d'interfaces utilisateur graphiques
  • Application de la programmation fonctionnelle avec les lambdas et les streams
  • Ecriture de tests unitaires
  • Travail avec les E/S de fichiers et la serialisation
  • Utilisation des outils de developpement Java (IDE, debogueur, profileur)

Auto-evaluation

Apprendre Java apres le C et le C++ s'est fait relativement en douceur, car de nombreux concepts etaient familiers. Cependant, la gestion automatique de la memoire de Java (ramasse-miettes) constituait un changement de paradigme significatif. Ne pas avoir a gerer manuellement la memoire etait liberateur mais impliquait aussi moins de controle sur les performances.

Le Collections Framework est l'une des fonctionnalites les plus puissantes de Java. Apprendre quand utiliser ArrayList vs LinkedList, HashMap vs TreeMap, etc., necessitait de comprendre les compromis de complexite temporelle. L'API Stream et les expressions lambda ont rendu le code plus concis et lisible.

Le multithreading en Java etait un defi. Comprendre la synchronisation des threads, les interblocages et les conditions de concurrence necessitait une reflexion approfondie. Le package java.util.concurrent fournissait des outils puissants mais avait une courbe d'apprentissage abrupte.

Le developpement d'interfaces graphiques avec Swing etait interessant mais semblait quelque peu date compare aux frameworks UI modernes. La programmation evenementielle necessitait un etat d'esprit different. Construire des interfaces reactives tout en evitant de bloquer le thread principal etait une lecon precieuse.

Mon avis

Java est un excellent langage pour apprendre la POO et construire des applications d'entreprise. Sa philosophie "ecrire une fois, executer partout" est puissante, bien qu'en pratique, les differences de plateforme puissent encore causer des problemes. La bibliotheque standard etendue et l'ecosysteme de bibliotheques tierces rendent Java tres productif.

Le cours a fourni des bases solides en developpement Java. Ce que j'ai particulierement apprecie :

  • Systeme de types fort empechant de nombreuses erreurs d'execution
  • Documentation complete et grande communaute
  • Excellent outillage (IDE, profileurs, debogueurs)
  • Independance de plateforme

Axes d'amelioration :

  • Plus de couverture des fonctionnalites modernes de Java (Java 11+)
  • Patrons de conception et principes architecturaux
  • Outils de build (Maven, Gradle)
  • Introduction au Spring Framework
  • Bases du developpement Android

La verbosite de Java comparee aux langages modernes peut etre fastidieuse (beaucoup de code boilerplate), mais cela s'ameliore avec les nouvelles versions de Java. Dans l'ensemble, Java reste pertinent et precieux pour le developpement logiciel professionnel, en particulier dans le developpement d'entreprise et Android.

Object-Oriented Programming Java

PART A: GENERALITIES

Presentation

The "Programmation Orientee Objets Java" course provided comprehensive training in object-oriented programming using Java. As one of the most popular programming languages for enterprise applications, web services, and Android development, Java's platform independence and extensive ecosystem make it essential for modern software development.

Academic Year: 2023-2024
Semester: 7
Category: Programming


PART B: DESCRIPTIVE PART

Experience Details

Environment and Context

The course covered Java fundamentals through advanced topics including object-oriented principles, exception handling, collections framework, multithreading, and GUI development. We used industry-standard IDEs like Eclipse and IntelliJ IDEA for development and practiced Test-Driven Development (TDD) principles.

My Function

In this course, I was responsible for:

  • Understanding Java syntax and object-oriented concepts
  • Implementing classes, interfaces, and inheritance hierarchies
  • Using Java Collections Framework effectively
  • Developing multithreaded applications
  • Creating graphical user interfaces with Swing/JavaFX
  • Handling exceptions and managing resources
  • Writing unit tests with JUnit
  • Working with Java I/O and networking APIs

PART C: TECHNICAL PART

This section explores the technical aspects of Java programming and object-oriented development.

Technical Concepts Learned

1. Java Fundamentals and OOP Basics

Class Structure:

public class BankAccount {
    // Instance variables (private for encapsulation)
    private String accountNumber;
    private double balance;
    private String owner;

    // Constructor
    public BankAccount(String accountNumber, String owner) {
        this.accountNumber = accountNumber;
        this.owner = owner;
        this.balance = 0.0;
    }

    // Methods
    public void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("Deposited: " + amount);
        }
    }

    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }

    // Getters
    public double getBalance() {
        return balance;
    }

    public String getAccountNumber() {
        return accountNumber;
    }
}

Key OOP Principles:

  • Encapsulation: Data hiding using private fields and public methods
  • Inheritance: Code reuse through class hierarchies
  • Polymorphism: Dynamic method dispatch
  • Abstraction: Hiding implementation details

2. Inheritance and Interfaces

Inheritance Example:

public abstract class Animal {
    protected String name;
    protected int age;

    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Abstract method (must be implemented by subclasses)
    public abstract void makeSound();

    // Concrete method
    public void sleep() {
        System.out.println(name + " is sleeping");
    }
}

public class Dog extends Animal {
    private String breed;

    public Dog(String name, int age, String breed) {
        super(name, age);  // Call parent constructor
        this.breed = breed;
    }

    @Override
    public void makeSound() {
        System.out.println("Woof! Woof!");
    }

    public void fetch() {
        System.out.println(name + " is fetching");
    }
}

Interfaces:

public interface Drawable {
    void draw();
    default void display() {  // Default method (Java 8+)
        System.out.println("Displaying drawable object");
    }
}

public interface Resizable {
    void resize(double factor);
}

public class Circle implements Drawable, Resizable {
    private double radius;

    @Override
    public void draw() {
        System.out.println("Drawing a circle");
    }

    @Override
    public void resize(double factor) {
        radius *= factor;
    }
}

Interface vs Abstract Class:

  • Interfaces: Multiple inheritance, contract definition
  • Abstract classes: Partial implementation, shared state

3. Exception Handling

Robust error management in Java:

public class FileProcessor {
    public String readFile(String filename) throws IOException {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(filename));
            StringBuilder content = new StringBuilder();
            String line;

            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }

            return content.toString();

        } catch (FileNotFoundException e) {
            System.err.println("File not found: " + filename);
            throw e;
        } catch (IOException e) {
            System.err.println("Error reading file: " + e.getMessage());
            throw e;
        } finally {
            // Always executed, even if exception occurs
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    System.err.println("Error closing file");
                }
            }
        }
    }

    // Try-with-resources (Java 7+)
    public String readFileModern(String filename) throws IOException {
        try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
            StringBuilder content = new StringBuilder();
            String line;

            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }

            return content.toString();
        }
        // reader.close() called automatically
    }
}

// Custom exception
public class InsufficientFundsException extends Exception {
    private double amount;

    public InsufficientFundsException(double amount) {
        super("Insufficient funds: " + amount);
        this.amount = amount;
    }

    public double getAmount() {
        return amount;
    }
}

4. Collections Framework

Powerful data structures and algorithms:

import java.util.*;

public class CollectionsDemo {
    public void demonstrateCollections() {
        // List (ordered, allows duplicates)
        List<String> arrayList = new ArrayList<>();
        arrayList.add("Apple");
        arrayList.add("Banana");
        arrayList.add("Apple");  // Duplicate allowed

        List<String> linkedList = new LinkedList<>();

        // Set (no duplicates)
        Set<String> hashSet = new HashSet<>();
        hashSet.add("Apple");
        hashSet.add("Banana");
        hashSet.add("Apple");  // Ignored
        // Size will be 2

        Set<String> treeSet = new TreeSet<>();  // Sorted

        // Map (key-value pairs)
        Map<String, Integer> hashMap = new HashMap<>();
        hashMap.put("Alice", 25);
        hashMap.put("Bob", 30);
        hashMap.put("Charlie", 28);

        // Iteration
        for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // Queue
        Queue<String> queue = new LinkedList<>();
        queue.offer("First");
        queue.offer("Second");
        String first = queue.poll();  // Removes and returns "First"

        // Priority Queue
        PriorityQueue<Integer> pq = new PriorityQueue<>();
        pq.offer(5);
        pq.offer(2);
        pq.offer(8);
        System.out.println(pq.poll());  // Returns 2 (smallest)
    }
}

Comparators and Sorting:

public class Student {
    private String name;
    private double gpa;

    // Constructor, getters, setters...
}

// Sorting with Comparator
List<Student> students = new ArrayList<>();
// Add students...

// Sort by GPA (descending)
students.sort((s1, s2) -> Double.compare(s2.getGpa(), s1.getGpa()));

// Or using Comparator
students.sort(Comparator.comparingDouble(Student::getGpa).reversed());

5. Generics

Type-safe programming:

public class Box<T> {
    private T content;

    public void set(T content) {
        this.content = content;
    }

    public T get() {
        return content;
    }
}

// Usage
Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get();  // No casting needed

// Generic method
public <T> void printArray(T[] array) {
    for (T element : array) {
        System.out.print(element + " ");
    }
    System.out.println();
}

// Bounded type parameters
public <T extends Comparable<T>> T findMax(T[] array) {
    T max = array[0];
    for (T element : array) {
        if (element.compareTo(max) > 0) {
            max = element;
        }
    }
    return max;
}

6. Multithreading

Concurrent programming in Java:

// Extending Thread class
public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// Implementing Runnable interface
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // Task code
    }
}

// Using ExecutorService
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
    executor.submit(new MyRunnable());
}
executor.shutdown();

Synchronization:

public class Counter {
    private int count = 0;

    // Synchronized method
    public synchronized void increment() {
        count++;
    }

    // Synchronized block
    public void incrementBlock() {
        synchronized(this) {
            count++;
        }
    }

    public int getCount() {
        return count;
    }
}

7. Lambda Expressions and Streams (Java 8+)

Functional programming features:

// Lambda expressions
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

// Old way
for (String name : names) {
    System.out.println(name);
}

// With lambda
names.forEach(name -> System.out.println(name));
// Or method reference
names.forEach(System.out::println);

// Streams
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// Filter, map, and collect
List<Integer> evenSquares = numbers.stream()
    .filter(n -> n % 2 == 0)
    .map(n -> n * n)
    .collect(Collectors.toList());
// Result: [4, 16, 36, 64, 100]

// Sum with reduce
int sum = numbers.stream()
    .reduce(0, (a, b) -> a + b);

// Group by
Map<Integer, List<String>> groupedByLength = names.stream()
    .collect(Collectors.groupingBy(String::length));

8. File I/O and Serialization

Working with files and data persistence:

import java.io.*;
import java.nio.file.*;

// Reading from file
public List<String> readLines(String filename) throws IOException {
    return Files.readAllLines(Paths.get(filename));
}

// Writing to file
public void writeLines(String filename, List<String> lines) throws IOException {
    Files.write(Paths.get(filename), lines);
}

// Serialization
public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    // Constructor, getters, setters...
}

// Save object
public void saveObject(Person person, String filename) throws IOException {
    try (ObjectOutputStream oos = new ObjectOutputStream(
            new FileOutputStream(filename))) {
        oos.writeObject(person);
    }
}

// Load object
public Person loadObject(String filename) throws IOException, ClassNotFoundException {
    try (ObjectInputStream ois = new ObjectInputStream(
            new FileInputStream(filename))) {
        return (Person) ois.readObject();
    }
}

9. GUI Development with Swing

Creating graphical user interfaces:

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class SimpleCalculator extends JFrame {
    private JTextField display;
    private double result = 0;
    private String operator = "=";
    private boolean startNewNumber = true;

    public SimpleCalculator() {
        setTitle("Calculator");
        setSize(300, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // Create display
        display = new JTextField("0");
        display.setEditable(false);
        display.setHorizontalAlignment(JTextField.RIGHT);
        add(display, BorderLayout.NORTH);

        // Create button panel
        JPanel buttonPanel = new JPanel(new GridLayout(4, 4));
        String[] buttons = {
            "7", "8", "9", "/",
            "4", "5", "6", "*",
            "1", "2", "3", "-",
            "0", ".", "=", "+"
        };

        for (String text : buttons) {
            JButton button = new JButton(text);
            button.addActionListener(new ButtonClickListener());
            buttonPanel.add(button);
        }

        add(buttonPanel, BorderLayout.CENTER);
    }

    private class ButtonClickListener implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            String command = e.getActionCommand();
            // Handle button click logic...
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            SimpleCalculator calc = new SimpleCalculator();
            calc.setVisible(true);
        });
    }
}

10. Unit Testing with JUnit

Test-driven development:

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;

public class CalculatorTest {
    private Calculator calculator;

    @BeforeEach
    public void setUp() {
        calculator = new Calculator();
    }

    @Test
    public void testAddition() {
        assertEquals(5, calculator.add(2, 3));
        assertEquals(-1, calculator.add(2, -3));
    }

    @Test
    public void testDivision() {
        assertEquals(2.0, calculator.divide(6, 3), 0.001);
    }

    @Test
    public void testDivisionByZero() {
        assertThrows(ArithmeticException.class, () -> {
            calculator.divide(5, 0);
        });
    }

    @AfterEach
    public void tearDown() {
        calculator = null;
    }
}

PART D: ANALYTICAL PART

Knowledge and Skills Mobilized

  • Understanding Java syntax and OOP principles
  • Implementing class hierarchies and interfaces
  • Using Collections Framework effectively
  • Writing exception-safe code
  • Developing multithreaded applications
  • Creating graphical user interfaces
  • Applying functional programming with lambdas and streams
  • Writing unit tests
  • Working with file I/O and serialization
  • Using Java development tools (IDE, debugger, profiler)

Self Evaluation

Learning Java after C and C++ was relatively smooth, as many concepts were familiar. However, Java's automatic memory management (garbage collection) was a significant paradigm shift. Not having to manually manage memory was liberating but also meant less control over performance.

The Collections Framework is one of Java's strongest features. Learning when to use ArrayList vs LinkedList, HashMap vs TreeMap, etc., required understanding time complexity trade-offs. The Stream API and lambda expressions made code more concise and readable.

Multithreading in Java was challenging. Understanding thread synchronization, deadlocks, and race conditions required careful thinking. The java.util.concurrent package provided powerful tools but had a steep learning curve.

GUI development with Swing was interesting but felt somewhat dated compared to modern UI frameworks. Event-driven programming required a different mindset. Building responsive UIs while avoiding freezing the main thread was a valuable lesson.

My Opinion

Java is an excellent language for learning OOP and building enterprise applications. Its "write once, run anywhere" philosophy is powerful, though in practice, platform differences can still cause issues. The extensive standard library and ecosystem of third-party libraries make Java very productive.

The course provided solid foundations in Java development. What I particularly appreciated:

  • Strong type system preventing many runtime errors
  • Comprehensive documentation and large community
  • Excellent tooling (IDEs, profilers, debuggers)
  • Platform independence

Areas for improvement:

  • More coverage of modern Java features (Java 11+)
  • Design patterns and architectural principles
  • Build tools (Maven, Gradle)
  • Spring Framework introduction
  • Android development basics

Java's verbosity compared to modern languages can be tedious (lots of boilerplate), but this is improving with newer Java versions. Overall, Java remains relevant and valuable for professional software development, particularly in enterprise and Android development.