Learning Object-Oriented Programming in Java
- Reduced Complexity
- Easier Maintenance
- Code Reuse
- Faster Development
Process of defining a problem, identifying and comparing different solutions, and picking the one that best solves that problem with respect to the context and constraints.
- Procedural
- Functional
- Object-oriented
- Event-driven
- Logic
- Aspect-oriented
A class is a blueprint or template for creating an object.
An object is an instance of a class.
Instantiating means creating an instance of a class.
Stack memory is used for storing primitive types and variables that store reference to objects in heap.
Variables stored in the stack are immediately cleared when they go out of scope (e.g. when a method finishes execution). Objects stored in the heap get removed later on when they’re no longer references. This is done by Java’s garbage collector.
A programming paradigm that uses a linear or top-down approach.
Numeric value that is calculated based on the address of the object in memory.
Big classes with several unrelated methods focusing on different concerns and responsibilities. These methods often have several parameters. You often see the same group of parameters repeated across these methods. All you see is procedures calling each other passing arguments around.
By applying object-oriented programming techniques, we extract these repetitive parameters and declare them as fields in our classes. Our classes will then encapsulate both the data and the operations on the data (methods). As a result, our methods will have fewer parameters and our code will be cleaner and more reusable.
-
Instance members
- Fields and method that belongs to an object (instance of a class)
-
Static members
- Fields and method that belongs to a class. We use Static Members to represent a concept in a single place.
First principle of OOP, and suggests to bundle the data and methods that operate on the data inside a single unit(class).
Reduce complexity by hiding unnecessary details
The level of dependency between classes
⚠️ Always reduce coupling
Constructors are called when we instantiate our class. We use them to initialize our objects.
A method that is implemented multiple times. Method overloading means declaring a method with the same name but with different signatures. Example of usage is setting a default value:
public int calculateWage(int extraHours) {
return getBaseSalary() + (getHourlyRate() * extraHours);
}
// Here we are calling the calculateWage method to pass a default argument
public int calculateWage() {
return calculateWage(0);
}
There are two ways to overload a constructor:
// First: By calling the getters
public Employee(int baseSalary) {
setBaseSalary(baseSalary);
setHourlyRate(0);
}
// By reusing the logic from original constructor
public Employee(int baseSalary) {
this(baseSalary, 0);
}
// Here is the original constructor
public Employee(int baseSalary, int hourlyRate) {
this.setBaseSalary(baseSalary);
this.setHourlyRate(hourlyRate);
}
A mechanism that allows clas to inherit properties and behaviour from another class.
Modifying inherited method from a base class
A label attached to a class member, to give extra information to a compiler.
Casting an object to one of its super types
Casting an object one of its sub types
public class Main {
public static void main(String[] args) {
// TextBox inherits from UIControl
var control = new UIControl(true);
var textBox = new TextBox();
// Upcasting -> TextBox automatically cast as UIControl
show(textBox);
}
public static void show(UIControl control) {
// At runtime, you only have access to UIControl: control object (Properties and methods)
// At compile time you don't have access to TextBox object
if (control instanceof TextBox) {
// We need to implement Downcasting to access properties and methods of TextBox in complile time
// Downcasting -> Explicitly cast object to different type
// In this case casting control object type from UIControl to TextBox
var textBox = (TextBox)control;
textBox.setText("Downcasting");
}
System.out.println(control);
}
}
A mechanism that allows an object to take many forms and behave differently. This will help us build extensible applications.
Prevent instantiating of a class or method
public abstract class UIControl {
public abstract void render();
}
A type similar to a class, but it only includes method declarations and no implementation. It only defines capabilities a class should have.
Interface defines WHAT should be done and Classes defines HOW it should be done.
This method removes class dependencies/ tight coupling and avoid re-compiling and re-deploy.
Classes should not instantiate their dependencies
public class TaxReport {
public TaxCalculator calculator;
public TaxReport(TaxCalculator calculator) {
this.calculator = calculator;
}
public void show() {
var tax = calculator.calculcateTax();
System.out.println(tax);
}
}
public class TaxReport {
public TaxCalculator calculator;
public void show() {
var tax = calculator.calculcateTax();
System.out.println(tax);
}
public void setCalculator(TaxCalculator calculator) {
this.calculator = calculator;
}
}
public class TaxReport {
public void show(TaxCalculator calculator) {
System.out.println(calculator.calculcateTax());
}
}
Principle of dividing big interfaces into smaller ones.
Here's an example:
public interface UIWidget {
void drag();
void resize();
void render();
}
Here we can extract each method into its own interface and let UIWidget inherits them all. See example below:
public interface UIWidget
extends Draggable, Resizable, Renderable {
}