Skip to content

NiemaAM/OOP-java-tutorial

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 

Repository files navigation

Java

Table of contents

Introduction: What is OOP?

OOP stands for Object-Oriented Programming.

  • Procedural programming is about writing procedures or methods that perform operations on the data.
  • Object-oriented programming is about creating objects that contain both data and methods.

Source:

W3Schools

Part 1: OOP core concepts

Tip

Click Here to Run The Code of this part!

Table of contents

1. What is a class?

A class is a Blueprint or template that defines properties and behaviors.

Components of a class

Class Attributes (properties):

  • Can be:
    • static: not an instance of the class (global variable).
    • 🛑 final: constant variable.
  • Must have visibility:
    • 🌍 public: accessible for all classes.
    • 🔒 default (nothing): accessible in the same package.
    • 🔑 private: accessible within the declared class.
    • 🛡️ protected: accessible in the same package and subclasses.
  • Must have a datatype:
    • 🧬 Primitive: Specifies the size and type of variable:
      • boolean (1 bit), byte (1 byte), short & char (2 bytes), int & float (4 bytes), long & double (8 bytes).
    • 🔗 Reference: A pointer that holds the address to the object.
  • Have a name:
    • ✨ Always start with a lowercase letter and then capitalize the first letter of every subsequent word.
    • 💡 For constants (final), use uppercase with underscore (e.g., MAX_SIZE).

Class Constructor:

  • 🛠️ A special method used to initialize objects.
  • A class can have many constructors with different parameters (overloading).

Class Methods (behaviors):

  • Must have a visibility: A method can be public, package, private, or protected.
  • Must have a return type:
    • void (returns nothing) or returns a value (e.g., int, double, boolean, String, Object...).
  • Have a name:
    • 📝 Methods should be verbs in mixed case with the first letter lowercase, and capitalize the first letter of each subsequent word (e.g., calculateTotal).
  • May have parameters: The values passed to the method.
  • May have local variables: Temporary variables that exist only while a method or block runs.
  • Have a body: 🧠 The logic of the method.

Example:

Animal is a class, it has a name and a race and it can eat() and sleep().

+----------------------------+
|           Animal           |  <-- Class name
+----------------------------+
| + name: String             |  <-- Attributes (properties)
| + race: String             |
+----------------------------+
| + eat():void               |  <-- Methods (behaviors)
| + sleep():void             |
+----------------------------+
class Animal {
    // Class Attributes
    public String name;
    public String race;

    // Class Constructors
    public Animal() {} // default constructor

    public Animal(String name) {
        this.name = name; // 'this.name' refer to the instance variable
    }

    public Animal(String name, String race) {
        this.name = name; // 'this.name' refer to the instance variable
        this.race = race; // 'this.race' refer to the instance variable
    }

    // Class Methods
    public void eat() {
        System.out.println("Miam Miam!");
    }

    public void sleep() {
        System.out.println("ZZzz...");
    }
}

🧰 2. Encapsulation:

Putting attributes & behaviors (methods) of the same kind in one class.

Example:

The Animal class encapsulates attributes (name, race) and methods (eat(), sleep()) together within one class.

+----------------------------+
|           Animal           |
+----------------------------+
| + name: String             |  <-- Attributes (public)
| + race: String             |
+----------------------------+
| + eat():void               |  <-- Methods (public)
| + sleep():void             |
+----------------------------+

🌐 3. Coherence:

Intra module relationships within a class.

Example:

In the Animal class, the name and race attributes and the methods eat(), sleep() and makeSound() are all related and belong to the same class module, showing strong intra-module cohesion.

+--------------------------------+
|             Animal             |
+--------------------------------+
| + name: String                 |
| + race: String                 |
+--------------------------------+
| + eat():void                   |
| + sleep():void                 |
+--------------------------------+

👁️ 4. Information hiding:

Hide sensitive information from the user by:

  • declare class variables/attributes as private.
  • provide public getters and setters to access the data.

Example:

The name attribute is private, and it can only be accessed or modified using the public getName() and setName() methods.

+--------------------------------+
|         Class: Animal          |
+--------------------------------+
| - name: String                 |  <-- Attributes (private)
| - race: String                 |
+--------------------------------+
| + eat():void                   |  <-- Methods (public)
| + sleep():void                 |
| + getName():String             |  <-- Public getter (returns the name value)
| + setName(newName:String):void |  <-- Public setter (take the new name value as an attribute and update the name value)
+--------------------------------+
class Animal {
    // Class Attributes
    private String name;
    private String race;

    // Class Constructors
    public Animal() {}

    public Animal(String name) {
        this.name = name; // 'this.name' refer to the instance variable
    }

    public Animal(String name, String race) {
        this.name = name; // 'this.name' refer to the instance variable
        this.race = race; // 'this.race' refer to the instance variable
    }

    // Class Methods
    public void eat() {
        System.out.println("Miam Miam!");
    }

    public void sleep() {
        System.out.println("ZZzz...");
    }

    // Class Getters
    public String getName() {
        return name;
    }

    public String getRace() {
        return race;
    }

    // Class Setters
    public void setName(String newName) {
        this.name = newName;
    }

    public void setRace(String newRace) {
        this.race = newRace;
    }
}

👨‍👦 5. Inheritance:

Inheriting attributes and methods from one class to another.

  • superclass (parent): the class being inherited from.
  • subclass (child): the class that inherits from another class.

Example:

The Dog class inherits from the Animal class, so it gets the eat() and sleep() methods, and it can also add its own methods, like bark().

+--------------------------------+
|             Animal             |
+--------------------------------+
| - name: String                 |                   +--------------------+
| - race: String                 |                   |        Dog         |
+--------------------------------+ <──────────────── +--------------------+
| + eat():void                   |                   | + bark():void      |
| + sleep():void                 |                   +--------------------+
| + getName():String             |
| + setName(newName:String):void |
+--------------------------------+
class Dog extends Animal {
    // Class constructor
    public Dog(String name, String race){
      super(name,race); // call the Animal super class constructor
    }
    public void bark() {
        System.out.println("WOUF!");
    }
}

👨‍👦‍👦 6. Polymorphism:

Many classes related to each other by inheritance.

  • Overriding: Two methods have the same name and same parameters.

Example:

Both Dog and Cat classes override the makeSound() method, but each class implements it differently. The type of object determines which version of the method is called.

+--------------------------------+
|             Animal             |                   +--------------------+
+--------------------------------+                   |        Dog         |
| - name: String                 | <──────────────── +--------------------+
| - race: String                 |                   | + makeSound():void | <---- makeSound print "WOUF!"
+--------------------------------+                   +--------------------+
| + eat():void                   |                   +--------------------+
| + sleep():void                 |                   |        Cat         |
| + makeSound():void             | <──────────────── +--------------------+
| + getName():String             |                   | + makeSound():void | <---- makeSound print "MEOW!"
| + setName(newName:String):void |                   +--------------------+
+--------------------------------+
class Animal {
    public void makeSound() {
        System.out.println("Say something...");
    }
}
class Dog extends Animal {
    // Class constructor
    public Dog(String name, String race){
      super(name,race); // call the Animal super class constructor
    }
    @Override
    public void makeSound() {
        System.out.println(super.getName() + " the " + super.getRace() + ": WOUF!");
    }
}
class Cat extends Animal {
    // Class constructor
    public Cat(String name, String race){
      super(name,race); // call the Animal super class constructor
    }
    @Override
    public void makeSound() {
        System.out.println(super.getName() + " the " + super.getRace() + ": MEOW!");
    }
}

🗔 7. Abstraction:

Hiding certain details by:

  • creating abstract classes: A class that cannot be instantiated and may have both abstract (unimplemented) and concrete (implemented) methods.

Example:

The Animal class is abstract with an abstract method makeSound(). The Dog class implements this method.

+--------------------------------+                   +--------------------+
|             Animal             |                   |        Dog         |
+--------------------------------+                   +--------------------+
| + eat():void                   | <---------------- | + makeSound():void | <-- Concrete implementation
| + sleep():void                 |                   +--------------------+
| + makeSound():void             | <-- Abstract method
+--------------------------------+
abstract class Animal {
    // Concrete (implemented) methods
    public void eat() {
        System.out.println("Miam Miam!");
    }
    public void sleep() {
        System.out.println("ZZzz...");
    }
    // Abstract (unimplemented) method
    public abstract void makeSound();
}
public class Dog extends Animal {
    // Concrete implementation
    public void makeSound() {
        System.out.println("WOUF!");
    }
}
  • creating interfaces: An interface is a contract that defines a set of abstract methods without providing any implementation.

Example:

Animal is an interface with tree methods: makeSound(), eat() and sleep().

+--------------------------------+                   +--------------------+
|        interface: Animal       |                   |        Dog         | <-- Dog implement Animale interface
+--------------------------------+                   +--------------------+
| + eat():void                   | <---------------- | + eat():void       | <-- Concrete implementations
| + sleep():void                 |                   | + steep():void     |
| + makeSound():void             |                   | + makeSound():void |
+--------------------------------+                   +--------------------+
interface Animal {
    // Abstract (unimplemented) method
    public void eat();
    public void sleep();
    public abstract void makeSound();
}
public class Dog implements Animal {
    // Concrete implementation
    public void eat() {
        System.out.println("Miam Miam!");
    }
    public void sleep() {
        System.out.println("ZZzz...");
    }
    public void makeSound() {
        System.out.println("WOUF!");
    }
}

🔗 8. Coupling:

Inter module relationships between classes (one class depends on the other).

Example:

Animal and Food are coupled, meaning that Animal depends on Food for some behavior or data, in that case Animal eat(something:Food), creating a relationship between them.

+--------------------------------+
|             Animal             |
+--------------------------------+
| - name: String                 |                   +--------------------+
| - race: String                 |                   |        Food        |
+--------------------------------+ _________________ +--------------------+
| + eat(something:Food):void     |                   | + type: String     |
| + sleep():void                 |                   | + qantity: int     |
| + makeSound():void             |                   +--------------------+
+--------------------------------+
class Food {
    private String type;
    private int quantity;
    // Class constructor
    public Food(String type, int quantity){
      this.type = type;
      this.quantity = quantity;
    }
    // Class Getters
    public String getType() {
        return type;
    }
    public int getQuantity() {
        return quantity;
    }
}
public class Animal {
    public void eat(Food something) {
        System.out.println(name + " the " + race + ": Miam Miam! *eats "+something.getQuantity()+" "+something.getType()+"*");
    }
}

🗍 9. Overloading:

Two or more methods in the same class have the same name but different parameters (signature).

Example:

The Addition class has several add() methods with different parameters (int and double), which allows different combinations of input.

+------------------------------+
|           Addition           |
+------------------------------+
| + add(int, int):int          |
| + add(double, double):double |
| + add(int, int, int):int     |
+------------------------------+
class Addition {
    public int add(int nbr1, int nbr2) {
        return nbr1 + nbr2;
    }
    public double add(double nbr1, double nbr2) {
        return nbr1 + nbr2;
    }
    public int add(int nbr1, int nbr2, int nbr3) {
        return nbr1 + nbr2 + nbr3;
    }
}

🗇 10. Shadowing:

Variable declared within a specific scope has the same name as a variable declared in an outer scope.

Example:

The InnerScope method add() has a value variable that shadows the value variable declared in the OuterScope class Addition, meaning the InnerScope version takes precedence within its scope.

class Addition {
    public int value = 5;  // Class-level variable
    public int add(int nbr1, int nbr2) {
        int value = nbr1 + nbr2;  // Method variable, shadows the class-level variable
        return value;
    }
}

🏃 11. the Main Class:

The Main class typically contains the entry point to a Java program, the main() method. This is where the program starts executing.

Example:

The Main class demonstrates how to create instances of an Addition classe, call methods add(), and access attribute value.

public class Main {
    public static void main(String[] args) {
      Addition addition = new Addition();
      System.out.println(addition.add(1,2));
      System.out.println(addition.value);
      System.out.println(addition.add(1.5,2.5));
      System.out.println(addition.add(1,2,3));
    }
}

Part 2: Collections & Generics

Tip

Click Here to Run The Code of this part!

Table of contents

🗂️ 1. Collections:

📚 Array:

Collection of elements of the same datatypes with a fixed size that start at index 0.

Example:

MyArray is a collection of 3 integers.

import java.util.Arrays; // Import the Arrays class
public class Main {
    public static void main(String[] args) {
      int[] MyArray = new int[3]; // Create an array with 3 elements
      MyArray[0] = 1; // Set the first element to 1
      MyArray[1] = 2; // Set the second element to 2
      MyArray[2] = 3; // Set the third element to 3
      System.out.println("The Array contains: " + MyArray.length + " elements"); // Access the size of the array
      System.out.println("Element at index 0: " + MyArray[0]); // Access the first element
      System.out.println("Element at index 1: " + MyArray[1]); // Access the second element
      System.out.println("Element at index 2: " + MyArray[2]); // Access the third element
    }
}
+---------+---------+---------+
| Index 0 | Index 1 | Index 2 |
+---------+---------+---------+
|    1    |    2    |    3    |
+---------+---------+---------+

📃 ArrayList:

Attribute ArrayList
Structure A resizable array implementation in the java.util package.
Storage Elements are stored in a contiguous block of memory, like an array.
Access Time Fast random access using an index.
Insertions & Deletions Slower for operations in the middle or beginning of the list because elements need to be shifted.
Use Case Good for scenarios where you need fast access to elements but don't frequently insert/remove items in the middle.

Example:

MyArrayList is a collection of integers with dynamic sizing.

import java.util.ArrayList; // Import the ArrayList class
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
      // Initialize an ArrayList with elements using Arrays.asList()
      ArrayList<Integer> myArrayList = new ArrayList<>(Arrays.asList(1, 2, 3));
      // Print the contents of the ArrayList
      System.out.println(myArrayList); // Output: [1, 2, 3]
    }
}

🔗 LinkedList:

Attribute LinkedList
Structure A doubly linked list implementation in the java.util package.
Storage Elements are stored in nodes, each containing a data element and pointers to the next and previous nodes.
Access Time Slower random access because you have to traverse from the start or end.
Insertions & Deletions Faster insertions and deletions since it only requires updating node references.
Use Case Suitable when you need frequent insertions and deletions, especially in the middle of the list.

Example:

MyLinkedList is a collection of integers that supports efficient insertions and deletions.

import java.util.LinkedList; // Import the LinkedList class
public class Main {
    public static void main(String[] args) {  
      LinkedList<Integer> myLinkedList = new LinkedList<>(); // Create a LinkedList of integers
      myLinkedList.add(1); // Add 1 to the LinkedList
      myLinkedList.add(2); // Add 2 to the LinkedList
      myLinkedList.add(3); // Add 3 to the LinkedList
      System.out.println("The LinkedList contains: " + myLinkedList.size() + " elements");
      System.out.println("Element at index 0: " + myLinkedList.get(0)); // Access the first element
      System.out.println("Element at index 1: " + myLinkedList.get(1)); // Access the second element
      System.out.println("Element at index 2: " + myLinkedList.get(2)); // Access the third element
      myLinkedList.remove(1); // Remove the element at index 1
      myLinkedList.set(1, 2); // Update the new element at index 1 to 2 instead of 3
      System.out.println(myLinkedList); // Output: [1, 2]
    }
}
+---------+---------+---------+
| Index 0 | Index 1 | Index 2 |
+---------+---------+---------+
|    1    |    2    |    3    |
+---------+---------+---------+
# after removing index 1
+---------+---------+
| Index 0 | Index 1 |
+---------+---------+
|    1    |    3    |
+---------+---------+
# after updating index 1 to 2
+---------+---------+
| Index 0 | Index 1 |
+---------+---------+
|    1    |    2    |
+---------+---------+

🔄 Iterator:

Attribute Iterator
Structure An interface that provides a way to traverse a collection sequentially without exposing the underlying structure.
Methods hasNext(): Checks if there are more elements in the collection.
next(): Returns the next element in the collection.
remove(): Removes the current element from the collection (optional operation).
Use Case Useful when you need to iterate through a collection, such as a List, Set, or Map.

Example:

Using an Iterator to traverse a LinkedList.

import java.util.LinkedList; // Import the LinkedList class
import java.util.Iterator;  // Import the Iterator interface
public class Main {
    public static void main(String[] args) {  
      LinkedList<Integer> myLinkedList = new LinkedList<>(); // Create a LinkedList of integers
      Iterator<Integer> iterator = myLinkedList.iterator(); // Get an iterator for the LinkedList
      while (iterator.hasNext()) {
        System.out.println(iterator.next()); // Print each element
      }
    }
}

📥 Queue:

Attribute Queue
Structure An interface that represents a First-In-First-Out (FIFO) data structure.
Methods add() / offer(): Add elements to the end of the queue.
remove() / poll(): Remove elements from the front of the queue.
peek(): Retrieve the element at the front without removing it.
Use Case Ideal for scenarios where you need to manage elements in the order they are processed, like task scheduling, handling requests, or buffering.

Example:

Using a Queue to handle tasks.

import java.util.LinkedList; // Import the LinkedList class
import java.util.Queue; // Import the Queue interface
public class Main {
    public static void main(String[] args) {  
      Queue<Integer> taskQueue = new LinkedList<>(); // Create a Queue using LinkedList
      taskQueue.add(1); // Add task 1 to the Queue
      taskQueue.add(2); // Add task 2 to the Queue
      taskQueue.add(3); // Add task 3 to the Queue
      System.out.println("The Queue contains: " + taskQueue.size() + " tasks");
      System.out.println("Processing task: " + taskQueue.poll()); // Remove and process the first task
      System.out.println("Next task to process: " + taskQueue.peek()); // Peek the next task without removing it
    }
}

🔣 2. Generics:

Objects or functions in a Generic class can be generalized with any datatype.

Example:

MyClass is a class with 2 Generic Objects object1 of type T and object2 of type I.

class MyClass <T, I> {
  private T object1; // Generic Object
  private I object2;
  // Constructor
  public MyClass(T object1, I object2){
    this.object1 = object1;
    this.object2 = object2;
  }
  // Getters
  public T getT(){
    return this.object1;
  }
  public I getI(){
    return this.object2;
  }
  // Setters
  public void setT(T object1){
    this.object1 = object1;
  }
  public void setI(T objec2){
    this.object2 = object2;
  }
}
  public class Main {
    public static void main(String[] args) {
      MyClass<Double, String> myClass1 = new MyClass<>(20.5, "object2");
      MyClass<String, Integer> myClass2 = new MyClass<>("object1", 20);
      System.out.println("Class1 Object1: " + myClass1.getT()); // output: Class1 Object1: 20.5
      System.out.println("Class1 Object2: " + myClass1.getI()); // output: Class1 Object1: object2
      System.out.println("Class2 Object1: " + myClass2.getT()); // output: Class1 Object1: object1
      System.out.println("Class2 Object2: " + myClass2.getI()); // output: Class1 Object1: 20
  }

Part 3: Conditions & Loops

Tip

Click Here to Run The Code of this part!

Table of contents

ℹ️ 1. Conditions:

↔️ Comparators:

  • == : to compare if two values are equal.
  • != : to compare if two values are not equal.
  • object1.equals(object2) : to compare objects.
  • && : AND comparator.
  • || : OR comparator.

➡️ if:

Example:

public class Main {
    public static void main(String[] args) {
        int number = 10;
        if (number >= 10) {
            System.out.println("The number is greater than 10.");
        } else if (number < 10 && number > 5) {
            System.out.println("The number is between 5 and 10.");
        } else {
            System.out.println("The number is 5 less.");
        }
    }
}

🔀 switch case:

Example:

public class Main {
    public static void main(String[] args) {
        int day = 3;
        switch (day) {
            case 1:
                System.out.println("Monday");
                break;
            case 2:
                System.out.println("Tuesday");
                break;
            case 3:
                System.out.println("Wednesday");
                break;
            default:
                System.out.println("Another day");
        }
    }
}

🔄 2. Loops:

🔁 for:

Get and set data.

Example:

import java.util.ArrayList;
public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> myArrayList = new ArrayList<>();
        // Initialize an ArrayList
        for (int nbr = 1; nbr <= 10; nbr++){
            myArrayList.add(nbr);
        }
        // Print the contents of the ArrayList
        for (int i = 0; i < myArrayList.size(); i++){
            System.out.println(myArrayList.get(i));
        }
    }
}

↪️ for each:

Get data only.

Example:

import java.util.ArrayList;
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> myArrayList = new ArrayList<>(Arrays.asList(1, 2, 3));
        // Print the contents of the ArrayList
        for (int element : myArrayList){
            System.out.println(element);
        }
    }
}

🔃 while:

Cannot or can be executed.

Example:

public class Main {
    public static void main(String[] args) {
        int counter = 0;
        while (counter < 3) {
            System.out.println("Counter: " + counter);
            counter++;
        }
    }
}

🔃 do while:

Execute at least once.

Example:

public class Main {
    public static void main(String[] args) {
        int counter = 0;
        do {
            System.out.println("Counter: " + counter);
            counter++;
        } while (counter < 3);
    }
}

Part 4: Console Reading & Printing

Tip

Click Here to Run The Code of this part!

Table of contents

✍️ 1. Scanner:

🖨️ 2. Printer:

Part 5: Exceptions & Errors Handeling

Tip

Click Here to Run The Code of this part!

Table of contents

⚠️ 1. Exceptions:

🛑 2. Exceptions Handeling:

Part 6: Serilization & Deserilization

Tip

Click Here to Run The Code of this part!

Table of contents

💾 1. Serilization:

📄 2. Deserilization:

Part 7: How to create a full project using java

Tip

Java

Table of contents

📄 1. Project Structure:

🖥 2. User Interface:

💾 3. Database: