GitHub - NiemaAM/OOP-java-tutorial: Full Java OOP tutorial!
Table of contents
- Introduction: What is OOP?
- Part 1: OOP core concepts
- Part 2: Collections & Generics
- Part 3: Conditions & Loops
- Part 4: Console Reading & Printing
- Part 5: Exceptions & Errors Handeling
- Part 6: Serilization & Deserilization
- Part 7: How to create a full project using java
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:
Part 1: OOP core concepts
Table of contents
- 1. What is a class?
- ๐งฐ 2. Encapsulation
- ๐ 3. Coherence
- ๐๏ธ 4. Information hiding
- ๐จโ๐ฆ 5. Inheritance
- ๐จโ๐ฆโ๐ฆ 6. Polymorphism
- ๐ 7. Abstraction
- ๐ 8. Coupling
- ๐ 9. Overloading
- ๐ 10. Shadowing
- ๐ 11. The Main Class
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.
- ๐งฌ Primitive: Specifies the size and type of variable:
- 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, orprotected. - 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).
- ๐ Methods should be verbs in mixed case with the first letter lowercase, and capitalize the first letter of each subsequent word (e.g.,
- 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:
Animalis a class, it has anameand araceand it caneat()andsleep().
+----------------------------+ | 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
Animalclass 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
Animalclass, thenameandraceattributes and the methodseat(),sleep()andmakeSound()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
nameattribute is private, and it can only be accessed or modified using the publicgetName()andsetName()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
Dogclass inherits from theAnimalclass, so it gets theeat()andsleep()methods, and it can also add its own methods, likebark().
+--------------------------------+ | 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
DogandCatclasses override themakeSound()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
Animalclass is abstract with an abstract methodmakeSound(). TheDogclass 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:
Animalis an interface with tree methods:makeSound(),eat()andsleep().
+--------------------------------+ +--------------------+ | 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:
AnimalandFoodare coupled, meaning thatAnimaldepends onFoodfor some behavior or data, in that caseAnimaleat(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
Additionclass has severaladd()methods with different parameters (intanddouble), 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 avaluevariable that shadows thevaluevariable declared in the OuterScope classAddition, 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
Mainclass demonstrates how to create instances of anAdditionclasse, call methodsadd(), and access attributevalue.
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
Table of contents
๐๏ธ 1. Collections:
๐ Array:
Collection of elements of the same datatypes with a fixed size that start at index 0.
Example:
MyArrayis 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:
MyArrayListis 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:
MyLinkedListis 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:
MyClassis a class with 2 Generic Objectsobject1of typeTandobject2of typeI.
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
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."); } } }
Another way to do a condition is using ? and : to simplify.
public class Main { public static void main(String[] args) { boolean condition = true; System.out.println("The condition is "+(condition ? "valid" : "non valid")); } }
๐ 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
Table of contents
โ๏ธ 1. Scanner:
A Scanner allows us to get user input from the keyboard.
Example:
Let's do a little survey to see who likes our Java course!
import java.util.Scanner; // import the Scanner from the java.util package. class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // create a new scanner String name = sc.nextLine(); // read a String int age = sc.nextInt(); // read an Integer double rate = sc.nextDouble(); // read a Double boolean like = sc.nextBoolean(); // read a Boolean sc.close(); // close the scanner } }
๐จ๏ธ 2. Printer:
A printer allows us to display values or data in the console.
Example:
Now we want to see the result of our survey...
import java.util.Scanner; // import the Scanner class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // create a new scanner // "System.out.print" print one line without a break System.out.print("What's your name? "); String name = sc.nextLine(); System.out.print("What's your age? "); int age = sc.nextInt(); System.out.print("Rate this java course out of 5: "); double rate = sc.nextDouble(); System.out.print("Do you like java? (true/false) "); boolean like = sc.nextBoolean(); sc.close(); // "System.out.println" print one line after a break System.out.println("Hello, " + name + "! ๐" + "\nYou are " + age + " years old ๐" + "\nYou rated this course " + rate + " out of 5 ๐คฉ" + "\nYou " + (like ? "like" : "do not like") + " Java โ"); } }